OpenAPI Support (#2560)
* Start work on OpenAPI * Fixes * Work on OpenAPI * Cleanup * Cleanup * More swagger work * Build fix * More work * More work * Add documentation * Docs fixes * Add changelog * License updates * Add API * Cleanup * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/openapi.md Co-authored-by: patrick-vachon-smilecdr <81274188+patrick-vachon-smilecdr@users.noreply.github.com> * Work on scripts * Add docs * Compile fix * Work on fixes * Fix tests * Test fix * Test fix * Build fix * Tests * Build fix * Work on pipeline * Version bump * Test fix * Version bump * Test fix attempts * Test fix * Test fix * Remove accidentally committed files * Fixes * Tets fixes * Test fix * Test fix * Test fix * Test fixes * Test fixes * Test fixes * License header updates * test fix * Test fixes * Test fix * Test fixes * Test fix * Checkstyle bump Co-authored-by: patrick-vachon-smilecdr <81274188+patrick-vachon-smilecdr@users.noreply.github.com>
This commit is contained in:
parent
f842c234eb
commit
2ba1005762
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.context;
|
|||
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -60,7 +61,7 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
|
|||
myElementName = theElementName;
|
||||
if (theDescriptionAnnotation != null) {
|
||||
myShortDefinition = theDescriptionAnnotation.shortDefinition();
|
||||
myFormalDefinition = theDescriptionAnnotation.formalDefinition();
|
||||
myFormalDefinition = ParametersUtil.extractDescription(theDescriptionAnnotation);
|
||||
} else {
|
||||
myShortDefinition = null;
|
||||
myFormalDefinition = null;
|
||||
|
|
|
@ -30,17 +30,33 @@ import java.lang.annotation.Target;
|
|||
* a search parameter definition in order to provide documentation for that item.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value= {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Target(value = {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
|
||||
public @interface Description {
|
||||
|
||||
/**
|
||||
* Optional short name for this child
|
||||
* A description of this method or parameter
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Optional short name for this child
|
||||
*/
|
||||
String shortDefinition() default "";
|
||||
|
||||
|
||||
/**
|
||||
* Optional formal definition for this child
|
||||
*
|
||||
* @deprecated Use {@link #value()} instead. Deprecated in 5.4.0.
|
||||
*/
|
||||
@Deprecated
|
||||
String formalDefinition() default "";
|
||||
|
||||
|
||||
/**
|
||||
* May be used to supply example values for this
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String[] example() default {};
|
||||
}
|
||||
|
|
|
@ -78,4 +78,13 @@ public @interface AddTags {
|
|||
*/
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
}
|
||||
|
|
|
@ -45,6 +45,15 @@ public @interface Create {
|
|||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
}
|
||||
|
|
|
@ -47,5 +47,16 @@ public @interface Delete {
|
|||
* for client implementations.
|
||||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
}
|
||||
|
|
|
@ -74,4 +74,14 @@ public @interface DeleteTags {
|
|||
*/
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
}
|
||||
|
|
|
@ -80,5 +80,15 @@ public @interface History {
|
|||
* for information on usage patterns.
|
||||
*/
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
}
|
||||
|
|
|
@ -132,7 +132,10 @@ public @interface Operation {
|
|||
|
||||
/**
|
||||
* If this is set to <code>true</code>, this method will be a <b>global operation</b>
|
||||
* meaning that it applies to all resource types
|
||||
* meaning that it applies to all resource types. Operations with this flag set should be
|
||||
* placed in Plain Providers (i.e. they don't need to be placed in a resource-type-specific
|
||||
* <code>IResourceProvider</code> instance) and should have a parameter annotated with
|
||||
* {@link IdParam}.
|
||||
*/
|
||||
boolean global() default false;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public @interface OperationParam {
|
|||
/**
|
||||
* Value for {@link OperationParam#max()} indicating no maximum
|
||||
*/
|
||||
final int MAX_UNLIMITED = -1;
|
||||
int MAX_UNLIMITED = -1;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -57,7 +57,7 @@ public @interface OperationParam {
|
|||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final int MAX_DEFAULT = -2;
|
||||
int MAX_DEFAULT = -2;
|
||||
|
||||
/**
|
||||
* The name of the parameter
|
||||
|
|
|
@ -50,4 +50,13 @@ public @interface Patch {
|
|||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
}
|
||||
|
|
|
@ -78,6 +78,16 @@ public @interface Search {
|
|||
// NB: Read, Search (maybe others) share this annotation method, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
/**
|
||||
* In a REST server, should this method be invoked even if it does not have method parameters
|
||||
* which correspond to all of the URL parameters passed in by the client (default is <code>false</code>).
|
||||
|
@ -91,4 +101,5 @@ public @interface Search {
|
|||
* </p>
|
||||
*/
|
||||
boolean allowUnknownParams() default false;
|
||||
|
||||
}
|
||||
|
|
|
@ -44,9 +44,19 @@ public @interface Update {
|
|||
* The return type for this search method. This generally does not need
|
||||
* to be populated for a server implementation, since servers will return
|
||||
* only one resource per class, but generally does need to be populated
|
||||
* for client implementations.
|
||||
* for client implementations.
|
||||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IResource> type() default IResource.class;
|
||||
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
* <p>
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
}
|
||||
|
|
|
@ -52,7 +52,17 @@ public @interface Validate {
|
|||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
|
||||
/**
|
||||
* This method allows the return type for this method to be specified in a
|
||||
* non-type-specific way, using the text name of the resource, e.g. "Patient".
|
||||
*
|
||||
* This attribute should be populate, or {@link #type()} should be, but not both.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
String typeName() default "";
|
||||
|
||||
/**
|
||||
* Validation mode parameter annotation for the validation mode parameter (only supported
|
||||
* in FHIR DSTU2+). Parameter must be of type {@link ValidationModeEnum}.
|
||||
|
|
|
@ -36,6 +36,13 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public class ExtensionUtil {
|
||||
|
||||
/**
|
||||
* Non instantiable
|
||||
*/
|
||||
private ExtensionUtil() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an extension with the specified URL creating one if it doesn't exist.
|
||||
*
|
||||
|
@ -46,7 +53,7 @@ public class ExtensionUtil {
|
|||
*/
|
||||
public static IBaseExtension<?, ?> getOrCreateExtension(IBase theBase, String theUrl) {
|
||||
IBaseHasExtensions baseHasExtensions = validateExtensionSupport(theBase);
|
||||
IBaseExtension extension = getExtensionByUrl(baseHasExtensions, theUrl);
|
||||
IBaseExtension<?,?> extension = getExtensionByUrl(baseHasExtensions, theUrl);
|
||||
if (extension == null) {
|
||||
extension = baseHasExtensions.addExtension();
|
||||
extension.setUrl(theUrl);
|
||||
|
@ -75,13 +82,27 @@ public class ExtensionUtil {
|
|||
*/
|
||||
public static IBaseExtension<?, ?> addExtension(IBase theBase, String theUrl) {
|
||||
IBaseHasExtensions baseHasExtensions = validateExtensionSupport(theBase);
|
||||
IBaseExtension extension = baseHasExtensions.addExtension();
|
||||
IBaseExtension<?,?> extension = baseHasExtensions.addExtension();
|
||||
if (theUrl != null) {
|
||||
extension.setUrl(theUrl);
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extension with the specified value
|
||||
*
|
||||
* @param theBase The resource to update extension on
|
||||
* @param theUrl Extension URL
|
||||
* @param theValueType Type of the value to set in the extension
|
||||
* @param theValue Extension value
|
||||
* @param theFhirContext The context containing FHIR resource definitions
|
||||
*/
|
||||
public static void addExtension(FhirContext theFhirContext, IBase theBase, String theUrl, String theValueType, Object theValue) {
|
||||
IBaseExtension<?,?> ext = addExtension(theBase, theUrl);
|
||||
setExtension(theFhirContext, ext, theValueType, theValue);
|
||||
}
|
||||
|
||||
private static IBaseHasExtensions validateExtensionSupport(IBase theBase) {
|
||||
if (!(theBase instanceof IBaseHasExtensions)) {
|
||||
throw new IllegalArgumentException(String.format("Expected instance that supports extensions, but got %s", theBase));
|
||||
|
@ -118,7 +139,7 @@ public class ExtensionUtil {
|
|||
if (!hasExtension(theBase, theExtensionUrl)) {
|
||||
return false;
|
||||
}
|
||||
IBaseDatatype value = getExtensionByUrl((IBaseHasExtensions) theBase, theExtensionUrl).getValue();
|
||||
IBaseDatatype value = getExtensionByUrl(theBase, theExtensionUrl).getValue();
|
||||
if (value == null) {
|
||||
return theExtensionValue == null;
|
||||
}
|
||||
|
@ -133,7 +154,7 @@ public class ExtensionUtil {
|
|||
* @return Returns the first available extension with the specified URL, or null if such extension doesn't exist
|
||||
*/
|
||||
public static IBaseExtension<?, ?> getExtensionByUrl(IBase theBase, String theExtensionUrl) {
|
||||
Predicate<IBaseExtension> filter;
|
||||
Predicate<IBaseExtension<?,?>> filter;
|
||||
if (theExtensionUrl == null) {
|
||||
filter = (e -> true);
|
||||
} else {
|
||||
|
@ -153,7 +174,7 @@ public class ExtensionUtil {
|
|||
* @param theFilter Predicate to match the extension against
|
||||
* @return Returns all extension with the specified URL, or an empty list if such extensions do not exist
|
||||
*/
|
||||
public static List<IBaseExtension<?, ?>> getExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension> theFilter) {
|
||||
public static List<IBaseExtension<?, ?>> getExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension<?,?>> theFilter) {
|
||||
return validateExtensionSupport(theBase)
|
||||
.getExtension()
|
||||
.stream()
|
||||
|
@ -189,7 +210,7 @@ public class ExtensionUtil {
|
|||
* @param theFilter Defines which extensions should be cleared
|
||||
* @return Returns all extension that were removed
|
||||
*/
|
||||
private static List<IBaseExtension<?, ?>> clearExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension> theFilter) {
|
||||
private static List<IBaseExtension<?, ?>> clearExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension<?,?>> theFilter) {
|
||||
List<IBaseExtension<?, ?>> retVal = getExtensionsMatchingPredicate(theBase, theFilter);
|
||||
validateExtensionSupport(theBase)
|
||||
.getExtension()
|
||||
|
@ -205,7 +226,7 @@ public class ExtensionUtil {
|
|||
* @return Returns all extension with the specified URL, or an empty list if such extensions do not exist
|
||||
*/
|
||||
public static List<IBaseExtension<?, ?>> getExtensionsByUrl(IBaseHasExtensions theBase, String theExtensionUrl) {
|
||||
Predicate<IBaseExtension> urlEqualityPredicate = e -> theExtensionUrl.equals(e.getUrl());
|
||||
Predicate<IBaseExtension<?,?>> urlEqualityPredicate = e -> theExtensionUrl.equals(e.getUrl());
|
||||
return getExtensionsMatchingPredicate(theBase, urlEqualityPredicate);
|
||||
}
|
||||
|
||||
|
@ -216,8 +237,8 @@ public class ExtensionUtil {
|
|||
* @param theValue The value to set
|
||||
* @param theFhirContext The context containing FHIR resource definitions
|
||||
*/
|
||||
public static void setExtension(FhirContext theFhirContext, IBaseExtension theExtension, String theValue) {
|
||||
setExtension(theFhirContext, theExtension, "string", (Object) theValue);
|
||||
public static void setExtension(FhirContext theFhirContext, IBaseExtension<?,?> theExtension, String theValue) {
|
||||
setExtension(theFhirContext, theExtension, "string", theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,7 +249,7 @@ public class ExtensionUtil {
|
|||
* @param theValue The value to set
|
||||
* @param theFhirContext The context containing FHIR resource definitions
|
||||
*/
|
||||
public static void setExtension(FhirContext theFhirContext, IBaseExtension theExtension, String theExtensionType, Object theValue) {
|
||||
public static void setExtension(FhirContext theFhirContext, IBaseExtension<?,?> theExtension, String theExtensionType, Object theValue) {
|
||||
theExtension.setValue(TerserUtil.newElement(theFhirContext, theExtensionType, theValue));
|
||||
}
|
||||
|
||||
|
@ -241,7 +262,7 @@ public class ExtensionUtil {
|
|||
* @param theFhirContext The context containing FHIR resource definitions
|
||||
*/
|
||||
public static void setExtensionAsString(FhirContext theFhirContext, IBase theBase, String theUrl, String theValue) {
|
||||
IBaseExtension ext = getOrCreateExtension(theBase, theUrl);
|
||||
IBaseExtension<?,?> ext = getOrCreateExtension(theBase, theUrl);
|
||||
setExtension(theFhirContext, ext, theValue);
|
||||
}
|
||||
|
||||
|
@ -255,7 +276,7 @@ public class ExtensionUtil {
|
|||
* @param theFhirContext The context containing FHIR resource definitions
|
||||
*/
|
||||
public static void setExtension(FhirContext theFhirContext, IBase theBase, String theUrl, String theValueType, Object theValue) {
|
||||
IBaseExtension ext = getOrCreateExtension(theBase, theUrl);
|
||||
IBaseExtension<?,?> ext = getOrCreateExtension(theBase, theUrl);
|
||||
setExtension(theFhirContext, ext, theValueType, theValue);
|
||||
}
|
||||
|
||||
|
@ -266,7 +287,7 @@ public class ExtensionUtil {
|
|||
* @param theRightExtension : Extension to be evaluated #2
|
||||
* @return Result of the comparison
|
||||
*/
|
||||
public static boolean equals(IBaseExtension theLeftExtension, IBaseExtension theRightExtension) {
|
||||
public static boolean equals(IBaseExtension<?,?> theLeftExtension, IBaseExtension<?,?> theRightExtension) {
|
||||
return TerserUtil.equals(theLeftExtension, theRightExtension);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,13 @@ public class HapiExtensions {
|
|||
*/
|
||||
public static final String ASSOCIATED_GOLDEN_RESOURCE_EXTENSION_URL = "https://hapifhir.org/associated-patient-golden-resource/";
|
||||
|
||||
/**
|
||||
/**
|
||||
* This extension provides an example value for a parameter value for
|
||||
* a REST operation (eg for an OperationDefinition)
|
||||
*/
|
||||
public static final String EXT_OP_PARAMETER_EXAMPLE_VALUE = "http://hapifhir.io/fhir/StructureDefinition/op-parameter-example-value";
|
||||
|
||||
/**
|
||||
* Non instantiable
|
||||
*/
|
||||
private HapiExtensions() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
|||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -34,8 +35,13 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -43,6 +49,7 @@ import java.util.function.Function;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with parameters resources in a version indepenedent way
|
||||
|
@ -418,4 +425,60 @@ public class ParametersUtil {
|
|||
.findFirst();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String extractDescription(AnnotatedElement theType) {
|
||||
Description description = theType.getAnnotation(Description.class);
|
||||
if (description != null) {
|
||||
return extractDescription(description);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String extractDescription(Description desc) {
|
||||
String description = desc.value();
|
||||
if (isBlank(description)) {
|
||||
description = desc.formalDefinition();
|
||||
}
|
||||
if (isBlank(description)) {
|
||||
description = desc.shortDefinition();
|
||||
}
|
||||
return defaultIfBlank(description, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String extractShortDefinition(AnnotatedElement theType) {
|
||||
Description description = theType.getAnnotation(Description.class);
|
||||
if (description != null) {
|
||||
return defaultIfBlank(description.shortDefinition(), null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String extractDescription(Annotation[] theParameterAnnotations) {
|
||||
for (Annotation next : theParameterAnnotations) {
|
||||
if (next instanceof Description) {
|
||||
return extractDescription((Description)next);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> extractExamples(Annotation[] theParameterAnnotations) {
|
||||
ArrayList<String> retVal = null;
|
||||
for (Annotation next : theParameterAnnotations) {
|
||||
if (next instanceof Description) {
|
||||
String[] examples = ((Description) next).example();
|
||||
if (examples.length > 0) {
|
||||
if (retVal == null) {
|
||||
retVal = new ArrayList<>();
|
||||
}
|
||||
retVal.addAll(Arrays.asList(examples));
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class);
|
||||
private static final Set<String> SCHEMA_NAMES;
|
||||
private static boolean ourJaxp15Supported;
|
||||
|
||||
static {
|
||||
HashSet<String> sn = new HashSet<>();
|
||||
|
@ -132,7 +133,9 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
* https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
|
||||
*/
|
||||
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
ourJaxp15Supported = true;
|
||||
} catch (SAXNotRecognizedException e) {
|
||||
ourJaxp15Supported = false;
|
||||
ourLog.warn("Jaxp 1.5 Support not found.", e);
|
||||
}
|
||||
schema = schemaFactory.newSchema(new Source[]{baseSource});
|
||||
|
@ -216,4 +219,8 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
|
||||
}
|
||||
|
||||
public static boolean isJaxp15Supported() {
|
||||
return ourJaxp15Supported;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -41,6 +41,11 @@
|
|||
<artifactId>hapi-fhir-server-mdm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>hapi-fhir-validation</artifactId>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -64,10 +65,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
Description description = theMethod.getAnnotation(Description.class);
|
||||
if (description != null) {
|
||||
myDescription = description.formalDefinition();
|
||||
if (isBlank(myDescription)) {
|
||||
myDescription = description.shortDefinition();
|
||||
}
|
||||
myDescription = ParametersUtil.extractDescription(description);
|
||||
}
|
||||
if (isBlank(myDescription)) {
|
||||
myDescription = null;
|
||||
|
|
|
@ -33,6 +33,7 @@ import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -59,15 +60,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null);
|
||||
this.myCompartmentName = StringUtils.defaultIfBlank(search.compartmentName(), null);
|
||||
this.myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
|
||||
|
||||
Description desc = theMethod.getAnnotation(Description.class);
|
||||
if (desc != null) {
|
||||
if (isNotBlank(desc.formalDefinition())) {
|
||||
myDescription = StringUtils.defaultIfBlank(desc.formalDefinition(), null);
|
||||
} else {
|
||||
myDescription = StringUtils.defaultIfBlank(desc.shortDefinition(), null);
|
||||
}
|
||||
}
|
||||
this.myDescription = ParametersUtil.extractDescription(theMethod);
|
||||
|
||||
/*
|
||||
* Check for parameter combinations and names that are invalid
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -55,6 +55,11 @@
|
|||
<artifactId>hapi-fhir-jpaserver-base</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
|
@ -78,13 +83,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -101,7 +106,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -118,7 +123,7 @@
|
|||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package ca.uhn.hapi.fhir.docs;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Docs
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Composition;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CreateCompositionAndGenerateDocument {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CreateCompositionAndGenerateDocument.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// START SNIPPET: CreateCompositionAndGenerateDocument
|
||||
FhirContext ctx = FhirContext.forR4();
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://hapi.fhir.org/baseR4");
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("PATIENT-ABC");
|
||||
patient.setActive(true);
|
||||
client.update().resource(patient).execute();
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setId("OBSERVATION-ABC");
|
||||
observation.setSubject(new Reference("Patient/PATIENT-ABC"));
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
client.update().resource(observation).execute();
|
||||
|
||||
Composition composition = new Composition();
|
||||
composition.setId("COMPOSITION-ABC");
|
||||
composition.setSubject(new Reference("Patient/PATIENT-ABC"));
|
||||
composition.addSection().setFocus(new Reference("Observation/OBSERVATION-ABC"));
|
||||
client.update().resource(composition).execute();
|
||||
|
||||
Bundle document = client
|
||||
.operation()
|
||||
.onInstance("Composition/COMPOSITION-ABC")
|
||||
.named("$document")
|
||||
.withNoParameters(Parameters.class)
|
||||
.returnResourceType(Bundle.class)
|
||||
.execute();
|
||||
|
||||
ourLog.info("Document bundle: {}", ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(document));
|
||||
// END SNIPPET: CreateCompositionAndGenerateDocument
|
||||
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ package ca.uhn.hapi.fhir.docs;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.rest.api.PreferHandlingEnum;
|
||||
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
|
@ -65,6 +66,24 @@ public class ServletExamples {
|
|||
}
|
||||
// END SNIPPET: loggingInterceptor
|
||||
|
||||
// START SNIPPET: OpenApiInterceptor
|
||||
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
|
||||
public class RestfulServerWithOpenApi extends RestfulServer {
|
||||
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
|
||||
// ... define your resource providers here ...
|
||||
|
||||
// Now register the interceptor
|
||||
OpenApiInterceptor openApiInterceptor = new OpenApiInterceptor();
|
||||
registerInterceptor(openApiInterceptor);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// END SNIPPET: OpenApiInterceptor
|
||||
|
||||
// START SNIPPET: validatingInterceptor
|
||||
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
|
||||
public class ValidatingServerWithLogging extends RestfulServer {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2560
|
||||
title: "A new interceptor called `OpenApiInterceptor` has been added. This interceptor can be registered against FHIR Servers to
|
||||
automatically add support for OpenAPI / Swagger."
|
|
@ -161,3 +161,13 @@ This following example shows how to load all pages of a bundle by fetching each
|
|||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/BundleFetcher.java|loadAll}}
|
||||
```
|
||||
|
||||
# Create Composition and Generate Document
|
||||
|
||||
This example shows how to generate a Composition resource with two linked resources, then apply the server `$document` operation to generate a document based on this composition.
|
||||
|
||||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/CreateCompositionAndGenerateDocument.java|CreateCompositionAndGenerateDocument}}
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ page.server_plain.web_testpage_overlay=Web Testpage Overlay
|
|||
page.server_plain.multitenancy=Multitenancy
|
||||
page.server_plain.jax_rs=JAX-RS Support
|
||||
page.server_plain.customizing_the_capabilitystatement=Customizing the CapabilityStatement
|
||||
page.server_plain.openapi=OpenAPI / Swagger
|
||||
|
||||
section.server_jpa.title=JPA Server
|
||||
page.server_jpa.introduction=Introduction
|
||||
|
|
|
@ -196,6 +196,11 @@ Some security audit tools require that servers return an HTTP 405 if an unsuppor
|
|||
* [BanUnsupportedHttpMethodsInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.html)
|
||||
* [BanUnsupportedHttpMethodsInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java)
|
||||
|
||||
# Server: OpenAPI / Swagger Support
|
||||
|
||||
An interceptor can be registered against your server that enables support for OpenAPI (aka Swagger) automatically. See [OpenAPI](/docs/server_plain/openapi.html) for more information.
|
||||
|
||||
|
||||
# Subscription: Subscription Debug Log Interceptor
|
||||
|
||||
When using Subscriptions, the debug log interceptor can be used to add a number of additional lines to the server logs showing the internals of the subscription processing pipeline.
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# OpenAPI / Swagger Support
|
||||
|
||||
In HAPI FHIR, support for OpenAPI (aka Swagger) is supported via the [OpenApiInterceptor](/hapi-fhir/apidocs/hapi-fhir-server-openapi/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.html).
|
||||
|
||||
Note that this interceptor supports servers using the RestfulServer (aka HAPI FHIR Plain Server and JPA Server), and does not currently support JAX-RS servers.
|
||||
|
||||
When this interceptor is registered against the server, it performs the following 3 tasks:
|
||||
|
||||
### System Functionality
|
||||
|
||||
* OpenAPI 3.0 Documentation will be served at `[baseUrl]/api-docs`. This documentation is generated by the interceptor using information from the server's CapabilityStatement as well as from its automatically generated OperationDefinitions.
|
||||
|
||||
### User Functionality
|
||||
|
||||
* Anytime a user using a browser navigates to the Base URL of the server, they will be automatically redirected to `[baseUrl]/swagger-ui/`
|
||||
|
||||
* A customized version of the [Swagger UI](https://swagger.io/tools/swagger-ui/) tool will be served at `[baseUrl]/swagger-ui/`
|
||||
|
||||
# Enabling OpenAPI
|
||||
|
||||
The HAPI FHIR OpenAPI functionality is supplied in a dedicated module called `hapi-fhir-server-openapi`. To enable this functionality you must first include this module in your project. For example, Maven users should include the following dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
You then simply have to register the interceptor against your RestfulServer instance.
|
||||
|
||||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|OpenApiInterceptor}}
|
||||
```
|
||||
|
||||
# Demonstration
|
||||
|
||||
See the HAPI FHIR Test Server for a demonstration of HAPI FHIR OpenAPI functionality: http://hapi.fhir.org/baseR4/swagger-ui/
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -36,6 +36,11 @@
|
|||
<artifactId>hapi-fhir-server-mdm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-client</artifactId>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -577,7 +577,14 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
|
|
@ -8,10 +8,10 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
|||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.jboss.logging.MDC;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
@ -78,13 +78,11 @@ public class BaseJpaProvider {
|
|||
return options;
|
||||
}
|
||||
|
||||
protected Parameters createExpungeResponse(ExpungeOutcome theOutcome) {
|
||||
Parameters retVal = new Parameters();
|
||||
retVal
|
||||
.addParameter()
|
||||
.setName(JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT)
|
||||
.setValue(new IntegerType(theOutcome.getDeletedCount()));
|
||||
return retVal;
|
||||
protected IBaseParameters createExpungeResponse(ExpungeOutcome theOutcome) {
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(getContext());
|
||||
String value = Integer.toString(theOutcome.getDeletedCount());
|
||||
ParametersUtil.addParameterToParameters(getContext(), parameters, JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, "integer", value);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,30 +24,47 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.At;
|
||||
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META;
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD;
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE;
|
||||
|
||||
public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends BaseJpaProvider implements IResourceProvider {
|
||||
|
||||
private IFhirResourceDao<T> myDao;
|
||||
|
@ -62,7 +79,7 @@ public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
|
||||
protected Parameters doExpunge(IIdType theIdParam, IPrimitiveType<? extends Integer> theLimit, IPrimitiveType<? extends Boolean> theExpungeDeletedResources, IPrimitiveType<? extends Boolean> theExpungeOldVersions, IPrimitiveType<? extends Boolean> theExpungeEverything, RequestDetails theRequest) {
|
||||
protected IBaseParameters doExpunge(IIdType theIdParam, IPrimitiveType<? extends Integer> theLimit, IPrimitiveType<? extends Boolean> theExpungeDeletedResources, IPrimitiveType<? extends Boolean> theExpungeOldVersions, IPrimitiveType<? extends Boolean> theExpungeEverything, RequestDetails theRequest) {
|
||||
|
||||
ExpungeOptions options = createExpungeOptions(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything);
|
||||
|
||||
|
@ -143,4 +160,134 @@ public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().create(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().create(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IIdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().deleteByUrl(theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().delete(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
|
||||
})
|
||||
public IBaseParameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer") IPrimitiveType<Integer> theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES, typeName = "boolean") IPrimitiveType<Boolean> theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS, typeName = "boolean") IPrimitiveType<Boolean> theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
return doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
|
||||
})
|
||||
public IBaseParameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer") IPrimitiveType<Integer> theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES, typeName = "boolean") IPrimitiveType<Boolean> theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS, typeName = "boolean") IPrimitiveType<Boolean> theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
return doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
}
|
||||
|
||||
@Description("Request a global list of tags, profiles, and security labels")
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", typeName = "Meta")
|
||||
})
|
||||
public IBaseParameters meta(RequestDetails theRequestDetails) {
|
||||
Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
|
||||
IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theRequestDetails);
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Description("Request a list of tags, profiles, and security labels for a specfic resource instance")
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", typeName = "Meta")
|
||||
})
|
||||
public IBaseParameters meta(@IdParam IIdType theId, RequestDetails theRequestDetails) {
|
||||
Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
|
||||
IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theId, theRequestDetails);
|
||||
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Description("Add tags, profiles, and/or security labels to a resource")
|
||||
@Operation(name = OPERATION_META_ADD, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = "return", typeName = "Meta")
|
||||
})
|
||||
public IBaseParameters metaAdd(@IdParam IIdType theId, @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
IBaseMetaType metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaAddOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Description("Delete tags, profiles, and/or security labels from a resource")
|
||||
@Operation(name = OPERATION_META_DELETE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = "return", typeName = "Meta")
|
||||
})
|
||||
public IBaseParameters metaDelete(@IdParam IIdType theId, @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
IBaseMetaType metaDelete = getDao().metaDeleteOperation(theId, theMeta, theRequestDetails);
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaDelete);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IIdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().update(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().update(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IIdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,15 +23,18 @@ package ca.uhn.fhir.jpa.provider;
|
|||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.rest.annotation.At;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
|
@ -55,7 +58,22 @@ public class BaseJpaSystemProvider<T, MT> extends BaseJpaProvider implements IJp
|
|||
return myResourceReindexingSvc;
|
||||
}
|
||||
|
||||
protected Parameters doExpunge(IPrimitiveType<? extends Integer> theLimit, IPrimitiveType<? extends Boolean> theExpungeDeletedResources, IPrimitiveType<? extends Boolean> theExpungeOldVersions, IPrimitiveType<? extends Boolean> theExpungeEverything, RequestDetails theRequestDetails) {
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
|
||||
})
|
||||
public IBaseParameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer") IPrimitiveType<Integer> theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES, typeName = "boolean") IPrimitiveType<Boolean> theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS, typeName = "boolean") IPrimitiveType<Boolean> theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING, typeName = "boolean") IPrimitiveType<Boolean> theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
ExpungeOptions options = createExpungeOptions(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything);
|
||||
ExpungeOutcome outcome = getDao().expunge(options, theRequestDetails);
|
||||
return createExpungeResponse(outcome);
|
||||
}
|
||||
|
||||
protected IBaseParameters doExpunge(IPrimitiveType<? extends Integer> theLimit, IPrimitiveType<? extends Boolean> theExpungeDeletedResources, IPrimitiveType<? extends Boolean> theExpungeOldVersions, IPrimitiveType<? extends Boolean> theExpungeEverything, RequestDetails theRequestDetails) {
|
||||
ExpungeOptions options = createExpungeOptions(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything);
|
||||
ExpungeOutcome outcome = getDao().expunge(options, theRequestDetails);
|
||||
return createExpungeResponse(outcome);
|
||||
|
|
|
@ -20,19 +20,27 @@ package ca.uhn.fhir.jpa.provider;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSystemProvider<T, MT> {
|
||||
|
||||
|
||||
@Operation(name = MARK_ALL_RESOURCES_FOR_REINDEXING, idempotent = true, returnParameters = {
|
||||
@Description("Marks all currently existing resources of a given type, or all resources of all types, for reindexing.")
|
||||
@Operation(name = MARK_ALL_RESOURCES_FOR_REINDEXING, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = "status")
|
||||
})
|
||||
public IBaseResource markAllResourcesForReindexing(
|
||||
|
@ -53,7 +61,8 @@ public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSyste
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Operation(name = PERFORM_REINDEXING_PASS, idempotent = true, returnParameters = {
|
||||
@Description("Forces a single pass of the resource reindexing processor")
|
||||
@Operation(name = PERFORM_REINDEXING_PASS, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = "status")
|
||||
})
|
||||
public IBaseResource performReindexingPass() {
|
||||
|
@ -72,4 +81,28 @@ public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSyste
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* $process-message
|
||||
*/
|
||||
@Description("Accept a FHIR Message Bundle for processing")
|
||||
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
|
||||
public IBaseBundle processMessage(
|
||||
HttpServletRequest theServletRequest,
|
||||
RequestDetails theRequestDetails,
|
||||
|
||||
@OperationParam(name = "content", min = 1, max = 1, typeName = "Bundle")
|
||||
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
|
||||
IBaseBundle theMessageToProcess
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return getDao().processMessage(theRequestDetails, theMessageToProcess);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.patch.FhirPatch;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
|
@ -49,14 +50,23 @@ public class DiffProvider {
|
|||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
|
||||
@Description(
|
||||
value="This operation examines two resource versions (can be two versions of the same resource, or two different resources) and generates a FHIR Patch document showing the differences.",
|
||||
shortDefinition = "Comparte two resources or two versions of a single resource")
|
||||
@Operation(name = ProviderConstants.DIFF_OPERATION_NAME, global = true, idempotent = true)
|
||||
public IBaseParameters diff(
|
||||
@IdParam IIdType theResourceId,
|
||||
@OperationParam(name = ProviderConstants.DIFF_FROM_VERSION_PARAMETER, typeName = "string", min = 0, max = 1) IPrimitiveType<?> theFromVersion,
|
||||
@OperationParam(name = ProviderConstants.DIFF_INCLUDE_META_PARAMETER, typeName = "boolean", min = 0, max = 1) IPrimitiveType<Boolean> theIncludeMeta,
|
||||
|
||||
@Description(value = "The resource ID and version to diff from", example = "Patient/example/version/1")
|
||||
@OperationParam(name = ProviderConstants.DIFF_FROM_VERSION_PARAMETER, typeName = "string", min = 0, max = 1)
|
||||
IPrimitiveType<?> theFromVersion,
|
||||
|
||||
@Description(value = "Should differences in the Resource.meta element be included in the diff", example = "false")
|
||||
@OperationParam(name = ProviderConstants.DIFF_INCLUDE_META_PARAMETER, typeName = "boolean", min = 0, max = 1)
|
||||
IPrimitiveType<Boolean> theIncludeMeta,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceId.getResourceType());
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(theResourceId.getResourceType());
|
||||
IBaseResource targetResource = dao.read(theResourceId, theRequestDetails);
|
||||
IBaseResource sourceResource = null;
|
||||
|
||||
|
@ -82,15 +92,23 @@ public class DiffProvider {
|
|||
}
|
||||
|
||||
FhirPatch fhirPatch = newPatch(theIncludeMeta);
|
||||
IBaseParameters diff = fhirPatch.diff(sourceResource, targetResource);
|
||||
return diff;
|
||||
return fhirPatch.diff(sourceResource, targetResource);
|
||||
}
|
||||
|
||||
@Description("This operation examines two resource versions (can be two versions of the same resource, or two different resources) and generates a FHIR Patch document showing the differences.")
|
||||
@Operation(name = ProviderConstants.DIFF_OPERATION_NAME, idempotent = true)
|
||||
public IBaseParameters diff(
|
||||
@OperationParam(name = ProviderConstants.DIFF_FROM_PARAMETER, typeName = "id", min = 1, max = 1) IIdType theFromVersion,
|
||||
@OperationParam(name = ProviderConstants.DIFF_TO_PARAMETER, typeName = "id", min = 1, max = 1) IIdType theToVersion,
|
||||
@OperationParam(name = ProviderConstants.DIFF_INCLUDE_META_PARAMETER, typeName = "boolean", min = 0, max = 1) IPrimitiveType<Boolean> theIncludeMeta,
|
||||
@Description(value = "The resource ID and version to diff from", example = "Patient/example/version/1")
|
||||
@OperationParam(name = ProviderConstants.DIFF_FROM_PARAMETER, typeName = "id", min = 1, max = 1)
|
||||
IIdType theFromVersion,
|
||||
|
||||
@Description(value = "The resource ID and version to diff to", example = "Patient/example/version/2")
|
||||
@OperationParam(name = ProviderConstants.DIFF_TO_PARAMETER, typeName = "id", min = 1, max = 1)
|
||||
IIdType theToVersion,
|
||||
|
||||
@Description(value = "Should differences in the Resource.meta element be included in the diff", example = "false")
|
||||
@OperationParam(name = ProviderConstants.DIFF_INCLUDE_META_PARAMETER, typeName = "boolean", min = 0, max = 1)
|
||||
IPrimitiveType<Boolean> theIncludeMeta,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
if (!Objects.equal(theFromVersion.getResourceType(), theToVersion.getResourceType())) {
|
||||
|
@ -98,13 +116,12 @@ public class DiffProvider {
|
|||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theFromVersion.getResourceType());
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(theFromVersion.getResourceType());
|
||||
IBaseResource sourceResource = dao.read(theFromVersion, theRequestDetails);
|
||||
IBaseResource targetResource = dao.read(theToVersion, theRequestDetails);
|
||||
|
||||
FhirPatch fhirPatch = newPatch(theIncludeMeta);
|
||||
IBaseParameters diff = fhirPatch.diff(sourceResource, targetResource);
|
||||
return diff;
|
||||
return fhirPatch.diff(sourceResource, targetResource);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.ConfigurationException;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.GraphQL;
|
||||
import ca.uhn.fhir.rest.annotation.GraphQLQueryBody;
|
||||
import ca.uhn.fhir.rest.annotation.GraphQLQueryUrl;
|
||||
|
@ -110,18 +111,20 @@ public class GraphQLProvider {
|
|||
myStorageServices = theStorageServices;
|
||||
}
|
||||
|
||||
@Description(value="This operation invokes a GraphQL expression for fetching an joining a graph of resources, returning them in a custom format.")
|
||||
@GraphQL(type=RequestTypeEnum.GET)
|
||||
public String processGraphQlGetRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String queryUrl) {
|
||||
if (queryUrl != null) {
|
||||
return processGraphQLRequest(theRequestDetails, theId, queryUrl);
|
||||
public String processGraphQlGetRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String theQueryUrl) {
|
||||
if (theQueryUrl != null) {
|
||||
return processGraphQLRequest(theRequestDetails, theId, theQueryUrl);
|
||||
}
|
||||
throw new InvalidRequestException("Unable to parse empty GraphQL expression");
|
||||
}
|
||||
|
||||
@Description(value="This operation invokes a GraphQL expression for fetching an joining a graph of resources, returning them in a custom format.")
|
||||
@GraphQL(type=RequestTypeEnum.POST)
|
||||
public String processGraphQlPostRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryBody String queryBody) {
|
||||
if (queryBody != null) {
|
||||
return processGraphQLRequest(theRequestDetails, theId, queryBody);
|
||||
public String processGraphQlPostRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryBody String theQueryBody) {
|
||||
if (theQueryBody != null) {
|
||||
return processGraphQLRequest(theRequestDetails, theId, theQueryBody);
|
||||
}
|
||||
throw new InvalidRequestException("Unable to parse empty GraphQL expression");
|
||||
}
|
||||
|
|
|
@ -80,6 +80,11 @@ public class JpaCapabilityStatementProvider extends ServerCapabilityStatementPro
|
|||
if (isNotBlank(myImplementationDescription)) {
|
||||
theTerser.setElement(theCapabilityStatement, "implementation.description", myImplementationDescription);
|
||||
}
|
||||
|
||||
theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_JSON_NEW);
|
||||
theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_FHIR_XML_NEW);
|
||||
theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_JSON_PATCH);
|
||||
theTerser.addElement(theCapabilityStatement, "patchFormat", Constants.CT_XML_PATCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,129 +63,4 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
super(theDao);
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().create(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().create(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().deleteByUrl(theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().delete(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
return JpaSystemProviderDstu2.toExpungeResponse(retVal);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
|
||||
@OperationParam(name = OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
return JpaSystemProviderDstu2.toExpungeResponse(retVal);
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = MetaDt.class)
|
||||
})
|
||||
public Parameters meta(RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
MetaDt metaGetOperation = getDao().metaGetOperation(MetaDt.class, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = MetaDt.class)
|
||||
})
|
||||
public Parameters meta(@IdParam IdDt theId, RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
MetaDt metaGetOperation = getDao().metaGetOperation(MetaDt.class, theId, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_ADD, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = MetaDt.class)
|
||||
})
|
||||
public Parameters metaAdd(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
MetaDt metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaAddOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_DELETE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = MetaDt.class)
|
||||
})
|
||||
public Parameters metaDelete(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta, theRequestDetails));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().update(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
theResource.setId(theId);
|
||||
return getDao().update(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,38 +1,30 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters.Parameter;
|
||||
import ca.uhn.fhir.model.primitive.BooleanDt;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -60,38 +52,6 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
@Qualifier("mySystemDaoDstu2")
|
||||
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanDt theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
return toExpungeResponse(retVal);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanDt theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
return toExpungeResponse(retVal);
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
// This is generated by hand:
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerDt.class, min=0, max=1),/"
|
||||
|
@ -222,28 +182,6 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* /$process-message
|
||||
*/
|
||||
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
|
||||
public IBaseBundle processMessage(
|
||||
HttpServletRequest theServletRequest,
|
||||
RequestDetails theRequestDetails,
|
||||
|
||||
@OperationParam(name = "content", min = 1, max = 1)
|
||||
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
|
||||
Bundle theMessageToProcess
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return getDao().processMessage(theRequestDetails, theMessageToProcess);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Parameters toExpungeResponse(org.hl7.fhir.r4.model.Parameters theRetVal) {
|
||||
Integer count = ((IntegerType) theRetVal.getParameterFirstRep().getValue()).getValue();
|
||||
return new Parameters()
|
||||
|
|
|
@ -64,136 +64,4 @@ public class JpaResourceProviderDstu3<T extends IAnyResource> extends BaseJpaRes
|
|||
super(theDao);
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().create(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().create(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().deleteByUrl(theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().delete(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
try {
|
||||
return convertParameters(retVal);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
try {
|
||||
return convertParameters(retVal);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(@IdParam IdType theId, RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theId, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_ADD, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaAdd(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaAddOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_DELETE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaDelete(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta, theRequestDetails));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().update(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().update(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,46 +65,6 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
@Qualifier("mySystemDaoDstu3")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
try {
|
||||
return convertParameters(retVal);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
try {
|
||||
return convertParameters(retVal);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// This is generated by hand:
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
||||
@Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = {
|
||||
|
@ -233,32 +193,4 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* /$process-message
|
||||
*/
|
||||
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
|
||||
public IBaseBundle processMessage(
|
||||
HttpServletRequest theServletRequest,
|
||||
RequestDetails theRequestDetails,
|
||||
|
||||
@OperationParam(name = "content", min = 1, max = 1)
|
||||
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
|
||||
Bundle theMessageToProcess
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return getDao().processMessage(theRequestDetails, theMessageToProcess);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void validateFulltextSearchEnabled(IFulltextSearchSvc theSearchDao) {
|
||||
if (theSearchDao == null || theSearchDao.isDisabled()) {
|
||||
throw new InvalidRequestException("Fulltext searching is disabled on this server");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -61,128 +61,4 @@ public class JpaResourceProviderR4<T extends IAnyResource> extends BaseJpaResour
|
|||
super(theDao);
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().create(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().create(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().deleteByUrl(theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().delete(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
return super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
return super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(@IdParam IdType theId, RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theId, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_ADD, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaAdd(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaAddOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_DELETE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaDelete(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta, theRequestDetails));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().update(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().update(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,40 +1,28 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Meta;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -62,36 +50,6 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
|||
@Qualifier("mySystemDaoR4")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
return super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
return super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
}
|
||||
|
||||
// This is generated by hand:
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
||||
@Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = {
|
||||
|
@ -210,28 +168,6 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
|||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* /$process-message
|
||||
*/
|
||||
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
|
||||
public IBaseBundle processMessage(
|
||||
HttpServletRequest theServletRequest,
|
||||
RequestDetails theRequestDetails,
|
||||
|
||||
@OperationParam(name = "content", min = 1, max = 1)
|
||||
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
|
||||
Bundle theMessageToProcess
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return getDao().processMessage(theRequestDetails, theMessageToProcess);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
||||
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
|
|
|
@ -61,130 +61,4 @@ public class JpaResourceProviderR5<T extends IAnyResource> extends BaseJpaResour
|
|||
super(theDao);
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().create(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().create(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().deleteByUrl(theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().delete(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
|
||||
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters);
|
||||
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
RequestDetails theRequest) {
|
||||
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
|
||||
return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters);
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters meta(@IdParam IdType theId, RequestDetails theRequestDetails) {
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theId, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaGetOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_ADD, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaAdd(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
Meta metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
|
||||
parameters.addParameter().setName("return").setValue(metaAddOperation);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name = OPERATION_META_DELETE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
public Parameters metaDelete(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
|
||||
if (theMeta == null) {
|
||||
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
|
||||
}
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta, theRequestDetails));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
if (theConditional != null) {
|
||||
return getDao().update(theResource, theConditional, theRequestDetails);
|
||||
} else {
|
||||
return getDao().update(theResource, theRequestDetails);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
|
||||
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,38 +62,6 @@ public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
|||
@Qualifier("mySystemDaoR5")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@IdParam IIdType theIdParam,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
|
||||
})
|
||||
public Parameters expunge(
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
|
||||
return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters);
|
||||
}
|
||||
|
||||
// This is generated by hand:
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
||||
@Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = {
|
||||
|
@ -212,28 +180,6 @@ public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
|||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* /$process-message
|
||||
*/
|
||||
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
|
||||
public IBaseBundle processMessage(
|
||||
HttpServletRequest theServletRequest,
|
||||
RequestDetails theRequestDetails,
|
||||
|
||||
@OperationParam(name = "content", min = 1, max = 1)
|
||||
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
|
||||
Bundle theMessageToProcess
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return getDao().processMessage(theRequestDetails, theMessageToProcess);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
||||
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.hl7.fhir.r4.model.Patient;
|
|||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.hl7.fhir.r4.model.PractitionerRole;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -2172,6 +2173,39 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
|
|||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Should try to get this down at some point
|
||||
*/
|
||||
@Test
|
||||
@Disabled
|
||||
public void testSearch_StringParam_SearchOnePartition_AddRevIncludes() {
|
||||
addReadPartition(1);
|
||||
addCreatePartition(1, null);
|
||||
Organization org = new Organization();
|
||||
org.setName("FOO");
|
||||
org.setId("FOO-ORG");
|
||||
myOrganizationDao.update(org, mySrd);
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
addCreatePartition(1, null);
|
||||
PractitionerRole pr = new PractitionerRole();
|
||||
pr.getOrganization().setReference("Organization/FOO-ORG");
|
||||
myPractitionerRoleDao.create(pr, mySrd);
|
||||
}
|
||||
|
||||
addReadPartition(1);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.addRevInclude(PractitionerRole.INCLUDE_ORGANIZATION);
|
||||
map.setCount(10);
|
||||
IBundleProvider results = myOrganizationDao.search(map, mySrd);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
assertEquals(10, ids.size(), () -> ids.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_TagNotParam_SearchAllPartitions() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue(), withTag("http://system", "code"), withIdentifier("http://foo", "bar"));
|
||||
|
@ -2914,7 +2948,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
|
|||
myResourceReindexingSvc.markAllResourcesForReindexing();
|
||||
myResourceReindexingSvc.forceReindexingPass();
|
||||
|
||||
runInTransaction(()->{
|
||||
runInTransaction(() -> {
|
||||
assertNotEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXING_FAILED, myResourceTableDao.findById(patientIdNull.getIdPartAsLong()).get().getIndexStatus());
|
||||
assertNotEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXING_FAILED, myResourceTableDao.findById(patientId1.getIdPartAsLong()).get().getIndexStatus());
|
||||
});
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.common.base.Charsets;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
@ -387,8 +388,8 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
@Test
|
||||
public void testMarkResourcesForReindexing() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
HttpPost post = new HttpPost(ourServerBase + "/$mark-all-resources-for-reindexing");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(post);
|
||||
try {
|
||||
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(output);
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
|
@ -16,6 +17,7 @@ import org.hl7.fhir.dstu3.model.CapabilityStatement;
|
|||
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
@ -24,7 +26,13 @@ public class ServerDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerDstu3Test.class);
|
||||
|
||||
|
||||
@Override
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
ourRestServer.getInterceptorService().unregisterInterceptorsIf(t->t instanceof OpenApiInterceptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #519
|
||||
|
@ -61,5 +69,18 @@ public class ServerDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFetchOpenApi() throws IOException {
|
||||
ourRestServer.registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/api-docs");
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
|
||||
String string = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(string);
|
||||
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class OpenApiInterceptorJpaTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OpenApiInterceptorJpaTest.class);
|
||||
|
||||
@Override
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
ourRestServer.getInterceptorService().unregisterInterceptorsIf(t -> t instanceof OpenApiInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchOpenApi() throws IOException {
|
||||
ourRestServer.registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/metadata?_format=json&_pretty=true");
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
|
||||
String string = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(string);
|
||||
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/api-docs");
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
|
||||
String string = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(string);
|
||||
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -26,6 +27,7 @@ import static org.hamcrest.Matchers.hasItem;
|
|||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
public class ServerCapabilityStatementProviderJpaR4Test extends BaseResourceProviderR4Test {
|
||||
|
||||
|
@ -39,6 +41,38 @@ public class ServerCapabilityStatementProviderJpaR4Test extends BaseResourceProv
|
|||
assertThat(resourceTypes, hasItems("Patient", "Observation", "SearchParameter"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoDuplicateResourceOperationNames() {
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(cs));
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceComponent next : cs.getRestFirstRep().getResource()) {
|
||||
List<String> opNames = next
|
||||
.getOperation()
|
||||
.stream()
|
||||
.map(t -> t.getName())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
ourLog.info("System ops: {}", opNames);
|
||||
assertEquals(opNames.stream().distinct().sorted().collect(Collectors.toList()), opNames);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoDuplicateSystemOperationNames() {
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(cs));
|
||||
List<String> systemOpNames = cs
|
||||
.getRestFirstRep()
|
||||
.getOperation()
|
||||
.stream()
|
||||
.map(t -> t.getName())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
ourLog.info("System ops: {}", systemOpNames);
|
||||
assertEquals(systemOpNames.stream().distinct().sorted().collect(Collectors.toList()), systemOpNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomSearchParamsReflectedInSearchParams() {
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
|
|
|
@ -18,6 +18,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
|
@ -36,6 +37,7 @@ import org.apache.http.Header;
|
|||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
@ -49,6 +51,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
|
@ -75,6 +78,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -247,8 +251,8 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
|
|||
|
||||
@Test
|
||||
public void testMarkResourcesForReindexing() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
HttpRequestBase post = new HttpPost(ourServerBase + "/$mark-all-resources-for-reindexing");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(post);
|
||||
try {
|
||||
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(output);
|
||||
|
@ -257,8 +261,8 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
|
|||
IOUtils.closeQuietly(http);
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/$perform-reindexing-pass");
|
||||
http = ourHttpClient.execute(get);
|
||||
post = new HttpPost(ourServerBase + "/$perform-reindexing-pass");
|
||||
http = ourHttpClient.execute(post);
|
||||
try {
|
||||
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(output);
|
||||
|
@ -272,8 +276,10 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
|
|||
|
||||
@Test
|
||||
public void testMarkResourcesForReindexingTyped() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing?type=Patient");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/$mark-all-resources-for-reindexing?type=Patient");
|
||||
post.setEntity(new ResourceEntity(myFhirCtx, new Parameters().addParameter("type", new CodeType("Patient"))));
|
||||
CloseableHttpResponse http = ourHttpClient.execute(post);
|
||||
try {
|
||||
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(output);
|
||||
|
@ -282,8 +288,9 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
|
|||
IOUtils.closeQuietly(http);
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing?type=FOO");
|
||||
http = ourHttpClient.execute(get);
|
||||
post = new HttpPost(ourServerBase + "/$mark-all-resources-for-reindexing?type=FOO");
|
||||
post.setEntity(new ResourceEntity(myFhirCtx, new Parameters().addParameter("type", new CodeType("FOO"))));
|
||||
http = ourHttpClient.execute(post);
|
||||
try {
|
||||
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(output);
|
||||
|
|
|
@ -29,16 +29,6 @@ import org.junit.jupiter.api.BeforeAll;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
|
@ -66,20 +56,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
public class LastNElasticsearchSvcMultipleObservationsIT {
|
||||
|
||||
static private final Calendar baseObservationDate = new GregorianCalendar();
|
||||
private static ObjectMapper ourMapperNonPrettyPrint;
|
||||
|
||||
private static boolean indexLoaded = false;
|
||||
|
||||
private final Map<String, Map<String, List<Date>>> createdPatientObservationMap = new HashMap<>();
|
||||
|
||||
private final FhirContext myFhirContext = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
|
||||
|
||||
@Container
|
||||
public static ElasticsearchContainer elasticsearchContainer = TestElasticsearchContainerHelper.getEmbeddedElasticSearch();
|
||||
|
||||
|
||||
|
||||
private static ObjectMapper ourMapperNonPrettyPrint;
|
||||
private static boolean indexLoaded = false;
|
||||
private final Map<String, Map<String, List<Date>>> createdPatientObservationMap = new HashMap<>();
|
||||
private final FhirContext myFhirContext = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
private ElasticsearchSvcImpl elasticsearchSvc;
|
||||
|
||||
@BeforeEach
|
||||
|
|
|
@ -40,12 +40,10 @@ import ca.uhn.fhir.util.StopWatch;
|
|||
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.internal.SessionImpl;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.ExplanationOfBenefit;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
|
|
@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BaseTermReadSvcImplTest {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -131,6 +131,11 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
|
@ -144,13 +149,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -13,11 +13,8 @@ import org.cqframework.cql.cql2elm.NamespaceManager;
|
|||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.cqframework.cql.elm.tracking.TrackBack;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Matchers;
|
||||
|
@ -33,8 +30,11 @@ import java.io.InputStream;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.reset;
|
||||
|
@ -61,7 +61,7 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
|
||||
//@BeforeEach
|
||||
//@BeforeAll
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void createMocks() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
//libraryManager = Mockito.mock(LibraryManager.class);
|
||||
|
@ -85,7 +85,7 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
when(libraryManager.getNamespaceManager()).thenReturn(namespaceManager);
|
||||
when(namespaceManager.hasNamespaces()).thenReturn(false);
|
||||
CqlTranslator translator = TranslatorHelper.getTranslator(sampleCql, libraryManager, modelManager);
|
||||
assertNotNull("translator should not be NULL!", translator);
|
||||
assertNotNull(translator, "translator should not be NULL!");
|
||||
}
|
||||
|
||||
//@Test
|
||||
|
@ -95,7 +95,7 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
when(libraryManager.getLibrarySourceLoader()).thenReturn(librarySourceLoader);
|
||||
when(namespaceManager.hasNamespaces()).thenReturn(true);
|
||||
CqlTranslator translator = TranslatorHelper.getTranslator(sampleCql, libraryManager, modelManager);
|
||||
assertNotNull("translator should not be NULL!", translator);
|
||||
assertNotNull(translator, "translator should not be NULL!");
|
||||
}
|
||||
|
||||
//@Test
|
||||
|
@ -108,8 +108,8 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
try {
|
||||
translator = TranslatorHelper.getTranslator(" ", libraryManager, modelManager);
|
||||
fail();
|
||||
} catch(NullPointerException e) {
|
||||
assertNull("translator should be NULL!", translator);
|
||||
} catch (NullPointerException e) {
|
||||
assertNull(translator, "translator should be NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,9 +133,9 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
when(CqlTranslator.fromStream(any(InputStream.class), any(ModelManager.class), any(LibraryManager.class), Matchers.<CqlTranslator.Options>anyVararg())).thenThrow(IOException.class);
|
||||
translator = TranslatorHelper.getTranslator(new ByteArrayInputStream("INVALID-FILENAME".getBytes(StandardCharsets.UTF_8)), libraryManager, modelManager);
|
||||
fail();
|
||||
} catch(IllegalArgumentException | IOException e) {
|
||||
} catch (IllegalArgumentException | IOException e) {
|
||||
assertTrue(e instanceof IllegalArgumentException);
|
||||
assertNull("translator should be NULL!", translator);
|
||||
assertNull(translator, "translator should be NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,11 +153,11 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
Library library = null;
|
||||
try {
|
||||
library = TranslatorHelper.translateLibrary("INVALID-FILENAME", libraryManager, modelManager);
|
||||
} catch(Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
}
|
||||
assertNotNull("library should not be NULL!", library);
|
||||
assertNotNull(library, "library should not be NULL!");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -166,8 +166,8 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
try {
|
||||
library = TranslatorHelper.readLibrary(new ByteArrayInputStream("INVALID-XML-DOCUMENT".getBytes()));
|
||||
fail();
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertNull("library should be NULL!", library);
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertNull(library, "library should be NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ public class TranslatorHelperTest implements CqlProviderTestBase {
|
|||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
assertNotNull("library should not be NULL!", library);
|
||||
assertNotNull(library, "library should not be NULL!");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -55,13 +55,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -47,6 +47,11 @@
|
|||
<version>${project.version}</version>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.helger</groupId>
|
||||
|
@ -164,7 +169,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-converter</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
|
|
@ -19,6 +19,7 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
|||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
|
@ -38,6 +39,7 @@ import ca.uhn.fhirtest.config.TestR4Config;
|
|||
import ca.uhn.fhirtest.config.TestR5Config;
|
||||
import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
@ -79,11 +81,11 @@ public class TestRestfulServer extends RestfulServer {
|
|||
WebApplicationContext parentAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
||||
// These two parmeters are also declared in web.xml
|
||||
String implDesc = getInitParameter("ImplementationDescription");
|
||||
String fhirVersionParam = getInitParameter("FhirVersion");
|
||||
if (StringUtils.isBlank(fhirVersionParam)) {
|
||||
fhirVersionParam = "DSTU1";
|
||||
}
|
||||
Validate.notNull(fhirVersionParam);
|
||||
|
||||
setImplementationDescription("HAPI FHIR Test/Demo Server " + fhirVersionParam + " Endpoint");
|
||||
setCopyright("This server is **Open Source Software**, licensed under the terms of the [Apache Software License 2.0](https://www.apache.org/licenses/LICENSE-2.0).");
|
||||
|
||||
// Depending on the version this server is supporing, we will
|
||||
// retrieve all the appropriate resource providers and the
|
||||
|
@ -110,7 +112,6 @@ public class TestRestfulServer extends RestfulServer {
|
|||
systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
|
||||
etagSupport = ETagSupportEnum.ENABLED;
|
||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class));
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
break;
|
||||
}
|
||||
|
@ -127,7 +128,6 @@ public class TestRestfulServer extends RestfulServer {
|
|||
systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
|
||||
etagSupport = ETagSupportEnum.ENABLED;
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class));
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
@ -147,7 +147,6 @@ public class TestRestfulServer extends RestfulServer {
|
|||
etagSupport = ETagSupportEnum.ENABLED;
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class), validationSupport);
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
@ -271,6 +270,10 @@ public class TestRestfulServer extends RestfulServer {
|
|||
*/
|
||||
registerProvider(myAppCtx.getBean(DiffProvider.class));
|
||||
|
||||
/*
|
||||
* OpenAPI
|
||||
*/
|
||||
registerInterceptor(new OpenApiInterceptor());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -109,7 +109,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
|||
retVal.setUsername(myDbUsername);
|
||||
retVal.setPassword(myDbPassword);
|
||||
retVal.setDefaultQueryTimeout(20);
|
||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||
retVal.setTestOnBorrow(true);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
|
|
@ -125,7 +125,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
retVal.setUsername(myDbUsername);
|
||||
retVal.setPassword(myDbPassword);
|
||||
retVal.setDefaultQueryTimeout(20);
|
||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||
retVal.setTestOnBorrow(true);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
|
|
@ -109,7 +109,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
retVal.setUsername(myDbUsername);
|
||||
retVal.setPassword(myDbPassword);
|
||||
retVal.setDefaultQueryTimeout(20);
|
||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||
retVal.setTestOnBorrow(true);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
|
|
@ -109,7 +109,7 @@ public class TestR5Config extends BaseJavaConfigR5 {
|
|||
retVal.setUsername(myDbUsername);
|
||||
retVal.setPassword(myDbPassword);
|
||||
retVal.setDefaultQueryTimeout(20);
|
||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||
retVal.setTestOnBorrow(true);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
|
|
@ -31,10 +31,6 @@
|
|||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
ca.uhn.fhirtest.config.FhirTesterConfig
|
||||
<!--
|
||||
ca.uhn.fhir.jpa.config.WebsocketDstu2Config
|
||||
ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config
|
||||
-->
|
||||
</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>2</load-on-startup>
|
||||
|
@ -43,10 +39,6 @@
|
|||
<servlet>
|
||||
<servlet-name>fhirServletR5</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>UHN Test Server (R5 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>R5</param-value>
|
||||
|
@ -57,10 +49,6 @@
|
|||
<servlet>
|
||||
<servlet-name>fhirServletR4</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>UHN Test Server (R4 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>R4</param-value>
|
||||
|
@ -71,10 +59,6 @@
|
|||
<servlet>
|
||||
<servlet-name>fhirServletDstu2</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>UHN Test Server (DSTU2 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>DSTU2</param-value>
|
||||
|
@ -85,10 +69,6 @@
|
|||
<servlet>
|
||||
<servlet-name>fhirServletDstu3</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>UHN Test Server (STU3 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>DSTU3</param-value>
|
||||
|
@ -96,35 +76,6 @@
|
|||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<!--
|
||||
<servlet>
|
||||
<servlet-name>fhirServletTdl2</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>Test Data Library (DSTU2 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>TDL2</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>fhirServletTdl3</servlet-name>
|
||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>FHIR Test Data Library (STU3 Resources)</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>TDL3</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
-->
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>fhirServletR5</servlet-name>
|
||||
<url-pattern>/baseR5/*</url-pattern>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hapi-fhir-server-openapi</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- HAPI FHIR -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-r4</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-converter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenAPI/Swagger -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>2.1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>swagger-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Thymeleaf -->
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Unit Test Deps-->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.htmlunit</groupId>
|
||||
<artifactId>htmlunit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,903 @@
|
|||
package ca.uhn.fhir.rest.openapi;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* hapi-fhir-server-openapi
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import io.swagger.v3.core.util.Yaml;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.examples.Example;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.media.Content;
|
||||
import io.swagger.v3.oas.models.media.MediaType;
|
||||
import io.swagger.v3.oas.models.media.ObjectSchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_30_40;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Type;
|
||||
import org.thymeleaf.IEngineConfiguration;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.cache.AlwaysValidCacheEntryValidity;
|
||||
import org.thymeleaf.cache.ICacheEntryValidity;
|
||||
import org.thymeleaf.cache.NonCacheableCacheEntryValidity;
|
||||
import org.thymeleaf.context.IExpressionContext;
|
||||
import org.thymeleaf.context.WebContext;
|
||||
import org.thymeleaf.linkbuilder.AbstractLinkBuilder;
|
||||
import org.thymeleaf.standard.StandardDialect;
|
||||
import org.thymeleaf.templatemode.TemplateMode;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolution;
|
||||
import org.thymeleaf.templateresource.ClassLoaderTemplateResource;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class OpenApiInterceptor {
|
||||
|
||||
public static final String FHIR_JSON_RESOURCE = "FHIR-JSON-RESOURCE";
|
||||
public static final String FHIR_XML_RESOURCE = "FHIR-XML-RESOURCE";
|
||||
public static final String PAGE_SYSTEM = "System Level Operations";
|
||||
public static final String PAGE_ALL = "All";
|
||||
public static final FhirContext FHIR_CONTEXT_CANONICAL = FhirContext.forR4();
|
||||
public static final String REQUEST_DETAILS = "REQUEST_DETAILS";
|
||||
public static final String RACCOON_PNG = "raccoon.png";
|
||||
private final String mySwaggerUiVersion;
|
||||
private final TemplateEngine myTemplateEngine;
|
||||
private final Parser myFlexmarkParser;
|
||||
private final HtmlRenderer myFlexmarkRenderer;
|
||||
private final Map<String, String> myResourcePathToClasspath = new HashMap<>();
|
||||
private final Map<String, String> myExtensionToContentType = new HashMap<>();
|
||||
private String myBannerImage;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public OpenApiInterceptor() {
|
||||
mySwaggerUiVersion = initSwaggerUiWebJar();
|
||||
|
||||
myTemplateEngine = new TemplateEngine();
|
||||
ITemplateResolver resolver = new SwaggerUiTemplateResolver();
|
||||
myTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
myTemplateEngine.setDialect(dialect);
|
||||
|
||||
myTemplateEngine.setLinkBuilder(new TemplateLinkBuilder());
|
||||
|
||||
myFlexmarkParser = Parser.builder().build();
|
||||
myFlexmarkRenderer = HtmlRenderer.builder().build();
|
||||
|
||||
initResources();
|
||||
}
|
||||
|
||||
private void initResources() {
|
||||
setBannerImage(RACCOON_PNG);
|
||||
|
||||
addResourcePathToClasspath("/swagger-ui/index.html", "/ca/uhn/fhir/rest/openapi/index.html");
|
||||
addResourcePathToClasspath("/swagger-ui/" + RACCOON_PNG, "/ca/uhn/fhir/rest/openapi/raccoon.png");
|
||||
addResourcePathToClasspath("/swagger-ui/index.css", "/ca/uhn/fhir/rest/openapi/index.css");
|
||||
|
||||
myExtensionToContentType.put(".png", "image/png");
|
||||
myExtensionToContentType.put(".css", "text/css; charset=UTF-8");
|
||||
}
|
||||
|
||||
protected void addResourcePathToClasspath(String thePath, String theClasspath) {
|
||||
myResourcePathToClasspath.put(thePath, theClasspath);
|
||||
}
|
||||
|
||||
private String initSwaggerUiWebJar() {
|
||||
final String mySwaggerUiVersion;
|
||||
Properties props = new Properties();
|
||||
String resourceName = "/META-INF/maven/org.webjars/swagger-ui/pom.properties";
|
||||
try {
|
||||
InputStream resourceAsStream = ClasspathUtil.loadResourceAsStream(resourceName);
|
||||
props.load(resourceAsStream);
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException("Failed to load resource: " + resourceName);
|
||||
}
|
||||
mySwaggerUiVersion = props.getProperty("version");
|
||||
return mySwaggerUiVersion;
|
||||
}
|
||||
|
||||
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED)
|
||||
public boolean serveSwaggerUi(HttpServletRequest theRequest, HttpServletResponse theResponse, ServletRequestDetails theRequestDetails) throws IOException {
|
||||
String requestPath = theRequest.getPathInfo();
|
||||
|
||||
if (isBlank(requestPath) || requestPath.equals("/")) {
|
||||
Set<String> highestRankedAcceptValues = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequest);
|
||||
if (highestRankedAcceptValues.contains(Constants.CT_HTML)) {
|
||||
theResponse.sendRedirect("./swagger-ui/");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (requestPath.startsWith("/swagger-ui/")) {
|
||||
|
||||
return !handleResourceRequest(theResponse, theRequestDetails, requestPath);
|
||||
|
||||
} else if (requestPath.equals("/api-docs")) {
|
||||
|
||||
OpenAPI openApi = generateOpenApi(theRequestDetails);
|
||||
String response = Yaml.pretty(openApi);
|
||||
|
||||
theResponse.setContentType("text/yaml");
|
||||
theResponse.setStatus(200);
|
||||
theResponse.getWriter().write(response);
|
||||
theResponse.getWriter().close();
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean handleResourceRequest(HttpServletResponse theResponse, ServletRequestDetails theRequestDetails, String requestPath) throws IOException {
|
||||
if (requestPath.equals("/swagger-ui/") || requestPath.equals("/swagger-ui/index.html")) {
|
||||
serveSwaggerUiHtml(theRequestDetails, theResponse);
|
||||
return true;
|
||||
}
|
||||
|
||||
String resourceClasspath = myResourcePathToClasspath.get(requestPath);
|
||||
if (resourceClasspath != null) {
|
||||
theResponse.setStatus(200);
|
||||
|
||||
String extension = requestPath.substring(requestPath.lastIndexOf('.'));
|
||||
String contentType = myExtensionToContentType.get(extension);
|
||||
assert contentType != null;
|
||||
theResponse.setContentType(contentType);
|
||||
try (InputStream resource = ClasspathUtil.loadResourceAsStream(resourceClasspath)) {
|
||||
IOUtils.copy(resource, theResponse.getOutputStream());
|
||||
theResponse.getOutputStream().close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
String resourcePath = requestPath.substring("/swagger-ui/".length());
|
||||
try (InputStream resource = ClasspathUtil.loadResourceAsStream("/META-INF/resources/webjars/swagger-ui/" + mySwaggerUiVersion + "/" + resourcePath)) {
|
||||
|
||||
if (resourcePath.endsWith(".js") || resourcePath.endsWith(".map")) {
|
||||
theResponse.setContentType("application/javascript");
|
||||
theResponse.setStatus(200);
|
||||
IOUtils.copy(resource, theResponse.getOutputStream());
|
||||
theResponse.getOutputStream().close();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resourcePath.endsWith(".css")) {
|
||||
theResponse.setContentType("text/css");
|
||||
theResponse.setStatus(200);
|
||||
IOUtils.copy(resource, theResponse.getOutputStream());
|
||||
theResponse.getOutputStream().close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void serveSwaggerUiHtml(ServletRequestDetails theRequestDetails, HttpServletResponse theResponse) throws IOException {
|
||||
CapabilityStatement cs = getCapabilityStatement(theRequestDetails);
|
||||
|
||||
theResponse.setStatus(200);
|
||||
theResponse.setContentType(Constants.CT_HTML);
|
||||
|
||||
HttpServletRequest servletRequest = theRequestDetails.getServletRequest();
|
||||
ServletContext servletContext = servletRequest.getServletContext();
|
||||
WebContext context = new WebContext(servletRequest, theResponse, servletContext);
|
||||
context.setVariable(REQUEST_DETAILS, theRequestDetails);
|
||||
context.setVariable("DESCRIPTION", cs.getImplementation().getDescription());
|
||||
context.setVariable("SERVER_NAME", cs.getSoftware().getName());
|
||||
context.setVariable("SERVER_VERSION", cs.getSoftware().getVersion());
|
||||
context.setVariable("BASE_URL", cs.getImplementation().getUrl());
|
||||
context.setVariable("BANNER_IMAGE_URL", getBannerImage());
|
||||
context.setVariable("OPENAPI_DOCS", cs.getImplementation().getUrl() + "/api-docs");
|
||||
context.setVariable("FHIR_VERSION", cs.getFhirVersion().toCode());
|
||||
context.setVariable("FHIR_VERSION_CODENAME", FhirVersionEnum.forVersionString(cs.getFhirVersion().toCode()).name());
|
||||
|
||||
String copyright = cs.getCopyright();
|
||||
if (isNotBlank(copyright)) {
|
||||
copyright = myFlexmarkRenderer.render(myFlexmarkParser.parse(copyright));
|
||||
context.setVariable("COPYRIGHT_HTML", copyright);
|
||||
}
|
||||
|
||||
List<String> pageNames = new ArrayList<>();
|
||||
Map<String, Integer> resourceToCount = new HashMap<>();
|
||||
cs.getRestFirstRep().getResource().stream().forEach(t -> {
|
||||
String type = t.getType();
|
||||
pageNames.add(type);
|
||||
Extension countExtension = t.getExtensionByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
|
||||
if (countExtension != null) {
|
||||
IPrimitiveType<? extends Number> countExtensionValue = (IPrimitiveType<? extends Number>) countExtension.getValueAsPrimitive();
|
||||
if (countExtensionValue != null && countExtensionValue.hasValue()) {
|
||||
resourceToCount.put(type, countExtensionValue.getValue().intValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
pageNames.sort((o1, o2) -> {
|
||||
Integer count1 = resourceToCount.get(o1);
|
||||
Integer count2 = resourceToCount.get(o2);
|
||||
if (count1 != null && count2 != null) {
|
||||
return count2 - count1;
|
||||
}
|
||||
if (count1 != null) {
|
||||
return -1;
|
||||
}
|
||||
if (count2 != null) {
|
||||
return 1;
|
||||
}
|
||||
return o1.compareTo(o2);
|
||||
});
|
||||
|
||||
pageNames.add(0, PAGE_ALL);
|
||||
pageNames.add(1, PAGE_SYSTEM);
|
||||
|
||||
context.setVariable("PAGE_NAMES", pageNames);
|
||||
context.setVariable("PAGE_NAME_TO_COUNT", resourceToCount);
|
||||
|
||||
String page = extractPageName(theRequestDetails, PAGE_SYSTEM);
|
||||
context.setVariable("PAGE", page);
|
||||
|
||||
String outcome = myTemplateEngine.process("index.html", context);
|
||||
|
||||
theResponse.getWriter().write(outcome);
|
||||
theResponse.getWriter().close();
|
||||
}
|
||||
|
||||
private String extractPageName(ServletRequestDetails theRequestDetails, String theDefault) {
|
||||
String[] pageValues = theRequestDetails.getParameters().get("page");
|
||||
String page = null;
|
||||
if (pageValues != null && pageValues.length > 0) {
|
||||
page = pageValues[0];
|
||||
}
|
||||
if (isBlank(page)) {
|
||||
page = theDefault;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
private OpenAPI generateOpenApi(ServletRequestDetails theRequestDetails) {
|
||||
String page = extractPageName(theRequestDetails, null);
|
||||
|
||||
CapabilityStatement cs = getCapabilityStatement(theRequestDetails);
|
||||
FhirContext ctx = theRequestDetails.getFhirContext();
|
||||
|
||||
IServerConformanceProvider<?> capabilitiesProvider = null;
|
||||
RestfulServer restfulServer = theRequestDetails.getServer();
|
||||
if (restfulServer.getServerConformanceProvider() instanceof IServerConformanceProvider) {
|
||||
capabilitiesProvider = (IServerConformanceProvider<?>) restfulServer.getServerConformanceProvider();
|
||||
}
|
||||
|
||||
|
||||
OpenAPI openApi = new OpenAPI();
|
||||
|
||||
openApi.setInfo(new Info());
|
||||
openApi.getInfo().setDescription(cs.getDescription());
|
||||
openApi.getInfo().setTitle(cs.getSoftware().getName());
|
||||
openApi.getInfo().setVersion(cs.getSoftware().getVersion());
|
||||
openApi.getInfo().setContact(new Contact());
|
||||
openApi.getInfo().getContact().setName(cs.getContactFirstRep().getName());
|
||||
openApi.getInfo().getContact().setEmail(cs.getContactFirstRep().getTelecomFirstRep().getValue());
|
||||
|
||||
Server server = new Server();
|
||||
openApi.addServersItem(server);
|
||||
server.setUrl(cs.getImplementation().getUrl());
|
||||
server.setDescription(cs.getSoftware().getName());
|
||||
|
||||
Paths paths = new Paths();
|
||||
openApi.setPaths(paths);
|
||||
|
||||
if (page == null || page.equals(PAGE_SYSTEM) || page.equals(PAGE_ALL)) {
|
||||
Tag serverTag = new Tag();
|
||||
serverTag.setName(PAGE_SYSTEM);
|
||||
serverTag.setDescription("Server-level operations");
|
||||
openApi.addTagsItem(serverTag);
|
||||
|
||||
Operation capabilitiesOperation = getPathItem(paths, "/metadata", PathItem.HttpMethod.GET);
|
||||
capabilitiesOperation.addTagsItem(PAGE_SYSTEM);
|
||||
capabilitiesOperation.setSummary("server-capabilities: Fetch the server FHIR CapabilityStatement");
|
||||
addFhirResourceResponse(ctx, openApi, capabilitiesOperation, "CapabilityStatement");
|
||||
|
||||
Set<CapabilityStatement.SystemRestfulInteraction> systemInteractions = cs.getRestFirstRep().getInteraction().stream().map(t -> t.getCode()).collect(Collectors.toSet());
|
||||
|
||||
// Transaction Operation
|
||||
if (systemInteractions.contains(CapabilityStatement.SystemRestfulInteraction.TRANSACTION) || systemInteractions.contains(CapabilityStatement.SystemRestfulInteraction.BATCH)) {
|
||||
Operation transaction = getPathItem(paths, "/", PathItem.HttpMethod.POST);
|
||||
transaction.addTagsItem(PAGE_SYSTEM);
|
||||
transaction.setSummary("server-transaction: Execute a FHIR Transaction (or FHIR Batch) Bundle");
|
||||
addFhirResourceResponse(ctx, openApi, transaction, null);
|
||||
addFhirResourceRequestBody(openApi, transaction, ctx, null);
|
||||
}
|
||||
|
||||
// System History Operation
|
||||
if (systemInteractions.contains(CapabilityStatement.SystemRestfulInteraction.HISTORYSYSTEM)) {
|
||||
Operation systemHistory = getPathItem(paths, "/_history", PathItem.HttpMethod.GET);
|
||||
systemHistory.addTagsItem(PAGE_SYSTEM);
|
||||
systemHistory.setSummary("server-history: Fetch the resource change history across all resource types on the server");
|
||||
addFhirResourceResponse(ctx, openApi, systemHistory, null);
|
||||
}
|
||||
|
||||
// System-level Operations
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceOperationComponent nextOperation : cs.getRestFirstRep().getOperation()) {
|
||||
addFhirOperation(ctx, openApi, theRequestDetails, capabilitiesProvider, paths, null, nextOperation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceComponent nextResource : cs.getRestFirstRep().getResource()) {
|
||||
String resourceType = nextResource.getType();
|
||||
|
||||
if (page != null && !page.equals(resourceType) && !page.equals(PAGE_ALL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<CapabilityStatement.TypeRestfulInteraction> typeRestfulInteractions = nextResource.getInteraction().stream().map(t -> t.getCodeElement().getValue()).collect(Collectors.toSet());
|
||||
|
||||
Tag resourceTag = new Tag();
|
||||
resourceTag.setName(resourceType);
|
||||
resourceTag.setDescription("The " + resourceType + " FHIR resource type");
|
||||
openApi.addTagsItem(resourceTag);
|
||||
|
||||
// Instance Read
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.READ)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}", PathItem.HttpMethod.GET);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("read-instance: Read " + resourceType + " instance");
|
||||
addResourceIdParameter(operation);
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Instance VRead
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.VREAD)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}/_history/{version_id}", PathItem.HttpMethod.GET);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("vread-instance: Read " + resourceType + " instance with specific version");
|
||||
addResourceIdParameter(operation);
|
||||
addResourceVersionIdParameter(operation);
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Type Create
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.CREATE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType, PathItem.HttpMethod.POST);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("create-type: Create a new " + resourceType + " instance");
|
||||
addFhirResourceRequestBody(openApi, operation, ctx, genericExampleSupplier(ctx, resourceType));
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Instance Update
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.UPDATE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}", PathItem.HttpMethod.PUT);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("update-instance: Update an existing " + resourceType + " instance, or create using a client-assigned ID");
|
||||
addResourceIdParameter(operation);
|
||||
addFhirResourceRequestBody(openApi, operation, ctx, genericExampleSupplier(ctx, resourceType));
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Type history
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.HISTORYTYPE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/_history", PathItem.HttpMethod.GET);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("type-history: Fetch the resource change history for all resources of type " + resourceType);
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Instance history
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.HISTORYTYPE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}/_history", PathItem.HttpMethod.GET);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("instance-history: Fetch the resource change history for all resources of type " + resourceType);
|
||||
addResourceIdParameter(operation);
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Instance Patch
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.PATCH)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}", PathItem.HttpMethod.PATCH);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("instance-patch: Patch a resource instance of type " + resourceType + " by ID");
|
||||
addResourceIdParameter(operation);
|
||||
addFhirResourceRequestBody(openApi, operation, FHIR_CONTEXT_CANONICAL, patchExampleSupplier());
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Instance Delete
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.DELETE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType + "/{id}", PathItem.HttpMethod.DELETE);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setSummary("instance-delete: Perform a logical delete on a resource instance");
|
||||
addResourceIdParameter(operation);
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
}
|
||||
|
||||
// Search
|
||||
if (typeRestfulInteractions.contains(CapabilityStatement.TypeRestfulInteraction.SEARCHTYPE)) {
|
||||
Operation operation = getPathItem(paths, "/" + resourceType, PathItem.HttpMethod.GET);
|
||||
operation.addTagsItem(resourceType);
|
||||
operation.setDescription("This is a search type");
|
||||
operation.setSummary("search-type: Update an existing " + resourceType + " instance, or create using a client-assigned ID");
|
||||
addFhirResourceResponse(ctx, openApi, operation, null);
|
||||
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent nextSearchParam : nextResource.getSearchParam()) {
|
||||
Parameter parametersItem = new Parameter();
|
||||
operation.addParametersItem(parametersItem);
|
||||
|
||||
parametersItem.setName(nextSearchParam.getName());
|
||||
parametersItem.setIn("query");
|
||||
parametersItem.setDescription(nextSearchParam.getDocumentation());
|
||||
parametersItem.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Resource-level Operations
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceOperationComponent nextOperation : nextResource.getOperation()) {
|
||||
addFhirOperation(ctx, openApi, theRequestDetails, capabilitiesProvider, paths, resourceType, nextOperation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return openApi;
|
||||
}
|
||||
|
||||
private Supplier<IBaseResource> patchExampleSupplier() {
|
||||
return () -> {
|
||||
Parameters example = new Parameters();
|
||||
Parameters.ParametersParameterComponent operation = example
|
||||
.addParameter()
|
||||
.setName("operation");
|
||||
operation.addPart().setName("type").setValue(new StringType("add"));
|
||||
operation.addPart().setName("path").setValue(new StringType("Patient"));
|
||||
operation.addPart().setName("name").setValue(new StringType("birthDate"));
|
||||
operation.addPart().setName("value").setValue(new DateType("1930-01-01"));
|
||||
return example;
|
||||
};
|
||||
}
|
||||
|
||||
private void addSchemaFhirResource(OpenAPI theOpenApi) {
|
||||
ensureComponentsSchemasPopulated(theOpenApi);
|
||||
|
||||
if (!theOpenApi.getComponents().getSchemas().containsKey(FHIR_JSON_RESOURCE)) {
|
||||
ObjectSchema fhirJsonSchema = new ObjectSchema();
|
||||
fhirJsonSchema.setDescription("A FHIR resource");
|
||||
theOpenApi.getComponents().addSchemas(FHIR_JSON_RESOURCE, fhirJsonSchema);
|
||||
}
|
||||
|
||||
if (!theOpenApi.getComponents().getSchemas().containsKey(FHIR_XML_RESOURCE)) {
|
||||
ObjectSchema fhirXmlSchema = new ObjectSchema();
|
||||
fhirXmlSchema.setDescription("A FHIR resource");
|
||||
theOpenApi.getComponents().addSchemas(FHIR_XML_RESOURCE, fhirXmlSchema);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureComponentsSchemasPopulated(OpenAPI theOpenApi) {
|
||||
if (theOpenApi.getComponents() == null) {
|
||||
theOpenApi.setComponents(new Components());
|
||||
}
|
||||
if (theOpenApi.getComponents().getSchemas() == null) {
|
||||
theOpenApi.getComponents().setSchemas(new LinkedHashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
private CapabilityStatement getCapabilityStatement(ServletRequestDetails theRequestDetails) {
|
||||
RestfulServer restfulServer = theRequestDetails.getServer();
|
||||
IBaseConformance versionIndependentCapabilityStatement = restfulServer.getCapabilityStatement(theRequestDetails);
|
||||
return toCanonicalVersion(versionIndependentCapabilityStatement);
|
||||
}
|
||||
|
||||
private void addFhirOperation(FhirContext theFhirContext, OpenAPI theOpenApi, ServletRequestDetails theRequestDetails, IServerConformanceProvider<?> theCapabilitiesProvider, Paths thePaths, String theResourceType, CapabilityStatement.CapabilityStatementRestResourceOperationComponent theOperation) {
|
||||
if (theCapabilitiesProvider != null) {
|
||||
IdType definitionId = new IdType(theOperation.getDefinition());
|
||||
IBaseResource operationDefinitionNonCanonical = theCapabilitiesProvider.readOperationDefinition(definitionId, theRequestDetails);
|
||||
if (operationDefinitionNonCanonical == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OperationDefinition operationDefinition = toCanonicalVersion(operationDefinitionNonCanonical);
|
||||
|
||||
if (!operationDefinition.getAffectsState()) {
|
||||
|
||||
// GET form for non-state-affecting operations
|
||||
if (theResourceType != null) {
|
||||
if (operationDefinition.getType()) {
|
||||
Operation operation = getPathItem(thePaths, "/" + theResourceType + "/$" + operationDefinition.getCode(), PathItem.HttpMethod.GET);
|
||||
populateOperation(theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, true);
|
||||
}
|
||||
if (operationDefinition.getInstance()) {
|
||||
Operation operation = getPathItem(thePaths, "/" + theResourceType + "/{id}/$" + operationDefinition.getCode(), PathItem.HttpMethod.GET);
|
||||
addResourceIdParameter(operation);
|
||||
populateOperation(theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, true);
|
||||
}
|
||||
} else {
|
||||
if (operationDefinition.getSystem()) {
|
||||
Operation operation = getPathItem(thePaths, "/$" + operationDefinition.getCode(), PathItem.HttpMethod.GET);
|
||||
populateOperation(theFhirContext, theOpenApi, null, operationDefinition, operation, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// POST form for all operations
|
||||
if (theResourceType != null) {
|
||||
if (operationDefinition.getType()) {
|
||||
Operation operation = getPathItem(thePaths, "/" + theResourceType + "/$" + operationDefinition.getCode(), PathItem.HttpMethod.POST);
|
||||
populateOperation(theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, false);
|
||||
}
|
||||
if (operationDefinition.getInstance()) {
|
||||
Operation operation = getPathItem(thePaths, "/" + theResourceType + "/{id}/$" + operationDefinition.getCode(), PathItem.HttpMethod.POST);
|
||||
addResourceIdParameter(operation);
|
||||
populateOperation(theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, false);
|
||||
}
|
||||
} else {
|
||||
if (operationDefinition.getSystem()) {
|
||||
Operation operation = getPathItem(thePaths, "/$" + operationDefinition.getCode(), PathItem.HttpMethod.POST);
|
||||
populateOperation(theFhirContext, theOpenApi, null, operationDefinition, operation, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void populateOperation(FhirContext theFhirContext, OpenAPI theOpenApi, String theResourceType, OperationDefinition theOperationDefinition, Operation theOperation, boolean theGet) {
|
||||
if (theResourceType == null) {
|
||||
theOperation.addTagsItem(PAGE_SYSTEM);
|
||||
} else {
|
||||
theOperation.addTagsItem(theResourceType);
|
||||
}
|
||||
theOperation.setSummary(theOperationDefinition.getTitle());
|
||||
theOperation.setDescription(theOperationDefinition.getDescription());
|
||||
addFhirResourceResponse(theFhirContext, theOpenApi, theOperation, null);
|
||||
|
||||
if (theGet) {
|
||||
|
||||
for (OperationDefinition.OperationDefinitionParameterComponent nextParameter : theOperationDefinition.getParameter()) {
|
||||
Parameter parametersItem = new Parameter();
|
||||
theOperation.addParametersItem(parametersItem);
|
||||
|
||||
parametersItem.setName(nextParameter.getName());
|
||||
parametersItem.setIn("query");
|
||||
parametersItem.setDescription(nextParameter.getDocumentation());
|
||||
parametersItem.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
parametersItem.setRequired(nextParameter.getMin() > 0);
|
||||
|
||||
List<Extension> exampleExtensions = nextParameter.getExtensionsByUrl(HapiExtensions.EXT_OP_PARAMETER_EXAMPLE_VALUE);
|
||||
if (exampleExtensions.size() == 1) {
|
||||
parametersItem.setExample(exampleExtensions.get(0).getValueAsPrimitive().getValueAsString());
|
||||
} else if (exampleExtensions.size() > 1) {
|
||||
for (Extension next : exampleExtensions) {
|
||||
String nextExample = next.getValueAsPrimitive().getValueAsString();
|
||||
parametersItem.addExample(nextExample, new Example().value(nextExample));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Parameters exampleRequestBody = new Parameters();
|
||||
for (OperationDefinition.OperationDefinitionParameterComponent nextSearchParam : theOperationDefinition.getParameter()) {
|
||||
Parameters.ParametersParameterComponent param = exampleRequestBody.addParameter();
|
||||
param.setName(nextSearchParam.getName());
|
||||
String paramType = nextSearchParam.getType();
|
||||
switch (defaultString(paramType)) {
|
||||
case "uri":
|
||||
case "url":
|
||||
case "code":
|
||||
case "string": {
|
||||
IPrimitiveType<?> type = (IPrimitiveType<?>) FHIR_CONTEXT_CANONICAL.getElementDefinition(paramType).newInstance();
|
||||
type.setValueAsString("example");
|
||||
param.setValue((Type) type);
|
||||
break;
|
||||
}
|
||||
case "integer": {
|
||||
IPrimitiveType<?> type = (IPrimitiveType<?>) FHIR_CONTEXT_CANONICAL.getElementDefinition(paramType).newInstance();
|
||||
type.setValueAsString("0");
|
||||
param.setValue((Type) type);
|
||||
break;
|
||||
}
|
||||
case "boolean": {
|
||||
IPrimitiveType<?> type = (IPrimitiveType<?>) FHIR_CONTEXT_CANONICAL.getElementDefinition(paramType).newInstance();
|
||||
type.setValueAsString("false");
|
||||
param.setValue((Type) type);
|
||||
break;
|
||||
}
|
||||
case "CodeableConcept": {
|
||||
CodeableConcept type = new CodeableConcept();
|
||||
type.getCodingFirstRep().setSystem("http://example.com");
|
||||
type.getCodingFirstRep().setCode("1234");
|
||||
param.setValue(type);
|
||||
break;
|
||||
}
|
||||
case "Coding": {
|
||||
Coding type = new Coding();
|
||||
type.setSystem("http://example.com");
|
||||
type.setCode("1234");
|
||||
param.setValue(type);
|
||||
break;
|
||||
}
|
||||
case "Reference":
|
||||
Reference reference = new Reference("example");
|
||||
param.setValue(reference);
|
||||
break;
|
||||
case "Resource":
|
||||
if (theResourceType != null) {
|
||||
IBaseResource resource = FHIR_CONTEXT_CANONICAL.getResourceDefinition(theResourceType).newInstance();
|
||||
resource.setId("1");
|
||||
param.setResource((Resource) resource);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String exampleRequestBodyString = FHIR_CONTEXT_CANONICAL.newJsonParser().setPrettyPrint(true).encodeResourceToString(exampleRequestBody);
|
||||
theOperation.setRequestBody(new RequestBody());
|
||||
theOperation.getRequestBody().setContent(new Content());
|
||||
MediaType mediaType = new MediaType();
|
||||
mediaType.setExample(exampleRequestBodyString);
|
||||
mediaType.setSchema(new Schema().type("object").title("FHIR Resource"));
|
||||
theOperation.getRequestBody().getContent().addMediaType(Constants.CT_FHIR_JSON_NEW, mediaType);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Operation getPathItem(Paths thePaths, String thePath, PathItem.HttpMethod theMethod) {
|
||||
PathItem pathItem;
|
||||
if (thePaths.containsKey(thePath)) {
|
||||
pathItem = thePaths.get(thePath);
|
||||
} else {
|
||||
pathItem = new PathItem();
|
||||
thePaths.addPathItem(thePath, pathItem);
|
||||
}
|
||||
|
||||
switch (theMethod) {
|
||||
case POST:
|
||||
assert pathItem.getPost() == null : "Have duplicate POST at path: " + thePath;
|
||||
return pathItem.post(new Operation()).getPost();
|
||||
case GET:
|
||||
assert pathItem.getGet() == null : "Have duplicate GET at path: " + thePath;
|
||||
return pathItem.get(new Operation()).getGet();
|
||||
case PUT:
|
||||
assert pathItem.getPut() == null;
|
||||
return pathItem.put(new Operation()).getPut();
|
||||
case PATCH:
|
||||
assert pathItem.getPatch() == null;
|
||||
return pathItem.patch(new Operation()).getPatch();
|
||||
case DELETE:
|
||||
assert pathItem.getDelete() == null;
|
||||
return pathItem.delete(new Operation()).getDelete();
|
||||
case HEAD:
|
||||
case OPTIONS:
|
||||
case TRACE:
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private void addFhirResourceRequestBody(OpenAPI theOpenApi, Operation theOperation, FhirContext theExampleFhirContext, Supplier<IBaseResource> theExampleSupplier) {
|
||||
RequestBody requestBody = new RequestBody();
|
||||
requestBody.setContent(provideContentFhirResource(theOpenApi, theExampleFhirContext, theExampleSupplier));
|
||||
theOperation.setRequestBody(requestBody);
|
||||
}
|
||||
|
||||
private void addResourceVersionIdParameter(Operation theOperation) {
|
||||
Parameter parameter = new Parameter();
|
||||
parameter.setName("version_id");
|
||||
parameter.setIn("path");
|
||||
parameter.setDescription("The resource version ID");
|
||||
parameter.setExample("1");
|
||||
parameter.setSchema(new Schema().type("string").minimum(new BigDecimal(1)));
|
||||
parameter.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
theOperation.addParametersItem(parameter);
|
||||
}
|
||||
|
||||
private void addFhirResourceResponse(FhirContext theFhirContext, OpenAPI theOpenApi, Operation theOperation, String theResourceType) {
|
||||
theOperation.setResponses(new ApiResponses());
|
||||
ApiResponse response200 = new ApiResponse();
|
||||
response200.setDescription("Success");
|
||||
response200.setContent(provideContentFhirResource(theOpenApi, theFhirContext, genericExampleSupplier(theFhirContext, theResourceType)));
|
||||
theOperation.getResponses().addApiResponse("200", response200);
|
||||
}
|
||||
|
||||
private Supplier<IBaseResource> genericExampleSupplier(FhirContext theFhirContext, String theResourceType) {
|
||||
if (theResourceType == null) {
|
||||
return null;
|
||||
}
|
||||
return () -> {
|
||||
IBaseResource example = null;
|
||||
if (theResourceType != null) {
|
||||
example = theFhirContext.getResourceDefinition(theResourceType).newInstance();
|
||||
}
|
||||
return example;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private Content provideContentFhirResource(OpenAPI theOpenApi, FhirContext theExampleFhirContext, Supplier<IBaseResource> theExampleSupplier) {
|
||||
addSchemaFhirResource(theOpenApi);
|
||||
Content retVal = new Content();
|
||||
|
||||
MediaType jsonSchema = new MediaType().schema(new ObjectSchema().$ref("#/components/schemas/" + FHIR_JSON_RESOURCE));
|
||||
if (theExampleSupplier != null) {
|
||||
jsonSchema.setExample(theExampleFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(theExampleSupplier.get()));
|
||||
}
|
||||
retVal.addMediaType(Constants.CT_FHIR_JSON_NEW, jsonSchema);
|
||||
|
||||
MediaType xmlSchema = new MediaType().schema(new ObjectSchema().$ref("#/components/schemas/" + FHIR_XML_RESOURCE));
|
||||
if (theExampleSupplier != null) {
|
||||
xmlSchema.setExample(theExampleFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(theExampleSupplier.get()));
|
||||
}
|
||||
retVal.addMediaType(Constants.CT_FHIR_XML_NEW, xmlSchema);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void addResourceIdParameter(Operation theOperation) {
|
||||
Parameter parameter = new Parameter();
|
||||
parameter.setName("id");
|
||||
parameter.setIn("path");
|
||||
parameter.setDescription("The resource ID");
|
||||
parameter.setExample("123");
|
||||
parameter.setSchema(new Schema().type("string").minimum(new BigDecimal(1)));
|
||||
parameter.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
theOperation.addParametersItem(parameter);
|
||||
}
|
||||
|
||||
protected ClassLoaderTemplateResource getIndexTemplate() {
|
||||
return new ClassLoaderTemplateResource(myResourcePathToClasspath.get("/swagger-ui/index.html"), StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
public void setBannerImage(String theBannerImage) {
|
||||
myBannerImage = theBannerImage;
|
||||
}
|
||||
|
||||
public String getBannerImage() {
|
||||
return myBannerImage;
|
||||
}
|
||||
|
||||
private class SwaggerUiTemplateResolver implements ITemplateResolver {
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemplateResolution resolveTemplate(IEngineConfiguration configuration, String ownerTemplate, String template, Map<String, Object> templateResolutionAttributes) {
|
||||
ClassLoaderTemplateResource resource = getIndexTemplate();
|
||||
ICacheEntryValidity cacheValidity = new AlwaysValidCacheEntryValidity();
|
||||
return new TemplateResolution(resource, TemplateMode.HTML, cacheValidity);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TemplateLinkBuilder extends AbstractLinkBuilder {
|
||||
|
||||
@Override
|
||||
public String buildLink(IExpressionContext theExpressionContext, String theBase, Map<String, Object> theParameters) {
|
||||
|
||||
ServletRequestDetails requestDetails = (ServletRequestDetails) theExpressionContext.getVariable(REQUEST_DETAILS);
|
||||
|
||||
IServerAddressStrategy addressStrategy = requestDetails.getServer().getServerAddressStrategy();
|
||||
String baseUrl = addressStrategy.determineServerBase(requestDetails.getServletRequest().getServletContext(), requestDetails.getServletRequest());
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(baseUrl);
|
||||
builder.append(theBase);
|
||||
if (!theParameters.isEmpty()) {
|
||||
builder.append("?");
|
||||
for (Iterator<Map.Entry<String, Object>> iter = theParameters.entrySet().iterator(); iter.hasNext(); ) {
|
||||
Map.Entry<String, Object> nextEntry = iter.next();
|
||||
builder.append(UrlUtil.escapeUrlParam(nextEntry.getKey()));
|
||||
builder.append("=");
|
||||
builder.append(UrlUtil.escapeUrlParam(defaultIfNull(nextEntry.getValue(), "").toString()));
|
||||
if (iter.hasNext()) {
|
||||
builder.append("&");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Resource> T toCanonicalVersion(IBaseResource theNonCanonical) {
|
||||
IBaseResource canonical;
|
||||
if (theNonCanonical instanceof org.hl7.fhir.dstu3.model.Resource) {
|
||||
canonical = VersionConvertor_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theNonCanonical, true);
|
||||
} else if (theNonCanonical instanceof org.hl7.fhir.r5.model.Resource) {
|
||||
canonical = VersionConvertor_40_50.convertResource((org.hl7.fhir.r5.model.Resource) theNonCanonical);
|
||||
} else {
|
||||
canonical = theNonCanonical;
|
||||
}
|
||||
return (T) canonical;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.scheme-container, .information-container
|
||||
{
|
||||
display: none
|
||||
}
|
||||
|
||||
.banner {
|
||||
padding-top: 20px;
|
||||
padding-left: 25px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #AAA;
|
||||
border-bottom: 2px solid #888;
|
||||
}
|
||||
|
||||
.banner H1 {
|
||||
font-family: sans-serif;
|
||||
height: 150%;
|
||||
position: relative;
|
||||
line-height: 120%;
|
||||
top: 6px;
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
.banner .version {
|
||||
font-size: 30%;
|
||||
position: relative;
|
||||
top: -11px;
|
||||
background-color: #888;
|
||||
color: #FFF;
|
||||
padding: 5px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.bannerCopyright {
|
||||
font-family: sans-serif;
|
||||
padding-top: 20px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
padding-bottom: 20px;
|
||||
background-color: #CCC;
|
||||
border-bottom: 2px solid #888;
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.banner2 {
|
||||
font-family: sans-serif;
|
||||
padding-top: 30px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
padding-bottom: 20px;
|
||||
display: table;
|
||||
flex-direction: row;
|
||||
background-color: #CCC;
|
||||
border-bottom: 2px solid #888;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.banner2 > DIV {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.banner2 > DIV > DIV {
|
||||
display: table-cell;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.banner2_key {
|
||||
min-width: 100px;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.banner2_value {
|
||||
padding-left: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.banner3 {
|
||||
font-family: sans-serif;
|
||||
padding-top: 20px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
padding-bottom: 20px;
|
||||
background-color: #EEE;
|
||||
border-bottom: 2px solid #888;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pageButtons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pageButton {
|
||||
background-color: #FFF;
|
||||
color: #666;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
line-height: 0.5em;
|
||||
border: 1px solid #FFF;
|
||||
}
|
||||
|
||||
.pageButton:HOVER {
|
||||
border: 1px solid #888;
|
||||
}
|
||||
|
||||
.pageButtonSelected {
|
||||
background-color: #888;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.resourceCountBadge {
|
||||
font-size: 0.8em;
|
||||
background: #DDD;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
color: #000;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<link rel="stylesheet" type="text/css" href="./index.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
</div>
|
||||
<div class="banner">
|
||||
<img th:src="${BANNER_IMAGE_URL}" style="height: 100px;"/>
|
||||
<h1>[[${DESCRIPTION}]]<br/><span class="version" th:text="${SERVER_NAME} + ' ' + ${SERVER_VERSION}"></span></h1>
|
||||
</div>
|
||||
<div class="banner2">
|
||||
<div>
|
||||
<div class="banner2_key">FHIR Server Base URL</div><div class="banner2_value"><a th:href="${BASE_URL}" th:text="${BASE_URL}"></a></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="banner2_key">OpenAPI Docs</div><div class="banner2_value"><a th:href="${OPENAPI_DOCS}" th:text="${OPENAPI_DOCS}"></a></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="banner2_key">FHIR Version</div><div class="banner2_value">[[${FHIR_VERSION}]] ([[${FHIR_VERSION_CODENAME}]])</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bannerCopyright" th:if="${COPYRIGHT_HTML} != null" th:utext="${COPYRIGHT_HTML}">
|
||||
</div>
|
||||
<div class="banner3">
|
||||
<div class="pageButtons" id="pageButtons">
|
||||
<a class="pageButton" th:each="pageName : ${PAGE_NAMES}" th:classappend="${pageName} == ${PAGE} ? 'pageButtonSelected' : ''" th:href="@{/swagger-ui/(page=${pageName})}">
|
||||
[[${pageName}]]
|
||||
<span th:if="${PAGE_NAME_TO_COUNT.containsKey(pageName)}" th:text="${PAGE_NAME_TO_COUNT.get(pageName)}" class="resourceCountBadge"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "[[@{/api-docs(page=${PAGE})}]]",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
// SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
// layout: "StandaloneLayout"
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
|
@ -0,0 +1,274 @@
|
|||
package ca.uhn.fhir.rest.openapi;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.test.utilities.HtmlUtil;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import com.gargoylesoftware.htmlunit.html.DomElement;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlDivision;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import io.swagger.v3.core.util.Yaml;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class OpenApiInterceptorTest {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(OpenApiInterceptorTest.class);
|
||||
private FhirContext myFhirContext = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
@RegisterExtension
|
||||
@Order(0)
|
||||
protected RestfulServerExtension myServer = new RestfulServerExtension(myFhirContext)
|
||||
.withServletPath("/fhir/*")
|
||||
.withServer(t -> t.registerProvider(new HashMapResourceProvider<>(myFhirContext, Patient.class)))
|
||||
.withServer(t -> t.registerProvider(new HashMapResourceProvider<>(myFhirContext, Observation.class)))
|
||||
.withServer(t -> t.registerProvider(new MyLastNProvider()))
|
||||
.withServer(t -> t.registerInterceptor(new ResponseHighlighterInterceptor()));
|
||||
private CloseableHttpClient myClient;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myClient = HttpClientBuilder.create().build();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws IOException {
|
||||
myClient.close();
|
||||
myServer.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchSwagger() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
String resp;
|
||||
HttpGet get = new HttpGet("http://localhost:" + myServer.getPort() + "/fhir/metadata?_pretty=true");
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("CapabilityStatement: {}", resp);
|
||||
}
|
||||
|
||||
get = new HttpGet("http://localhost:" + myServer.getPort() + "/fhir/api-docs");
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", response.getStatusLine());
|
||||
ourLog.info("Response: {}", resp);
|
||||
}
|
||||
|
||||
OpenAPI parsed = Yaml.mapper().readValue(resp, OpenAPI.class);
|
||||
|
||||
PathItem fooOpPath = parsed.getPaths().get("/$foo-op");
|
||||
assertNull(fooOpPath.getGet());
|
||||
assertNotNull(fooOpPath.getPost());
|
||||
assertEquals("Foo Op Description", fooOpPath.getPost().getDescription());
|
||||
assertEquals("Foo Op Short", fooOpPath.getPost().getSummary());
|
||||
|
||||
PathItem lastNPath = parsed.getPaths().get("/Observation/$lastn");
|
||||
assertNull(lastNPath.getPost());
|
||||
assertNotNull(lastNPath.getGet());
|
||||
assertEquals("LastN Description", lastNPath.getGet().getDescription());
|
||||
assertEquals("LastN Short", lastNPath.getGet().getSummary());
|
||||
assertEquals(4, lastNPath.getGet().getParameters().size());
|
||||
assertEquals("Subject description", lastNPath.getGet().getParameters().get(0).getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectFromBaseUrl() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
HttpGet get;
|
||||
|
||||
get = new HttpGet("http://localhost:" + myServer.getPort() + "/fhir/");
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
get = new HttpGet("http://localhost:" + myServer.getPort() + "/fhir/");
|
||||
get.addHeader(Constants.HEADER_ACCEPT, Constants.CT_HTML);
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", response);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(responseString, containsString("<title>Swagger UI</title>"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSwaggerUiWithResourceCounts() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor());
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
|
||||
String resp = fetchSwaggerUi(url);
|
||||
List<String> buttonTexts = parsePageButtonTexts(resp, url);
|
||||
assertThat(buttonTexts.toString(), buttonTexts, Matchers.contains("All", "System Level Operations", "Patient 2", "OperationDefinition 1", "Observation 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwaggerUiWithCopyright() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor());
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
|
||||
String resp = fetchSwaggerUi(url);
|
||||
assertThat(resp, resp, containsString("<p>This server is copyright <strong>Example Org</strong> 2021</p>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwaggerUiWithResourceCounts_OneResourceOnly() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor("OperationDefinition"));
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
|
||||
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
|
||||
String resp = fetchSwaggerUi(url);
|
||||
List<String> buttonTexts = parsePageButtonTexts(resp, url);
|
||||
assertThat(buttonTexts.toString(), buttonTexts, Matchers.contains("All", "System Level Operations", "OperationDefinition 1", "Observation", "Patient"));
|
||||
}
|
||||
|
||||
private String fetchSwaggerUi(String url) throws IOException {
|
||||
String resp;
|
||||
HttpGet get = new HttpGet(url);
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", response.getStatusLine());
|
||||
ourLog.info("Response: {}", resp);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
private List<String> parsePageButtonTexts(String resp, String url) throws IOException {
|
||||
HtmlPage html = HtmlUtil.parseAsHtml(resp, new URL(url));
|
||||
HtmlDivision pageButtons = (HtmlDivision) html.getElementById("pageButtons");
|
||||
List<String> buttonTexts = new ArrayList<>();
|
||||
for (DomElement next : pageButtons.getChildElements()) {
|
||||
buttonTexts.add(next.asNormalizedText());
|
||||
}
|
||||
return buttonTexts;
|
||||
}
|
||||
|
||||
|
||||
public static class AddResourceCountsInterceptor {
|
||||
|
||||
private final HashSet<String> myResourceNamesToAddTo;
|
||||
|
||||
public AddResourceCountsInterceptor(String... theResourceNamesToAddTo) {
|
||||
myResourceNamesToAddTo = new HashSet<>(Arrays.asList(theResourceNamesToAddTo));
|
||||
}
|
||||
|
||||
@Hook(Pointcut.SERVER_CAPABILITY_STATEMENT_GENERATED)
|
||||
public void capabilityStatementGenerated(IBaseConformance theCapabilityStatement) {
|
||||
CapabilityStatement cs = (CapabilityStatement) theCapabilityStatement;
|
||||
cs.setCopyright("This server is copyright **Example Org** 2021");
|
||||
|
||||
int numResources = cs.getRestFirstRep().getResource().size();
|
||||
for (int i = 0; i < numResources; i++) {
|
||||
|
||||
CapabilityStatement.CapabilityStatementRestResourceComponent restResource = cs.getRestFirstRep().getResource().get(i);
|
||||
if (!myResourceNamesToAddTo.isEmpty() && !myResourceNamesToAddTo.contains(restResource.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
restResource.addExtension(
|
||||
ExtensionConstants.CONF_RESOURCE_COUNT,
|
||||
new DecimalType(i) // reverse order
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MyLastNProvider {
|
||||
|
||||
|
||||
@Description(value = "LastN Description", shortDefinition = "LastN Short")
|
||||
@Operation(name = Constants.OPERATION_LASTN, typeName = "Observation", idempotent = true)
|
||||
public IBaseBundle lastN(
|
||||
@Description(value = "Subject description", shortDefinition = "Subject short", example = {"Patient/456", "Patient/789"})
|
||||
@OperationParam(name = "subject", typeName = "reference", min = 0, max = 1) IBaseReference theSubject,
|
||||
@OperationParam(name = "category", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List<IBaseCoding> theCategories,
|
||||
@OperationParam(name = "code", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List<IBaseCoding> theCodes,
|
||||
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1) IPrimitiveType<Integer> theMax
|
||||
) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Description(value = "Foo Op Description", shortDefinition = "Foo Op Short")
|
||||
@Operation(name = "foo-op", idempotent = false)
|
||||
public IBaseBundle foo(
|
||||
ServletRequestDetails theRequestDetails,
|
||||
@Description(shortDefinition = "Reference description", example = "Patient/123")
|
||||
@OperationParam(name = "subject", typeName = "reference", min = 0, max = 1) IBaseReference theSubject,
|
||||
@OperationParam(name = "category", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List<IBaseCoding> theCategories,
|
||||
@OperationParam(name = "code", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List<IBaseCoding> theCodes,
|
||||
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1) IPrimitiveType<Integer> theMax
|
||||
) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Patch(type = Patient.class)
|
||||
public MethodOutcome patch(HttpServletRequest theRequest, @IdParam IIdType theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails, @ResourceParam String theBody, PatchTypeEnum thePatchType, @ResourceParam IBaseParameters theRequestBody) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package ca.uhn.fhir.rest.openapi;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
|
||||
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.test.utilities.HtmlUtil;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import com.gargoylesoftware.htmlunit.html.DomElement;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlDivision;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import io.swagger.v3.core.util.Yaml;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class OpenApiInterceptorWithAuthorizationInterceptorTest {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(OpenApiInterceptorWithAuthorizationInterceptorTest.class);
|
||||
private FhirContext myFhirContext = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
@RegisterExtension
|
||||
@Order(0)
|
||||
protected RestfulServerExtension myServer = new RestfulServerExtension(myFhirContext)
|
||||
.withServletPath("/fhir/*")
|
||||
.withServer(t -> t.registerProvider(new HashMapResourceProvider<>(myFhirContext, Patient.class)))
|
||||
.withServer(t -> t.registerProvider(new HashMapResourceProvider<>(myFhirContext, Observation.class)))
|
||||
.withServer(t -> t.registerProvider(new OpenApiInterceptorTest.MyLastNProvider()))
|
||||
.withServer(t -> t.registerInterceptor(new ResponseHighlighterInterceptor()));
|
||||
private CloseableHttpClient myClient;
|
||||
private AuthorizationInterceptor myAuthorizationInterceptor;
|
||||
private List<IAuthRule> myRules;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myClient = HttpClientBuilder.create().build();
|
||||
myAuthorizationInterceptor = new AuthorizationInterceptor() {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
return myRules;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws IOException {
|
||||
myClient.close();
|
||||
myServer.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchSwagger_AllowAll() throws IOException {
|
||||
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor());
|
||||
myServer.getRestfulServer().registerInterceptor(myAuthorizationInterceptor);
|
||||
|
||||
myRules = new RuleBuilder()
|
||||
.allowAll()
|
||||
.build();
|
||||
|
||||
String resp;
|
||||
HttpGet get;
|
||||
|
||||
get = new HttpGet("http://localhost:" + myServer.getPort() + "/fhir/api-docs");
|
||||
try (CloseableHttpResponse response = myClient.execute(get)) {
|
||||
resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", response.getStatusLine());
|
||||
ourLog.info("Response: {}", resp);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
OpenAPI parsed = Yaml.mapper().readValue(resp, OpenAPI.class);
|
||||
assertNotNull(parsed.getPaths().get("/Patient").getPost());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE7-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE8-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -51,7 +52,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
|
||||
public abstract class RequestDetails {
|
||||
|
||||
private final StopWatch myRequestStopwatch = new StopWatch();
|
||||
private final StopWatch myRequestStopwatch;
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
private String myTenantId;
|
||||
private String myCompartmentName;
|
||||
|
@ -81,6 +82,37 @@ public abstract class RequestDetails {
|
|||
*/
|
||||
public RequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||
myInterceptorBroadcaster = theInterceptorBroadcaster;
|
||||
myRequestStopwatch = new StopWatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
public RequestDetails(ServletRequestDetails theRequestDetails) {
|
||||
myInterceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
|
||||
myRequestStopwatch = theRequestDetails.getRequestStopwatch();
|
||||
myTenantId = theRequestDetails.getTenantId();
|
||||
myCompartmentName = theRequestDetails.getCompartmentName();
|
||||
myCompleteUrl = theRequestDetails.getCompleteUrl();
|
||||
myFhirServerBase = theRequestDetails.getFhirServerBase();
|
||||
myId = theRequestDetails.getId();
|
||||
myOperation = theRequestDetails.getOperation();
|
||||
myParameters = theRequestDetails.getParameters();
|
||||
myRequestContents = theRequestDetails.getRequestContentsIfLoaded();
|
||||
myRequestPath = theRequestDetails.getRequestPath();
|
||||
myRequestType = theRequestDetails.getRequestType();
|
||||
myResourceName = theRequestDetails.getResourceName();
|
||||
myRespondGzip = theRequestDetails.isRespondGzip();
|
||||
myResponse = theRequestDetails.getResponse();
|
||||
myRestOperationType = theRequestDetails.getRestOperationType();
|
||||
mySecondaryOperation = theRequestDetails.getSecondaryOperation();
|
||||
mySubRequest = theRequestDetails.isSubRequest();
|
||||
myUnqualifiedToQualifiedNames = theRequestDetails.getUnqualifiedToQualifiedNames();
|
||||
myUserData = theRequestDetails.getUserData();
|
||||
myResource = theRequestDetails.getResource();
|
||||
myRequestId = theRequestDetails.getRequestId();
|
||||
myTransactionGuid = theRequestDetails.getTransactionGuid();
|
||||
myFixedConditionalUrl = theRequestDetails.getFixedConditionalUrl();
|
||||
}
|
||||
|
||||
public String getFixedConditionalUrl() {
|
||||
|
|
|
@ -30,14 +30,14 @@ import java.util.List;
|
|||
public class Bindings {
|
||||
private final IdentityHashMap<SearchMethodBinding, String> myNamedSearchMethodBindingToName;
|
||||
private final HashMap<String, List<SearchMethodBinding>> mySearchNameToBindings;
|
||||
private final HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
||||
private final IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private final HashMap<String, List<OperationMethodBinding>> myOperationIdToBindings;
|
||||
private final IdentityHashMap<OperationMethodBinding, String> myOperationBindingToId;
|
||||
|
||||
public Bindings(IdentityHashMap<SearchMethodBinding, String> theNamedSearchMethodBindingToName, HashMap<String, List<SearchMethodBinding>> theSearchNameToBindings, HashMap<String, List<OperationMethodBinding>> theOperationNameToBindings, IdentityHashMap<OperationMethodBinding, String> theOperationBindingToName) {
|
||||
public Bindings(IdentityHashMap<SearchMethodBinding, String> theNamedSearchMethodBindingToName, HashMap<String, List<SearchMethodBinding>> theSearchNameToBindings, HashMap<String, List<OperationMethodBinding>> theOperationIdToBindings, IdentityHashMap<OperationMethodBinding, String> theOperationBindingToName) {
|
||||
myNamedSearchMethodBindingToName = theNamedSearchMethodBindingToName;
|
||||
mySearchNameToBindings = theSearchNameToBindings;
|
||||
myOperationNameToBindings = theOperationNameToBindings;
|
||||
myOperationBindingToName = theOperationBindingToName;
|
||||
myOperationIdToBindings = theOperationIdToBindings;
|
||||
myOperationBindingToId = theOperationBindingToName;
|
||||
}
|
||||
|
||||
public IdentityHashMap<SearchMethodBinding, String> getNamedSearchMethodBindingToName() {
|
||||
|
@ -48,11 +48,11 @@ public class Bindings {
|
|||
return mySearchNameToBindings;
|
||||
}
|
||||
|
||||
public HashMap<String, List<OperationMethodBinding>> getOperationNameToBindings() {
|
||||
return myOperationNameToBindings;
|
||||
public HashMap<String, List<OperationMethodBinding>> getOperationIdToBindings() {
|
||||
return myOperationIdToBindings;
|
||||
}
|
||||
|
||||
public IdentityHashMap<OperationMethodBinding, String> getOperationBindingToName() {
|
||||
return myOperationBindingToName;
|
||||
public IdentityHashMap<OperationMethodBinding, String> getOperationBindingToId() {
|
||||
return myOperationBindingToId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,11 @@ package ca.uhn.fhir.rest.server;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
public interface IServerConformanceProvider<T extends IBaseResource> {
|
||||
|
||||
|
@ -34,6 +37,11 @@ public interface IServerConformanceProvider<T extends IBaseResource> {
|
|||
*/
|
||||
T getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails);
|
||||
|
||||
@Read(typeName = "OperationDefinition")
|
||||
default IBaseResource readOperationDefinition(@IdParam IIdType theId, RequestDetails theRequestDetails) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setter is needed in implementation classes (along with
|
||||
* a no-arg constructor) to avoid reference cycles in the
|
||||
|
|
|
@ -67,6 +67,7 @@ import com.google.common.collect.Lists;
|
|||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -148,6 +149,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
private FhirContext myFhirContext;
|
||||
private boolean myIgnoreServerParsedRequestParameters = true;
|
||||
private String myImplementationDescription;
|
||||
private String myCopyright;
|
||||
private IPagingProvider myPagingProvider;
|
||||
private Integer myDefaultPageSize;
|
||||
private Integer myMaximumPageSize;
|
||||
|
@ -157,7 +159,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
|
||||
private ResourceBinding myServerBinding = new ResourceBinding();
|
||||
private ResourceBinding myGlobalBinding = new ResourceBinding();
|
||||
private BaseMethodBinding<?> myServerConformanceMethod;
|
||||
private ConformanceMethodBinding myServerConformanceMethod;
|
||||
private Object myServerConformanceProvider;
|
||||
private String myServerName = "HAPI FHIR Server";
|
||||
/**
|
||||
|
@ -234,6 +236,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
RestfulServerConfiguration result = new RestfulServerConfiguration();
|
||||
result.setResourceBindings(getResourceBindings());
|
||||
result.setServerBindings(getServerBindings());
|
||||
result.setGlobalBindings(getGlobalBindings());
|
||||
result.setImplementationDescription(getImplementationDescription());
|
||||
result.setServerVersion(getServerVersion());
|
||||
result.setServerName(getServerName());
|
||||
|
@ -252,6 +255,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
return result;
|
||||
}
|
||||
|
||||
private List<BaseMethodBinding<?>> getGlobalBindings() {
|
||||
return myGlobalBinding.getMethodBindings();
|
||||
}
|
||||
|
||||
protected List<String> createPoweredByAttributes() {
|
||||
return Lists.newArrayList("FHIR Server", "FHIR " + myFhirContext.getVersion().getVersion().getFhirVersionString() + "/" + myFhirContext.getVersion().getVersion().name());
|
||||
}
|
||||
|
@ -458,7 +465,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
count++;
|
||||
|
||||
if (foundMethodBinding instanceof ConformanceMethodBinding) {
|
||||
myServerConformanceMethod = foundMethodBinding;
|
||||
myServerConformanceMethod = (ConformanceMethodBinding) foundMethodBinding;
|
||||
if (myServerConformanceProvider == null) {
|
||||
myServerConformanceProvider = theProvider;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -635,6 +645,20 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
myImplementationDescription = theImplementationDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server copyright (will be added to the CapabilityStatement). Note that FHIR allows Markdown in this string.
|
||||
*/
|
||||
public String getCopyright() {
|
||||
return myCopyright;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server copyright (will be added to the CapabilityStatement). Note that FHIR allows Markdown in this string.
|
||||
*/
|
||||
public void setCopyright(String theCopyright) {
|
||||
myCopyright = theCopyright;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all registered server interceptors
|
||||
*
|
||||
|
@ -806,8 +830,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
/**
|
||||
* Provides the resource providers for this server
|
||||
*/
|
||||
public Collection<IResourceProvider> getResourceProviders() {
|
||||
return myResourceProviders;
|
||||
public List<IResourceProvider> getResourceProviders() {
|
||||
return Collections.unmodifiableList(myResourceProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -853,6 +877,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
public String getServerBaseForRequest(ServletRequestDetails theRequest) {
|
||||
String fhirServerBase;
|
||||
fhirServerBase = myServerAddressStrategy.determineServerBase(getServletContext(), theRequest.getServletRequest());
|
||||
assert isNotBlank(fhirServerBase) : "Server Address Strategy did not return a value";
|
||||
|
||||
if (fhirServerBase.endsWith("/")) {
|
||||
fhirServerBase = fhirServerBase.substring(0, fhirServerBase.length() - 1);
|
||||
|
@ -1363,9 +1388,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
*/
|
||||
findResourceMethods(new PageProvider());
|
||||
|
||||
} catch (Exception ex) {
|
||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("An error occurred while loading request handlers!", e);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
myStarted = true;
|
||||
|
@ -1984,6 +2009,17 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
myDefaultPreferReturn = theDefaultPreferReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CapabilityStatement based on the given request
|
||||
*/
|
||||
public IBaseConformance getCapabilityStatement(ServletRequestDetails theRequestDetails) {
|
||||
// Create a cloned request details so we can make it indicate that this is a capabilities request
|
||||
ServletRequestDetails requestDetails = new ServletRequestDetails(theRequestDetails);
|
||||
requestDetails.setRestOperationType(RestOperationTypeEnum.METADATA);
|
||||
|
||||
return myServerConformanceMethod.provideCapabilityStatement(this, requestDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
|
||||
*/
|
||||
|
|
|
@ -30,6 +30,9 @@ import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
|||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -46,27 +49,32 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public class RestfulServerConfiguration implements ISearchParamRegistry {
|
||||
|
||||
public static final String GLOBAL = "GLOBAL";
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class);
|
||||
private Collection<ResourceBinding> resourceBindings;
|
||||
private List<BaseMethodBinding<?>> serverBindings;
|
||||
private List<BaseMethodBinding<?>> myGlobalBindings;
|
||||
private Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype;
|
||||
private String implementationDescription;
|
||||
private String serverVersion = VersionUtil.getVersion();
|
||||
private String serverName = "HAPI FHIR";
|
||||
private FhirContext fhirContext;
|
||||
private IServerAddressStrategy serverAddressStrategy;
|
||||
private String myImplementationDescription;
|
||||
private String myServerName = "HAPI FHIR";
|
||||
private String myServerVersion = VersionUtil.getVersion();
|
||||
private FhirContext myFhirContext;
|
||||
private IServerAddressStrategy myServerAddressStrategy;
|
||||
private IPrimitiveType<Date> myConformanceDate;
|
||||
|
||||
/**
|
||||
|
@ -127,10 +135,10 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @return the implementationDescription
|
||||
*/
|
||||
public String getImplementationDescription() {
|
||||
if (isBlank(implementationDescription)) {
|
||||
if (isBlank(myImplementationDescription)) {
|
||||
return "HAPI FHIR";
|
||||
}
|
||||
return implementationDescription;
|
||||
return myImplementationDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,7 +147,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @param implementationDescription the implementationDescription to set
|
||||
*/
|
||||
public RestfulServerConfiguration setImplementationDescription(String implementationDescription) {
|
||||
this.implementationDescription = implementationDescription;
|
||||
this.myImplementationDescription = implementationDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -149,7 +157,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @return the serverVersion
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return serverVersion;
|
||||
return myServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,7 +166,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @param serverVersion the serverVersion to set
|
||||
*/
|
||||
public RestfulServerConfiguration setServerVersion(String serverVersion) {
|
||||
this.serverVersion = serverVersion;
|
||||
this.myServerVersion = serverVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -168,7 +176,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @return the serverName
|
||||
*/
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
return myServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +185,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @param serverName the serverName to set
|
||||
*/
|
||||
public RestfulServerConfiguration setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
this.myServerName = serverName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -186,7 +194,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return this.fhirContext;
|
||||
return this.myFhirContext;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,7 +203,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @param fhirContext the fhirContext to set
|
||||
*/
|
||||
public RestfulServerConfiguration setFhirContext(FhirContext fhirContext) {
|
||||
this.fhirContext = fhirContext;
|
||||
this.myFhirContext = fhirContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -205,7 +213,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @return the serverAddressStrategy
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return serverAddressStrategy;
|
||||
return myServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,7 +222,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
* @param serverAddressStrategy the serverAddressStrategy to set
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy serverAddressStrategy) {
|
||||
this.serverAddressStrategy = serverAddressStrategy;
|
||||
this.myServerAddressStrategy = serverAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,48 +244,114 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
}
|
||||
|
||||
public Bindings provideBindings() {
|
||||
IdentityHashMap<SearchMethodBinding, String> myNamedSearchMethodBindingToName = new IdentityHashMap<>();
|
||||
HashMap<String, List<SearchMethodBinding>> mySearchNameToBindings = new HashMap<>();
|
||||
IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName = new IdentityHashMap<>();
|
||||
HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings = new HashMap<>();
|
||||
IdentityHashMap<SearchMethodBinding, String> namedSearchMethodBindingToName = new IdentityHashMap<>();
|
||||
HashMap<String, List<SearchMethodBinding>> searchNameToBindings = new HashMap<>();
|
||||
IdentityHashMap<OperationMethodBinding, String> operationBindingToId = new IdentityHashMap<>();
|
||||
HashMap<String, List<OperationMethodBinding>> operationIdToBindings = new HashMap<>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Map.Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
List<BaseMethodBinding<?>> nextMethodBindings = nextEntry.getValue();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextMethodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
if (myOperationBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
List<BaseMethodBinding<?>> methodBindings = resourceToMethods
|
||||
.values()
|
||||
.stream().flatMap(t -> t.stream())
|
||||
.collect(Collectors.toList());
|
||||
if (myGlobalBindings != null) {
|
||||
methodBindings.addAll(myGlobalBindings);
|
||||
}
|
||||
|
||||
String name = createOperationName(methodBinding);
|
||||
ourLog.debug("Detected operation: {}", name);
|
||||
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
if (myOperationNameToBindings.containsKey(name) == false) {
|
||||
myOperationNameToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
myOperationNameToBindings.get(name).add(methodBinding);
|
||||
} else if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (myNamedSearchMethodBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createNamedQueryName(methodBinding);
|
||||
ourLog.debug("Detected named query: {}", name);
|
||||
|
||||
myNamedSearchMethodBindingToName.put(methodBinding, name);
|
||||
if (!mySearchNameToBindings.containsKey(name)) {
|
||||
mySearchNameToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
mySearchNameToBindings.get(name).add(methodBinding);
|
||||
ListMultimap<String, OperationMethodBinding> nameToOperationMethodBindings = ArrayListMultimap.create();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
nameToOperationMethodBindings.put(methodBinding.getName(), methodBinding);
|
||||
} else if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (namedSearchMethodBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createNamedQueryName(methodBinding);
|
||||
ourLog.debug("Detected named query: {}", name);
|
||||
|
||||
namedSearchMethodBindingToName.put(methodBinding, name);
|
||||
if (!searchNameToBindings.containsKey(name)) {
|
||||
searchNameToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
searchNameToBindings.get(name).add(methodBinding);
|
||||
}
|
||||
}
|
||||
|
||||
return new Bindings(myNamedSearchMethodBindingToName, mySearchNameToBindings, myOperationNameToBindings, myOperationBindingToName);
|
||||
for (String nextName : nameToOperationMethodBindings.keySet()) {
|
||||
List<OperationMethodBinding> nextMethodBindings = nameToOperationMethodBindings.get(nextName);
|
||||
|
||||
boolean global = false;
|
||||
boolean system = false;
|
||||
boolean instance = false;
|
||||
boolean type = false;
|
||||
Set<String> resourceTypes = null;
|
||||
|
||||
for (OperationMethodBinding nextMethodBinding : nextMethodBindings) {
|
||||
global |= nextMethodBinding.isGlobalMethod();
|
||||
system |= nextMethodBinding.isCanOperateAtServerLevel();
|
||||
type |= nextMethodBinding.isCanOperateAtTypeLevel();
|
||||
instance |= nextMethodBinding.isCanOperateAtInstanceLevel();
|
||||
if (nextMethodBinding.getResourceName() != null) {
|
||||
resourceTypes = resourceTypes != null ? resourceTypes : new TreeSet<>();
|
||||
resourceTypes.add(nextMethodBinding.getResourceName());
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder operationIdBuilder = new StringBuilder();
|
||||
if (global) {
|
||||
operationIdBuilder.append("Global");
|
||||
} else if (resourceTypes != null && resourceTypes.size() == 1) {
|
||||
operationIdBuilder.append(resourceTypes.iterator().next());
|
||||
} else if (resourceTypes != null && resourceTypes.size() == 2) {
|
||||
Iterator<String> iterator = resourceTypes.iterator();
|
||||
operationIdBuilder.append(iterator.next());
|
||||
operationIdBuilder.append(iterator.next());
|
||||
} else if (resourceTypes != null) {
|
||||
operationIdBuilder.append("Multi");
|
||||
}
|
||||
|
||||
operationIdBuilder.append('-');
|
||||
if (instance) {
|
||||
operationIdBuilder.append('i');
|
||||
}
|
||||
if (type) {
|
||||
operationIdBuilder.append('t');
|
||||
}
|
||||
if (system) {
|
||||
operationIdBuilder.append('s');
|
||||
}
|
||||
operationIdBuilder.append('-');
|
||||
|
||||
// Exclude the leading $
|
||||
operationIdBuilder.append(nextName, 1, nextName.length());
|
||||
|
||||
String operationId = operationIdBuilder.toString();
|
||||
operationIdToBindings.put(operationId, nextMethodBindings);
|
||||
nextMethodBindings.forEach(t->operationBindingToId.put(t, operationId));
|
||||
}
|
||||
|
||||
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
if (operationBindingToId.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createOperationName(methodBinding);
|
||||
ourLog.debug("Detected operation: {}", name);
|
||||
|
||||
operationBindingToId.put(methodBinding, name);
|
||||
if (operationIdToBindings.containsKey(name) == false) {
|
||||
operationIdToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
operationIdToBindings.get(name).add(methodBinding);
|
||||
}
|
||||
}
|
||||
|
||||
return new Bindings(namedSearchMethodBindingToName, searchNameToBindings, operationIdToBindings, operationBindingToId);
|
||||
}
|
||||
|
||||
public Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
|
||||
|
@ -301,6 +375,14 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
return resourceToMethods;
|
||||
}
|
||||
|
||||
public List<BaseMethodBinding<?>> getGlobalBindings() {
|
||||
return myGlobalBindings;
|
||||
}
|
||||
|
||||
public void setGlobalBindings(List<BaseMethodBinding<?>> theGlobalBindings) {
|
||||
myGlobalBindings = theGlobalBindings;
|
||||
}
|
||||
|
||||
/*
|
||||
* Populates {@link #resourceNameToSharedSupertype} by scanning the given resource providers. Only resource provider getResourceType values
|
||||
* are taken into account. {@link ProvidesResources} and method return types are deliberately ignored.
|
||||
|
@ -329,28 +411,6 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
entry -> entry.getValue().getLowestCommonSuperclass().get()));
|
||||
}
|
||||
|
||||
private String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
if (theMethodBinding.getResourceName() != null) {
|
||||
retVal.append(theMethodBinding.getResourceName());
|
||||
}
|
||||
|
||||
retVal.append('-');
|
||||
if (theMethodBinding.isCanOperateAtInstanceLevel()) {
|
||||
retVal.append('i');
|
||||
}
|
||||
if (theMethodBinding.isCanOperateAtServerLevel()) {
|
||||
retVal.append('s');
|
||||
}
|
||||
retVal.append('-');
|
||||
|
||||
// Exclude the leading $
|
||||
retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length());
|
||||
|
||||
return retVal.toString();
|
||||
}
|
||||
|
||||
|
||||
private String createNamedQueryName(SearchMethodBinding searchMethodBinding) {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
if (searchMethodBinding.getResourceName() != null) {
|
||||
|
@ -443,7 +503,6 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
|
||||
}
|
||||
|
||||
|
||||
private static class SearchParameterComparator implements Comparator<SearchParameter> {
|
||||
private static final SearchParameterComparator INSTANCE = new SearchParameterComparator();
|
||||
|
||||
|
@ -458,4 +517,27 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
if (theMethodBinding.getResourceName() != null) {
|
||||
retVal.append(theMethodBinding.getResourceName());
|
||||
} else if (theMethodBinding.isGlobalMethod()) {
|
||||
retVal.append("Global");
|
||||
}
|
||||
|
||||
retVal.append('-');
|
||||
if (theMethodBinding.isCanOperateAtInstanceLevel()) {
|
||||
retVal.append('i');
|
||||
}
|
||||
if (theMethodBinding.isCanOperateAtServerLevel()) {
|
||||
retVal.append('s');
|
||||
}
|
||||
retVal.append('-');
|
||||
|
||||
// Exclude the leading $
|
||||
retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length());
|
||||
|
||||
return retVal.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetai
|
|||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -386,6 +387,10 @@ public abstract class BaseMethodBinding<T> {
|
|||
+ " returns a collection with generic type " + toLogString(returnTypeFromMethod)
|
||||
+ " - Must return a resource type or a collection (List, Set) with a resource type parameter (e.g. List<Patient> or List<IBaseResource> )");
|
||||
}
|
||||
} else if (IBaseBundle.class.isAssignableFrom(returnTypeFromMethod) && returnTypeFromRp == null) {
|
||||
// If a plain provider method returns a Bundle, we'll assume it to be a system
|
||||
// level operation and not a type/instance level operation on the Bundle type.
|
||||
returnTypeFromMethod = null;
|
||||
} else {
|
||||
if (!isResourceInterface(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
|
||||
|
@ -395,30 +400,41 @@ public abstract class BaseMethodBinding<T> {
|
|||
}
|
||||
|
||||
Class<? extends IBaseResource> returnTypeFromAnnotation = IBaseResource.class;
|
||||
String returnTypeNameFromAnnotation = null;
|
||||
if (read != null) {
|
||||
if (isNotBlank(read.typeName())) {
|
||||
returnTypeFromAnnotation = theContext.getResourceDefinition(read.typeName()).getImplementingClass();
|
||||
} else {
|
||||
returnTypeFromAnnotation = read.type();
|
||||
}
|
||||
returnTypeFromAnnotation = read.type();
|
||||
returnTypeNameFromAnnotation = read.typeName();
|
||||
} else if (search != null) {
|
||||
returnTypeFromAnnotation = search.type();
|
||||
returnTypeNameFromAnnotation = search.typeName();
|
||||
} else if (history != null) {
|
||||
returnTypeFromAnnotation = history.type();
|
||||
returnTypeNameFromAnnotation = history.typeName();
|
||||
} else if (delete != null) {
|
||||
returnTypeFromAnnotation = delete.type();
|
||||
returnTypeNameFromAnnotation = delete.typeName();
|
||||
} else if (patch != null) {
|
||||
returnTypeFromAnnotation = patch.type();
|
||||
returnTypeNameFromAnnotation = patch.typeName();
|
||||
} else if (create != null) {
|
||||
returnTypeFromAnnotation = create.type();
|
||||
returnTypeNameFromAnnotation = create.typeName();
|
||||
} else if (update != null) {
|
||||
returnTypeFromAnnotation = update.type();
|
||||
returnTypeNameFromAnnotation = update.typeName();
|
||||
} else if (validate != null) {
|
||||
returnTypeFromAnnotation = validate.type();
|
||||
returnTypeNameFromAnnotation = validate.typeName();
|
||||
} else if (addTags != null) {
|
||||
returnTypeFromAnnotation = addTags.type();
|
||||
returnTypeNameFromAnnotation = addTags.typeName();
|
||||
} else if (deleteTags != null) {
|
||||
returnTypeFromAnnotation = deleteTags.type();
|
||||
returnTypeNameFromAnnotation = deleteTags.typeName();
|
||||
}
|
||||
|
||||
if (isNotBlank(returnTypeNameFromAnnotation)) {
|
||||
returnTypeFromAnnotation = theContext.getResourceDefinition(returnTypeNameFromAnnotation).getImplementingClass();
|
||||
}
|
||||
|
||||
if (returnTypeFromRp != null) {
|
||||
|
@ -477,7 +493,7 @@ public abstract class BaseMethodBinding<T> {
|
|||
}
|
||||
|
||||
private static boolean isResourceInterface(Class<?> theReturnTypeFromMethod) {
|
||||
return theReturnTypeFromMethod.equals(IBaseResource.class) || theReturnTypeFromMethod.equals(IResource.class) || theReturnTypeFromMethod.equals(IAnyResource.class);
|
||||
return theReturnTypeFromMethod != null && (theReturnTypeFromMethod.equals(IBaseResource.class) || theReturnTypeFromMethod.equals(IResource.class) || theReturnTypeFromMethod.equals(IAnyResource.class));
|
||||
}
|
||||
|
||||
private static String toLogString(Class<?> theType) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue