Improve CapabilityStatement Generation (#2511)
* Start work * Work on testing * Version bump * Test fix * Compile fix * Test Fixes * Test fix * Test fix * Compile fix * Tst fix * Test fix * Test fix * Test fix
This commit is contained in:
parent
939a57960d
commit
049097e5b4
|
@ -148,6 +148,12 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return toList(provideStructureDefinitionMap());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchCodeSystem(String theSystem) {
|
||||
|
|
|
@ -36,11 +36,13 @@ import javax.annotation.Nullable;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
|
@ -109,6 +111,32 @@ public interface IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return all possible structure definitions aside from resource definitions themselves
|
||||
*/
|
||||
@Nullable
|
||||
default <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
|
||||
List<T> retVal = fetchAllStructureDefinitions();
|
||||
if (retVal != null) {
|
||||
List<T> newList = new ArrayList<>(retVal.size());
|
||||
for (T next : retVal) {
|
||||
String url = defaultString(getFhirContext().newTerser().getSinglePrimitiveValueOrNull(next, "url"));
|
||||
if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
String lastPart = url.substring("http://hl7.org/fhir/StructureDefinition/".length());
|
||||
if (getFhirContext().getResourceTypes().contains(lastPart)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
newList.add(next);
|
||||
}
|
||||
|
||||
retVal = newList;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a code system by ID
|
||||
*
|
||||
|
|
|
@ -222,6 +222,53 @@ public enum Pointcut implements IPointcut {
|
|||
"ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException"
|
||||
),
|
||||
|
||||
/**
|
||||
* <b>Server Hook:</b>
|
||||
* This method is immediately before the handling method is selected. Interceptors may make changes
|
||||
* to the request that can influence which handler will ultimately be called.
|
||||
* <p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the servlet request.
|
||||
* Note that the bean properties are not all guaranteed to be populated at the time this hook is called.
|
||||
* </li>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
|
||||
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
|
||||
* </li>
|
||||
* <li>
|
||||
* javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
|
||||
* </li>
|
||||
* <li>
|
||||
* javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hook methods may return <code>true</code> or <code>void</code> if processing should continue normally.
|
||||
* This is generally the right thing to do.
|
||||
* If your interceptor is providing an HTTP response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* </p>
|
||||
* <p>
|
||||
* Hook methods may also throw {@link AuthenticationException} if they would like. This exception may be thrown
|
||||
* to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED(boolean.class,
|
||||
"ca.uhn.fhir.rest.api.server.RequestDetails",
|
||||
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
|
||||
"javax.servlet.http.HttpServletRequest",
|
||||
"javax.servlet.http.HttpServletResponse"
|
||||
),
|
||||
|
||||
/**
|
||||
* <b>Server Hook:</b>
|
||||
* This method is called just before the actual implementing server method is invoked.
|
||||
|
|
|
@ -55,9 +55,18 @@ public @interface Read {
|
|||
// 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 "";
|
||||
|
||||
/**
|
||||
* If set to true (default is false), this method supports vread operation as well as read
|
||||
*/
|
||||
boolean version() default false;
|
||||
|
||||
}
|
||||
|
|
|
@ -131,6 +131,9 @@ public class Constants {
|
|||
public static final String HEADER_LOCATION_LC = HEADER_LOCATION.toLowerCase();
|
||||
public static final String HEADER_ORIGIN = "Origin";
|
||||
public static final String HEADER_PREFER = "Prefer";
|
||||
public static final String HEADER_PREFER_HANDLING = "handling";
|
||||
public static final String HEADER_PREFER_HANDLING_STRICT = "strict";
|
||||
public static final String HEADER_PREFER_HANDLING_LENIENT = "lenient";
|
||||
public static final String HEADER_PREFER_RETURN = "return";
|
||||
public static final String HEADER_PREFER_RETURN_MINIMAL = "minimal";
|
||||
public static final String HEADER_PREFER_RETURN_REPRESENTATION = "representation";
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package ca.uhn.fhir.rest.api;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* 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 java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Represents values for "handling" value as provided in the the <a href="http://hl7.org/fhir/search.html">FHIR Search Spec</a>.
|
||||
*/
|
||||
public enum PreferHandlingEnum {
|
||||
|
||||
STRICT(Constants.HEADER_PREFER_HANDLING_STRICT), LENIENT(Constants.HEADER_PREFER_HANDLING_LENIENT);
|
||||
|
||||
private static HashMap<String, PreferHandlingEnum> ourValues;
|
||||
private String myHeaderValue;
|
||||
|
||||
PreferHandlingEnum(String theHeaderValue) {
|
||||
myHeaderValue = theHeaderValue;
|
||||
}
|
||||
|
||||
public String getHeaderValue() {
|
||||
return myHeaderValue;
|
||||
}
|
||||
|
||||
public static PreferHandlingEnum fromHeaderValue(String theHeaderValue) {
|
||||
if (ourValues == null) {
|
||||
HashMap<String, PreferHandlingEnum> values = new HashMap<>();
|
||||
for (PreferHandlingEnum next : PreferHandlingEnum.values()) {
|
||||
values.put(next.getHeaderValue(), next);
|
||||
}
|
||||
ourValues = values;
|
||||
}
|
||||
return ourValues.get(theHeaderValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -26,9 +26,10 @@ public class PreferHeader {
|
|||
|
||||
private PreferReturnEnum myReturn;
|
||||
private boolean myRespondAsync;
|
||||
private PreferHandlingEnum myHanding;
|
||||
|
||||
public @Nullable
|
||||
PreferReturnEnum getReturn() {
|
||||
@Nullable
|
||||
public PreferReturnEnum getReturn() {
|
||||
return myReturn;
|
||||
}
|
||||
|
||||
|
@ -46,4 +47,12 @@ public class PreferHeader {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PreferHandlingEnum getHanding() {
|
||||
return myHanding;
|
||||
}
|
||||
|
||||
public void setHanding(PreferHandlingEnum theHanding) {
|
||||
myHanding = theHanding;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.api;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
|
@ -27,7 +28,7 @@ import java.util.HashMap;
|
|||
*/
|
||||
public enum PreferReturnEnum {
|
||||
|
||||
REPRESENTATION("representation"), MINIMAL("minimal"), OPERATION_OUTCOME("OperationOutcome");
|
||||
REPRESENTATION(Constants.HEADER_PREFER_RETURN_REPRESENTATION), MINIMAL(Constants.HEADER_PREFER_RETURN_MINIMAL), OPERATION_OUTCOME(Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME);
|
||||
|
||||
private static HashMap<String, PreferReturnEnum> ourValues;
|
||||
private String myHeaderValue;
|
||||
|
@ -40,6 +41,7 @@ public enum PreferReturnEnum {
|
|||
return myHeaderValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PreferReturnEnum fromHeaderValue(String theHeaderValue) {
|
||||
if (ourValues == null) {
|
||||
HashMap<String, PreferReturnEnum> values = new HashMap<>();
|
||||
|
|
|
@ -25,17 +25,22 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@CoverageIgnore
|
||||
public enum RestOperationTypeEnum {
|
||||
|
||||
ADD_TAGS("add-tags"),
|
||||
BATCH("batch", true, false, false),
|
||||
|
||||
DELETE_TAGS("delete-tags"),
|
||||
ADD_TAGS("add-tags", false, false, true),
|
||||
|
||||
GET_TAGS("get-tags"),
|
||||
DELETE_TAGS("delete-tags", false, false, true),
|
||||
|
||||
GET_PAGE("get-page"),
|
||||
GET_TAGS("get-tags", false, true, true),
|
||||
|
||||
GET_PAGE("get-page", false, false, false),
|
||||
|
||||
/**
|
||||
* <b>
|
||||
|
@ -43,111 +48,111 @@ public enum RestOperationTypeEnum {
|
|||
* change as the GraphQL interface matures
|
||||
* </b>
|
||||
*/
|
||||
GRAPHQL_REQUEST("graphql-request"),
|
||||
GRAPHQL_REQUEST("graphql-request", false, false, false),
|
||||
|
||||
/**
|
||||
* E.g. $everything, $validate, etc.
|
||||
*/
|
||||
EXTENDED_OPERATION_SERVER("extended-operation-server"),
|
||||
EXTENDED_OPERATION_SERVER("extended-operation-server", false, false, false),
|
||||
|
||||
/**
|
||||
* E.g. $everything, $validate, etc.
|
||||
*/
|
||||
EXTENDED_OPERATION_TYPE("extended-operation-type"),
|
||||
EXTENDED_OPERATION_TYPE("extended-operation-type", false, false, false),
|
||||
|
||||
/**
|
||||
* E.g. $everything, $validate, etc.
|
||||
*/
|
||||
EXTENDED_OPERATION_INSTANCE("extended-operation-instance"),
|
||||
EXTENDED_OPERATION_INSTANCE("extended-operation-instance", false, false, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>create</b>
|
||||
*/
|
||||
CREATE("create"),
|
||||
CREATE("create", false, true, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>delete</b>
|
||||
*/
|
||||
DELETE("delete"),
|
||||
DELETE("delete", false, false, true),
|
||||
|
||||
/**
|
||||
* Code Value: <b>history-instance</b>
|
||||
*/
|
||||
HISTORY_INSTANCE("history-instance"),
|
||||
HISTORY_INSTANCE("history-instance", false, false, true),
|
||||
|
||||
/**
|
||||
* Code Value: <b>history-system</b>
|
||||
*/
|
||||
HISTORY_SYSTEM("history-system"),
|
||||
HISTORY_SYSTEM("history-system", true, false, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>history-type</b>
|
||||
*/
|
||||
HISTORY_TYPE("history-type"),
|
||||
HISTORY_TYPE("history-type", false, true, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>read</b>
|
||||
*/
|
||||
READ("read"),
|
||||
READ("read", false, false, true),
|
||||
|
||||
/**
|
||||
* Code Value: <b>search-system</b>
|
||||
*/
|
||||
SEARCH_SYSTEM("search-system"),
|
||||
SEARCH_SYSTEM("search-system", true, false, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>search-type</b>
|
||||
*/
|
||||
SEARCH_TYPE("search-type"),
|
||||
SEARCH_TYPE("search-type", false, true, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>transaction</b>
|
||||
*/
|
||||
TRANSACTION("transaction"),
|
||||
TRANSACTION("transaction", true, false, false),
|
||||
|
||||
/**
|
||||
* Code Value: <b>update</b>
|
||||
*/
|
||||
UPDATE("update"),
|
||||
UPDATE("update", false, false, true),
|
||||
|
||||
/**
|
||||
* Code Value: <b>validate</b>
|
||||
*/
|
||||
VALIDATE("validate"),
|
||||
VALIDATE("validate", false, true, true),
|
||||
|
||||
/**
|
||||
* Code Value: <b>vread</b>
|
||||
*/
|
||||
VREAD("vread"),
|
||||
VREAD("vread", false, false, true),
|
||||
|
||||
/**
|
||||
* Load the server's metadata
|
||||
*/
|
||||
METADATA("metadata"),
|
||||
METADATA("metadata", false, false, false),
|
||||
|
||||
/**
|
||||
* $meta-add extended operation
|
||||
*/
|
||||
META_ADD("$meta-add"),
|
||||
META_ADD("$meta-add", false, false, false),
|
||||
|
||||
/**
|
||||
* $meta-add extended operation
|
||||
*/
|
||||
META("$meta"),
|
||||
META("$meta", false, false, false),
|
||||
|
||||
/**
|
||||
* $meta-delete extended operation
|
||||
*/
|
||||
META_DELETE("$meta-delete"),
|
||||
META_DELETE("$meta-delete", false, false, false),
|
||||
|
||||
/**
|
||||
* Patch operation
|
||||
*/
|
||||
PATCH("patch"),
|
||||
PATCH("patch", false, false, true),
|
||||
|
||||
;
|
||||
|
||||
private static Map<String, RestOperationTypeEnum> CODE_TO_ENUM = new HashMap<String, RestOperationTypeEnum>();
|
||||
private static final Map<String, RestOperationTypeEnum> CODE_TO_ENUM = new HashMap<String, RestOperationTypeEnum>();
|
||||
|
||||
/**
|
||||
* Identifier for this Value Set: http://hl7.org/fhir/vs/type-restful-operation
|
||||
|
@ -166,27 +171,45 @@ public enum RestOperationTypeEnum {
|
|||
}
|
||||
|
||||
private final String myCode;
|
||||
private final boolean mySystemLevel;
|
||||
private final boolean myTypeLevel;
|
||||
private final boolean myInstanceLevel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
RestOperationTypeEnum(String theCode) {
|
||||
RestOperationTypeEnum(@Nonnull String theCode, boolean theSystemLevel, boolean theTypeLevel, boolean theInstanceLevel) {
|
||||
myCode = theCode;
|
||||
mySystemLevel = theSystemLevel;
|
||||
myTypeLevel = theTypeLevel;
|
||||
myInstanceLevel = theInstanceLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enumerated value associated with this code
|
||||
*/
|
||||
public RestOperationTypeEnum forCode(String theCode) {
|
||||
RestOperationTypeEnum retVal = CODE_TO_ENUM.get(theCode);
|
||||
return retVal;
|
||||
public RestOperationTypeEnum forCode(@Nonnull String theCode) {
|
||||
Validate.notNull(theCode, "theCode must not be null");
|
||||
return CODE_TO_ENUM.get(theCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code associated with this enumerated value
|
||||
*/
|
||||
@Nonnull
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
public boolean isSystemLevel() {
|
||||
return mySystemLevel;
|
||||
}
|
||||
|
||||
public boolean isTypeLevel() {
|
||||
return myTypeLevel;
|
||||
}
|
||||
|
||||
public boolean isInstanceLevel() {
|
||||
return myInstanceLevel;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ public class ClasspathUtil {
|
|||
public static InputStream loadResourceAsStream(String theClasspath) {
|
||||
InputStream retVal = ClasspathUtil.class.getResourceAsStream(theClasspath);
|
||||
if (retVal == null) {
|
||||
if (!theClasspath.startsWith("/")) {
|
||||
if (theClasspath.startsWith("/")) {
|
||||
retVal = ClasspathUtil.class.getResourceAsStream(theClasspath.substring(1));
|
||||
} else {
|
||||
retVal = ClasspathUtil.class.getResourceAsStream("/" + theClasspath);
|
||||
}
|
||||
if (retVal == null) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -34,6 +35,8 @@ import org.hl7.fhir.instance.model.api.IDomainResource;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -42,6 +45,7 @@ import java.util.EnumSet;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -317,6 +321,14 @@ public class FhirTerser {
|
|||
return retVal.get(0);
|
||||
}
|
||||
|
||||
public Optional<String> getSinglePrimitiveValue(IBase theTarget, String thePath) {
|
||||
return getSingleValue(theTarget, thePath, IPrimitiveType.class).map(t->t.getValueAsString());
|
||||
}
|
||||
|
||||
public String getSinglePrimitiveValueOrNull(IBase theTarget, String thePath) {
|
||||
return getSingleValue(theTarget, thePath, IPrimitiveType.class).map(t->t.getValueAsString()).orElse(null);
|
||||
}
|
||||
|
||||
public <T extends IBase> Optional<T> getSingleValue(IBase theTarget, String thePath, Class<T> theWantedType) {
|
||||
return Optional.ofNullable(getSingleValueOrNull(theTarget, thePath, theWantedType));
|
||||
}
|
||||
|
@ -671,10 +683,8 @@ public class FhirTerser {
|
|||
|
||||
parts.add(thePath.substring(currentStart));
|
||||
|
||||
if (theElementDef instanceof RuntimeResourceDefinition) {
|
||||
if (parts.size() > 0 && parts.get(0).equals(theElementDef.getName())) {
|
||||
parts = parts.subList(1, parts.size());
|
||||
}
|
||||
if (parts.size() > 0 && parts.get(0).equals(theElementDef.getName())) {
|
||||
parts = parts.subList(1, parts.size());
|
||||
}
|
||||
|
||||
if (parts.size() < 1) {
|
||||
|
@ -1168,6 +1178,194 @@ public class FhirTerser {
|
|||
return containedResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds and returns a new element at the given path within the given structure. The paths used here
|
||||
* are <b>not FHIRPath expressions</b> but instead just simple dot-separated path expressions.
|
||||
* <p>
|
||||
* Only the last entry in the path is always created, existing repetitions of elements before
|
||||
* the final dot are returned if they exists (although they are created if they do not). For example,
|
||||
* given the path <code>Patient.name.given</code>, a new repetition of <code>given</code> is always
|
||||
* added to the first (index 0) repetition of the name. If an index-0 repetition of <code>name</code>
|
||||
* already exists, it is added to. If one does not exist, it if created and then added to.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the last element in the path refers to a non-repeatable element that is already present and
|
||||
* is not empty, a {@link DataFormatException} error will be thrown.
|
||||
* </p>
|
||||
*
|
||||
* @param theTarget The element to add to. This will often be a {@link IBaseResource resource}
|
||||
* instance, but does not need to be.
|
||||
* @param thePath The path.
|
||||
* @return The newly added element
|
||||
* @throws DataFormatException If the path is invalid or does not end with either a repeatable element, or
|
||||
* an element that is non-repeatable but not already populated.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nonnull
|
||||
public <T extends IBase> T addElement(@Nonnull IBase theTarget, @Nonnull String thePath) {
|
||||
return (T) doAddElement(theTarget, thePath, 1).get(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends IBase> List<T> doAddElement(IBase theTarget, String thePath, int theElementsToAdd) {
|
||||
if (theElementsToAdd == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
IBase target = theTarget;
|
||||
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(target.getClass());
|
||||
List<String> parts = parsePath(def, thePath);
|
||||
|
||||
for (int i = 0, partsSize = parts.size(); ; i++) {
|
||||
String nextPart = parts.get(i);
|
||||
boolean lastPart = i == partsSize - 1;
|
||||
|
||||
BaseRuntimeChildDefinition nextChild = def.getChildByName(nextPart);
|
||||
if (nextChild == null) {
|
||||
throw new DataFormatException("Invalid path " + thePath + ": Element of type " + def.getName() + " has no child named " + nextPart + ". Valid names: " + def.getChildrenAndExtension().stream().map(t -> t.getElementName()).sorted().collect(Collectors.joining(", ")));
|
||||
}
|
||||
|
||||
List<IBase> childValues = nextChild.getAccessor().getValues(target);
|
||||
IBase childValue;
|
||||
if (childValues.size() > 0 && !lastPart) {
|
||||
childValue = childValues.get(0);
|
||||
} else {
|
||||
|
||||
if (lastPart) {
|
||||
if (!childValues.isEmpty()) {
|
||||
if (theElementsToAdd == -1) {
|
||||
return (List<T>) Collections.singletonList(childValues.get(0));
|
||||
} else if (nextChild.getMax() == 1 && !childValues.get(0).isEmpty()) {
|
||||
throw new DataFormatException("Element at path " + thePath + " is not repeatable and not empty");
|
||||
} else if (nextChild.getMax() == 1 && childValues.get(0).isEmpty()) {
|
||||
return (List<T>) Collections.singletonList(childValues.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseRuntimeElementDefinition<?> elementDef = nextChild.getChildByName(nextPart);
|
||||
childValue = elementDef.newInstance(nextChild.getInstanceConstructorArguments());
|
||||
nextChild.getMutator().addValue(target, childValue);
|
||||
|
||||
if (lastPart) {
|
||||
if (theElementsToAdd == 1 || theElementsToAdd == -1) {
|
||||
return (List<T>) Collections.singletonList(childValue);
|
||||
} else {
|
||||
if (nextChild.getMax() == 1) {
|
||||
throw new DataFormatException("Can not add multiple values at path " + thePath + ": Element does not repeat");
|
||||
}
|
||||
|
||||
List<T> values = (List<T>) Lists.newArrayList(childValue);
|
||||
for (int j = 1; j < theElementsToAdd; j++) {
|
||||
childValue = elementDef.newInstance(nextChild.getInstanceConstructorArguments());
|
||||
nextChild.getMutator().addValue(target, childValue);
|
||||
values.add((T) childValue);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
target = childValue;
|
||||
|
||||
if (!lastPart) {
|
||||
BaseRuntimeElementDefinition<?> nextDef = myContext.getElementDefinition(target.getClass());
|
||||
if (!(nextDef instanceof BaseRuntimeElementCompositeDefinition)) {
|
||||
throw new DataFormatException("Invalid path " + thePath + ": Element of type " + def.getName() + " has no child named " + nextPart + " (this is a primitive type)");
|
||||
}
|
||||
def = (BaseRuntimeElementCompositeDefinition<?>) nextDef;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds and returns a new element at the given path within the given structure. The paths used here
|
||||
* are <b>not FHIRPath expressions</b> but instead just simple dot-separated path expressions.
|
||||
* <p>
|
||||
* This method follows all of the same semantics as {@link #addElement(IBase, String)} but it
|
||||
* requires the path to point to an element with a primitive datatype and set the value of
|
||||
* the datatype to the given value.
|
||||
* </p>
|
||||
*
|
||||
* @param theTarget The element to add to. This will often be a {@link IBaseResource resource}
|
||||
* instance, but does not need to be.
|
||||
* @param thePath The path.
|
||||
* @param theValue The value to set, or <code>null</code>.
|
||||
* @return The newly added element
|
||||
* @throws DataFormatException If the path is invalid or does not end with either a repeatable element, or
|
||||
* an element that is non-repeatable but not already populated.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nonnull
|
||||
public <T extends IBase> T addElement(@Nonnull IBase theTarget, @Nonnull String thePath, @Nullable String theValue) {
|
||||
T value = (T) doAddElement(theTarget, thePath, 1).get(0);
|
||||
if (!(value instanceof IPrimitiveType)) {
|
||||
throw new DataFormatException("Element at path " + thePath + " is not a primitive datatype. Found: " + myContext.getElementDefinition(value.getClass()).getName());
|
||||
}
|
||||
|
||||
((IPrimitiveType<?>) value).setValueAsString(theValue);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds and returns a new element at the given path within the given structure. The paths used here
|
||||
* are <b>not FHIRPath expressions</b> but instead just simple dot-separated path expressions.
|
||||
* <p>
|
||||
* This method follows all of the same semantics as {@link #addElement(IBase, String)} but it
|
||||
* requires the path to point to an element with a primitive datatype and set the value of
|
||||
* the datatype to the given value.
|
||||
* </p>
|
||||
*
|
||||
* @param theTarget The element to add to. This will often be a {@link IBaseResource resource}
|
||||
* instance, but does not need to be.
|
||||
* @param thePath The path.
|
||||
* @param theValue The value to set, or <code>null</code>.
|
||||
* @return The newly added element
|
||||
* @throws DataFormatException If the path is invalid or does not end with either a repeatable element, or
|
||||
* an element that is non-repeatable but not already populated.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nonnull
|
||||
public <T extends IBase> T setElement(@Nonnull IBase theTarget, @Nonnull String thePath, @Nullable String theValue) {
|
||||
T value = (T) doAddElement(theTarget, thePath, -1).get(0);
|
||||
if (!(value instanceof IPrimitiveType)) {
|
||||
throw new DataFormatException("Element at path " + thePath + " is not a primitive datatype. Found: " + myContext.getElementDefinition(value.getClass()).getName());
|
||||
}
|
||||
|
||||
((IPrimitiveType<?>) value).setValueAsString(theValue);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method has the same semantics as {@link #addElement(IBase, String, String)} but adds
|
||||
* a collection of primitives instead of a single one.
|
||||
*
|
||||
* @param theTarget The element to add to. This will often be a {@link IBaseResource resource}
|
||||
* instance, but does not need to be.
|
||||
* @param thePath The path.
|
||||
* @param theValues The values to set, or <code>null</code>.
|
||||
*/
|
||||
public void addElements(IBase theTarget, String thePath, Collection<String> theValues) {
|
||||
List<IBase> targets = doAddElement(theTarget, thePath, theValues.size());
|
||||
Iterator<String> valuesIter = theValues.iterator();
|
||||
for (IBase target : targets) {
|
||||
|
||||
if (!(target instanceof IPrimitiveType)) {
|
||||
throw new DataFormatException("Element at path " + thePath + " is not a primitive datatype. Found: " + myContext.getElementDefinition(target.getClass()).getName());
|
||||
}
|
||||
|
||||
((IPrimitiveType<?>) target).setValueAsString(valuesIter.next());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum OptionsEnum {
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
|
||||
} catch (SAXNotRecognizedException ex) {
|
||||
ourLog.warn("Jaxp 1.5 Support not found.", ex);
|
||||
ourLog.debug("Jaxp 1.5 Support not found.", ex);
|
||||
}
|
||||
|
||||
validator.validate(new StreamSource(new StringReader(encodedResource)));
|
||||
|
|
|
@ -22,12 +22,12 @@ package ca.uhn.fhir.jpa.demo;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
|
@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
|||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
|
@ -135,8 +135,9 @@ public class JpaServerDemo extends RestfulServer {
|
|||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
IFhirSystemDao<org.hl7.fhir.r4.model.Bundle, org.hl7.fhir.r4.model.Meta> systemDao = myAppCtx
|
||||
.getBean("mySystemDaoR4", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao,
|
||||
myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class));
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, systemDao,
|
||||
myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class), validationSupport);
|
||||
confProvider.setImplementationDescription("Example Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
|
|
|
@ -41,6 +41,8 @@ import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
||||
|
|
|
@ -22,6 +22,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.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
|
@ -245,4 +246,28 @@ public class ServletExamples {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// START SNIPPET: preferHandling
|
||||
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
|
||||
public class RestfulServerWithPreferHandling extends RestfulServer {
|
||||
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
|
||||
// Create an interceptor
|
||||
SearchPreferHandlingInterceptor interceptor = new SearchPreferHandlingInterceptor();
|
||||
|
||||
// Optionally you can change the default behaviour for when the Prefer
|
||||
// header is not found in the request or does not have a handling
|
||||
// directive
|
||||
interceptor.setDefaultBehaviour(PreferHandlingEnum.LENIENT);
|
||||
|
||||
// Register the interceptor
|
||||
registerInterceptor(interceptor);
|
||||
|
||||
}
|
||||
// END SNIPPET: preferHandling
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2506
|
||||
title: "A new server interceptor has been added that allows servers to implement lenient search mode,
|
||||
where unknown search parameters are ignored if an optional HTTP Prefer header is provided."
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2506
|
||||
title: "The server generated CapabilityStatment will now include supported Profile declarations
|
||||
for FHIR R4+."
|
|
@ -151,6 +151,24 @@ The following example shows how to register this interceptor within a HAPI FHIR
|
|||
**See Also:** The [Repository Validating Interceptor](/docs/validation/repository_validating_interceptor.html) provides a different and potentially more powerful way of validating data when paired with a HAPI FHIR JPA Server.
|
||||
|
||||
|
||||
<a name="lenient_searching"/>
|
||||
|
||||
# Search: Allow Lenient Searching
|
||||
|
||||
By default, HAPI FHIR applies strict search parameter validation. This means that FHIR search requests will fail if the search contains search parameters (any parameter that does not begin with an underscore) that are not known to the server.
|
||||
|
||||
* [SearchPreferHandlingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.html)
|
||||
* [SearchPreferHandlingInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java)
|
||||
|
||||
The SearchPreferHandlingInterceptor looks for a header of the form `Prefer: handling=lenient` or `Prefer: handling=strict` as described in the [FHIR Search Specification](http://hl7.org/fhir/search.html#errors) and treats it appropriately. A non-strict can also optionally be set.
|
||||
|
||||
The following example shows how to register this interceptor within a HAPI FHIR REST server.
|
||||
|
||||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|preferHandling}}
|
||||
```
|
||||
|
||||
|
||||
# Security: CORS
|
||||
|
||||
HAPI FHIR includes an interceptor which can be used to implement CORS support on your server. See [Server CORS Documentation](/docs/security/cors.html#cors_interceptor) for information on how to use this interceptor.
|
||||
|
@ -281,7 +299,7 @@ The RepositoryValidatingInterceptor can be used to enforce validation rules on d
|
|||
|
||||
# Data Standardization
|
||||
|
||||
```StandardizingInterceptor``` handles data standardization (s13n) requirements. This interceptor applies standardization rules on all FHIR primitives as configured in the ```s13n.json``` file that should be made available on the classpath. This file contains FHIRPath definitions together with the standardizers that should be applied to that path. It comes with six per-build standardizers: NAME_FAMILY, NAME_GIVEN, EMAIL, TITLE, PHONE and TEXT. Custom standardizers can be developed by implementing ```ca.uhn.fhir.rest.server.interceptor.s13n.standardizers.IStandardizer``` interface.
|
||||
`StandardizingInterceptor` handles data standardization (s13n) requirements. This interceptor applies standardization rules on all FHIR primitives as configured in the `s13n.json` file that should be made available on the classpath. This file contains FHIRPath definitions together with the standardizers that should be applied to that path. It comes with six per-build standardizers: NAME_FAMILY, NAME_GIVEN, EMAIL, TITLE, PHONE and TEXT. Custom standardizers can be developed by implementing `ca.uhn.fhir.rest.server.interceptor.s13n.standardizers.IStandardizer` interface.
|
||||
|
||||
A sample configuration file can be found below:
|
||||
|
||||
|
@ -303,20 +321,20 @@ A sample configuration file can be found below:
|
|||
}
|
||||
```
|
||||
|
||||
Standardization can be disabled for a given request by providing ```HAPI-Standardization-Disabled: *``` request header. Header value can be any string, it is the presence of the header that disables the s13n.
|
||||
Standardization can be disabled for a given request by providing `HAPI-Standardization-Disabled: *` request header. Header value can be any string, it is the presence of the header that disables the s13n.
|
||||
|
||||
|
||||
# Validation: Address Validation
|
||||
|
||||
```AddressValidatingInterceptor``` takes care of validation of addresses on all incoming resources through a 3rd party address validation service. Before a resource containing an Address field is stored, this interceptor invokes address validation service and then stores validation results as an extension on the address with ```https://hapifhir.org/AddressValidation/``` URL.
|
||||
`AddressValidatingInterceptor` takes care of validation of addresses on all incoming resources through a 3rd party address validation service. Before a resource containing an Address field is stored, this interceptor invokes address validation service and then stores validation results as an extension on the address with `https://hapifhir.org/AddressValidation/` URL.
|
||||
|
||||
This interceptor is configured in ```address-validation.properties``` file that should be made available on the classpath. This file must contain ```validator.class``` property, which defines a fully qualified class implementing ```ca.uhn.fhir.rest.server.interceptor.validation.address.IAddressValidator``` interface. The specified implementation must provide service-specific logic for validating an Address instance. An example implementation can be found in ```ca.uhn.fhir.rest.server.interceptor.validation.address.impl.LoquateAddressValidator``` class which validates addresses by using Loquate Data Cleanse service.
|
||||
This interceptor is configured in `address-validation.properties` file that should be made available on the classpath. This file must contain `validator.class` property, which defines a fully qualified class implementing `ca.uhn.fhir.rest.server.interceptor.validation.address.IAddressValidator` interface. The specified implementation must provide service-specific logic for validating an Address instance. An example implementation can be found in `ca.uhn.fhir.rest.server.interceptor.validation.address.impl.LoquateAddressValidator` class which validates addresses by using Loquate Data Cleanse service.
|
||||
|
||||
Address validation can be disabled for a given request by providing ```HAPI-Address-Validation-Disabled: *``` request header. Header value can be any string, it is the presence of the header that disables the validation.
|
||||
Address validation can be disabled for a given request by providing `HAPI-Address-Validation-Disabled: *` request header. Header value can be any string, it is the presence of the header that disables the validation.
|
||||
|
||||
# Validation: Field-Level Validation
|
||||
|
||||
```FieldValidatingInterceptor``` allows validating primitive fields on various FHIR resources. It expects validation rules to be provided via ```field-validation-rules.json``` file that should be available on the classpath. JSON in this file defines a mapping of FHIRPath expressions to validators that should be applied to those fields. Custom validators that implement ```ca.uhn.fhir.rest.server.interceptor.validation.fields.IValidator``` interface can be provided.
|
||||
`FieldValidatingInterceptor` allows validating primitive fields on various FHIR resources. It expects validation rules to be provided via `field-validation-rules.json` file that should be available on the classpath. JSON in this file defines a mapping of FHIRPath expressions to validators that should be applied to those fields. Custom validators that implement `ca.uhn.fhir.rest.server.interceptor.validation.fields.IValidator` interface can be provided.
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -325,4 +343,4 @@ Address validation can be disabled for a given request by providing ```HAPI-Addr
|
|||
}
|
||||
```
|
||||
|
||||
Field validation can be disabled for a given request by providing ```HAPI-Field-Validation-Disabled: *``` request header. Header value can be any string, it is the presence of the header that disables the validation.
|
||||
Field validation can be disabled for a given request by providing `HAPI-Field-Validation-Disabled: *` request header. Header value can be any string, it is the presence of the header that disables the validation.
|
||||
|
|
|
@ -33,9 +33,11 @@ import javax.ws.rs.*;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.dstu2.hapi.rest.server.ServerConformanceProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
|
@ -137,8 +139,8 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
|
|||
FhirVersionEnum fhirContextVersion = super.getFhirContext().getVersion().getVersion();
|
||||
switch (fhirContextVersion) {
|
||||
case R4:
|
||||
org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider r4ServerCapabilityStatementProvider = new org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration);
|
||||
myR4CapabilityStatement = r4ServerCapabilityStatementProvider.getServerConformance(null, null);
|
||||
ServerCapabilityStatementProvider r4ServerCapabilityStatementProvider = new ServerCapabilityStatementProvider(getFhirContext(), serverConfiguration);
|
||||
myR4CapabilityStatement = (CapabilityStatement) r4ServerCapabilityStatementProvider.getServerConformance(null, null);
|
||||
break;
|
||||
case DSTU3:
|
||||
org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider dstu3ServerCapabilityStatementProvider = new org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration);
|
||||
|
@ -226,7 +228,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
|
|||
}
|
||||
|
||||
/**
|
||||
* This method will add a provider to the conformance. This method is almost an exact copy of {@link ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods(Object, Class)} }
|
||||
* This method will add a provider to the conformance. This method is almost an exact copy of {@link ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods(Object)}
|
||||
*
|
||||
* @param theProvider
|
||||
* an instance of the provider interface
|
||||
|
|
|
@ -46,7 +46,7 @@ public interface IFhirSystemDao<T, MT> extends IDao {
|
|||
|
||||
/**
|
||||
* Returns a cached count of resources using a cache that regularly
|
||||
* refreshes in the background. This method will never
|
||||
* refreshes in the background. This method will never block, and may return null if nothing is in the cache.
|
||||
*/
|
||||
@Nullable
|
||||
Map<String, Long> getResourceCountsFromCache();
|
||||
|
|
|
@ -50,6 +50,7 @@ import javax.annotation.Nullable;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -104,6 +105,13 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
return fetchResource(myStructureDefinitionType, theUrl);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
IBundleProvider search = myDaoRegistry.getResourceDao("StructureDefinition").search(new SearchParameterMap().setLoadSynchronousUpTo(1000));
|
||||
return (List<T>) search.getResources(0, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "unused"})
|
||||
|
|
|
@ -30,6 +30,9 @@ import org.springframework.data.repository.query.Param;
|
|||
|
||||
public interface INpmPackageVersionResourceDao extends JpaRepository<NpmPackageVersionResourceEntity, Long> {
|
||||
|
||||
@Query("SELECT e FROM NpmPackageVersionResourceEntity e WHERE e.myResourceType = :resourceType AND e.myFhirVersion = :fhirVersion AND e.myPackageVersion.myCurrentVersion = true")
|
||||
Slice<NpmPackageVersionResourceEntity> findCurrentVersionByResourceType(Pageable thePage, @Param("fhirVersion") FhirVersionEnum theFhirVersion, @Param("resourceType") String theResourceType);
|
||||
|
||||
@Query("SELECT e FROM NpmPackageVersionResourceEntity e WHERE e.myCanonicalUrl = :url AND e.myFhirVersion = :fhirVersion AND e.myPackageVersion.myCurrentVersion = true")
|
||||
Slice<NpmPackageVersionResourceEntity> findCurrentVersionByCanonicalUrl(Pageable thePage, @Param("fhirVersion") FhirVersionEnum theFhirVersion, @Param("url") String theCanonicalUrl);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hl7.fhir.utilities.npm.NpmPackage;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHapiPackageCacheManager extends IPackageCacheManager {
|
||||
|
||||
|
@ -43,6 +44,8 @@ public interface IHapiPackageCacheManager extends IPackageCacheManager {
|
|||
|
||||
PackageDeleteOutcomeJson uninstallPackage(String thePackageId, String theVersion);
|
||||
|
||||
List<IBaseResource> loadPackageAssetsByType(FhirVersionEnum theFhirVersion, String theResourceType);
|
||||
|
||||
|
||||
class PackageContents {
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
@ -467,16 +468,20 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
|||
return null;
|
||||
} else {
|
||||
NpmPackageVersionResourceEntity contents = slice.getContent().get(0);
|
||||
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
|
||||
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
|
||||
byte[] resourceContentsBytes = BinaryUtil.getOrCreateData(myCtx, binary).getValue();
|
||||
String resourceContents = new String(resourceContentsBytes, StandardCharsets.UTF_8);
|
||||
|
||||
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
|
||||
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
|
||||
return loadPackageEntity(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private IBaseResource loadPackageEntity(NpmPackageVersionResourceEntity contents) {
|
||||
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
|
||||
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
|
||||
byte[] resourceContentsBytes = BinaryUtil.getOrCreateData(myCtx, binary).getValue();
|
||||
String resourceContents = new String(resourceContentsBytes, StandardCharsets.UTF_8);
|
||||
|
||||
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
|
||||
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public NpmPackageMetadataJson loadPackageMetadata(String thePackageId) {
|
||||
|
@ -641,6 +646,14 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<IBaseResource> loadPackageAssetsByType(FhirVersionEnum theFhirVersion, String theResourceType) {
|
||||
// List<NpmPackageVersionResourceEntity> outcome = myPackageVersionResourceDao.findAll();
|
||||
Slice<NpmPackageVersionResourceEntity> outcome = myPackageVersionResourceDao.findCurrentVersionByResourceType(PageRequest.of(0, 1000), theFhirVersion, theResourceType);
|
||||
return outcome.stream().map(t->loadPackageEntity(t)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void deleteAndExpungeResourceBinary(IIdType theResourceBinaryId, ExpungeOptions theOptions) {
|
||||
|
||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class NpmJpaValidationSupport implements IValidationSupport {
|
||||
|
||||
|
@ -68,4 +69,14 @@ public class NpmJpaValidationSupport implements IValidationSupport {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
FhirVersionEnum fhirVersion = myFhirContext.getVersion().getVersion();
|
||||
return (List<T>) myHapiPackageCacheManager.loadPackageAssetsByType(fhirVersion, "StructureDefinition");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* 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.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import ca.uhn.fhir.util.ExtensionUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.ConditionalDeleteStatus;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.ResourceVersionPolicy;
|
||||
import org.hl7.fhir.r4.model.Meta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* R4+ Only
|
||||
*/
|
||||
public class JpaCapabilityStatementProvider extends ServerCapabilityStatementProvider {
|
||||
|
||||
private final FhirContext myContext;
|
||||
private DaoConfig myDaoConfig;
|
||||
private String myImplementationDescription;
|
||||
private boolean myIncludeResourceCounts;
|
||||
private IFhirSystemDao<?, ?> mySystemDao;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JpaCapabilityStatementProvider(@Nonnull RestfulServer theRestfulServer, @Nonnull IFhirSystemDao<?, ?> theSystemDao, @Nonnull DaoConfig theDaoConfig, @Nonnull ISearchParamRegistry theSearchParamRegistry, IValidationSupport theValidationSupport) {
|
||||
super(theRestfulServer, theSearchParamRegistry, theValidationSupport);
|
||||
|
||||
Validate.notNull(theRestfulServer);
|
||||
Validate.notNull(theSystemDao);
|
||||
Validate.notNull(theDaoConfig);
|
||||
Validate.notNull(theSearchParamRegistry);
|
||||
|
||||
myContext = theRestfulServer.getFhirContext();
|
||||
mySystemDao = theSystemDao;
|
||||
myDaoConfig = theDaoConfig;
|
||||
setIncludeResourceCounts(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess(FhirTerser theTerser, IBaseConformance theCapabilityStatement) {
|
||||
super.postProcess(theTerser, theCapabilityStatement);
|
||||
|
||||
if (isNotBlank(myImplementationDescription)) {
|
||||
theTerser.setElement(theCapabilityStatement, "implementation.description", myImplementationDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessRest(FhirTerser theTerser, IBase theRest) {
|
||||
super.postProcessRest(theTerser, theRest);
|
||||
|
||||
if (myDaoConfig.getSupportedSubscriptionTypes().contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) {
|
||||
if (isNotBlank(myDaoConfig.getWebsocketContextPath())) {
|
||||
ExtensionUtil.setExtension(myContext, theRest, Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL, "uri", myDaoConfig.getWebsocketContextPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessRestResource(FhirTerser theTerser, IBase theResource, String theResourceName) {
|
||||
super.postProcessRestResource(theTerser, theResource, theResourceName);
|
||||
|
||||
theTerser.addElement(theResource, "versioning", ResourceVersionPolicy.VERSIONEDUPDATE.toCode());
|
||||
|
||||
if (myDaoConfig.isAllowMultipleDelete()) {
|
||||
theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.MULTIPLE.toCode());
|
||||
} else {
|
||||
theTerser.addElement(theResource, "conditionalDelete", ConditionalDeleteStatus.SINGLE.toCode());
|
||||
}
|
||||
|
||||
// Add resource counts
|
||||
if (myIncludeResourceCounts) {
|
||||
Map<String, Long> counts = mySystemDao.getResourceCountsFromCache();
|
||||
if (counts != null) {
|
||||
Long count = counts.get(theResourceName);
|
||||
if (count != null) {
|
||||
ExtensionUtil.setExtension(myContext, theResource, ExtensionConstants.CONF_RESOURCE_COUNT, "decimal", Long.toString(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isIncludeResourceCounts() {
|
||||
return myIncludeResourceCounts;
|
||||
}
|
||||
|
||||
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
|
||||
myIncludeResourceCounts = theIncludeResourceCounts;
|
||||
}
|
||||
|
||||
public void setDaoConfig(DaoConfig myDaoConfig) {
|
||||
this.myDaoConfig = myDaoConfig;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setImplementationDescription(String theImplDesc) {
|
||||
myImplementationDescription = theImplDesc;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) {
|
||||
this.mySystemDao = mySystemDao;
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* 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.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.ConditionalDeleteStatus;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.ResourceVersionPolicy;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Enumerations.SearchParamType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Meta;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider {
|
||||
|
||||
private volatile CapabilityStatement myCachedValue;
|
||||
private DaoConfig myDaoConfig;
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
private String myImplementationDescription;
|
||||
private boolean myIncludeResourceCounts;
|
||||
private RestfulServer myRestfulServer;
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@CoverageIgnore
|
||||
public JpaConformanceProviderR4() {
|
||||
super();
|
||||
super.setCache(false);
|
||||
setIncludeResourceCounts(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JpaConformanceProviderR4(@Nonnull RestfulServer theRestfulServer, @Nonnull IFhirSystemDao<Bundle, Meta> theSystemDao, @Nonnull DaoConfig theDaoConfig, @Nonnull ISearchParamRegistry theSearchParamRegistry) {
|
||||
super(theRestfulServer);
|
||||
|
||||
Validate.notNull(theRestfulServer);
|
||||
Validate.notNull(theSystemDao);
|
||||
Validate.notNull(theDaoConfig);
|
||||
Validate.notNull(theSearchParamRegistry);
|
||||
|
||||
myRestfulServer = theRestfulServer;
|
||||
mySystemDao = theSystemDao;
|
||||
myDaoConfig = theDaoConfig;
|
||||
super.setCache(false);
|
||||
setIncludeResourceCounts(true);
|
||||
setSearchParamRegistry(theSearchParamRegistry);
|
||||
}
|
||||
|
||||
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
CapabilityStatement retVal = myCachedValue;
|
||||
|
||||
Map<String, Long> counts = null;
|
||||
if (myIncludeResourceCounts) {
|
||||
counts = mySystemDao.getResourceCountsFromCache();
|
||||
}
|
||||
counts = defaultIfNull(counts, Collections.emptyMap());
|
||||
|
||||
retVal = super.getServerConformance(theRequest, theRequestDetails);
|
||||
for (CapabilityStatementRestComponent nextRest : retVal.getRest()) {
|
||||
|
||||
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
|
||||
|
||||
nextResource.setVersioning(ResourceVersionPolicy.VERSIONEDUPDATE);
|
||||
|
||||
ConditionalDeleteStatus conditionalDelete = nextResource.getConditionalDelete();
|
||||
if (conditionalDelete == ConditionalDeleteStatus.MULTIPLE && myDaoConfig.isAllowMultipleDelete() == false) {
|
||||
nextResource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
|
||||
}
|
||||
|
||||
// Add resource counts
|
||||
Long count = counts.get(nextResource.getTypeElement().getValueAsString());
|
||||
if (count != null) {
|
||||
nextResource.addExtension(new Extension(ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalType(count)));
|
||||
}
|
||||
|
||||
nextResource.getSearchParam().clear();
|
||||
String resourceName = nextResource.getType();
|
||||
RuntimeResourceDefinition resourceDef = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
|
||||
for (RuntimeSearchParam runtimeSp : searchParams) {
|
||||
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
|
||||
|
||||
confSp.setName(runtimeSp.getName());
|
||||
confSp.setDocumentation(runtimeSp.getDescription());
|
||||
confSp.setDefinition(runtimeSp.getUri());
|
||||
switch (runtimeSp.getParamType()) {
|
||||
case COMPOSITE:
|
||||
confSp.setType(SearchParamType.COMPOSITE);
|
||||
break;
|
||||
case DATE:
|
||||
confSp.setType(SearchParamType.DATE);
|
||||
break;
|
||||
case NUMBER:
|
||||
confSp.setType(SearchParamType.NUMBER);
|
||||
break;
|
||||
case QUANTITY:
|
||||
confSp.setType(SearchParamType.QUANTITY);
|
||||
break;
|
||||
case REFERENCE:
|
||||
confSp.setType(SearchParamType.REFERENCE);
|
||||
break;
|
||||
case STRING:
|
||||
confSp.setType(SearchParamType.STRING);
|
||||
break;
|
||||
case TOKEN:
|
||||
confSp.setType(SearchParamType.TOKEN);
|
||||
break;
|
||||
case URI:
|
||||
confSp.setType(SearchParamType.URI);
|
||||
break;
|
||||
case SPECIAL:
|
||||
confSp.setType(SearchParamType.SPECIAL);
|
||||
break;
|
||||
case HAS:
|
||||
// Shouldn't happen
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (myDaoConfig.getSupportedSubscriptionTypes().contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) {
|
||||
if (isNotBlank(myDaoConfig.getWebsocketContextPath())) {
|
||||
Extension websocketExtension = new Extension();
|
||||
websocketExtension.setUrl(Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL);
|
||||
websocketExtension.setValue(new UriType(myDaoConfig.getWebsocketContextPath()));
|
||||
retVal.getRestFirstRep().addExtension(websocketExtension);
|
||||
}
|
||||
}
|
||||
|
||||
massage(retVal);
|
||||
|
||||
retVal.getImplementation().setDescription(myImplementationDescription);
|
||||
myCachedValue = retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public boolean isIncludeResourceCounts() {
|
||||
return myIncludeResourceCounts;
|
||||
}
|
||||
|
||||
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
|
||||
myIncludeResourceCounts = theIncludeResourceCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
protected void massage(CapabilityStatement theStatement) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public void setDaoConfig(DaoConfig myDaoConfig) {
|
||||
this.myDaoConfig = myDaoConfig;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setImplementationDescription(String theImplDesc) {
|
||||
myImplementationDescription = theImplDesc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestfulServer(RestfulServer theRestfulServer) {
|
||||
this.myRestfulServer = theRestfulServer;
|
||||
super.setRestfulServer(theRestfulServer);
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) {
|
||||
this.mySystemDao = mySystemDao;
|
||||
}
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.provider.r5;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* 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.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import org.hl7.fhir.r5.model.Bundle;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ConditionalDeleteStatus;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ResourceVersionPolicy;
|
||||
import org.hl7.fhir.r5.model.DecimalType;
|
||||
import org.hl7.fhir.r5.model.Enumerations.SearchParamType;
|
||||
import org.hl7.fhir.r5.model.Extension;
|
||||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.ServerCapabilityStatementProvider {
|
||||
|
||||
private volatile CapabilityStatement myCachedValue;
|
||||
private DaoConfig myDaoConfig;
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
private String myImplementationDescription;
|
||||
private boolean myIncludeResourceCounts;
|
||||
private RestfulServer myRestfulServer;
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@CoverageIgnore
|
||||
public JpaConformanceProviderR5() {
|
||||
super();
|
||||
setIncludeResourceCounts(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JpaConformanceProviderR5(RestfulServer theRestfulServer, IFhirSystemDao<Bundle, Meta> theSystemDao, DaoConfig theDaoConfig, ISearchParamRegistry theSearchParamRegistry) {
|
||||
super();
|
||||
myRestfulServer = theRestfulServer;
|
||||
mySystemDao = theSystemDao;
|
||||
myDaoConfig = theDaoConfig;
|
||||
setIncludeResourceCounts(true);
|
||||
setSearchParamRegistry(theSearchParamRegistry);
|
||||
}
|
||||
|
||||
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
CapabilityStatement retVal = myCachedValue;
|
||||
|
||||
Map<String, Long> counts = null;
|
||||
if (myIncludeResourceCounts) {
|
||||
counts = mySystemDao.getResourceCountsFromCache();
|
||||
}
|
||||
counts = defaultIfNull(counts, Collections.emptyMap());
|
||||
|
||||
retVal = super.getServerConformance(theRequest, theRequestDetails);
|
||||
for (CapabilityStatementRestComponent nextRest : retVal.getRest()) {
|
||||
|
||||
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
|
||||
|
||||
nextResource.setVersioning(ResourceVersionPolicy.VERSIONEDUPDATE);
|
||||
|
||||
ConditionalDeleteStatus conditionalDelete = nextResource.getConditionalDelete();
|
||||
if (conditionalDelete == ConditionalDeleteStatus.MULTIPLE && myDaoConfig.isAllowMultipleDelete() == false) {
|
||||
nextResource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
|
||||
}
|
||||
|
||||
// Add resource counts
|
||||
Long count = counts.get(nextResource.getTypeElement().getValueAsString());
|
||||
if (count != null) {
|
||||
nextResource.addExtension(new Extension(ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalType(count)));
|
||||
}
|
||||
|
||||
nextResource.getSearchParam().clear();
|
||||
String resourceName = nextResource.getType();
|
||||
RuntimeResourceDefinition resourceDef = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
|
||||
for (RuntimeSearchParam runtimeSp : searchParams) {
|
||||
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
|
||||
|
||||
confSp.setName(runtimeSp.getName());
|
||||
confSp.setDocumentation(runtimeSp.getDescription());
|
||||
confSp.setDefinition(runtimeSp.getUri());
|
||||
switch (runtimeSp.getParamType()) {
|
||||
case COMPOSITE:
|
||||
confSp.setType(SearchParamType.COMPOSITE);
|
||||
break;
|
||||
case DATE:
|
||||
confSp.setType(SearchParamType.DATE);
|
||||
break;
|
||||
case NUMBER:
|
||||
confSp.setType(SearchParamType.NUMBER);
|
||||
break;
|
||||
case QUANTITY:
|
||||
confSp.setType(SearchParamType.QUANTITY);
|
||||
break;
|
||||
case REFERENCE:
|
||||
confSp.setType(SearchParamType.REFERENCE);
|
||||
break;
|
||||
case STRING:
|
||||
confSp.setType(SearchParamType.STRING);
|
||||
break;
|
||||
case TOKEN:
|
||||
confSp.setType(SearchParamType.TOKEN);
|
||||
break;
|
||||
case URI:
|
||||
confSp.setType(SearchParamType.URI);
|
||||
break;
|
||||
case SPECIAL:
|
||||
confSp.setType(SearchParamType.SPECIAL);
|
||||
break;
|
||||
case HAS:
|
||||
// Shouldn't happen
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (myDaoConfig.getSupportedSubscriptionTypes().contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) {
|
||||
if (isNotBlank(myDaoConfig.getWebsocketContextPath())) {
|
||||
Extension websocketExtension = new Extension();
|
||||
websocketExtension.setUrl(Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL);
|
||||
websocketExtension.setValue(new UriType(myDaoConfig.getWebsocketContextPath()));
|
||||
retVal.getRestFirstRep().addExtension(websocketExtension);
|
||||
}
|
||||
}
|
||||
|
||||
massage(retVal);
|
||||
|
||||
retVal.getImplementation().setDescription(myImplementationDescription);
|
||||
myCachedValue = retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public boolean isIncludeResourceCounts() {
|
||||
return myIncludeResourceCounts;
|
||||
}
|
||||
|
||||
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
|
||||
myIncludeResourceCounts = theIncludeResourceCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
protected void massage(CapabilityStatement theStatement) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public void setDaoConfig(DaoConfig myDaoConfig) {
|
||||
this.myDaoConfig = myDaoConfig;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setImplementationDescription(String theImplDesc) {
|
||||
myImplementationDescription = theImplDesc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestfulServer(RestfulServer theRestfulServer) {
|
||||
this.myRestfulServer = theRestfulServer;
|
||||
super.setRestfulServer(theRestfulServer);
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) {
|
||||
this.mySystemDao = mySystemDao;
|
||||
}
|
||||
}
|
|
@ -64,6 +64,7 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
|||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||
import ca.uhn.fhir.jpa.provider.r4.BaseJpaResourceProviderObservationR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
|
@ -93,6 +94,8 @@ import ca.uhn.fhir.rest.server.BasePagingProvider;
|
|||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.ResourceUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
@ -199,6 +202,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
private static IValidationSupport ourJpaValidationSupportChainR4;
|
||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||
|
||||
@Autowired
|
||||
protected IPackageInstallerSvc myPackageInstallerSvc;
|
||||
@Autowired
|
||||
protected ITermConceptMappingSvc myConceptMappingSvc;
|
||||
@Autowired
|
||||
|
@ -591,13 +596,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
}
|
||||
|
||||
protected <T extends IBaseResource> T loadResourceFromClasspath(Class<T> type, String resourceName) throws IOException {
|
||||
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
|
||||
if (stream == null) {
|
||||
fail("Unable to load resource: " + resourceName);
|
||||
}
|
||||
String string = IOUtils.toString(stream, "UTF-8");
|
||||
IParser newJsonParser = EncodingEnum.detectEncodingNoDefault(string).newParser(myFhirCtx);
|
||||
return newJsonParser.parseResource(type, string);
|
||||
return ClasspathUtil.loadResource(myFhirCtx, type, resourceName);
|
||||
}
|
||||
|
||||
protected void validate(IBaseResource theResource) {
|
||||
|
|
|
@ -146,7 +146,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
myCaptureQueriesListener.clear();
|
||||
myObservationDao.validate(obs, null, null, null, null, null, null);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(10, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
assertEquals(12, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size());
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package ca.uhn.fhir.jpa.interceptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.SearchPreferHandlingInterceptor;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.fail;
|
||||
|
||||
public class SearchPreferHandlingInterceptorJpaTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private SearchPreferHandlingInterceptor mySvc;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
|
||||
mySvc = new SearchPreferHandlingInterceptor(mySearchParamRegistry);
|
||||
ourRestServer.registerInterceptor(mySvc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
ourRestServer.unregisterInterceptor(mySvc);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_NoHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_StrictHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_HANDLING + "=" + Constants.HEADER_PREFER_HANDLING_STRICT)
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_UnrelatedPreferHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION)
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_LenientHeader() {
|
||||
Bundle outcome = myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.and(Patient.IDENTIFIER.exactly().codes("BLAH"))
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_HANDLING + "=" + Constants.HEADER_PREFER_HANDLING_LENIENT)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
assertEquals(0, outcome.getTotal());
|
||||
|
||||
assertEquals(ourServerBase + "/Patient?_format=json&_pretty=true&identifier=BLAH", outcome.getLink(Constants.LINK_SELF).getUrl());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -78,8 +78,6 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(NpmR4Test.class);
|
||||
@Autowired
|
||||
public IPackageInstallerSvc igInstaller;
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager myPackageCacheManager;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
|
@ -140,7 +138,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
.setVersion("3.1.0")
|
||||
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL)
|
||||
.setFetchDependencies(true);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
runInTransaction(()->{
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous(SearchParameter.SP_BASE, new TokenParam("NamingSystem"));
|
||||
|
@ -154,7 +152,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
});
|
||||
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,7 +162,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/nictiz.fhir.nl.stu3.questionnaires/1.0.2", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("nictiz.fhir.nl.stu3.questionnaires").setVersion("1.0.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
@ -203,7 +201,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
|
@ -273,7 +271,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
resourceList.add("Organization");
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
spec.setInstallResourceTypes(resourceList);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(3, outcome.getResourcesInstalled().get("Organization"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
|
@ -310,7 +308,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
resourceList.add("Organization");
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
spec.setInstallResourceTypes(resourceList);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(3, outcome.getResourcesInstalled().get("Organization"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
|
@ -350,7 +348,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
spec.setInstallResourceTypes(resourceList);
|
||||
try {
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
fail();
|
||||
} catch (ImplementationGuideInstallationException theE) {
|
||||
assertThat(theE.getMessage(), containsString("Resources in a package must have a url or identifier to be loaded by the package installer."));
|
||||
|
@ -370,7 +368,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
resourceList.add("ImplementationGuide");
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-ig").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
spec.setInstallResourceTypes(resourceList);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(1, outcome.getResourcesInstalled().get("ImplementationGuide"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
|
@ -393,7 +391,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(0, outcome.getResourcesInstalled().size(), outcome.getResourcesInstalled().toString());
|
||||
|
||||
}
|
||||
|
@ -408,11 +406,11 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
PackageInstallOutcomeJson outcome;
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
outcome = igInstaller.install(spec);
|
||||
outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||
|
||||
igInstaller.install(spec);
|
||||
outcome = igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
outcome = myPackageInstallerSvc.install(spec);
|
||||
assertEquals(null, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||
|
||||
// Ensure that we loaded the contents
|
||||
|
@ -430,7 +428,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/UK.Core.r4/1.1.0", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("UK.Core.r4").setVersion("1.1.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
@ -450,9 +448,9 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageMetadataJson metadata = myPackageCacheManager.loadPackageMetadata("hl7.fhir.uv.shorthand");
|
||||
|
@ -484,18 +482,18 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
PackageInstallationSpec spec;
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||
ourLog.info("Install messages:\n * {}", outcome.getMessage().stream().collect(Collectors.joining("\n * ")));
|
||||
assertThat(outcome.getMessage(), hasItem("Marking package hl7.fhir.uv.shorthand#0.12.0 as current version"));
|
||||
assertThat(outcome.getMessage(), hasItem("Indexing CodeSystem Resource[package/CodeSystem-shorthand-code-system.json] with URL: http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system|0.12.0"));
|
||||
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
outcome = igInstaller.install(spec);
|
||||
outcome = myPackageInstallerSvc.install(spec);
|
||||
ourLog.info("Install messages:\n * {}", outcome.getMessage().stream().collect(Collectors.joining("\n * ")));
|
||||
assertThat(outcome.getMessage(), not(hasItem("Marking package hl7.fhir.uv.shorthand#0.11.1 as current version")));
|
||||
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
|
||||
NpmPackage pkg;
|
||||
|
@ -519,7 +517,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install older version
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
// Older version is current
|
||||
runInTransaction(() -> {
|
||||
|
@ -535,7 +533,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Now install newer version
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
// Newer version is current
|
||||
runInTransaction(() -> {
|
||||
|
@ -562,7 +560,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install newer version
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
|
||||
runInTransaction(() -> {
|
||||
|
@ -578,7 +576,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install older version
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
// Newer version is still current
|
||||
runInTransaction(() -> {
|
||||
|
@ -604,7 +602,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
|
@ -613,7 +611,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install same again
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
|
@ -634,7 +632,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install older version
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-exchange.fhir.us.com").setVersion("2.1.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
IBundleProvider spSearch = mySearchParameterDao.search(SearchParameterMap.newSynchronous("code", new TokenParam("network-id")));
|
||||
assertEquals(1, spSearch.sizeOrThrowNpe());
|
||||
|
@ -657,7 +655,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
|
||||
// Install newer version
|
||||
spec = new PackageInstallationSpec().setName("test-exchange.fhir.us.com").setVersion("2.1.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
spSearch = mySearchParameterDao.search(SearchParameterMap.newSynchronous("code", new TokenParam("network-id")));
|
||||
assertEquals(1, spSearch.sizeOrThrowNpe());
|
||||
|
@ -675,9 +673,9 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
byte[] contents0120 = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(contents0111);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(contents0120);
|
||||
igInstaller.install(spec);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
|
||||
assertArrayEquals(contents0111, myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "0.11.1").getBytes());
|
||||
|
@ -699,9 +697,9 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.0.tgz"));
|
||||
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
myPackageInstallerSvc.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
myPackageInstallerSvc.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
myPackageInstallerSvc.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
|
||||
runInTransaction(() -> {
|
||||
Slice<NpmPackageVersionResourceEntity> versions = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ValueSet/shorthand-instance-tags");
|
||||
|
|
|
@ -7,12 +7,12 @@ import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
|
|||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
|
||||
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
|
||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
|
@ -23,7 +23,6 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
|||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
@ -38,7 +37,6 @@ import org.hl7.fhir.r4.model.Patient;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
@ -57,8 +55,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||
private static final Logger ourLog = getLogger(BaseResourceProviderR4Test.class);
|
||||
|
||||
|
||||
protected static IValidationSupport myValidationSupport;
|
||||
protected static CloseableHttpClient ourHttpClient;
|
||||
|
@ -155,8 +151,9 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
ourRestServer.registerInterceptor(corsInterceptor);
|
||||
|
||||
ourSearchParamRegistry = myAppCtx.getBean(SearchParamRegistryImpl.class);
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry, validationSupport);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
ourRestServer.setServerConformanceProvider(confProvider);
|
||||
|
||||
|
@ -169,8 +166,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
myValidationSupport = wac.getBean(IValidationSupport.class);
|
||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
|
||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(400000);
|
||||
|
||||
|
@ -191,6 +186,13 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
protected static void clearRestfulServer() throws Exception {
|
||||
if (ourServer != null) {
|
||||
JettyUtil.closeServer(ourServer);
|
||||
}
|
||||
ourServer = null;
|
||||
}
|
||||
|
||||
protected boolean shouldLogClient() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ServerCapabilityStatementProviderJpaR4Test extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ServerCapabilityStatementProviderJpaR4Test.class);
|
||||
|
||||
@Test
|
||||
public void testCorrectResourcesReflected() {
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
List<String> resourceTypes = cs.getRest().get(0).getResource().stream().map(t -> t.getType()).collect(Collectors.toList());
|
||||
assertThat(resourceTypes, hasItems("Patient", "Observation", "SearchParameter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomSearchParamsReflected() {
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("foo");
|
||||
fooSp.setUrl("http://acme.com/foo");
|
||||
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
|
||||
fooSp.setTitle("FOO SP");
|
||||
fooSp.setDescription("This is a search param!");
|
||||
fooSp.setExpression("Patient.gender");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(fooSp);
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
List<CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent> fooSearchParams = findSearchParams(cs, "Patient", "foo");
|
||||
assertEquals(1, fooSearchParams.size());
|
||||
assertEquals("foo", fooSearchParams.get(0).getName());
|
||||
assertEquals("http://acme.com/foo", fooSearchParams.get(0).getDefinition());
|
||||
assertEquals("This is a search param!", fooSearchParams.get(0).getDocumentation());
|
||||
assertEquals(Enumerations.SearchParamType.TOKEN, fooSearchParams.get(0).getType());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisteredProfilesReflected_StoredInServer() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/StructureDefinition-kfdrc-patient.json");
|
||||
myStructureDefinitionDao.update(sd);
|
||||
StructureDefinition sd2 = loadResourceFromClasspath(StructureDefinition.class, "/r4/StructureDefinition-kfdrc-patient-no-phi.json");
|
||||
myStructureDefinitionDao.update(sd2);
|
||||
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
List<String> supportedProfiles = findSupportedProfiles(cs, "Patient");
|
||||
assertThat(supportedProfiles.toString(), supportedProfiles, containsInAnyOrder(
|
||||
"http://fhir.kids-first.io/StructureDefinition/kfdrc-patient",
|
||||
"http://fhir.kids-first.io/StructureDefinition/kfdrc-patient-no-phi"
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Universal profiles like vitalsigns should not be excluded
|
||||
*/
|
||||
@Test
|
||||
public void testRegisteredProfilesReflected_Universal() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/r4-create-structuredefinition-vital-signs.json");
|
||||
ourLog.info("Stored SD to ID: {}", myStructureDefinitionDao.update(sd).getId());
|
||||
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
List<String> supportedProfiles = findSupportedProfiles(cs, "Observation");
|
||||
assertThat(supportedProfiles.toString(), supportedProfiles, containsInAnyOrder(
|
||||
"http://hl7.org/fhir/StructureDefinition/vitalsigns"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisteredProfilesReflected_StoredInPackageRegistry() throws IOException {
|
||||
byte[] bytes = loadClasspathBytes("/packages/UK.Core.r4-1.1.0.tgz");
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec()
|
||||
.setName("UK.Core.r4")
|
||||
.setVersion("1.1.0")
|
||||
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY)
|
||||
.setPackageContents(bytes);
|
||||
myPackageInstallerSvc.install(spec);
|
||||
|
||||
CapabilityStatement cs = myClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
List<String> supportedProfiles = findSupportedProfiles(cs, "Patient");
|
||||
assertThat(supportedProfiles.toString(), supportedProfiles, containsInAnyOrder(
|
||||
"https://fhir.nhs.uk/R4/StructureDefinition/UKCore-Patient"
|
||||
));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<String> findSupportedProfiles(CapabilityStatement theCapabilityStatement, String theResourceType) {
|
||||
assertEquals(1, theCapabilityStatement.getRest().size());
|
||||
return theCapabilityStatement
|
||||
.getRest()
|
||||
.get(0)
|
||||
.getResource()
|
||||
.stream()
|
||||
.filter(t -> t.getType().equals(theResourceType))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException())
|
||||
.getSupportedProfile()
|
||||
.stream()
|
||||
.map(t -> t.getValue())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent> findSearchParams(CapabilityStatement theCapabilityStatement, String theResourceType, String theParamName) {
|
||||
assertEquals(1, theCapabilityStatement.getRest().size());
|
||||
return theCapabilityStatement
|
||||
.getRest()
|
||||
.get(0)
|
||||
.getResource()
|
||||
.stream()
|
||||
.filter(t -> t.getType().equals(theResourceType))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException())
|
||||
.getSearchParam()
|
||||
.stream()
|
||||
.filter(t -> t.getName().equals(theParamName))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
|||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
|
@ -19,10 +20,8 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
|||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
@ -110,8 +109,11 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
|||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry);
|
||||
ourSearchParamRegistry = myAppCtx.getBean(SearchParamRegistryImpl.class);
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry, validationSupport);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
ourRestServer.setServerConformanceProvider(confProvider);
|
||||
|
||||
|
@ -166,11 +168,9 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
|||
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
|
||||
myValidationSupport = wac.getBean(IValidationSupport.class);
|
||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryImpl.class);
|
||||
ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -69,7 +69,11 @@
|
|||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
|
|
|
@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmGoldenResourceDeletingSvc;
|
|||
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
|
||||
import ca.uhn.fhir.jpa.mdm.svc.MdmSubmitSvcImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
||||
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -51,8 +51,8 @@ public class MdmSubmitterConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext) {
|
||||
return new MdmRuleValidator(theFhirContext, mdmSearchParamSvc());
|
||||
MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext, ISearchParamRetriever theSearchParamRetriever) {
|
||||
return new MdmRuleValidator(theFhirContext, theSearchParamRetriever);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -31,7 +31,6 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
|||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -41,7 +40,7 @@ import javax.annotation.Nullable;
|
|||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class MdmSearchParamSvc implements ISearchParamRetriever {
|
||||
public class MdmSearchParamSvc {
|
||||
@Autowired
|
||||
FhirContext myFhirContext;
|
||||
@Autowired
|
||||
|
@ -66,18 +65,12 @@ public class MdmSearchParamSvc implements ISearchParamRetriever {
|
|||
return mySearchParamExtractorService.extractParamValuesAsStrings(activeSearchParam, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) {
|
||||
return mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a source type, and a criteria string of the shape name=x&birthDate=y, generate a {@link SearchParameterMap}
|
||||
* that represents this query.
|
||||
*
|
||||
* @param theSourceType the resource type to execute the search on
|
||||
* @param theCriteria the string search criteria.
|
||||
*
|
||||
* @param theCriteria the string search criteria.
|
||||
* @return the generated SearchParameterMap, or an empty one if there is no criteria.
|
||||
*/
|
||||
public SearchParameterMap getSearchParameterMapFromCriteria(String theSourceType, @Nullable String theCriteria) {
|
||||
|
@ -91,17 +84,18 @@ public class MdmSearchParamSvc implements ISearchParamRetriever {
|
|||
}
|
||||
|
||||
public ISearchBuilder generateSearchBuilderForType(String theSourceType) {
|
||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theSourceType);
|
||||
return mySearchBuilderFactory.newSearchBuilder(resourceDao, theSourceType, resourceDao.getResourceType());
|
||||
}
|
||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theSourceType);
|
||||
return mySearchBuilderFactory.newSearchBuilder(resourceDao, theSourceType, resourceDao.getResourceType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return true if the types match, or the search param type is '*', otherwise false.
|
||||
*
|
||||
* @param theSearchParamType
|
||||
* @param theResourceType
|
||||
* @return
|
||||
*/
|
||||
public boolean searchParamTypeIsValidForResourceType(String theSearchParamType, String theResourceType) {
|
||||
return theSearchParamType.equalsIgnoreCase(theResourceType) || theSearchParamType.equalsIgnoreCase("*");
|
||||
}
|
||||
public boolean searchParamTypeIsValidForResourceType(String theSearchParamType, String theResourceType) {
|
||||
return theSearchParamType.equalsIgnoreCase(theResourceType) || theSearchParamType.equalsIgnoreCase("*");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,12 @@ 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 java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -38,22 +43,12 @@ public class JpaRuntimeSearchParam extends RuntimeSearchParam {
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JpaRuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, boolean theUnique, List<Component> theComponents, Collection<? extends IPrimitiveType<String>> theBase) {
|
||||
super(theId, theUri, theName, theDescription, thePath, theParamType, createCompositeList(theParamType), theProvidesMembershipInCompartments, theTargets, theStatus, toStrings(theBase));
|
||||
public JpaRuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, boolean theUnique, List<Component> theComponents, Collection<String> theBase) {
|
||||
super(theId, theUri, theName, theDescription, thePath, theParamType, createCompositeList(theParamType), theProvidesMembershipInCompartments, theTargets, theStatus, theBase);
|
||||
myUnique = theUnique;
|
||||
myComponents = Collections.unmodifiableList(theComponents);
|
||||
}
|
||||
|
||||
private static Collection<String> toStrings(Collection<? extends IPrimitiveType<String>> theBase) {
|
||||
HashSet<String> retVal = new HashSet<>();
|
||||
for (IPrimitiveType<String> next : theBase) {
|
||||
if (isNotBlank(next.getValueAsString())) {
|
||||
retVal.add(next.getValueAsString());
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<Component> getComponents() {
|
||||
return myComponents;
|
||||
}
|
||||
|
@ -62,14 +57,6 @@ public class JpaRuntimeSearchParam extends RuntimeSearchParam {
|
|||
return myUnique;
|
||||
}
|
||||
|
||||
private static ArrayList<RuntimeSearchParam> createCompositeList(RestSearchParameterTypeEnum theParamType) {
|
||||
if (theParamType == RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Component {
|
||||
private final String myExpression;
|
||||
private final IBaseReference myReference;
|
||||
|
@ -89,5 +76,13 @@ public class JpaRuntimeSearchParam extends RuntimeSearchParam {
|
|||
}
|
||||
}
|
||||
|
||||
private static ArrayList<RuntimeSearchParam> createCompositeList(RestSearchParameterTypeEnum theParamType) {
|
||||
if (theParamType == RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -25,24 +25,19 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
|||
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
||||
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
|
||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ISearchParamRegistry {
|
||||
public interface ISearchParamRegistry extends ISearchParamRetriever {
|
||||
|
||||
/**
|
||||
* Request that the cache be refreshed now, in the current thread
|
||||
*/
|
||||
void forceRefresh();
|
||||
|
||||
/**
|
||||
* @return Returns {@literal null} if no match
|
||||
*/
|
||||
RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName);
|
||||
|
||||
/**
|
||||
* @return the number of search parameter entries changed
|
||||
*/
|
||||
|
@ -50,8 +45,6 @@ public interface ISearchParamRegistry {
|
|||
|
||||
ReadOnlySearchParamCache getActiveSearchParams();
|
||||
|
||||
Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName);
|
||||
|
||||
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames);
|
||||
|
||||
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName);
|
||||
|
|
|
@ -28,10 +28,13 @@ import ca.uhn.fhir.model.api.ExtensionDt;
|
|||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.DatatypeUtil;
|
||||
import ca.uhn.fhir.util.ExtensionUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.hl7.fhir.dstu3.model.Extension;
|
||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||
|
@ -47,8 +50,10 @@ import org.springframework.stereotype.Service;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -74,10 +79,8 @@ public class SearchParameterCanonicalizer {
|
|||
retVal = canonicalizeSearchParameterDstu3((org.hl7.fhir.dstu3.model.SearchParameter) theSearchParameter);
|
||||
break;
|
||||
case R4:
|
||||
retVal = canonicalizeSearchParameterR4((org.hl7.fhir.r4.model.SearchParameter) theSearchParameter);
|
||||
break;
|
||||
case R5:
|
||||
retVal = canonicalizeSearchParameterR5((org.hl7.fhir.r5.model.SearchParameter) theSearchParameter);
|
||||
retVal = canonicalizeSearchParameterR4Plus(theSearchParameter);
|
||||
break;
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
|
@ -161,7 +164,7 @@ public class SearchParameterCanonicalizer {
|
|||
|
||||
List<JpaRuntimeSearchParam.Component> components = Collections.emptyList();
|
||||
Collection<? extends IPrimitiveType<String>> base = Collections.singletonList(theNextSp.getBaseElement());
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, toStrings(base));
|
||||
}
|
||||
|
||||
private JpaRuntimeSearchParam canonicalizeSearchParameterDstu3(org.hl7.fhir.dstu3.model.SearchParameter theNextSp) {
|
||||
|
@ -244,66 +247,63 @@ public class SearchParameterCanonicalizer {
|
|||
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
|
||||
}
|
||||
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, toStrings(theNextSp.getBase()));
|
||||
}
|
||||
|
||||
private JpaRuntimeSearchParam canonicalizeSearchParameterR4(org.hl7.fhir.r4.model.SearchParameter theNextSp) {
|
||||
String name = theNextSp.getCode();
|
||||
String description = theNextSp.getDescription();
|
||||
String path = theNextSp.getExpression();
|
||||
private JpaRuntimeSearchParam canonicalizeSearchParameterR4Plus(IBaseResource theNextSp) {
|
||||
FhirTerser terser = myFhirContext.newTerser();
|
||||
String name = terser.getSinglePrimitiveValueOrNull(theNextSp, "code");
|
||||
String description = terser.getSinglePrimitiveValueOrNull(theNextSp, "description");
|
||||
String path = terser.getSinglePrimitiveValueOrNull(theNextSp, "expression");
|
||||
List<String> base = terser.getValues(theNextSp, "base", IPrimitiveType.class).stream().map(t -> t.getValueAsString()).collect(Collectors.toList());
|
||||
|
||||
RestSearchParameterTypeEnum paramType = null;
|
||||
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
|
||||
switch (theNextSp.getType()) {
|
||||
case COMPOSITE:
|
||||
switch (terser.getSinglePrimitiveValue(theNextSp, "type").orElse("")) {
|
||||
case "composite":
|
||||
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
||||
break;
|
||||
case DATE:
|
||||
case "date":
|
||||
paramType = RestSearchParameterTypeEnum.DATE;
|
||||
break;
|
||||
case NUMBER:
|
||||
case "number":
|
||||
paramType = RestSearchParameterTypeEnum.NUMBER;
|
||||
break;
|
||||
case QUANTITY:
|
||||
case "quantity":
|
||||
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
break;
|
||||
case REFERENCE:
|
||||
case "reference":
|
||||
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
break;
|
||||
case STRING:
|
||||
case "string":
|
||||
paramType = RestSearchParameterTypeEnum.STRING;
|
||||
break;
|
||||
case TOKEN:
|
||||
case "token":
|
||||
paramType = RestSearchParameterTypeEnum.TOKEN;
|
||||
break;
|
||||
case URI:
|
||||
case "uri":
|
||||
paramType = RestSearchParameterTypeEnum.URI;
|
||||
break;
|
||||
case SPECIAL:
|
||||
case "special":
|
||||
paramType = RestSearchParameterTypeEnum.SPECIAL;
|
||||
break;
|
||||
case NULL:
|
||||
}
|
||||
switch (terser.getSinglePrimitiveValue(theNextSp, "status").orElse("")) {
|
||||
case "active":
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
|
||||
break;
|
||||
case "draft":
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
|
||||
break;
|
||||
case "retired":
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
|
||||
break;
|
||||
case "unknown":
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||
break;
|
||||
}
|
||||
if (theNextSp.getStatus() != null) {
|
||||
switch (theNextSp.getStatus()) {
|
||||
case ACTIVE:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
|
||||
break;
|
||||
case DRAFT:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
|
||||
break;
|
||||
case RETIRED:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Set<String> providesMembershipInCompartments = Collections.emptySet();
|
||||
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
|
||||
Set<String> targets = terser.getValues(theNextSp, "target", IPrimitiveType.class).stream().map(t -> t.getValueAsString()).collect(Collectors.toSet());
|
||||
|
||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
|
@ -312,111 +312,29 @@ public class SearchParameterCanonicalizer {
|
|||
}
|
||||
|
||||
IIdType id = theNextSp.getIdElement();
|
||||
String uri = "";
|
||||
String uri = terser.getSinglePrimitiveValueOrNull(theNextSp, "url");
|
||||
boolean unique = false;
|
||||
|
||||
List<org.hl7.fhir.r4.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
|
||||
if (uniqueExts.size() > 0) {
|
||||
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
|
||||
if (uniqueExtsValuePrimitive != null) {
|
||||
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
|
||||
unique = true;
|
||||
}
|
||||
}
|
||||
String value = ((IBaseHasExtensions) theNextSp).getExtension()
|
||||
.stream()
|
||||
.filter(e -> HapiExtensions.EXT_SP_UNIQUE.equals(e.getUrl()))
|
||||
.filter(t->t.getValue() instanceof IPrimitiveType)
|
||||
.map(t->(IPrimitiveType<?>)t.getValue())
|
||||
.map(t->t.getValueAsString())
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
if ("true".equalsIgnoreCase(value)) {
|
||||
unique = true;
|
||||
}
|
||||
|
||||
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
|
||||
for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
|
||||
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new Reference(next.getDefinition())));
|
||||
for (IBase next : terser.getValues(theNextSp, "component")) {
|
||||
String expression = terser.getSinglePrimitiveValueOrNull(next, "expression");
|
||||
String definition = terser.getSinglePrimitiveValueOrNull(next, "definition");
|
||||
components.add(new JpaRuntimeSearchParam.Component(expression, new Reference(definition)));
|
||||
}
|
||||
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
|
||||
}
|
||||
|
||||
private JpaRuntimeSearchParam canonicalizeSearchParameterR5(org.hl7.fhir.r5.model.SearchParameter theNextSp) {
|
||||
String name = theNextSp.getCode();
|
||||
String description = theNextSp.getDescription();
|
||||
String path = theNextSp.getExpression();
|
||||
RestSearchParameterTypeEnum paramType = null;
|
||||
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
|
||||
switch (theNextSp.getType()) {
|
||||
case COMPOSITE:
|
||||
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
||||
break;
|
||||
case DATE:
|
||||
paramType = RestSearchParameterTypeEnum.DATE;
|
||||
break;
|
||||
case NUMBER:
|
||||
paramType = RestSearchParameterTypeEnum.NUMBER;
|
||||
break;
|
||||
case QUANTITY:
|
||||
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
break;
|
||||
case REFERENCE:
|
||||
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
break;
|
||||
case STRING:
|
||||
paramType = RestSearchParameterTypeEnum.STRING;
|
||||
break;
|
||||
case TOKEN:
|
||||
paramType = RestSearchParameterTypeEnum.TOKEN;
|
||||
break;
|
||||
case URI:
|
||||
paramType = RestSearchParameterTypeEnum.URI;
|
||||
break;
|
||||
case SPECIAL:
|
||||
paramType = RestSearchParameterTypeEnum.SPECIAL;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
if (theNextSp.getStatus() != null) {
|
||||
switch (theNextSp.getStatus()) {
|
||||
case ACTIVE:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
|
||||
break;
|
||||
case DRAFT:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
|
||||
break;
|
||||
case RETIRED:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Set<String> providesMembershipInCompartments = Collections.emptySet();
|
||||
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
|
||||
|
||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = theNextSp.getIdElement();
|
||||
String uri = "";
|
||||
boolean unique = false;
|
||||
|
||||
List<org.hl7.fhir.r5.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
|
||||
if (uniqueExts.size() > 0) {
|
||||
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
|
||||
if (uniqueExtsValuePrimitive != null) {
|
||||
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
|
||||
unique = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
|
||||
for (org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
|
||||
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new org.hl7.fhir.r5.model.Reference(next.getDefinition())));
|
||||
}
|
||||
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
|
||||
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
|
||||
}
|
||||
|
||||
|
||||
|
@ -449,4 +367,16 @@ public class SearchParameterCanonicalizer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<String> toStrings(Collection<? extends IPrimitiveType<String>> theBase) {
|
||||
HashSet<String> retVal = new HashSet<>();
|
||||
for (IPrimitiveType<String> next : theBase) {
|
||||
if (isNotBlank(next.getValueAsString())) {
|
||||
retVal.add(next.getValueAsString());
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
|
@ -9,14 +10,13 @@ import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider;
|
|||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
|
@ -145,7 +145,8 @@ public class TestRestfulServer extends RestfulServer {
|
|||
providers.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class));
|
||||
systemDao = myAppCtx.getBean("mySystemDaoR4", IFhirSystemDao.class);
|
||||
etagSupport = ETagSupportEnum.ENABLED;
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class));
|
||||
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));
|
||||
|
@ -164,8 +165,8 @@ public class TestRestfulServer extends RestfulServer {
|
|||
providers.add(myAppCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class));
|
||||
systemDao = myAppCtx.getBean("mySystemDaoR5", IFhirSystemDao.class);
|
||||
etagSupport = ETagSupportEnum.ENABLED;
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class));
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class), validationSupport);
|
||||
setServerConformanceProvider(confProvider);
|
||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
|
|
@ -1013,11 +1013,14 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
* Notify interceptors about the incoming request
|
||||
* *************************/
|
||||
|
||||
HookParams preProcessedParams = new HookParams();
|
||||
preProcessedParams.add(HttpServletRequest.class, theRequest);
|
||||
preProcessedParams.add(HttpServletResponse.class, theResponse);
|
||||
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED, preProcessedParams)) {
|
||||
return;
|
||||
// Interceptor: SERVER_INCOMING_REQUEST_PRE_PROCESSED
|
||||
if (myInterceptorService.hasHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED)) {
|
||||
HookParams preProcessedParams = new HookParams();
|
||||
preProcessedParams.add(HttpServletRequest.class, theRequest);
|
||||
preProcessedParams.add(HttpServletResponse.class, theResponse);
|
||||
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED, preProcessedParams)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String requestPath = getRequestPath(requestFullPath, servletContextPath, servletPath);
|
||||
|
@ -1056,6 +1059,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
requestDetails.setFhirServerBase(fhirServerBase);
|
||||
requestDetails.setCompleteUrl(completeUrl);
|
||||
|
||||
// Interceptor: SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED
|
||||
if (myInterceptorService.hasHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED)) {
|
||||
HookParams preProcessedParams = new HookParams();
|
||||
preProcessedParams.add(HttpServletRequest.class, theRequest);
|
||||
preProcessedParams.add(HttpServletResponse.class, theResponse);
|
||||
preProcessedParams.add(RequestDetails.class, requestDetails);
|
||||
preProcessedParams.add(ServletRequestDetails.class, requestDetails);
|
||||
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED, preProcessedParams)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
validateRequest(requestDetails);
|
||||
|
||||
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
|
||||
|
@ -1063,14 +1078,16 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
RestOperationTypeEnum operation = resourceMethod.getRestOperationType(requestDetails);
|
||||
requestDetails.setRestOperationType(operation);
|
||||
|
||||
// Handle server interceptors
|
||||
HookParams postProcessedParams = new HookParams();
|
||||
postProcessedParams.add(RequestDetails.class, requestDetails);
|
||||
postProcessedParams.add(ServletRequestDetails.class, requestDetails);
|
||||
postProcessedParams.add(HttpServletRequest.class, theRequest);
|
||||
postProcessedParams.add(HttpServletResponse.class, theResponse);
|
||||
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED, postProcessedParams)) {
|
||||
return;
|
||||
// Interceptor: SERVER_INCOMING_REQUEST_POST_PROCESSED
|
||||
if (myInterceptorService.hasHooks(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)) {
|
||||
HookParams postProcessedParams = new HookParams();
|
||||
postProcessedParams.add(RequestDetails.class, requestDetails);
|
||||
postProcessedParams.add(ServletRequestDetails.class, requestDetails);
|
||||
postProcessedParams.add(HttpServletRequest.class, theRequest);
|
||||
postProcessedParams.add(HttpServletResponse.class, theResponse);
|
||||
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED, postProcessedParams)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -21,28 +21,44 @@ package ca.uhn.fhir.rest.server;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import java.util.stream.Collectors;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
public class RestfulServerConfiguration {
|
||||
public class RestfulServerConfiguration implements ISearchParamRetriever {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class);
|
||||
private Collection<ResourceBinding> resourceBindings;
|
||||
private List<BaseMethodBinding<?>> serverBindings;
|
||||
private Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype;
|
||||
private Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype;
|
||||
private String implementationDescription;
|
||||
private String serverVersion = VersionUtil.getVersion();
|
||||
private String serverName = "HAPI FHIR";
|
||||
|
@ -92,15 +108,15 @@ public class RestfulServerConfiguration {
|
|||
this.serverBindings = theServerBindings;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Class<? extends IBaseResource>> getNameToSharedSupertype() {
|
||||
return resourceNameToSharedSupertype;
|
||||
}
|
||||
|
||||
public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype) {
|
||||
this.resourceNameToSharedSupertype = resourceNameToSharedSupertype;
|
||||
return this;
|
||||
}
|
||||
public Map<String, Class<? extends IBaseResource>> getNameToSharedSupertype() {
|
||||
return resourceNameToSharedSupertype;
|
||||
}
|
||||
|
||||
public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype) {
|
||||
this.resourceNameToSharedSupertype = resourceNameToSharedSupertype;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the implementationDescription
|
||||
|
@ -343,4 +359,94 @@ public class RestfulServerConfiguration {
|
|||
return retVal.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) {
|
||||
return getActiveSearchParams(theResourceName).get(theParamName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
||||
|
||||
Map<String, RuntimeSearchParam> retVal = new LinkedHashMap<>();
|
||||
|
||||
collectMethodBindings()
|
||||
.getOrDefault(theResourceName, Collections.emptyList())
|
||||
.stream()
|
||||
.filter(t -> t.getResourceName().equals(theResourceName))
|
||||
.filter(t -> t instanceof SearchMethodBinding)
|
||||
.map(t -> (SearchMethodBinding) t)
|
||||
.filter(t -> t.getQueryName() == null)
|
||||
.forEach(t -> createRuntimeBinding(retVal, t));
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void createRuntimeBinding(Map<String, RuntimeSearchParam> theMapToPopulate, SearchMethodBinding theSearchMethodBinding) {
|
||||
|
||||
List<SearchParameter> parameters = theSearchMethodBinding
|
||||
.getParameters()
|
||||
.stream()
|
||||
.filter(t -> t instanceof SearchParameter)
|
||||
.map(t -> (SearchParameter) t)
|
||||
.sorted(SearchParameterComparator.INSTANCE)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (SearchParameter nextParameter : parameters) {
|
||||
|
||||
String nextParamName = nextParameter.getName();
|
||||
|
||||
String nextParamUnchainedName = nextParamName;
|
||||
if (nextParamName.contains(".")) {
|
||||
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||
}
|
||||
|
||||
String nextParamDescription = nextParameter.getDescription();
|
||||
|
||||
/*
|
||||
* If the parameter has no description, default to the one from the resource
|
||||
*/
|
||||
if (StringUtils.isBlank(nextParamDescription)) {
|
||||
RuntimeResourceDefinition def = getFhirContext().getResourceDefinition(theSearchMethodBinding.getResourceName());
|
||||
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
||||
if (paramDef != null) {
|
||||
nextParamDescription = paramDef.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
if (theMapToPopulate.containsKey(nextParamUnchainedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = getFhirContext().getVersion().newIdType().setValue("SearchParameter/" + theSearchMethodBinding.getResourceName() + "-" + nextParamName);
|
||||
String uri = null;
|
||||
String description = nextParamDescription;
|
||||
String path = null;
|
||||
RestSearchParameterTypeEnum type = nextParameter.getParamType();
|
||||
List<RuntimeSearchParam> compositeOf = Collections.emptyList();
|
||||
Set<String> providesMembershipInCompartments = Collections.emptySet();
|
||||
Set<String> targets = Collections.emptySet();
|
||||
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
|
||||
Collection<String> base = Collections.singletonList(theSearchMethodBinding.getResourceName());
|
||||
RuntimeSearchParam param = new RuntimeSearchParam(id, uri, nextParamName, description, path, type, compositeOf, providesMembershipInCompartments, targets, status, base);
|
||||
theMapToPopulate.put(nextParamName, param);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class SearchParameterComparator implements Comparator<SearchParameter> {
|
||||
private static final SearchParameterComparator INSTANCE = new SearchParameterComparator();
|
||||
|
||||
@Override
|
||||
public int compare(SearchParameter theO1, SearchParameter theO2) {
|
||||
if (theO1.isRequired() == theO2.isRequired()) {
|
||||
return theO1.getName().compareTo(theO2.getName());
|
||||
}
|
||||
if (theO1.isRequired()) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import ca.uhn.fhir.rest.api.BundleLinks;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.PreferHandlingEnum;
|
||||
import ca.uhn.fhir.rest.api.PreferHeader;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
|
@ -795,7 +796,7 @@ public class RestfulServerUtils {
|
|||
PreferHeader retVal = new PreferHeader();
|
||||
|
||||
if (isNotBlank(theValue)) {
|
||||
StringTokenizer tok = new StringTokenizer(theValue, ";");
|
||||
StringTokenizer tok = new StringTokenizer(theValue, ";,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String next = trim(tok.nextToken());
|
||||
int eqIndex = next.indexOf('=');
|
||||
|
@ -812,15 +813,14 @@ public class RestfulServerUtils {
|
|||
|
||||
if (key.equals(Constants.HEADER_PREFER_RETURN)) {
|
||||
|
||||
if (value.length() < 2) {
|
||||
continue;
|
||||
}
|
||||
if ('"' == value.charAt(0) && '"' == value.charAt(value.length() - 1)) {
|
||||
value = value.substring(1, value.length() - 1);
|
||||
}
|
||||
|
||||
value = cleanUpValue(value);
|
||||
retVal.setReturn(PreferReturnEnum.fromHeaderValue(value));
|
||||
|
||||
} else if (key.equals(Constants.HEADER_PREFER_HANDLING)) {
|
||||
|
||||
value = cleanUpValue(value);
|
||||
retVal.setHanding(PreferHandlingEnum.fromHeaderValue(value));
|
||||
|
||||
} else if (key.equals(Constants.HEADER_PREFER_RESPOND_ASYNC)) {
|
||||
|
||||
retVal.setRespondAsync(true);
|
||||
|
@ -836,6 +836,16 @@ public class RestfulServerUtils {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private static String cleanUpValue(String value) {
|
||||
if (value.length() < 2) {
|
||||
value = "";
|
||||
}
|
||||
if ('"' == value.charAt(0) && '"' == value.charAt(value.length() - 1)) {
|
||||
value = value.substring(1, value.length() - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static boolean prettyPrintResponse(IRestfulServerDefaults theServer, RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* 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.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.PreferHandlingEnum;
|
||||
import ca.uhn.fhir.rest.api.PreferHeader;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* @since 5.4.0
|
||||
*/
|
||||
@Interceptor
|
||||
public class SearchPreferHandlingInterceptor {
|
||||
|
||||
@Nonnull
|
||||
private PreferHandlingEnum myDefaultBehaviour;
|
||||
@Nullable
|
||||
private ISearchParamRetriever mySearchParamRetriever;
|
||||
|
||||
/**
|
||||
* Constructor that uses the {@link RestfulServer} itself to determine
|
||||
* the allowable search params.
|
||||
*/
|
||||
public SearchPreferHandlingInterceptor() {
|
||||
setDefaultBehaviour(PreferHandlingEnum.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that uses a dedicated {@link ISearchParamRetriever} instance. This is mainly
|
||||
* intended for the JPA server.
|
||||
*/
|
||||
public SearchPreferHandlingInterceptor(ISearchParamRetriever theSearchParamRetriever) {
|
||||
this();
|
||||
mySearchParamRetriever = theSearchParamRetriever;
|
||||
}
|
||||
|
||||
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLER_SELECTED)
|
||||
public void incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
if (!SearchMethodBinding.isPlainSearchRequest(theRequestDetails)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String preferHeader = theRequestDetails.getHeader(Constants.HEADER_PREFER);
|
||||
PreferHandlingEnum handling = null;
|
||||
if (isNotBlank(preferHeader)) {
|
||||
PreferHeader parsedPreferHeader = RestfulServerUtils.parsePreferHeader((IRestfulServer<?>) theRequestDetails.getServer(), preferHeader);
|
||||
handling = parsedPreferHeader.getHanding();
|
||||
}
|
||||
|
||||
// Default behaviour
|
||||
if (handling == null) {
|
||||
handling = getDefaultBehaviour();
|
||||
}
|
||||
|
||||
removeUnwantedParams(handling, theRequestDetails);
|
||||
}
|
||||
|
||||
private void removeUnwantedParams(PreferHandlingEnum theHandling, RequestDetails theRequestDetails) {
|
||||
|
||||
ISearchParamRetriever searchParamRetriever = mySearchParamRetriever;
|
||||
if (searchParamRetriever == null) {
|
||||
searchParamRetriever = ((RestfulServer) theRequestDetails.getServer()).createConfiguration();
|
||||
}
|
||||
|
||||
String resourceName = theRequestDetails.getResourceName();
|
||||
HashMap<String, String[]> newMap = null;
|
||||
for (String paramName : theRequestDetails.getParameters().keySet()) {
|
||||
if (paramName.startsWith("_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RuntimeSearchParam activeSearchParam = searchParamRetriever.getActiveSearchParam(resourceName, paramName);
|
||||
if (activeSearchParam == null) {
|
||||
|
||||
if (theHandling == PreferHandlingEnum.LENIENT) {
|
||||
|
||||
if (newMap == null) {
|
||||
newMap = new HashMap<>(theRequestDetails.getParameters());
|
||||
}
|
||||
|
||||
newMap.remove(paramName);
|
||||
|
||||
} else {
|
||||
|
||||
// Strict handling
|
||||
List<String> allowedParams = searchParamRetriever.getActiveSearchParams(resourceName).keySet().stream().sorted().distinct().collect(Collectors.toList());
|
||||
HapiLocalizer localizer = theRequestDetails.getFhirContext().getLocalizer();
|
||||
String msg = localizer.getMessage("ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter", paramName, resourceName, allowedParams);
|
||||
throw new InvalidRequestException(msg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (newMap != null) {
|
||||
theRequestDetails.setParameters(newMap);
|
||||
}
|
||||
}
|
||||
|
||||
public PreferHandlingEnum getDefaultBehaviour() {
|
||||
return myDefaultBehaviour;
|
||||
}
|
||||
|
||||
public void setDefaultBehaviour(@Nonnull PreferHandlingEnum theDefaultBehaviour) {
|
||||
Validate.notNull(theDefaultBehaviour, "theDefaultBehaviour must not be null");
|
||||
myDefaultBehaviour = theDefaultBehaviour;
|
||||
}
|
||||
}
|
|
@ -55,6 +55,8 @@ import java.util.Set;
|
|||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseMethodBinding<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
||||
|
@ -379,7 +381,11 @@ public abstract class BaseMethodBinding<T> {
|
|||
|
||||
Class<? extends IBaseResource> returnTypeFromAnnotation = IBaseResource.class;
|
||||
if (read != null) {
|
||||
returnTypeFromAnnotation = read.type();
|
||||
if (isNotBlank(read.typeName())) {
|
||||
returnTypeFromAnnotation = theContext.getResourceDefinition(read.typeName()).getImplementingClass();
|
||||
} else {
|
||||
returnTypeFromAnnotation = read.type();
|
||||
}
|
||||
} else if (search != null) {
|
||||
returnTypeFromAnnotation = search.type();
|
||||
} else if (history != null) {
|
||||
|
|
|
@ -148,29 +148,18 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
@Override
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
|
||||
if (!mightBeSearchRequest(theRequest)) {
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
if (theRequest.getId() != null && myIdParamIndex == null) {
|
||||
ourLog.trace("Method {} doesn't match because ID is not null: {}", getMethod(), theRequest.getId());
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.GET && theRequest.getRequestType() != RequestTypeEnum.POST) {
|
||||
ourLog.trace("Method {} doesn't match because request type is {}", getMethod(), theRequest.getRequestType());
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (!StringUtils.equals(myCompartmentName, theRequest.getCompartmentName())) {
|
||||
ourLog.trace("Method {} doesn't match because it is for compartment {} but request is compartment {}", getMethod(), myCompartmentName, theRequest.getCompartmentName());
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getParameters().get(Constants.PARAM_PAGINGACTION) != null) {
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
if (myQueryName != null) {
|
||||
String[] queryNameValues = theRequest.getParameters().get(Constants.PARAM_QUERY);
|
||||
|
@ -271,6 +260,38 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request a request for a normal search - Ie. not a named search, nor a compartment
|
||||
* search, just a plain old search.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public static boolean isPlainSearchRequest(RequestDetails theRequest) {
|
||||
if (theRequest.getId() != null) {
|
||||
return false;
|
||||
}
|
||||
if (isNotBlank(theRequest.getCompartmentName())) {
|
||||
return false;
|
||||
}
|
||||
return mightBeSearchRequest(theRequest);
|
||||
}
|
||||
|
||||
private static boolean mightBeSearchRequest(RequestDetails theRequest) {
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
}
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
}
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.GET && theRequest.getRequestType() != RequestTypeEnum.POST) {
|
||||
return false;
|
||||
}
|
||||
if (theRequest.getParameters().get(Constants.PARAM_PAGINGACTION) != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
|
|
|
@ -0,0 +1,615 @@
|
|||
package ca.uhn.fhir.rest.server.provider;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Bindings;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.IParameter;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
|
||||
import ca.uhn.fhir.rest.server.method.OperationParameter;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
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.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* 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%
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
||||
* <p>
|
||||
* This class is version independent, but will only work on servers supporting FHIR R4+ (as this was
|
||||
* the first FHIR release where CapabilityStatement was a normative resource)
|
||||
*/
|
||||
public class ServerCapabilityStatementProvider implements IServerConformanceProvider<IBaseConformance> {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
|
||||
private final FhirContext myContext;
|
||||
private final RestfulServer myServer;
|
||||
private final ISearchParamRetriever mySearchParamRetriever;
|
||||
private final RestfulServerConfiguration myServerConfiguration;
|
||||
private final IValidationSupport myValidationSupport;
|
||||
private String myPublisher = "Not provided";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ServerCapabilityStatementProvider(RestfulServer theServer) {
|
||||
myServer = theServer;
|
||||
myContext = theServer.getFhirContext();
|
||||
mySearchParamRetriever = null;
|
||||
myServerConfiguration = null;
|
||||
myValidationSupport = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ServerCapabilityStatementProvider(FhirContext theContext, RestfulServerConfiguration theServerConfiguration) {
|
||||
myContext = theContext;
|
||||
myServerConfiguration = theServerConfiguration;
|
||||
mySearchParamRetriever = null;
|
||||
myServer = null;
|
||||
myValidationSupport = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ServerCapabilityStatementProvider(RestfulServer theRestfulServer, ISearchParamRetriever theSearchParamRetriever, IValidationSupport theValidationSupport) {
|
||||
myContext = theRestfulServer.getFhirContext();
|
||||
mySearchParamRetriever = theSearchParamRetriever;
|
||||
myServer = theRestfulServer;
|
||||
myServerConfiguration = null;
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(FhirTerser theTerser, IBase theRest, Set<String> theSystemOps, BaseMethodBinding<?> theMethodBinding) {
|
||||
RestOperationTypeEnum restOperationType = theMethodBinding.getRestOperationType();
|
||||
if (restOperationType.isSystemLevel()) {
|
||||
String sysOp = restOperationType.getCode();
|
||||
if (theSystemOps.contains(sysOp) == false) {
|
||||
theSystemOps.add(sysOp);
|
||||
IBase interaction = theTerser.addElement(theRest, "interaction");
|
||||
theTerser.addElement(interaction, "code", sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String conformanceDate(RestfulServerConfiguration theServerConfiguration) {
|
||||
IPrimitiveType<Date> buildDate = theServerConfiguration.getConformanceDate();
|
||||
if (buildDate != null && buildDate.getValue() != null) {
|
||||
try {
|
||||
return buildDate.getValueAsString();
|
||||
} catch (DataFormatException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
return InstantDt.withCurrentTime().getValueAsString();
|
||||
}
|
||||
|
||||
private RestfulServerConfiguration getServerConfiguration() {
|
||||
if (myServer != null) {
|
||||
return myServer.createConfiguration();
|
||||
}
|
||||
return myServerConfiguration;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public String getPublisher() {
|
||||
return myPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public void setPublisher(String thePublisher) {
|
||||
myPublisher = thePublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Metadata
|
||||
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
HttpServletRequest servletRequest = null;
|
||||
if (theRequestDetails instanceof ServletRequestDetails) {
|
||||
servletRequest = ((ServletRequestDetails) theRequestDetails).getServletRequest();
|
||||
}
|
||||
|
||||
RestfulServerConfiguration configuration = getServerConfiguration();
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
IBaseConformance retVal = (IBaseConformance) myContext.getResourceDefinition("CapabilityStatement").newInstance();
|
||||
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
|
||||
TreeMultimap<String, String> resourceTypeToSupportedProfiles = getSupportedProfileMultimap(terser);
|
||||
|
||||
terser.addElement(retVal, "name", "RestServer");
|
||||
terser.addElement(retVal, "publisher", myPublisher);
|
||||
terser.addElement(retVal, "date", conformanceDate(configuration));
|
||||
terser.addElement(retVal, "fhirVersion", myContext.getVersion().getVersion().getFhirVersionString());
|
||||
|
||||
ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE));
|
||||
String serverBase = configuration.getServerAddressStrategy().determineServerBase(servletContext, theRequest);
|
||||
terser.addElement(retVal, "implementation.url", serverBase);
|
||||
terser.addElement(retVal, "implementation.description", configuration.getImplementationDescription());
|
||||
terser.addElement(retVal, "kind", "instance");
|
||||
terser.addElement(retVal, "software.name", configuration.getServerName());
|
||||
terser.addElement(retVal, "software.version", configuration.getServerVersion());
|
||||
terser.addElement(retVal, "format", Constants.CT_FHIR_XML_NEW);
|
||||
terser.addElement(retVal, "format", Constants.CT_FHIR_JSON_NEW);
|
||||
terser.addElement(retVal, "format", Constants.FORMAT_JSON);
|
||||
terser.addElement(retVal, "format", Constants.FORMAT_XML);
|
||||
terser.addElement(retVal, "status", "active");
|
||||
|
||||
IBase rest = terser.addElement(retVal, "rest");
|
||||
terser.addElement(rest, "mode", "server");
|
||||
|
||||
Set<String> systemOps = new HashSet<>();
|
||||
Set<String> operationNames = new HashSet<>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
|
||||
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
|
||||
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
||||
if (nextEntry.getKey().isEmpty() == false) {
|
||||
Set<String> resourceOps = new HashSet<>();
|
||||
Set<String> resourceIncludes = new HashSet<>();
|
||||
IBase resource = terser.addElement(rest, "resource");
|
||||
String resourceName = nextEntry.getKey();
|
||||
|
||||
postProcessRestResource(terser, resource, resourceName);
|
||||
|
||||
RuntimeResourceDefinition def;
|
||||
FhirContext context = configuration.getFhirContext();
|
||||
if (resourceNameToSharedSupertype.containsKey(resourceName)) {
|
||||
def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName));
|
||||
} else {
|
||||
def = context.getResourceDefinition(resourceName);
|
||||
}
|
||||
terser.addElement(resource, "type", def.getName());
|
||||
terser.addElement(resource, "profile", def.getResourceProfile(serverBase));
|
||||
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
RestOperationTypeEnum resOpCode = nextMethodBinding.getRestOperationType();
|
||||
if (resOpCode.isTypeLevel() || resOpCode.isInstanceLevel()) {
|
||||
String resOp;
|
||||
resOp = resOpCode.getCode();
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
IBase interaction = terser.addElement(resource, "interaction");
|
||||
terser.addElement(interaction, "code", resOp);
|
||||
}
|
||||
if (RestOperationTypeEnum.VREAD.equals(resOpCode)) {
|
||||
// vread implies read
|
||||
resOp = "read";
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
IBase interaction = terser.addElement(resource, "interaction");
|
||||
terser.addElement(interaction, "code", resOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextMethodBinding.isSupportsConditional()) {
|
||||
switch (resOpCode) {
|
||||
case CREATE:
|
||||
terser.setElement(resource, "conditionalCreate", "true");
|
||||
break;
|
||||
case DELETE:
|
||||
if (nextMethodBinding.isSupportsConditionalMultiple()) {
|
||||
terser.setElement(resource, "conditionalDelete", "multiple");
|
||||
} else {
|
||||
terser.setElement(resource, "conditionalDelete", "single");
|
||||
}
|
||||
break;
|
||||
case UPDATE:
|
||||
terser.setElement(resource, "conditionalUpdate", "true");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
checkBindingForSystemOps(terser, rest, systemOps, nextMethodBinding);
|
||||
|
||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (methodBinding.getQueryName() != null) {
|
||||
String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding);
|
||||
if (operationNames.add(queryName)) {
|
||||
IBase operation = terser.addElement(rest, "operation");
|
||||
terser.addElement(operation, "name", methodBinding.getQueryName());
|
||||
terser.addElement(operation, "definition", (getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + queryName));
|
||||
}
|
||||
} else {
|
||||
|
||||
resourceIncludes.addAll(methodBinding.getIncludes());
|
||||
|
||||
}
|
||||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
// Only add each operation (by name) once
|
||||
if (operationNames.add(opName)) {
|
||||
IBase operation = terser.addElement(rest, "operation");
|
||||
terser.addElement(operation, "name", methodBinding.getName().substring(1));
|
||||
terser.addElement(operation, "definition", getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ISearchParamRetriever searchParamRetriever = mySearchParamRetriever;
|
||||
if (searchParamRetriever == null && myServerConfiguration != null) {
|
||||
searchParamRetriever = myServerConfiguration;
|
||||
} else if (searchParamRetriever == null) {
|
||||
searchParamRetriever = myServer.createConfiguration();
|
||||
}
|
||||
|
||||
Map<String, RuntimeSearchParam> searchParams = searchParamRetriever.getActiveSearchParams(resourceName);
|
||||
if (searchParams != null) {
|
||||
for (RuntimeSearchParam next : searchParams.values()) {
|
||||
IBase searchParam = terser.addElement(resource, "searchParam");
|
||||
terser.addElement(searchParam, "name", next.getName());
|
||||
terser.addElement(searchParam, "type", next.getParamType().getCode());
|
||||
if (isNotBlank(next.getDescription())) {
|
||||
terser.addElement(searchParam, "documentation", next.getDescription());
|
||||
}
|
||||
|
||||
String spUri = next.getUri();
|
||||
if (isBlank(spUri) && servletRequest != null) {
|
||||
String id;
|
||||
if (next.getId() != null) {
|
||||
id = next.getId().toUnqualifiedVersionless().getValue();
|
||||
} else {
|
||||
id = resourceName + "-" + next.getName();
|
||||
}
|
||||
spUri = configuration.getServerAddressStrategy().determineServerBase(servletRequest.getServletContext(), servletRequest) + "/" + id;
|
||||
}
|
||||
if (isNotBlank(spUri)) {
|
||||
terser.addElement(searchParam, "definition", spUri);
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceIncludes.isEmpty()) {
|
||||
for (String nextInclude : searchParams.values().stream().filter(t -> t.getParamType() == RestSearchParameterTypeEnum.REFERENCE).map(t -> t.getName()).sorted().collect(Collectors.toList())) {
|
||||
terser.addElement(resource, "searchInclude", nextInclude);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (String supportedProfile : resourceTypeToSupportedProfiles.get(resourceName)) {
|
||||
terser.addElement(resource, "supportedProfile", supportedProfile);
|
||||
}
|
||||
|
||||
for (String resourceInclude : resourceIncludes) {
|
||||
terser.addElement(resource, "searchInclude", resourceInclude);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
checkBindingForSystemOps(terser, rest, systemOps, nextMethodBinding);
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
ourLog.debug("Found bound operation: {}", opName);
|
||||
IBase operation = terser.addElement(rest, "operation");
|
||||
terser.addElement(operation, "name", methodBinding.getName().substring(1));
|
||||
terser.addElement(operation, "definition", getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
postProcessRest(terser, rest);
|
||||
|
||||
}
|
||||
|
||||
postProcess(terser, retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private TreeMultimap<String, String> getSupportedProfileMultimap(FhirTerser terser) {
|
||||
TreeMultimap<String, String> resourceTypeToSupportedProfiles = TreeMultimap.create();
|
||||
if (myValidationSupport != null) {
|
||||
List<IBaseResource> allStructureDefinitions = myValidationSupport.fetchAllNonBaseStructureDefinitions();
|
||||
if (allStructureDefinitions != null) {
|
||||
for (IBaseResource next : allStructureDefinitions) {
|
||||
String kind = terser.getSinglePrimitiveValueOrNull(next, "kind");
|
||||
String url = terser.getSinglePrimitiveValueOrNull(next, "url");
|
||||
String baseDefinition = defaultString(terser.getSinglePrimitiveValueOrNull(next, "baseDefinition"));
|
||||
if ("resource".equals(kind) && isNotBlank(url)) {
|
||||
|
||||
// Don't include the base resource definitions in the supported profile list - This isn't helpful
|
||||
if (baseDefinition.equals("http://hl7.org/fhir/StructureDefinition/DomainResource") || baseDefinition.equals("http://hl7.org/fhir/StructureDefinition/Resource")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceType = terser.getSinglePrimitiveValueOrNull(next, "snapshot.element.path");
|
||||
if (isBlank(resourceType)) {
|
||||
next = myValidationSupport.generateSnapshot(new ValidationSupportContext(myValidationSupport), next, null, null, null);
|
||||
if (next != null) {
|
||||
resourceType = terser.getSinglePrimitiveValueOrNull(next, "snapshot.element.path");
|
||||
}
|
||||
}
|
||||
|
||||
if (isNotBlank(resourceType)) {
|
||||
resourceTypeToSupportedProfiles.put(resourceType, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resourceTypeToSupportedProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
protected void postProcess(FhirTerser theTerser, IBaseConformance theCapabilityStatement) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
protected void postProcessRest(FhirTerser theTerser, IBase theRest) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
protected void postProcessRestResource(FhirTerser theTerser, IBase theResource, String theResourceName) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
protected String getOperationDefinitionPrefix(RequestDetails theRequestDetails) {
|
||||
if (theRequestDetails == null) {
|
||||
return "";
|
||||
}
|
||||
return theRequestDetails.getServerBaseForRequest() + "/";
|
||||
}
|
||||
|
||||
|
||||
@Read(typeName = "OperationDefinition")
|
||||
public IBaseResource readOperationDefinition(@IdParam IIdType theId, RequestDetails theRequestDetails) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
RestfulServerConfiguration configuration = getServerConfiguration();
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
List<OperationMethodBinding> operationBindings = bindings.getOperationNameToBindings().get(theId.getIdPart());
|
||||
if (operationBindings != null && !operationBindings.isEmpty()) {
|
||||
return readOperationDefinitionForOperation(operationBindings);
|
||||
}
|
||||
List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart());
|
||||
if (searchBindings != null && !searchBindings.isEmpty()) {
|
||||
return readOperationDefinitionForNamedSearch(searchBindings);
|
||||
}
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
private IBaseResource readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
|
||||
IBaseResource op = myContext.getResourceDefinition("OperationDefinition").newInstance();
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
|
||||
terser.addElement(op, "status", "active");
|
||||
terser.addElement(op, "kind", "query");
|
||||
terser.addElement(op, "affectsState", "false");
|
||||
|
||||
terser.addElement(op, "instance", "false");
|
||||
|
||||
Set<String> inParams = new HashSet<>();
|
||||
|
||||
String operationCode = null;
|
||||
for (SearchMethodBinding binding : bindings) {
|
||||
if (isNotBlank(binding.getDescription())) {
|
||||
terser.addElement(op, "description", binding.getDescription());
|
||||
}
|
||||
if (isBlank(binding.getResourceProviderResourceName())) {
|
||||
terser.addElement(op, "system", "true");
|
||||
terser.addElement(op, "type", "false");
|
||||
} else {
|
||||
terser.addElement(op, "system", "false");
|
||||
terser.addElement(op, "type", "true");
|
||||
terser.addElement(op, "resource", binding.getResourceProviderResourceName());
|
||||
}
|
||||
|
||||
if (operationCode == null) {
|
||||
operationCode = binding.getQueryName();
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : binding.getParameters()) {
|
||||
if (nextParamUntyped instanceof SearchParameter) {
|
||||
SearchParameter nextParam = (SearchParameter) nextParamUntyped;
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IBase param = terser.addElement(op, "parameter");
|
||||
terser.addElement(param, "use", "in");
|
||||
terser.addElement(param, "type", "string");
|
||||
terser.addElement(param, "searchType", nextParam.getParamType().getCode());
|
||||
terser.addElement(param, "min", nextParam.isRequired() ? "1" : "0");
|
||||
terser.addElement(param, "max", "1");
|
||||
terser.addElement(param, "name", nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
terser.addElement(op, "code", operationCode);
|
||||
terser.addElement(op, "name", "Search_" + operationCode);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private IBaseResource readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) {
|
||||
IBaseResource op = myContext.getResourceDefinition("OperationDefinition").newInstance();
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
|
||||
terser.addElement(op, "status", "active");
|
||||
terser.addElement(op, "kind", "operation");
|
||||
|
||||
boolean systemLevel = false;
|
||||
boolean typeLevel = false;
|
||||
boolean instanceLevel = false;
|
||||
boolean affectsState = false;
|
||||
String description = null;
|
||||
String code = null;
|
||||
String name;
|
||||
|
||||
Set<String> resourceNames = new TreeSet<>();
|
||||
Set<String> inParams = new HashSet<>();
|
||||
Set<String> outParams = new HashSet<>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : bindings) {
|
||||
if (isNotBlank(sharedDescription.getDescription()) && isBlank(description)) {
|
||||
description = sharedDescription.getDescription();
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
instanceLevel = true;
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
systemLevel = true;
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtTypeLevel()) {
|
||||
typeLevel = true;
|
||||
}
|
||||
if (!sharedDescription.isIdempotent()) {
|
||||
affectsState |= true;
|
||||
}
|
||||
|
||||
code = sharedDescription.getName().substring(1);
|
||||
|
||||
if (isNotBlank(sharedDescription.getResourceName())) {
|
||||
resourceNames.add(sharedDescription.getResourceName());
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
IBase param = terser.addElement(op, "parameter");
|
||||
terser.addElement(param, "use", "in");
|
||||
if (nextParam.getParamType() != null) {
|
||||
terser.addElement(param, "type", nextParam.getParamType());
|
||||
}
|
||||
if (nextParam.getSearchParamType() != null) {
|
||||
terser.addElement(param, "searchType", nextParam.getSearchParamType());
|
||||
}
|
||||
terser.addElement(param, "min", Integer.toString(nextParam.getMin()));
|
||||
terser.addElement(param, "max", (nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())));
|
||||
terser.addElement(param, "name", nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
|
||||
if (!outParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
IBase param = terser.addElement(op, "parameter");
|
||||
terser.addElement(param, "use", "out");
|
||||
if (nextParam.getType() != null) {
|
||||
terser.addElement(param, "type", nextParam.getType());
|
||||
}
|
||||
terser.addElement(param, "min", Integer.toString(nextParam.getMin()));
|
||||
terser.addElement(param, "max", (nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())));
|
||||
terser.addElement(param, "name", nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
name = "Operation_" + code;
|
||||
|
||||
terser.addElements(op, "resource", resourceNames);
|
||||
terser.addElement(op, "name", name);
|
||||
terser.addElement(op, "code", code);
|
||||
terser.addElement(op, "description", description);
|
||||
terser.addElement(op, "affectsState", Boolean.toString(affectsState));
|
||||
terser.addElement(op, "system", Boolean.toString(systemLevel));
|
||||
terser.addElement(op, "type", Boolean.toString(typeLevel));
|
||||
terser.addElement(op, "instance", Boolean.toString(instanceLevel));
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestfulServer(RestfulServer theRestfulServer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,8 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
|||
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BaseServerCapabilityStatementProvider {
|
||||
|
||||
private RestfulServerConfiguration myConfiguration;
|
||||
|
@ -39,7 +41,7 @@ public class BaseServerCapabilityStatementProvider {
|
|||
}
|
||||
|
||||
|
||||
protected RestfulServerConfiguration getServerConfiguration(RequestDetails theRequestDetails) {
|
||||
protected RestfulServerConfiguration getServerConfiguration(@Nullable RequestDetails theRequestDetails) {
|
||||
RestfulServerConfiguration retVal;
|
||||
if (theRequestDetails != null && theRequestDetails.getServer() instanceof RestfulServer) {
|
||||
retVal = ((RestfulServer) theRequestDetails.getServer()).createConfiguration();
|
||||
|
|
|
@ -22,9 +22,17 @@ package ca.uhn.fhir.rest.server.util;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ISearchParamRetriever {
|
||||
/**
|
||||
* @return Returns {@literal null} if no match
|
||||
*/
|
||||
RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName);
|
||||
|
||||
/**
|
||||
* @return Returns all active search params for the given resource
|
||||
*/
|
||||
Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.rest.api.PreferHandlingEnum;
|
||||
import ca.uhn.fhir.rest.api.PreferHeader;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -28,4 +29,28 @@ public class RestfulServerUtilsTest{
|
|||
assertEquals(null, header.getReturn());
|
||||
assertTrue(header.getRespondAsync());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHandlingLenient() {
|
||||
PreferHeader header = RestfulServerUtils.parsePreferHeader(null,"handling=lenient");
|
||||
assertEquals(null, header.getReturn());
|
||||
assertFalse(header.getRespondAsync());
|
||||
assertEquals(PreferHandlingEnum.LENIENT, header.getHanding());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHandlingLenientAndReturnRepresentation_CommaSeparatd() {
|
||||
PreferHeader header = RestfulServerUtils.parsePreferHeader(null,"handling=lenient, return=representation");
|
||||
assertEquals(PreferReturnEnum.REPRESENTATION, header.getReturn());
|
||||
assertFalse(header.getRespondAsync());
|
||||
assertEquals(PreferHandlingEnum.LENIENT, header.getHanding());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHandlingLenientAndReturnRepresentation_SemicolonSeparatd() {
|
||||
PreferHeader header = RestfulServerUtils.parsePreferHeader(null,"handling=lenient; return=representation");
|
||||
assertEquals(PreferReturnEnum.REPRESENTATION, header.getReturn());
|
||||
assertFalse(header.getRespondAsync());
|
||||
assertEquals(PreferHandlingEnum.LENIENT, header.getHanding());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ package org.hl7.fhir.r4.hapi.ctx;
|
|||
|
||||
import ca.uhn.fhir.rest.api.server.IFhirVersionServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
|
||||
public class FhirServerR4 implements IFhirVersionServer {
|
||||
|
||||
@Override
|
||||
public ServerCapabilityStatementProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||
return new ServerCapabilityStatementProvider(theServer);
|
||||
|
|
|
@ -1,578 +0,0 @@
|
|||
package org.hl7.fhir.r4.hapi.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Bindings;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.method.*;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
|
||||
import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.*;
|
||||
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationKind;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 University Health Network
|
||||
* %%
|
||||
* 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%
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
||||
*
|
||||
* <p>
|
||||
* Note: This class is safe to extend, but it is important to note that the same instance of {@link CapabilityStatement} is always returned unless {@link #setCache(boolean)} is called with a value of
|
||||
* <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerCapabilityStatementProvider extends BaseServerCapabilityStatementProvider implements IServerConformanceProvider<CapabilityStatement> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
|
||||
private String myPublisher = "Not provided";
|
||||
|
||||
/**
|
||||
* No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen.
|
||||
*/
|
||||
public ServerCapabilityStatementProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @deprecated Use no-args constructor instead. Deprecated in 4.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public ServerCapabilityStatementProvider(RestfulServer theRestfulServer) {
|
||||
this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor - This is intended only for JAX-RS server
|
||||
*/
|
||||
public ServerCapabilityStatementProvider(RestfulServerConfiguration theServerConfiguration) {
|
||||
super(theServerConfiguration);
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteraction sysOp;
|
||||
try {
|
||||
sysOp = SystemRestfulInteraction.fromCode(sysOpCode);
|
||||
} catch (FHIRException e) {
|
||||
return;
|
||||
}
|
||||
if (sysOp == null) {
|
||||
return;
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeType conformanceDate(RequestDetails theRequestDetails) {
|
||||
IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
|
||||
if (buildDate != null && buildDate.getValue() != null) {
|
||||
try {
|
||||
return new DateTimeType(buildDate.getValueAsString());
|
||||
} catch (DataFormatException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
return DateTimeType.now();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public String getPublisher() {
|
||||
return myPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public void setPublisher(String thePublisher) {
|
||||
myPublisher = thePublisher;
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
@Override
|
||||
@Metadata
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
CapabilityStatement retVal = new CapabilityStatement();
|
||||
|
||||
retVal.setPublisher(myPublisher);
|
||||
retVal.setDateElement(conformanceDate(theRequestDetails));
|
||||
retVal.setFhirVersion(Enumerations.FHIRVersion.fromCode(FhirVersionEnum.R4.getFhirVersionString()));
|
||||
|
||||
ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE));
|
||||
String serverBase = configuration.getServerAddressStrategy().determineServerBase(servletContext, theRequest);
|
||||
retVal
|
||||
.getImplementation()
|
||||
.setUrl(serverBase)
|
||||
.setDescription(configuration.getImplementationDescription());
|
||||
|
||||
retVal.setKind(CapabilityStatementKind.INSTANCE);
|
||||
retVal.getSoftware().setName(configuration.getServerName());
|
||||
retVal.getSoftware().setVersion(configuration.getServerVersion());
|
||||
retVal.addFormat(Constants.CT_FHIR_XML_NEW);
|
||||
retVal.addFormat(Constants.CT_FHIR_JSON_NEW);
|
||||
retVal.addFormat(Constants.FORMAT_JSON);
|
||||
retVal.addFormat(Constants.FORMAT_XML);
|
||||
retVal.setStatus(PublicationStatus.ACTIVE);
|
||||
|
||||
CapabilityStatementRestComponent rest = retVal.addRest();
|
||||
rest.setMode(RestfulCapabilityMode.SERVER);
|
||||
|
||||
Set<SystemRestfulInteraction> systemOps = new HashSet<>();
|
||||
Set<String> operationNames = new HashSet<>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
|
||||
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
||||
if (nextEntry.getKey().isEmpty() == false) {
|
||||
Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
|
||||
CapabilityStatementRestResourceComponent resource = rest.addResource();
|
||||
String resourceName = nextEntry.getKey();
|
||||
|
||||
RuntimeResourceDefinition def;
|
||||
FhirContext context = configuration.getFhirContext();
|
||||
if (resourceNameToSharedSupertype.containsKey(resourceName)) {
|
||||
def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName));
|
||||
} else {
|
||||
def = context.getResourceDefinition(resourceName);
|
||||
}
|
||||
resource.getTypeElement().setValue(def.getName());
|
||||
resource.getProfileElement().setValue((def.getResourceProfile(serverBase)));
|
||||
|
||||
TreeSet<String> includes = new TreeSet<>();
|
||||
|
||||
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// CapabilityStatement.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
nextMethodBinding.getRestOperationType();
|
||||
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (resOpCode != null) {
|
||||
TypeRestfulInteraction resOp;
|
||||
try {
|
||||
resOp = TypeRestfulInteraction.fromCode(resOpCode);
|
||||
} catch (Exception e) {
|
||||
resOp = null;
|
||||
}
|
||||
if (resOp != null) {
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
resource.addInteraction().setCode(resOp);
|
||||
}
|
||||
if ("vread".equals(resOpCode)) {
|
||||
// vread implies read
|
||||
resOp = TypeRestfulInteraction.READ;
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
resource.addInteraction().setCode(resOp);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextMethodBinding.isSupportsConditional()) {
|
||||
switch (resOp) {
|
||||
case CREATE:
|
||||
resource.setConditionalCreate(true);
|
||||
break;
|
||||
case DELETE:
|
||||
if (nextMethodBinding.isSupportsConditionalMultiple()) {
|
||||
resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE);
|
||||
} else {
|
||||
resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
|
||||
}
|
||||
break;
|
||||
case UPDATE:
|
||||
resource.setConditionalUpdate(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||
|
||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (methodBinding.getQueryName() != null) {
|
||||
String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding);
|
||||
if (operationNames.add(queryName)) {
|
||||
rest.addOperation().setName(methodBinding.getQueryName()).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + queryName));
|
||||
}
|
||||
} else {
|
||||
handleNamelessSearchMethodBinding(resource, def, includes, (SearchMethodBinding) nextMethodBinding, theRequestDetails);
|
||||
}
|
||||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
// Only add each operation (by name) once
|
||||
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName));
|
||||
}
|
||||
}
|
||||
|
||||
resource.getInteraction().sort(new Comparator<ResourceInteractionComponent>() {
|
||||
@Override
|
||||
public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) {
|
||||
TypeRestfulInteraction o1 = theO1.getCode();
|
||||
TypeRestfulInteraction o2 = theO2.getCode();
|
||||
if (o1 == null && o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o2 == null) {
|
||||
return -1;
|
||||
}
|
||||
return o1.ordinal() - o2.ordinal();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
for (String nextInclude : includes) {
|
||||
resource.addSearchInclude(nextInclude);
|
||||
}
|
||||
} else {
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
ourLog.debug("Found bound operation: {}", opName);
|
||||
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String getOperationDefinitionPrefix(RequestDetails theRequestDetails) {
|
||||
if (theRequestDetails == null) {
|
||||
return "";
|
||||
}
|
||||
return theRequestDetails.getServerBaseForRequest() + "/";
|
||||
}
|
||||
|
||||
private void handleNamelessSearchMethodBinding(CapabilityStatementRestResourceComponent resource, RuntimeResourceDefinition def, TreeSet<String> includes,
|
||||
SearchMethodBinding searchMethodBinding, RequestDetails theRequestDetails) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
List<IParameter> params = searchMethodBinding.getParameters();
|
||||
List<SearchParameter> searchParameters = new ArrayList<>();
|
||||
for (IParameter nextParameter : params) {
|
||||
if ((nextParameter instanceof SearchParameter)) {
|
||||
searchParameters.add((SearchParameter) nextParameter);
|
||||
}
|
||||
}
|
||||
sortSearchParameters(searchParameters);
|
||||
if (!searchParameters.isEmpty()) {
|
||||
|
||||
Set<String> paramNames = new HashSet<>();
|
||||
for (SearchParameter nextParameter : searchParameters) {
|
||||
|
||||
if (nextParameter.getParamType() == null) {
|
||||
ourLog.warn("SearchParameter {}:{} does not declare a type - Not exporting in CapabilityStatement", def.getName(), nextParameter.getName());
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextParamName = nextParameter.getName();
|
||||
|
||||
String nextParamUnchainedName = nextParamName;
|
||||
if (nextParamName.contains(".")) {
|
||||
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||
}
|
||||
|
||||
if (!paramNames.add(nextParamUnchainedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextParamDescription = nextParameter.getDescription();
|
||||
|
||||
/*
|
||||
* If the parameter has no description, default to the one from the resource
|
||||
*/
|
||||
if (StringUtils.isBlank(nextParamDescription)) {
|
||||
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
||||
if (paramDef != null) {
|
||||
nextParamDescription = paramDef.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam();
|
||||
String typeCode = nextParameter.getParamType().getCode();
|
||||
param.getTypeElement().setValueAsString(typeCode);
|
||||
param.setName(nextParamUnchainedName);
|
||||
param.setDocumentation(nextParamDescription);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
List<OperationMethodBinding> operationBindings = bindings.getOperationNameToBindings().get(theId.getIdPart());
|
||||
if (operationBindings != null && !operationBindings.isEmpty()) {
|
||||
return readOperationDefinitionForOperation(operationBindings);
|
||||
}
|
||||
List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart());
|
||||
if (searchBindings != null && !searchBindings.isEmpty()) {
|
||||
return readOperationDefinitionForNamedSearch(searchBindings);
|
||||
}
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(PublicationStatus.ACTIVE);
|
||||
op.setKind(OperationKind.QUERY);
|
||||
op.setAffectsState(false);
|
||||
|
||||
op.setSystem(false);
|
||||
op.setType(false);
|
||||
op.setInstance(false);
|
||||
|
||||
Set<String> inParams = new HashSet<>();
|
||||
|
||||
for (SearchMethodBinding binding : bindings) {
|
||||
if (isNotBlank(binding.getDescription())) {
|
||||
op.setDescription(binding.getDescription());
|
||||
}
|
||||
if (isBlank(binding.getResourceProviderResourceName())) {
|
||||
op.setSystem(true);
|
||||
} else {
|
||||
op.setType(true);
|
||||
op.addResourceElement().setValue(binding.getResourceProviderResourceName());
|
||||
}
|
||||
op.setCode(binding.getQueryName());
|
||||
for (IParameter nextParamUntyped : binding.getParameters()) {
|
||||
if (nextParamUntyped instanceof SearchParameter) {
|
||||
SearchParameter nextParam = (SearchParameter) nextParamUntyped;
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(OperationParameterUse.IN);
|
||||
param.setType("string");
|
||||
param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode());
|
||||
param.setMin(nextParam.isRequired() ? 1 : 0);
|
||||
param.setMax("1");
|
||||
param.setName(nextParam.getName());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(op.getName())) {
|
||||
if (isNotBlank(op.getDescription())) {
|
||||
op.setName(op.getDescription());
|
||||
} else {
|
||||
op.setName(op.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) {
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(PublicationStatus.ACTIVE);
|
||||
op.setKind(OperationKind.OPERATION);
|
||||
op.setAffectsState(false);
|
||||
|
||||
// We reset these to true below if we find a binding that can handle the level
|
||||
op.setSystem(false);
|
||||
op.setType(false);
|
||||
op.setInstance(false);
|
||||
|
||||
Set<String> inParams = new HashSet<>();
|
||||
Set<String> outParams = new HashSet<>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : bindings) {
|
||||
if (isNotBlank(sharedDescription.getDescription())) {
|
||||
op.setDescription(sharedDescription.getDescription());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(true);
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(true);
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtTypeLevel()) {
|
||||
op.setType(true);
|
||||
}
|
||||
if (!sharedDescription.isIdempotent()) {
|
||||
op.setAffectsState(!sharedDescription.isIdempotent());
|
||||
}
|
||||
op.setCode(sharedDescription.getName().substring(1));
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(sharedDescription.isCanOperateAtInstanceLevel());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(sharedDescription.isCanOperateAtServerLevel());
|
||||
}
|
||||
if (isNotBlank(sharedDescription.getResourceName())) {
|
||||
op.addResourceElement().setValue(sharedDescription.getResourceName());
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
param.setUse(OperationParameterUse.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(nextParam.getParamType());
|
||||
}
|
||||
if (nextParam.getSearchParamType() != null) {
|
||||
param.getSearchTypeElement().setValueAsString(nextParam.getSearchParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
|
||||
if (!outParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(OperationParameterUse.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(nextParam.getType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(op.getName())) {
|
||||
if (isNotBlank(op.getDescription())) {
|
||||
op.setName(op.getDescription());
|
||||
} else {
|
||||
op.setName(op.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
if (op.hasSystem() == false) {
|
||||
op.setSystem(false);
|
||||
}
|
||||
if (op.hasInstance() == false) {
|
||||
op.setInstance(false);
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
|
||||
* <p>
|
||||
* See the class documentation for an important note if you are extending this class
|
||||
* </p>
|
||||
*
|
||||
* @deprecated Since 4.0.0 - This method no longer does anything
|
||||
*/
|
||||
@Deprecated
|
||||
public ServerCapabilityStatementProvider setCache(boolean theCache) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestfulServer(RestfulServer theRestfulServer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
private void sortSearchParameters(List<SearchParameter> searchParameters) {
|
||||
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
|
||||
@Override
|
||||
public int compare(SearchParameter theO1, SearchParameter theO2) {
|
||||
if (theO1.isRequired() == theO2.isRequired()) {
|
||||
return theO1.getName().compareTo(theO2.getName());
|
||||
}
|
||||
if (theO1.isRequired()) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,31 +1,32 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.DateUtils;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
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.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -34,30 +35,34 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ReadDstu3Test {
|
||||
public class ReadR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadR4Test.class);
|
||||
private static CloseableHttpClient ourClient;
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx);
|
||||
private int myPort;
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu3Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myPort = myRestfulServerExtension.getPort();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
myRestfulServerExtension.getRestfulServer().registerProvider(new PatientProvider());
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_format=xml&_pretty=true");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION));
|
||||
assertEquals("http://localhost:" + myPort + "/Patient/2/_history/2", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION));
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/2/_history/2", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue());
|
||||
|
||||
assertThat(responseContent, stringContainsInOrder(
|
||||
assertThat(responseContent, stringContainsInOrder(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
" <id value=\"2\"/>",
|
||||
" <meta>",
|
||||
|
@ -67,14 +72,44 @@ public class ReadDstu3Test {
|
|||
" <valueDate value=\"2011-01-01\"/>",
|
||||
" </modifierExtension>",
|
||||
"</Patient>"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUsingPlainProvider() throws Exception {
|
||||
myRestfulServerExtension.getRestfulServer().registerProvider(new PlainGenericPatientProvider());
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_format=xml&_pretty=true");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION));
|
||||
assertEquals("http://localhost:" + myPort + "/Patient/2/_history/2", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue());
|
||||
|
||||
assertThat(responseContent, stringContainsInOrder(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
" <id value=\"2\"/>",
|
||||
" <meta>",
|
||||
" <profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
|
||||
" </meta>",
|
||||
" <modifierExtension url=\"http://example.com/ext/date\">",
|
||||
" <valueDate value=\"2011-01-01\"/>",
|
||||
" </modifierExtension>",
|
||||
"</Patient>"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQueryParamsInRead() throws Exception {
|
||||
myRestfulServerExtension.getRestfulServer().registerProvider(new PatientProvider());
|
||||
|
||||
CloseableHttpResponse status;
|
||||
HttpGet httpGet;
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_contained=both&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_contained=both&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -91,7 +126,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_containedType=contained&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_containedType=contained&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -108,7 +143,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_count=10&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_count=10&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -125,7 +160,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_include=Patient:organization&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_include=Patient:organization&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -142,7 +177,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_revinclude=Provenance:target&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_revinclude=Provenance:target&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -159,7 +194,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_sort=family&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_sort=family&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -176,7 +211,7 @@ public class ReadDstu3Test {
|
|||
));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_total=accurate&_format=xml&_pretty=true");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2?_total=accurate&_format=xml&_pretty=true");
|
||||
status = ourClient.execute(httpGet);
|
||||
try (InputStream inputStream = status.getEntity().getContent()) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
@ -196,77 +231,39 @@ public class ReadDstu3Test {
|
|||
|
||||
@Test
|
||||
public void testIfModifiedSince() throws Exception {
|
||||
myRestfulServerExtension.getRestfulServer().registerProvider(new PatientProvider());
|
||||
|
||||
CloseableHttpResponse status;
|
||||
HttpGet httpGet;
|
||||
|
||||
// Fixture was last modified at 2012-01-01T12:12:12Z
|
||||
// thus it hasn't changed after the later time of 2012-01-01T13:00:00Z
|
||||
// so we expect a 304 (Not Modified)
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2");
|
||||
httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T13:00:00Z").getValue()));
|
||||
status = ourClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(304, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
|
||||
// Fixture was last modified at 2012-01-01T12:12:12Z
|
||||
// thus it hasn't changed after the same time of 2012-01-01T12:12:12Z
|
||||
// so we expect a 304 (Not Modified)
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2");
|
||||
httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T12:12:12Z").getValue()));
|
||||
status = ourClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(304, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
|
||||
// Fixture was last modified at 2012-01-01T12:12:12Z
|
||||
// thus it has changed after the earlier time of 2012-01-01T10:00:00Z
|
||||
// so we expect a 200
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient/2");
|
||||
httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T10:00:00Z").getValue()));
|
||||
status = ourClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
JettyUtil.closeServer(ourServer);
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() throws Exception {
|
||||
ourServer = new Server(0);
|
||||
|
||||
PatientProvider patientProvider = new PatientProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
public static class PatientProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
|
@ -288,4 +285,33 @@ public class ReadDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
public static class PlainGenericPatientProvider {
|
||||
|
||||
@Read(version = true, typeName = "Patient")
|
||||
public IBaseResource read(@IdParam IIdType theIdParam) {
|
||||
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
|
||||
p0.getMeta().getLastUpdatedElement().setValueAsString("2012-01-01T12:12:12Z");
|
||||
p0.setId(theIdParam);
|
||||
if (theIdParam.hasVersionIdPart() == false) {
|
||||
p0.setIdElement(p0.getIdElement().withVersion("2"));
|
||||
}
|
||||
p0.setDateExt(new DateType("2011-01-01"));
|
||||
return p0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() throws Exception {
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClass() throws IOException {
|
||||
ourClient.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.SearchPreferHandlingInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.fail;
|
||||
|
||||
public class SearchPreferHandlingInterceptorTest {
|
||||
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx)
|
||||
.registerProvider(new DummyPatientResourceProvider())
|
||||
.withServer(t -> t.setDefaultResponseEncoding(EncodingEnum.JSON))
|
||||
.withServer(t -> t.setPagingProvider(new FifoMemoryPagingProvider(10)))
|
||||
.registerInterceptor(new SearchPreferHandlingInterceptor());
|
||||
private int myPort;
|
||||
private IGenericClient myClient;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myClient = myRestfulServerExtension.getFhirClient();
|
||||
myPort = myRestfulServerExtension.getPort();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_NoHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [identifier]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_StrictHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_HANDLING + "=" + Constants.HEADER_PREFER_HANDLING_STRICT)
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [identifier]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_UnrelatedPreferHeader() {
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION)
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [identifier]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidParam_LenientHeader() {
|
||||
Bundle outcome = myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.and(Patient.IDENTIFIER.exactly().codes("BLAH"))
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_HANDLING + "=" + Constants.HEADER_PREFER_HANDLING_LENIENT)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
assertEquals(200, outcome.getTotal());
|
||||
assertEquals("http://localhost:" + myPort + "/Patient?_format=json&_pretty=true&identifier=BLAH", outcome.getLink(Constants.LINK_SELF).getUrl());
|
||||
}
|
||||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Search()
|
||||
public List search(
|
||||
@OptionalParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
|
||||
ArrayList<Patient> retVal = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 200; i++) {
|
||||
Patient patient = new Patient();
|
||||
patient.getIdElement().setValue("Patient/" + i + "/_history/222");
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(patient, BundleEntrySearchModeEnum.INCLUDE.getCode());
|
||||
patient.addName(new HumanName().setFamily("FAMILY"));
|
||||
patient.setActive(true);
|
||||
retVal.add(patient);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
|
@ -17,6 +18,7 @@ import ca.uhn.fhir.rest.gclient.StringClientParam;
|
|||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
|
@ -43,6 +45,7 @@ import org.junit.jupiter.api.AfterAll;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -63,17 +66,22 @@ public class SearchR4Test {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR4Test.class);
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private static TokenAndListParam ourIdentifiers;
|
||||
private static String ourLastMethod;
|
||||
private static int ourPort;
|
||||
|
||||
private static Server ourServer;
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx)
|
||||
.registerProvider(new DummyPatientResourceProvider())
|
||||
.registerProvider(new DummyMedicationRequestResourceProvider())
|
||||
.withServer(t -> t.setDefaultResponseEncoding(EncodingEnum.JSON))
|
||||
.withServer(t -> t.setPagingProvider(new FifoMemoryPagingProvider(10)));
|
||||
private int myPort;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
ourIdentifiers = null;
|
||||
myPort = myRestfulServerExtension.getPort();
|
||||
}
|
||||
|
||||
private Bundle executeSearchAndValidateHasLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException {
|
||||
|
@ -93,7 +101,7 @@ public class SearchR4Test {
|
|||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
EncodingEnum ct = EncodingEnum.forContentType(status.getEntity().getContentType().getValue().replaceAll(";.*", "").trim());
|
||||
assertEquals(theExpectEncoding, ct);
|
||||
bundle = ct.newParser(ourCtx).parseResource(Bundle.class, responseContent);
|
||||
bundle = ct.newParser(myCtx).parseResource(Bundle.class, responseContent);
|
||||
validate(bundle);
|
||||
}
|
||||
return bundle;
|
||||
|
@ -104,7 +112,7 @@ public class SearchR4Test {
|
|||
*/
|
||||
@Test
|
||||
public void testPageRequestCantTriggerSearchAccidentally() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Constants.PARAM_PAGINGACTION + "=12345");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?" + Constants.PARAM_PAGINGACTION + "=12345");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
|
@ -119,7 +127,7 @@ public class SearchR4Test {
|
|||
*/
|
||||
@Test
|
||||
public void testSummaryCount() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&"+Constants.PARAM_SUMMARY + "=" + SummaryEnum.COUNT.getCode());
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&" + Constants.PARAM_SUMMARY + "=" + SummaryEnum.COUNT.getCode());
|
||||
Bundle bundle = executeSearch(httpGet, EncodingEnum.JSON);
|
||||
ourLog.info(toJson(bundle));
|
||||
assertEquals(200, bundle.getTotal());
|
||||
|
@ -128,7 +136,6 @@ public class SearchR4Test {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testPagingPreservesElements() throws Exception {
|
||||
HttpGet httpGet;
|
||||
|
@ -137,7 +144,7 @@ public class SearchR4Test {
|
|||
String linkSelf;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_elements=name&_elements:exclude=birthDate,active");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_elements=name&_elements:exclude=birthDate,active");
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), not(containsString("\"active\"")));
|
||||
linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl();
|
||||
|
@ -182,7 +189,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// No include specified
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/MedicationRequest");
|
||||
bundle = executeAndReturnBundle(httpGet);
|
||||
assertEquals(1, bundle.getEntry().size());
|
||||
}
|
||||
|
@ -196,7 +203,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// * include specified
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest?_include=" + UrlUtil.escapeUrlParam("*"));
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/MedicationRequest?_include=" + UrlUtil.escapeUrlParam("*"));
|
||||
bundle = executeAndReturnBundle(httpGet);
|
||||
assertEquals(2, bundle.getEntry().size());
|
||||
}
|
||||
|
@ -210,7 +217,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// MedicationRequest:medication include specified
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest?_include=" + UrlUtil.escapeUrlParam(MedicationRequest.INCLUDE_MEDICATION.getValue()));
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/MedicationRequest?_include=" + UrlUtil.escapeUrlParam(MedicationRequest.INCLUDE_MEDICATION.getValue()));
|
||||
bundle = executeAndReturnBundle(httpGet);
|
||||
assertEquals(2, bundle.getEntry().size());
|
||||
|
||||
|
@ -221,7 +228,7 @@ public class SearchR4Test {
|
|||
try (CloseableHttpResponse status = ourClient.execute(theHttpGet)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
bundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
|
||||
bundle = myCtx.newJsonParser().parseResource(Bundle.class, responseContent);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
@ -233,7 +240,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW);
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW);
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
@ -265,7 +272,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_format=json");
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), containsString("active"));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
|
@ -298,7 +305,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar");
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, not(containsString("_format")));
|
||||
|
@ -330,7 +337,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar");
|
||||
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
|
@ -366,7 +373,7 @@ public class SearchR4Test {
|
|||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml");
|
||||
httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_format=xml");
|
||||
bundle = executeSearchAndValidateHasLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
|
@ -393,11 +400,11 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testSearchNormal() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
validate(ourCtx.newJsonParser().parseResource(responseContent));
|
||||
validate(myCtx.newJsonParser().parseResource(responseContent));
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("search", ourLastMethod);
|
||||
|
@ -410,7 +417,7 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testRequestIdGeneratedAndReturned() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String requestId = status.getFirstHeader(Constants.HEADER_REQUEST_ID).getValue();
|
||||
|
@ -420,7 +427,7 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testRequestIdSuppliedAndReturned() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
httpGet.addHeader(Constants.HEADER_REQUEST_ID, "help im a bug");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
@ -431,7 +438,7 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testRequestIdSuppliedAndReturned_Invalid() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
|
||||
httpGet.addHeader(Constants.HEADER_REQUEST_ID, "help i'm a bug");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
@ -442,13 +449,13 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testSearchWithInvalidChain() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar");
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient?identifier.chain=foo%7Cbar");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) ourCtx.newJsonParser().parseResource(responseContent);
|
||||
OperationOutcome oo = (OperationOutcome) myCtx.newJsonParser().parseResource(responseContent);
|
||||
assertEquals(
|
||||
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
|
||||
oo.getIssueFirstRep().getDiagnostics());
|
||||
|
@ -458,7 +465,7 @@ public class SearchR4Test {
|
|||
|
||||
@Test
|
||||
public void testSearchWithPostAndInvalidParameters() {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://localhost:" + myPort);
|
||||
LoggingInterceptor interceptor = new LoggingInterceptor();
|
||||
interceptor.setLogRequestSummary(true);
|
||||
interceptor.setLogRequestBody(true);
|
||||
|
@ -485,14 +492,14 @@ public class SearchR4Test {
|
|||
}
|
||||
|
||||
private String toJson(Bundle theBundle) {
|
||||
return ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theBundle);
|
||||
return myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theBundle);
|
||||
}
|
||||
|
||||
protected void validate(IBaseResource theResource) {
|
||||
FhirValidator validatorModule = ourCtx.newValidator();
|
||||
FhirValidator validatorModule = myCtx.newValidator();
|
||||
ValidationResult result = validatorModule.validateWithResult(theResource);
|
||||
if (!result.isSuccessful()) {
|
||||
fail(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
|
||||
fail(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,34 +567,15 @@ public class SearchR4Test {
|
|||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
JettyUtil.closeServer(ourServer);
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
ourClient.close();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() throws Exception {
|
||||
ourServer = new Server(0);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
DummyMedicationRequestResourceProvider medRequestProvider = new DummyMedicationRequestResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
servlet.setResourceProviders(patientProvider, medRequestProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
|
@ -67,7 +68,116 @@ import static org.mockito.Mockito.when;
|
|||
public class FhirTerserR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirTerserR4Test.class);
|
||||
private FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
|
||||
@Test
|
||||
public void testAddElement() {
|
||||
Patient patient = new Patient();
|
||||
IBase family = myCtx.newTerser().addElement(patient, "Patient.name.family");
|
||||
|
||||
assertEquals(1, patient.getName().size());
|
||||
assertSame(family, patient.getName().get(0).getFamilyElement());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElementWithValue() {
|
||||
Patient patient = new Patient();
|
||||
IBase family = myCtx.newTerser().addElement(patient, "Patient.name.family", "FOO");
|
||||
|
||||
assertEquals(1, patient.getName().size());
|
||||
assertSame(family, patient.getName().get(0).getFamilyElement());
|
||||
assertEquals("FOO", patient.getName().get(0).getFamilyElement().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElementWithValue_NonPrimitivePath() {
|
||||
Patient patient = new Patient();
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "Patient.name", "FOO");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Element at path Patient.name is not a primitive datatype. Found: HumanName", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElements_NonRepeatingPath() {
|
||||
Patient patient = new Patient();
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElements(patient, "Patient.name.family", Lists.newArrayList("FOO", "BAR"));
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Can not add multiple values at path Patient.name.family: Element does not repeat", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElementReusesExisting() {
|
||||
Patient patient = new Patient();
|
||||
StringType existingFamily = patient.addName().getFamilyElement();
|
||||
patient.addName().setFamily("FAM2");
|
||||
patient.addName().setFamily("FAM3");
|
||||
|
||||
IBase family = myCtx.newTerser().addElement(patient, "Patient.name.family");
|
||||
|
||||
assertEquals(3, patient.getName().size());
|
||||
assertTrue(existingFamily == patient.getName().get(0).getFamilyElement());
|
||||
assertSame(family, patient.getName().get(0).getFamilyElement());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElementCantReuseExistingBecauseItIsNotEmpty() {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("FAM1");
|
||||
patient.addName().setFamily("FAM2");
|
||||
patient.addName().setFamily("FAM3");
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "Patient.name.family");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Element at path Patient.name.family is not repeatable and not empty", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddElementInvalidPath() {
|
||||
Patient patient = new Patient();
|
||||
|
||||
// So much foo....
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "foo");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Invalid path foo: Element of type Patient has no child named foo. Valid names: active, address, birthDate, communication, contact, contained, deceased, extension, gender, generalPractitioner, id, identifier, implicitRules, language, link, managingOrganization, maritalStatus, meta, modifierExtension, multipleBirth, name, photo, telecom, text", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "Patient.foo");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Invalid path Patient.foo: Element of type Patient has no child named foo. Valid names: active, address, birthDate, communication, contact, contained, deceased, extension, gender, generalPractitioner, id, identifier, implicitRules, language, link, managingOrganization, maritalStatus, meta, modifierExtension, multipleBirth, name, photo, telecom, text", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "Patient.name.foo");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Invalid path Patient.name.foo: Element of type HumanName has no child named foo. Valid names: extension, family, given, id, period, prefix, suffix, text, use", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myCtx.newTerser().addElement(patient, "Patient.name.family.foo");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Invalid path Patient.name.family.foo: Element of type HumanName has no child named family (this is a primitive type)", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainResourcesWithModify() {
|
||||
|
|
|
@ -2,12 +2,12 @@ package org.hl7.fhir.r5.hapi.ctx;
|
|||
|
||||
import ca.uhn.fhir.rest.api.server.IFhirVersionServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import org.hl7.fhir.r5.hapi.rest.server.ServerCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
|
||||
public class FhirServerR5 implements IFhirVersionServer {
|
||||
@Override
|
||||
public ServerCapabilityStatementProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||
return new ServerCapabilityStatementProvider();
|
||||
}
|
||||
@Override
|
||||
public ServerCapabilityStatementProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||
return new ServerCapabilityStatementProvider(theServer);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,606 +0,0 @@
|
|||
package org.hl7.fhir.r5.hapi.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Bindings;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.IParameter;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
|
||||
import ca.uhn.fhir.rest.server.method.OperationParameter;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ConditionalDeleteStatus;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
import org.hl7.fhir.r5.model.Enumerations;
|
||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r5.model.IdType;
|
||||
import org.hl7.fhir.r5.model.OperationDefinition;
|
||||
import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.r5.model.OperationDefinition.OperationKind;
|
||||
import org.hl7.fhir.r5.model.ResourceType;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 University Health Network
|
||||
* %%
|
||||
* 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%
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
||||
*
|
||||
* <p>
|
||||
* Note: This class is safe to extend, but it is important to note that the same instance of {@link CapabilityStatement} is always returned unless {@link #setCache(boolean)} is called with a value of
|
||||
* <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerCapabilityStatementProvider extends BaseServerCapabilityStatementProvider implements IServerConformanceProvider<CapabilityStatement> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
|
||||
private String myPublisher = "Not provided";
|
||||
|
||||
/**
|
||||
* No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen.
|
||||
*/
|
||||
public ServerCapabilityStatementProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor - This is intended only for JAX-RS server
|
||||
*/
|
||||
public ServerCapabilityStatementProvider(RestfulServerConfiguration theServerConfiguration) {
|
||||
super(theServerConfiguration);
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteraction sysOp;
|
||||
try {
|
||||
sysOp = SystemRestfulInteraction.fromCode(sysOpCode);
|
||||
} catch (FHIRException e) {
|
||||
return;
|
||||
}
|
||||
if (sysOp == null) {
|
||||
return;
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DateTimeType conformanceDate(RequestDetails theRequestDetails) {
|
||||
IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
|
||||
if (buildDate != null && buildDate.getValue() != null) {
|
||||
try {
|
||||
return new DateTimeType(buildDate.getValueAsString());
|
||||
} catch (DataFormatException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
return DateTimeType.now();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public String getPublisher() {
|
||||
return myPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
|
||||
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
|
||||
*/
|
||||
public void setPublisher(String thePublisher) {
|
||||
myPublisher = thePublisher;
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
@Override
|
||||
@Metadata
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
CapabilityStatement retVal = new CapabilityStatement();
|
||||
|
||||
retVal.setPublisher(myPublisher);
|
||||
retVal.setDateElement(conformanceDate(theRequestDetails));
|
||||
retVal.setFhirVersion(Enumerations.FHIRVersion.fromCode(FhirVersionEnum.R5.getFhirVersionString()));
|
||||
|
||||
ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE));
|
||||
String serverBase = configuration.getServerAddressStrategy().determineServerBase(servletContext, theRequest);
|
||||
retVal
|
||||
.getImplementation()
|
||||
.setUrl(serverBase)
|
||||
.setDescription(configuration.getImplementationDescription());
|
||||
|
||||
retVal.setKind(Enumerations.CapabilityStatementKind.INSTANCE);
|
||||
retVal.getSoftware().setName(configuration.getServerName());
|
||||
retVal.getSoftware().setVersion(configuration.getServerVersion());
|
||||
retVal.addFormat(Constants.CT_FHIR_XML_NEW);
|
||||
retVal.addFormat(Constants.CT_FHIR_JSON_NEW);
|
||||
retVal.addFormat(Constants.FORMAT_JSON);
|
||||
retVal.addFormat(Constants.FORMAT_XML);
|
||||
retVal.setStatus(PublicationStatus.ACTIVE);
|
||||
|
||||
CapabilityStatementRestComponent rest = retVal.addRest();
|
||||
rest.setMode(Enumerations.RestfulCapabilityMode.SERVER);
|
||||
|
||||
Set<SystemRestfulInteraction> systemOps = new HashSet<>();
|
||||
Set<String> operationNames = new HashSet<>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
|
||||
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
||||
if (nextEntry.getKey().isEmpty() == false) {
|
||||
Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
|
||||
CapabilityStatementRestResourceComponent resource = rest.addResource();
|
||||
String resourceName = nextEntry.getKey();
|
||||
RuntimeResourceDefinition def;
|
||||
FhirContext context = configuration.getFhirContext();
|
||||
if (resourceNameToSharedSupertype.containsKey(resourceName)) {
|
||||
def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName));
|
||||
} else {
|
||||
def = context.getResourceDefinition(resourceName);
|
||||
}
|
||||
resource.getTypeElement().setValue(def.getName());
|
||||
resource.getProfileElement().setValue((def.getResourceProfile(serverBase)));
|
||||
|
||||
TreeSet<String> includes = new TreeSet<>();
|
||||
|
||||
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// CapabilityStatement.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (resOpCode != null) {
|
||||
TypeRestfulInteraction resOp;
|
||||
try {
|
||||
resOp = TypeRestfulInteraction.fromCode(resOpCode);
|
||||
} catch (Exception e) {
|
||||
resOp = null;
|
||||
}
|
||||
if (resOp != null) {
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
resource.addInteraction().setCode(resOp);
|
||||
}
|
||||
if ("vread".equals(resOpCode)) {
|
||||
// vread implies read
|
||||
resOp = TypeRestfulInteraction.READ;
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
resource.addInteraction().setCode(resOp);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextMethodBinding.isSupportsConditional()) {
|
||||
switch (resOp) {
|
||||
case CREATE:
|
||||
resource.setConditionalCreate(true);
|
||||
break;
|
||||
case DELETE:
|
||||
if (nextMethodBinding.isSupportsConditionalMultiple()) {
|
||||
resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE);
|
||||
} else {
|
||||
resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
|
||||
}
|
||||
break;
|
||||
case UPDATE:
|
||||
resource.setConditionalUpdate(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||
|
||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (methodBinding.getQueryName() != null) {
|
||||
String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding);
|
||||
if (operationNames.add(queryName)) {
|
||||
rest.addOperation().setName(methodBinding.getQueryName()).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + queryName));
|
||||
}
|
||||
} else {
|
||||
handleNamelessSearchMethodBinding(resource, def, includes, (SearchMethodBinding) nextMethodBinding, theRequestDetails);
|
||||
}
|
||||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
// Only add each operation (by name) once
|
||||
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName));
|
||||
}
|
||||
}
|
||||
|
||||
resource.getInteraction().sort(new Comparator<ResourceInteractionComponent>() {
|
||||
@Override
|
||||
public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) {
|
||||
TypeRestfulInteraction o1 = theO1.getCode();
|
||||
TypeRestfulInteraction o2 = theO2.getCode();
|
||||
if (o1 == null && o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o2 == null) {
|
||||
return -1;
|
||||
}
|
||||
return o1.ordinal() - o2.ordinal();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
for (String nextInclude : includes) {
|
||||
resource.addSearchInclude(nextInclude);
|
||||
}
|
||||
} else {
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = bindings.getOperationBindingToName().get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
ourLog.debug("Found bound operation: {}", opName);
|
||||
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition((getOperationDefinitionPrefix(theRequestDetails) + "OperationDefinition/" + opName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String getOperationDefinitionPrefix(RequestDetails theRequestDetails) {
|
||||
if (theRequestDetails == null) {
|
||||
return "";
|
||||
}
|
||||
return theRequestDetails.getServerBaseForRequest() + "/";
|
||||
}
|
||||
|
||||
private void handleNamelessSearchMethodBinding(CapabilityStatementRestResourceComponent resource, RuntimeResourceDefinition def, TreeSet<String> includes,
|
||||
SearchMethodBinding searchMethodBinding, RequestDetails theRequestDetails) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
List<IParameter> params = searchMethodBinding.getParameters();
|
||||
List<SearchParameter> searchParameters = new ArrayList<>();
|
||||
for (IParameter nextParameter : params) {
|
||||
if ((nextParameter instanceof SearchParameter)) {
|
||||
searchParameters.add((SearchParameter) nextParameter);
|
||||
}
|
||||
}
|
||||
sortSearchParameters(searchParameters);
|
||||
if (!searchParameters.isEmpty()) {
|
||||
// boolean allOptional = searchParameters.get(0).isRequired() == false;
|
||||
//
|
||||
// OperationDefinition query = null;
|
||||
// if (!allOptional) {
|
||||
// RestOperation operation = rest.addOperation();
|
||||
// query = new OperationDefinition();
|
||||
// operation.setDefinition(new ResourceReferenceDt(query));
|
||||
// query.getDescriptionElement().setValue(searchMethodBinding.getDescription());
|
||||
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
|
||||
// for (String nextInclude : searchMethodBinding.getIncludes()) {
|
||||
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
|
||||
// }
|
||||
// }
|
||||
|
||||
for (SearchParameter nextParameter : searchParameters) {
|
||||
|
||||
String nextParamName = nextParameter.getName();
|
||||
|
||||
String chain = null;
|
||||
String nextParamUnchainedName = nextParamName;
|
||||
if (nextParamName.contains(".")) {
|
||||
chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
|
||||
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||
}
|
||||
|
||||
String nextParamDescription = nextParameter.getDescription();
|
||||
|
||||
/*
|
||||
* If the parameter has no description, default to the one from the resource
|
||||
*/
|
||||
if (StringUtils.isBlank(nextParamDescription)) {
|
||||
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
||||
if (paramDef != null) {
|
||||
nextParamDescription = paramDef.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam();
|
||||
param.setName(nextParamUnchainedName);
|
||||
|
||||
// if (StringUtils.isNotBlank(chain)) {
|
||||
// param.addChain(chain);
|
||||
// }
|
||||
//
|
||||
// if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
||||
// for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) {
|
||||
// if (nextWhitelist.startsWith(".")) {
|
||||
// param.addChain(nextWhitelist.substring(1));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
param.setDocumentation(nextParamDescription);
|
||||
if (nextParameter.getParamType() != null) {
|
||||
param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode());
|
||||
}
|
||||
for (Class<? extends IBaseResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||
RuntimeResourceDefinition targetDef = getServerConfiguration(theRequestDetails).getFhirContext().getResourceDefinition(nextTarget);
|
||||
if (targetDef != null) {
|
||||
ResourceType code;
|
||||
try {
|
||||
code = ResourceType.fromCode(targetDef.getName());
|
||||
} catch (FHIRException e) {
|
||||
code = null;
|
||||
}
|
||||
// if (code != null) {
|
||||
// param.addTarget(targetDef.getName());
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
|
||||
Bindings bindings = configuration.provideBindings();
|
||||
|
||||
List<OperationMethodBinding> operationBindings = bindings.getOperationNameToBindings().get(theId.getIdPart());
|
||||
if (operationBindings != null && !operationBindings.isEmpty()) {
|
||||
return readOperationDefinitionForOperation(operationBindings);
|
||||
}
|
||||
List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart());
|
||||
if (searchBindings != null && !searchBindings.isEmpty()) {
|
||||
return readOperationDefinitionForNamedSearch(searchBindings);
|
||||
}
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(PublicationStatus.ACTIVE);
|
||||
op.setKind(OperationKind.QUERY);
|
||||
op.setAffectsState(false);
|
||||
|
||||
op.setSystem(false);
|
||||
op.setType(false);
|
||||
op.setInstance(false);
|
||||
|
||||
Set<String> inParams = new HashSet<>();
|
||||
|
||||
for (SearchMethodBinding binding : bindings) {
|
||||
if (isNotBlank(binding.getDescription())) {
|
||||
op.setDescription(binding.getDescription());
|
||||
}
|
||||
if (isBlank(binding.getResourceProviderResourceName())) {
|
||||
op.setSystem(true);
|
||||
} else {
|
||||
op.setType(true);
|
||||
op.addResourceElement().setValue(binding.getResourceProviderResourceName());
|
||||
}
|
||||
op.setCode(binding.getQueryName());
|
||||
for (IParameter nextParamUntyped : binding.getParameters()) {
|
||||
if (nextParamUntyped instanceof SearchParameter) {
|
||||
SearchParameter nextParam = (SearchParameter) nextParamUntyped;
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(Enumerations.OperationParameterUse.IN);
|
||||
param.setType(Enumerations.FHIRAllTypes.STRING);
|
||||
param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode());
|
||||
param.setMin(nextParam.isRequired() ? 1 : 0);
|
||||
param.setMax("1");
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(op.getName())) {
|
||||
if (isNotBlank(op.getDescription())) {
|
||||
op.setName(op.getDescription());
|
||||
} else {
|
||||
op.setName(op.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) {
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(PublicationStatus.ACTIVE);
|
||||
op.setKind(OperationKind.OPERATION);
|
||||
op.setAffectsState(false);
|
||||
|
||||
// We reset these to true below if we find a binding that can handle the level
|
||||
op.setSystem(false);
|
||||
op.setType(false);
|
||||
op.setInstance(false);
|
||||
|
||||
Set<String> inParams = new HashSet<>();
|
||||
Set<String> outParams = new HashSet<>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : bindings) {
|
||||
if (isNotBlank(sharedDescription.getDescription())) {
|
||||
op.setDescription(sharedDescription.getDescription());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(true);
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(true);
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtTypeLevel()) {
|
||||
op.setType(true);
|
||||
}
|
||||
if (!sharedDescription.isIdempotent()) {
|
||||
op.setAffectsState(!sharedDescription.isIdempotent());
|
||||
}
|
||||
op.setCode(sharedDescription.getName().substring(1));
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(sharedDescription.isCanOperateAtInstanceLevel());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(sharedDescription.isCanOperateAtServerLevel());
|
||||
}
|
||||
if (isNotBlank(sharedDescription.getResourceName())) {
|
||||
op.addResourceElement().setValue(sharedDescription.getResourceName());
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
param.setUse(Enumerations.OperationParameterUse.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(Enumerations.FHIRAllTypes.fromCode(nextParam.getParamType()));
|
||||
}
|
||||
if (nextParam.getSearchParamType() != null) {
|
||||
param.getSearchTypeElement().setValueAsString(nextParam.getSearchParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
|
||||
if (!outParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(Enumerations.OperationParameterUse.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(Enumerations.FHIRAllTypes.fromCode(nextParam.getType()));
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(op.getName())) {
|
||||
if (isNotBlank(op.getDescription())) {
|
||||
op.setName(op.getDescription());
|
||||
} else {
|
||||
op.setName(op.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
if (op.hasSystem() == false) {
|
||||
op.setSystem(false);
|
||||
}
|
||||
if (op.hasInstance() == false) {
|
||||
op.setInstance(false);
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestfulServer(RestfulServer theRestfulServer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
private void sortSearchParameters(List<SearchParameter> searchParameters) {
|
||||
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
|
||||
@Override
|
||||
public int compare(SearchParameter theO1, SearchParameter theO2) {
|
||||
if (theO1.isRequired() == theO2.isRequired()) {
|
||||
return theO1.getName().compareTo(theO2.getName());
|
||||
}
|
||||
if (theO1.isRequired()) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
|
@ -21,12 +22,12 @@ import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
|||
import ca.uhn.fhir.rest.server.method.IParameter;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r5.hapi.rest.server.ServerCapabilityStatementProvider;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
|
@ -56,6 +57,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
@ -68,13 +70,9 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
public class ServerCapabilityStatementProviderR5Test {
|
||||
|
||||
private static FhirContext ourCtx;
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R5);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProviderR5Test.class);
|
||||
|
||||
static {
|
||||
ourCtx = FhirContext.forR5();
|
||||
}
|
||||
|
||||
private HttpServletRequest createHttpServletRequest() {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search");
|
||||
|
@ -106,16 +104,16 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testConditionalOperations() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ConditionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1);
|
||||
|
@ -135,24 +133,24 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testExtendedOperationReturningBundle() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertEquals(1, conformance.getRest().get(0).getOperation().size());
|
||||
assertEquals("everything", conformance.getRest().get(0).getOperation().get(0).getName());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
assertEquals("everything", opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
|
@ -163,19 +161,19 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testExtendedOperationReturningBundleOperation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertEquals("everything", opDef.getCode());
|
||||
|
@ -185,32 +183,32 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testInstanceHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new InstanceHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + TypeRestfulInteraction.HISTORYINSTANCE.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatIncludesSpecialNonMediaTypeFormats() throws ServletException {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
CapabilityStatement serverConformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement serverConformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
List<String> formatCodes = serverConformance.getFormat().stream().map(c -> c.getCode()).collect(Collectors.toList());;
|
||||
|
||||
|
@ -224,10 +222,10 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testMultiOptionalDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MultiOptionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -245,8 +243,8 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier\"/>"));
|
||||
|
@ -257,16 +255,16 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testNonConditionalOperations() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NonConditionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1);
|
||||
|
@ -280,18 +278,18 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
/** See #379 */
|
||||
@Test
|
||||
public void testOperationAcrossMultipleTypes() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertEquals(4, conformance.getRest().get(0).getOperation().size());
|
||||
|
@ -302,9 +300,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
assertThat(operationIdParts, containsInAnyOrder("Patient-i-someOp", "Encounter-i-someOp", "Patient-i-validate", "Encounter-i-validate"));
|
||||
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("someOp", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -317,9 +315,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
assertEquals("Patient", opDef.getParameter().get(1).getType().toCode());
|
||||
}
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("someOp", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -332,9 +330,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
assertEquals("Encounter", opDef.getParameter().get(1).getType().toCode());
|
||||
}
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("validate", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -349,17 +347,17 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testOperationDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
|
||||
assertThat(conf, containsString("<type value=\"token\"/>"));
|
||||
|
@ -368,20 +366,20 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
|
||||
@Test
|
||||
public void testOperationOnNoTypes() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new PlainProviderWithExtendedOperationOnNoType());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
|
||||
@Override
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
return super.getServerConformance(theRequest, createRequestDetails(rs));
|
||||
return (CapabilityStatement) super.getServerConformance(theRequest, createRequestDetails(rs));
|
||||
}
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
|
||||
assertEquals("plain", opDef.getCode());
|
||||
|
@ -408,24 +406,23 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testProviderWithRequiredAndOptional() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithRequiredAndOptional());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
CapabilityStatementRestComponent rest = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceComponent res = rest.getResource().get(0);
|
||||
assertEquals("DiagnosticReport", res.getType());
|
||||
|
||||
assertEquals(DiagnosticReport.SP_SUBJECT, res.getSearchParam().get(0).getName());
|
||||
// assertEquals("identifier", res.getSearchParam().get(0).getChain().get(0).getValue());
|
||||
assertEquals("subject.identifier", res.getSearchParam().get(0).getName());
|
||||
|
||||
assertEquals(DiagnosticReport.SP_CODE, res.getSearchParam().get(1).getName());
|
||||
|
||||
|
@ -438,19 +435,19 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testReadAndVReadSupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new VreadProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"vread\"/></interaction>"));
|
||||
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
|
||||
}
|
||||
|
@ -458,19 +455,19 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testReadSupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ReadProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, not(containsString("<interaction><code value=\"vread\"/></interaction>")));
|
||||
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
|
||||
}
|
||||
|
@ -478,10 +475,10 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testSearchParameterDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -502,9 +499,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
|
||||
|
@ -518,10 +515,10 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new PatientResourceProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -538,9 +535,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
}
|
||||
|
@ -551,10 +548,10 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterWithWhitelistDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProviderWithWhitelist());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -571,9 +568,9 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
CapabilityStatementRestResourceComponent resource = findRestResource(conformance, "Patient");
|
||||
|
@ -587,7 +584,7 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterWithList() throws Exception {
|
||||
|
||||
RestfulServer rsNoType = new RestfulServer(ourCtx){
|
||||
RestfulServer rsNoType = new RestfulServer(myCtx){
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -596,15 +593,15 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
};
|
||||
rsNoType.registerProvider(new SearchProviderWithListNoType());
|
||||
ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(rsNoType);
|
||||
rsNoType.setServerConformanceProvider(scNoType);
|
||||
rsNoType.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = scNoType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsNoType));
|
||||
String confNoType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) scNoType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsNoType));
|
||||
String confNoType = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(confNoType);
|
||||
|
||||
RestfulServer rsWithType = new RestfulServer(ourCtx){
|
||||
RestfulServer rsWithType = new RestfulServer(myCtx){
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -613,12 +610,12 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
};
|
||||
rsWithType.registerProvider(new SearchProviderWithListWithType());
|
||||
ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(rsWithType);
|
||||
rsWithType.setServerConformanceProvider(scWithType);
|
||||
rsWithType.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformanceWithType = scWithType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsWithType));
|
||||
String confWithType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformanceWithType);
|
||||
CapabilityStatement conformanceWithType = (CapabilityStatement) scWithType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsWithType));
|
||||
String confWithType = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformanceWithType);
|
||||
ourLog.info(confWithType);
|
||||
|
||||
assertEquals(confNoType, confWithType);
|
||||
|
@ -628,38 +625,38 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Test
|
||||
public void testSystemHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SystemHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + SystemRestfulInteraction.HISTORYSYSTEM.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new TypeHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + TypeRestfulInteraction.HISTORYTYPE.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
|
@ -667,34 +664,34 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
@Disabled
|
||||
public void testValidateGeneratedStatement() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MultiOptionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(conformance);
|
||||
ValidationResult result = myCtx.newValidator().validateWithResult(conformance);
|
||||
assertTrue(result.isSuccessful(), result.getMessages().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NamedQueryPlainProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
|
@ -703,11 +700,11 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
String operationReference = operationComponent.getDefinition();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
validate(operationDefinition);
|
||||
assertThat(operationDefinition.getCode(), is(NamedQueryPlainProvider.QUERY_NAME));
|
||||
assertThat("The operation name should be the description, if a description is set", operationDefinition.getName(), is(NamedQueryPlainProvider.DESCRIPTION));
|
||||
assertThat("The operation name should be the description, if a description is set", operationDefinition.getName(), equalTo("Search_testQuery"));
|
||||
assertThat(operationDefinition.getStatus(), is(PublicationStatus.ACTIVE));
|
||||
assertThat(operationDefinition.getKind(), is(OperationKind.QUERY));
|
||||
assertThat(operationDefinition.getDescription(), is(NamedQueryPlainProvider.DESCRIPTION));
|
||||
|
@ -729,27 +726,27 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
|
||||
@Test
|
||||
public void testResourceLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NamedQueryResourceProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
String operationReference = operationComponent.getDefinition();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
validate(operationDefinition);
|
||||
assertThat("The operation name should be the code if no description is set", operationDefinition.getName(), is(NamedQueryResourceProvider.QUERY_NAME));
|
||||
assertThat("The operation name should be the code if no description is set", operationDefinition.getName(), equalTo("Search_testQuery"));
|
||||
String patientResourceName = "Patient";
|
||||
assertThat("A resource level search targets the resource of the provider it's defined in", operationDefinition.getResource().get(0).getValue(), is(patientResourceName));
|
||||
assertThat(operationDefinition.getSystem(), is(false));
|
||||
|
@ -767,31 +764,32 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
|
||||
CapabilityStatementRestResourceComponent patientResource = restComponent.getResource().stream()
|
||||
.filter(r -> patientResourceName.equals(r.getType()))
|
||||
.findAny().get();
|
||||
.findAny()
|
||||
.get();
|
||||
assertThat("Named query parameters should not appear in the resource search params", patientResource.getSearchParam(), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtendedOperationAtTypeLevel() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new TypeLevelOperationProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
List<CapabilityStatementRestResourceOperationComponent> operations = conformance.getRest().get(0).getOperation();
|
||||
assertThat(operations.size(), is(1));
|
||||
assertThat(operations.get(0).getName(), is(TypeLevelOperationProvider.OPERATION_NAME));
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType(operations.get(0).getDefinition()), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType(operations.get(0).getDefinition()), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
assertEquals(TypeLevelOperationProvider.OPERATION_NAME, opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
|
@ -801,16 +799,16 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
|
||||
@Test
|
||||
public void testProfiledResourceStructureDefinitionLinks() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
List<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
|
||||
CapabilityStatementRestResourceComponent patientResource = resources.stream()
|
||||
|
@ -844,7 +842,7 @@ public class ServerCapabilityStatementProviderR5Test {
|
|||
}
|
||||
|
||||
private void validate(OperationDefinition theOpDef) {
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theOpDef);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theOpDef);
|
||||
ourLog.info("Def: {}", conf);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,19 +40,24 @@ import org.junit.jupiter.api.extension.ExtensionContext;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RestfulServerExtension implements BeforeEachCallback, AfterEachCallback {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerExtension.class);
|
||||
|
||||
private FhirContext myFhirContext;
|
||||
private Object[] myProviders;
|
||||
private List<Object> myProviders = new ArrayList<>();
|
||||
private FhirVersionEnum myFhirVersion;
|
||||
private Server myServer;
|
||||
private RestfulServer myServlet;
|
||||
private int myPort;
|
||||
private CloseableHttpClient myHttpClient;
|
||||
private IGenericClient myFhirClient;
|
||||
private List<Consumer<RestfulServer>> myConsumers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -60,7 +65,9 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
|||
public RestfulServerExtension(FhirContext theFhirContext, Object... theProviders) {
|
||||
Validate.notNull(theFhirContext);
|
||||
myFhirContext = theFhirContext;
|
||||
myProviders = theProviders;
|
||||
if (theProviders != null) {
|
||||
myProviders = new ArrayList<>(Arrays.asList(theProviders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,6 +105,8 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
|||
ServletHolder servletHolder = new ServletHolder(myServlet);
|
||||
servletHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
myConsumers.forEach(t -> t.accept(myServlet));
|
||||
|
||||
myServer.setHandler(servletHandler);
|
||||
myServer.start();
|
||||
myPort = JettyUtil.getPortForStartedServer(myServer);
|
||||
|
@ -140,4 +149,26 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
|||
createContextIfNeeded();
|
||||
startServer();
|
||||
}
|
||||
|
||||
public RestfulServerExtension registerProvider(Object theProvider) {
|
||||
if (myServlet != null) {
|
||||
myServlet.registerProvider(theProvider);
|
||||
} else {
|
||||
myProviders.add(theProvider);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public RestfulServerExtension withServer(Consumer<RestfulServer> theConsumer) {
|
||||
if (myServlet != null) {
|
||||
theConsumer.accept(myServlet);
|
||||
} else {
|
||||
myConsumers.add(theConsumer);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public RestfulServerExtension registerInterceptor(Object theInterceptor) {
|
||||
return withServer(t -> t.getInterceptorService().registerInterceptor(theInterceptor));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -39,6 +40,12 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.fetchAllConformanceResources();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
|
||||
return myWrap.fetchAllNonBaseStructureDefinitions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
return myWrap.fetchAllStructureDefinitions();
|
||||
|
|
|
@ -84,6 +84,12 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
return loadFromCache(myCache, key, t -> super.fetchAllStructureDefinitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
|
||||
String key = "fetchAllNonBaseStructureDefinitions";
|
||||
return loadFromCache(myCache, key, t -> super.fetchAllNonBaseStructureDefinitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
|
||||
return loadFromCache(myCache, "fetchResource " + theClass + " " + theUri,
|
||||
|
|
|
@ -15,9 +15,12 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public class ValidationSupportChain implements IValidationSupport {
|
||||
|
@ -182,10 +185,19 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllStructureDefinitions() {
|
||||
return doFetchStructureDefinitions(t->t.fetchAllStructureDefinitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllNonBaseStructureDefinitions() {
|
||||
return doFetchStructureDefinitions(t->t.fetchAllNonBaseStructureDefinitions());
|
||||
}
|
||||
|
||||
private List<IBaseResource> doFetchStructureDefinitions(Function<IValidationSupport, List<IBaseResource>> theFunction) {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<>();
|
||||
Set<String> urls = new HashSet<>();
|
||||
for (IValidationSupport nextSupport : myChain) {
|
||||
List<IBaseResource> allStructureDefinitions = nextSupport.fetchAllStructureDefinitions();
|
||||
List<IBaseResource> allStructureDefinitions = theFunction.apply(nextSupport);
|
||||
if (allStructureDefinitions != null) {
|
||||
for (IBaseResource next : allStructureDefinitions) {
|
||||
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
|
||||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Sort;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.param.UriAndListParam;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
// import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
// import ca.uhn.fhir.model.api.Bundle;
|
||||
|
||||
|
||||
public class PatientResourceProvider implements IResourceProvider
|
||||
{
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search()
|
||||
public IBundleProvider search(
|
||||
javax.servlet.http.HttpServletRequest theServletRequest,
|
||||
|
||||
@Description(shortDefinition="The resource identity")
|
||||
@OptionalParam(name="_id")
|
||||
StringAndListParam theId,
|
||||
|
||||
@Description(shortDefinition="The resource language")
|
||||
@OptionalParam(name="_language")
|
||||
StringAndListParam theResourceLanguage,
|
||||
|
||||
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
|
||||
@OptionalParam(name=Constants.PARAM_CONTENT)
|
||||
StringAndListParam theFtContent,
|
||||
|
||||
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
|
||||
@OptionalParam(name=Constants.PARAM_TEXT)
|
||||
StringAndListParam theFtText,
|
||||
|
||||
@Description(shortDefinition="Search for resources which have the given tag")
|
||||
@OptionalParam(name=Constants.PARAM_TAG)
|
||||
TokenAndListParam theSearchForTag,
|
||||
|
||||
@Description(shortDefinition="Search for resources which have the given security labels")
|
||||
@OptionalParam(name=Constants.PARAM_SECURITY)
|
||||
TokenAndListParam theSearchForSecurity,
|
||||
|
||||
@Description(shortDefinition="Search for resources which have the given profile")
|
||||
@OptionalParam(name=Constants.PARAM_PROFILE)
|
||||
UriAndListParam theSearchForProfile,
|
||||
|
||||
|
||||
@Description(shortDefinition="A patient identifier")
|
||||
@OptionalParam(name="identifier")
|
||||
TokenAndListParam theIdentifier,
|
||||
|
||||
@Description(shortDefinition="A portion of either family or given name of the patient")
|
||||
@OptionalParam(name="name")
|
||||
StringAndListParam theName,
|
||||
|
||||
@Description(shortDefinition="A portion of the family name of the patient")
|
||||
@OptionalParam(name="family")
|
||||
StringAndListParam theFamily,
|
||||
|
||||
@Description(shortDefinition="A portion of the given name of the patient")
|
||||
@OptionalParam(name="given")
|
||||
StringAndListParam theGiven,
|
||||
|
||||
@Description(shortDefinition="A portion of either family or given name using some kind of phonetic matching algorithm")
|
||||
@OptionalParam(name="phonetic")
|
||||
StringAndListParam thePhonetic,
|
||||
|
||||
@Description(shortDefinition="The value in any kind of telecom details of the patient")
|
||||
@OptionalParam(name="telecom")
|
||||
TokenAndListParam theTelecom,
|
||||
|
||||
@Description(shortDefinition="A value in a phone contact")
|
||||
@OptionalParam(name="phone")
|
||||
TokenAndListParam thePhone,
|
||||
|
||||
@Description(shortDefinition="A value in an email contact")
|
||||
@OptionalParam(name="email")
|
||||
TokenAndListParam theEmail,
|
||||
|
||||
@Description(shortDefinition="An address in any kind of address/part of the patient")
|
||||
@OptionalParam(name="address")
|
||||
StringAndListParam theAddress,
|
||||
|
||||
@Description(shortDefinition="A city specified in an address")
|
||||
@OptionalParam(name="address-city")
|
||||
StringAndListParam theAddress_city,
|
||||
|
||||
@Description(shortDefinition="A state specified in an address")
|
||||
@OptionalParam(name="address-state")
|
||||
StringAndListParam theAddress_state,
|
||||
|
||||
@Description(shortDefinition="A postalCode specified in an address")
|
||||
@OptionalParam(name="address-postalcode")
|
||||
StringAndListParam theAddress_postalcode,
|
||||
|
||||
@Description(shortDefinition="A country specified in an address")
|
||||
@OptionalParam(name="address-country")
|
||||
StringAndListParam theAddress_country,
|
||||
|
||||
@Description(shortDefinition="A use code specified in an address")
|
||||
@OptionalParam(name="address-use")
|
||||
TokenAndListParam theAddress_use,
|
||||
|
||||
@Description(shortDefinition="Gender of the patient")
|
||||
@OptionalParam(name="gender")
|
||||
TokenAndListParam theGender,
|
||||
|
||||
@Description(shortDefinition="Language code (irrespective of use value)")
|
||||
@OptionalParam(name="language")
|
||||
TokenAndListParam theLanguage,
|
||||
|
||||
@Description(shortDefinition="The patient's date of birth")
|
||||
@OptionalParam(name="birthdate")
|
||||
DateRangeParam theBirthdate,
|
||||
|
||||
@Description(shortDefinition="The organization at which this person is a patient")
|
||||
@OptionalParam(name="organization", targetTypes={ Organization.class } )
|
||||
ReferenceAndListParam theOrganization,
|
||||
|
||||
@Description(shortDefinition="Patient's nominated care provider, could be a care manager, not the organization that manages the record")
|
||||
@OptionalParam(name="careprovider", targetTypes={ Organization.class , Practitioner.class } )
|
||||
ReferenceAndListParam theCareprovider,
|
||||
|
||||
@Description(shortDefinition="Whether the patient record is active")
|
||||
@OptionalParam(name="active")
|
||||
TokenAndListParam theActive,
|
||||
|
||||
@Description(shortDefinition="The species for animal patients")
|
||||
@OptionalParam(name="animal-species")
|
||||
TokenAndListParam theAnimal_species,
|
||||
|
||||
@Description(shortDefinition="The breed for animal patients")
|
||||
@OptionalParam(name="animal-breed")
|
||||
TokenAndListParam theAnimal_breed,
|
||||
|
||||
@Description(shortDefinition="All patients linked to the given patient")
|
||||
@OptionalParam(name="link", targetTypes={ Patient.class } )
|
||||
ReferenceAndListParam theLink,
|
||||
|
||||
@Description(shortDefinition="This patient has been marked as deceased, or as a death date entered")
|
||||
@OptionalParam(name="deceased")
|
||||
TokenAndListParam theDeceased,
|
||||
|
||||
@Description(shortDefinition="The date of death has been provided and satisfies this search value")
|
||||
@OptionalParam(name="deathdate")
|
||||
DateRangeParam theDeathdate,
|
||||
|
||||
@IncludeParam(reverse=true)
|
||||
Set<Include> theRevIncludes,
|
||||
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
|
||||
@OptionalParam(name="_lastUpdated")
|
||||
DateRangeParam theLastUpdated,
|
||||
|
||||
@IncludeParam(allow= {
|
||||
"Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "*"
|
||||
})
|
||||
Set<Include> theIncludes,
|
||||
|
||||
@Sort
|
||||
SortSpec theSort,
|
||||
|
||||
@Count
|
||||
Integer theCount
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
|
@ -36,14 +37,14 @@ import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
|||
import ca.uhn.fhir.rest.server.method.IParameter;
|
||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
|
@ -63,9 +64,11 @@ import org.hl7.fhir.r4.model.OperationDefinition;
|
|||
import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationKind;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
|
@ -99,14 +102,13 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2";
|
||||
public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProviderR4Test.class);
|
||||
private static FhirContext ourCtx;
|
||||
private static FhirValidator ourValidator;
|
||||
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
private FhirValidator myValidator;
|
||||
|
||||
static {
|
||||
ourCtx = FhirContext.forR4();
|
||||
ourValidator = ourCtx.newValidator();
|
||||
ourValidator.setValidateAgainstStandardSchema(true);
|
||||
ourValidator.setValidateAgainstStandardSchematron(true);
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myValidator = myCtx.newValidator();
|
||||
myValidator.registerValidatorModule(new FhirInstanceValidator(myCtx));
|
||||
}
|
||||
|
||||
private HttpServletRequest createHttpServletRequest() {
|
||||
|
@ -140,18 +142,19 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testConditionalOperations() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ConditionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertEquals(2, conformance.getRest().get(0).getResource().size());
|
||||
CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1);
|
||||
assertEquals("Patient", res.getType());
|
||||
|
||||
|
@ -170,24 +173,22 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testExtendedOperationReturningBundle() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
validate(conformance);
|
||||
|
||||
assertEquals(1, conformance.getRest().get(0).getOperation().size());
|
||||
assertEquals("everything", conformance.getRest().get(0).getOperation().get(0).getName());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
assertEquals("everything", opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
|
@ -198,19 +199,19 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testExtendedOperationReturningBundleOperation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertEquals("everything", opDef.getCode());
|
||||
|
@ -220,29 +221,28 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testInstanceHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new InstanceHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = validate(conformance);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + TypeRestfulInteraction.HISTORYINSTANCE.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiOptionalDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MultiOptionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -260,9 +260,8 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = validate(conformance);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier\"/>"));
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's name\"/>"));
|
||||
|
@ -272,17 +271,16 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testNonConditionalOperations() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NonConditionalProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
validate(conformance);
|
||||
|
||||
CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1);
|
||||
assertEquals("Patient", res.getType());
|
||||
|
@ -297,19 +295,18 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
*/
|
||||
@Test
|
||||
public void testOperationAcrossMultipleTypes() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
validate(conformance);
|
||||
|
||||
assertEquals(4, conformance.getRest().get(0).getOperation().size());
|
||||
List<String> operationNames = toOperationNames(conformance.getRest().get(0).getOperation());
|
||||
|
@ -319,9 +316,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertThat(operationIdParts, containsInAnyOrder("Patient-i-someOp", "Encounter-i-someOp", "Patient-i-validate", "Encounter-i-validate"));
|
||||
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("someOp", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -334,9 +331,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertEquals("Patient", opDef.getParameter().get(1).getType());
|
||||
}
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("someOp", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -349,9 +346,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertEquals("Encounter", opDef.getParameter().get(1).getType());
|
||||
}
|
||||
{
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
|
||||
Set<String> types = toStrings(opDef.getResource());
|
||||
assertEquals("validate", opDef.getCode());
|
||||
assertEquals(true, opDef.getInstance());
|
||||
|
@ -366,17 +363,17 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testOperationDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = validate(conformance);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
|
||||
assertThat(conf, containsString("<type value=\"token\"/>"));
|
||||
|
@ -385,20 +382,20 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testOperationOnNoTypes() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new PlainProviderWithExtendedOperationOnNoType());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
|
||||
@Override
|
||||
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
return super.getServerConformance(theRequest, createRequestDetails(rs));
|
||||
return (CapabilityStatement) super.getServerConformance(theRequest, createRequestDetails(rs));
|
||||
}
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
|
||||
assertEquals("plain", opDef.getCode());
|
||||
|
@ -425,23 +422,22 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testProviderWithRequiredAndOptional() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ProviderWithRequiredAndOptional());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
validate(conformance);
|
||||
|
||||
CapabilityStatementRestComponent rest = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceComponent res = rest.getResource().get(0);
|
||||
assertEquals("DiagnosticReport", res.getType());
|
||||
|
||||
assertEquals(DiagnosticReport.SP_SUBJECT, res.getSearchParam().get(0).getName());
|
||||
assertEquals("subject.identifier", res.getSearchParam().get(0).getName());
|
||||
// assertEquals("identifier", res.getSearchParam().get(0).getChain().get(0).getValue());
|
||||
|
||||
assertEquals(DiagnosticReport.SP_CODE, res.getSearchParam().get(1).getName());
|
||||
|
@ -455,19 +451,17 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testReadAndVReadSupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new VreadProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = validate(conformance);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"vread\"/></interaction>"));
|
||||
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
|
||||
}
|
||||
|
@ -475,19 +469,19 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testReadSupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new ReadProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, not(containsString("<interaction><code value=\"vread\"/></interaction>")));
|
||||
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
|
||||
}
|
||||
|
@ -495,10 +489,10 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSearchParameterDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -519,10 +513,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
String conf = validate(conformance);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
|
||||
assertThat(conf, containsString("<type value=\"token\"/>"));
|
||||
|
@ -532,14 +525,14 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testFormatIncludesSpecialNonMediaTypeFormats() throws ServletException {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
CapabilityStatement serverConformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement serverConformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
List<String> formatCodes = serverConformance.getFormat().stream().map(c -> c.getCode()).collect(Collectors.toList());
|
||||
|
||||
|
@ -555,10 +548,10 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new PatientResourceProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -575,10 +568,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
String conf = validate(conformance);
|
||||
|
||||
}
|
||||
|
||||
|
@ -588,10 +580,10 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterWithWhitelistDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SearchProviderWithWhitelist());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
@ -608,10 +600,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
String conf = validate(conformance);
|
||||
|
||||
CapabilityStatementRestResourceComponent resource = findRestResource(conformance, "Patient");
|
||||
|
||||
|
@ -624,7 +615,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterWithList() throws Exception {
|
||||
|
||||
RestfulServer rsNoType = new RestfulServer(ourCtx) {
|
||||
RestfulServer rsNoType = new RestfulServer(myCtx) {
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -633,15 +624,14 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
};
|
||||
rsNoType.registerProvider(new SearchProviderWithListNoType());
|
||||
ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(rsNoType);
|
||||
rsNoType.setServerConformanceProvider(scNoType);
|
||||
rsNoType.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = scNoType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsNoType));
|
||||
String confNoType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(confNoType);
|
||||
CapabilityStatement conformance = (CapabilityStatement) scNoType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsNoType));
|
||||
String confNoType = validate(conformance);
|
||||
|
||||
RestfulServer rsWithType = new RestfulServer(ourCtx) {
|
||||
RestfulServer rsWithType = new RestfulServer(myCtx) {
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -650,13 +640,12 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
};
|
||||
rsWithType.registerProvider(new SearchProviderWithListWithType());
|
||||
ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(rsWithType);
|
||||
rsWithType.setServerConformanceProvider(scWithType);
|
||||
rsWithType.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformanceWithType = scWithType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsWithType));
|
||||
String confWithType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformanceWithType);
|
||||
ourLog.info(confWithType);
|
||||
CapabilityStatement conformanceWithType = (CapabilityStatement) scWithType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsWithType));
|
||||
String confWithType = validate(conformanceWithType);
|
||||
|
||||
assertEquals(confNoType, confWithType);
|
||||
assertThat(confNoType, containsString("<date value=\"2011-02-22T11:22:33Z\"/>"));
|
||||
|
@ -665,38 +654,34 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSystemHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new SystemHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = validate(conformance);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + SystemRestfulInteraction.HISTORYSYSTEM.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeHistorySupported() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new TypeHistoryProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
String conf = validate(conformance);
|
||||
|
||||
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance);
|
||||
assertThat(conf, containsString("<interaction><code value=\"" + TypeRestfulInteraction.HISTORYTYPE.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
|
@ -720,39 +705,38 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new MyProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement opDef = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement opDef = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
ourLog.info(conf);
|
||||
validate(opDef);
|
||||
|
||||
CapabilityStatementRestResourceComponent resource = opDef.getRest().get(0).getResource().get(0);
|
||||
assertEquals("DiagnosticReport", resource.getType());
|
||||
List<String> searchParamNames = resource.getSearchParam().stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
assertThat(searchParamNames, containsInAnyOrder("patient", "date"));
|
||||
assertThat(searchParamNames, containsInAnyOrder("patient.birthdate", "patient.family", "patient.given", "date"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NamedQueryPlainProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
validate(conformance);
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
|
@ -761,11 +745,11 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
String operationReference = operationComponent.getDefinition();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
validate(operationDefinition);
|
||||
assertThat(operationDefinition.getCode(), is(NamedQueryPlainProvider.QUERY_NAME));
|
||||
assertThat("The operation name should be the description, if a description is set", operationDefinition.getName(), is(NamedQueryPlainProvider.DESCRIPTION));
|
||||
assertThat(operationDefinition.getName(), is("Search_" + NamedQueryPlainProvider.QUERY_NAME));
|
||||
assertThat(operationDefinition.getStatus(), is(PublicationStatus.ACTIVE));
|
||||
assertThat(operationDefinition.getKind(), is(OperationKind.QUERY));
|
||||
assertThat(operationDefinition.getDescription(), is(NamedQueryPlainProvider.DESCRIPTION));
|
||||
|
@ -787,27 +771,27 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testResourceLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new NamedQueryResourceProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
validate(conformance);
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestResourceOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
String operationReference = operationComponent.getDefinition();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition));
|
||||
validate(operationDefinition);
|
||||
assertThat("The operation name should be the code if no description is set", operationDefinition.getName(), is(NamedQueryResourceProvider.QUERY_NAME));
|
||||
assertThat("The operation name should be the code if no description is set", operationDefinition.getName(), is("Search_" + NamedQueryResourceProvider.QUERY_NAME));
|
||||
String patientResourceName = "Patient";
|
||||
assertThat("A resource level search targets the resource of the provider it's defined in", operationDefinition.getResource().get(0).getValue(), is(patientResourceName));
|
||||
assertThat(operationDefinition.getSystem(), is(false));
|
||||
|
@ -831,25 +815,24 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testExtendedOperationAtTypeLevel() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setProviders(new TypeLevelOperationProvider());
|
||||
rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4"));
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
validate(conformance);
|
||||
|
||||
List<CapabilityStatementRestResourceOperationComponent> operations = conformance.getRest().get(0).getOperation();
|
||||
assertThat(operations.size(), is(1));
|
||||
assertThat(operations.get(0).getName(), is(TypeLevelOperationProvider.OPERATION_NAME));
|
||||
|
||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType(operations.get(0).getDefinition()), createRequestDetails(rs));
|
||||
OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType(operations.get(0).getDefinition()), createRequestDetails(rs));
|
||||
validate(opDef);
|
||||
assertEquals(TypeLevelOperationProvider.OPERATION_NAME, opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
|
@ -859,16 +842,16 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testProfiledResourceStructureDefinitionLinks() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
RestfulServer rs = new RestfulServer(myCtx);
|
||||
rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
List<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
|
||||
CapabilityStatementRestResourceComponent patientResource = resources.stream()
|
||||
|
@ -901,15 +884,24 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void validate(OperationDefinition theOpDef) {
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theOpDef);
|
||||
ourLog.info("Def: {}", conf);
|
||||
private String validate(IBaseResource theResource) {
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theResource);
|
||||
ourLog.info("Def:\n{}", conf);
|
||||
|
||||
ValidationResult result = ourValidator.validateWithResult(theOpDef);
|
||||
String outcome = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ValidationResult result = myValidator.validateWithResult(conf);
|
||||
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
|
||||
String outcome = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
|
||||
ourLog.info("Outcome: {}", outcome);
|
||||
|
||||
assertTrue(result.isSuccessful(), outcome);
|
||||
List<OperationOutcome.OperationOutcomeIssueComponent> warningsAndErrors = operationOutcome
|
||||
.getIssue()
|
||||
.stream()
|
||||
.filter(t -> t.getSeverity().ordinal() <= OperationOutcome.IssueSeverity.WARNING.ordinal()) // <= because this enum has a strange order
|
||||
.collect(Collectors.toList());
|
||||
assertThat(outcome, warningsAndErrors, is(empty()));
|
||||
|
||||
return myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(theResource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
Loading…
Reference in New Issue