Issue 430: Generate OperationDefinitions for named queries, fix minor errors in generation of OperationDefinition for operations. Closes #430.
This commit is contained in:
parent
62ae71c1c6
commit
f8b232bb67
|
@ -450,8 +450,7 @@ public abstract class BaseMethodBinding<T> {
|
|||
|
||||
if (returnTypeFromRp != null) {
|
||||
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
|
||||
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
|
||||
//FIXME potential null access on retunrTypeFromMethod
|
||||
if (returnTypeFromMethod != null && !returnTypeFromRp.isAssignableFrom(returnTypeFromMethod)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
|
||||
+ returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
|
||||
}
|
||||
|
@ -479,7 +478,7 @@ public abstract class BaseMethodBinding<T> {
|
|||
if (read != null) {
|
||||
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
|
||||
} else if (search != null) {
|
||||
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
|
||||
return new SearchMethodBinding(returnType, returnTypeFromRp, theMethod, theContext, theProvider);
|
||||
} else if (conformance != null) {
|
||||
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (create != null) {
|
||||
|
|
|
@ -59,7 +59,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private final boolean myIdempotent;
|
||||
private final Integer myIdParamIndex;
|
||||
private final String myName;
|
||||
private final RestOperationTypeEnum myOtherOperatiopnType;
|
||||
private final RestOperationTypeEnum myOtherOperationType;
|
||||
private final ReturnTypeEnum myReturnType;
|
||||
private BundleTypeEnum myBundleType;
|
||||
private boolean myCanOperateAtInstanceLevel;
|
||||
|
@ -69,22 +69,12 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private List<ReturnType> myReturnParams;
|
||||
|
||||
protected OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
|
||||
boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType,
|
||||
OperationParam[] theReturnParams, BundleTypeEnum theBundleType) {
|
||||
boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType,
|
||||
OperationParam[] theReturnParams, BundleTypeEnum theBundleType) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
|
||||
myBundleType = theBundleType;
|
||||
myIdempotent = theIdempotent;
|
||||
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
|
||||
if (myIdParamIndex != null) {
|
||||
for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) {
|
||||
if (next instanceof IdParam) {
|
||||
myCanOperateAtTypeLevel = ((IdParam) next).optional() == true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
myCanOperateAtTypeLevel = true;
|
||||
}
|
||||
|
||||
Description description = theMethod.getAnnotation(Description.class);
|
||||
if (description != null) {
|
||||
|
@ -99,7 +89,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
if (isBlank(theOperationName)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName()
|
||||
+ " but this annotation has no name defined");
|
||||
+ " but this annotation has no name defined");
|
||||
}
|
||||
if (theOperationName.startsWith("$") == false) {
|
||||
theOperationName = "$" + theOperationName;
|
||||
|
@ -108,13 +98,11 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
if (theReturnTypeFromRp != null) {
|
||||
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
|
||||
} else {
|
||||
if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
|
||||
} else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
|
||||
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
|
||||
} else {
|
||||
setResourceName(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (theMethod.getReturnType().equals(IBundleProvider.class)) {
|
||||
myReturnType = ReturnTypeEnum.BUNDLE;
|
||||
|
@ -122,12 +110,24 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
myReturnType = ReturnTypeEnum.RESOURCE;
|
||||
}
|
||||
|
||||
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
|
||||
if (getResourceName() == null) {
|
||||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER;
|
||||
myOtherOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER;
|
||||
myCanOperateAtServerLevel = true;
|
||||
if (myIdParamIndex != null) {
|
||||
myCanOperateAtInstanceLevel = true;
|
||||
}
|
||||
} else if (myIdParamIndex == null) {
|
||||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
|
||||
myOtherOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
|
||||
myCanOperateAtTypeLevel = true;
|
||||
} else {
|
||||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE;
|
||||
myOtherOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE;
|
||||
myCanOperateAtInstanceLevel = true;
|
||||
for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) {
|
||||
if (next instanceof IdParam) {
|
||||
myCanOperateAtTypeLevel = ((IdParam) next).optional() == true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myReturnParams = new ArrayList<>();
|
||||
|
@ -149,20 +149,12 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
myReturnParams.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (myIdParamIndex != null) {
|
||||
myCanOperateAtInstanceLevel = true;
|
||||
}
|
||||
if (getResourceName() == null) {
|
||||
myCanOperateAtServerLevel = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
|
||||
Operation theAnnotation) {
|
||||
Operation theAnnotation) {
|
||||
this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.returnParameters(),
|
||||
theAnnotation.bundleType());
|
||||
theAnnotation.bundleType());
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
|
@ -188,7 +180,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
@Nonnull
|
||||
@Override
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myOtherOperatiopnType;
|
||||
return myOtherOperationType;
|
||||
}
|
||||
|
||||
public List<ReturnType> getReturnParams() {
|
||||
|
@ -230,16 +222,12 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
boolean requestHasId = theRequest.getId() != null;
|
||||
if (requestHasId) {
|
||||
if (isCanOperateAtInstanceLevel() == false) {
|
||||
return false;
|
||||
return myCanOperateAtInstanceLevel;
|
||||
}
|
||||
} else {
|
||||
if (myCanOperateAtTypeLevel == false) {
|
||||
return false;
|
||||
if (isNotBlank(theRequest.getResourceName())) {
|
||||
return myCanOperateAtTypeLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return myCanOperateAtServerLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,6 +56,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private Integer myIdParamIndex;
|
||||
private String myQueryName;
|
||||
private boolean myAllowUnknownParams;
|
||||
private final String myResourceProviderResourceName;
|
||||
|
||||
static {
|
||||
HashSet<String> specialSearchParams = new HashSet<>();
|
||||
|
@ -64,7 +65,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
SPECIAL_SEARCH_PARAMS = Collections.unmodifiableSet(specialSearchParams);
|
||||
}
|
||||
|
||||
public SearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
public SearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Class<? extends IBaseResource> theResourceProviderResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
Search search = theMethod.getAnnotation(Search.class);
|
||||
this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null);
|
||||
|
@ -89,12 +90,26 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
throw new ConfigurationException(msg);
|
||||
}
|
||||
|
||||
if (theResourceProviderResourceType != null) {
|
||||
this.myResourceProviderResourceName = theContext.getResourceDefinition(theResourceProviderResourceType).getName();
|
||||
} else {
|
||||
this.myResourceProviderResourceName = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public String getQueryName() {
|
||||
return myQueryName;
|
||||
}
|
||||
|
||||
public String getResourceProviderResourceName() {
|
||||
return myResourceProviderResourceName;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
|
|
|
@ -71,6 +71,8 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
|
||||
private boolean myCache = true;
|
||||
private volatile CapabilityStatement myCapabilityStatement;
|
||||
private IdentityHashMap<SearchMethodBinding, String> myNamedSearchMethodBindingToName;
|
||||
private HashMap<String, List<SearchMethodBinding>> mySearchNameToBindings;
|
||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
||||
private String myPublisher = "Not provided";
|
||||
|
@ -151,6 +153,17 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
return DateTimeType.now();
|
||||
}
|
||||
|
||||
private String createNamedQueryName(SearchMethodBinding searchMethodBinding) {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
if (searchMethodBinding.getResourceName() != null) {
|
||||
retVal.append(searchMethodBinding.getResourceName());
|
||||
}
|
||||
retVal.append("-query-");
|
||||
retVal.append(searchMethodBinding.getQueryName());
|
||||
|
||||
return retVal.toString();
|
||||
}
|
||||
|
||||
private String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
if (theMethodBinding.getResourceName() != null) {
|
||||
|
@ -297,7 +310,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||
|
||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (methodBinding.getQueryName() != null) {
|
||||
String queryName = myNamedSearchMethodBindingToName.get(methodBinding);
|
||||
if (operationNames.add(queryName)) {
|
||||
rest.addOperation().setName(methodBinding.getQueryName()).setDefinition(new Reference("OperationDefinition/" + queryName));
|
||||
}
|
||||
} else {
|
||||
handleNamelessSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
|
||||
}
|
||||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = myOperationBindingToName.get(methodBinding);
|
||||
|
@ -349,7 +370,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void handleSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes,
|
||||
private void handleNamelessSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes,
|
||||
SearchMethodBinding searchMethodBinding) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
|
@ -438,8 +459,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
|
||||
@Initialize
|
||||
public void initializeOperations() {
|
||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
||||
myOperationNameToBindings = new HashMap<String, List<OperationMethodBinding>>();
|
||||
myNamedSearchMethodBindingToName = new IdentityHashMap<>();
|
||||
mySearchNameToBindings = new HashMap<>();
|
||||
myOperationBindingToName = new IdentityHashMap<>();
|
||||
myOperationNameToBindings = new HashMap<>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
@ -456,24 +479,96 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
if (myOperationNameToBindings.containsKey(name) == false) {
|
||||
myOperationNameToBindings.put(name, new ArrayList<OperationMethodBinding>());
|
||||
myOperationNameToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
myOperationNameToBindings.get(name).add(methodBinding);
|
||||
} else if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
if (myNamedSearchMethodBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createNamedQueryName(methodBinding);
|
||||
ourLog.debug("Detected named query: {}", name);
|
||||
|
||||
myNamedSearchMethodBindingToName.put(methodBinding, name);
|
||||
if (!mySearchNameToBindings.containsKey(name)) {
|
||||
mySearchNameToBindings.put(name, new ArrayList<>());
|
||||
}
|
||||
mySearchNameToBindings.get(name).add(methodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdType theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
List<OperationMethodBinding> sharedDescriptions = myOperationNameToBindings.get(theId.getIdPart());
|
||||
if (sharedDescriptions == null || sharedDescriptions.isEmpty()) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
List<OperationMethodBinding> operationBindings = myOperationNameToBindings.get(theId.getIdPart());
|
||||
if (operationBindings != null && !operationBindings.isEmpty()) {
|
||||
return readOperationDefinitionForOperation(operationBindings);
|
||||
}
|
||||
List<SearchMethodBinding> searchBindings = mySearchNameToBindings.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.setIdempotent(true);
|
||||
|
||||
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);
|
||||
|
@ -484,10 +579,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
|||
op.setType(false);
|
||||
op.setInstance(false);
|
||||
|
||||
Set<String> inParams = new HashSet<String>();
|
||||
Set<String> outParams = new HashSet<String>();
|
||||
Set<String> inParams = new HashSet<>();
|
||||
Set<String> outParams = new HashSet<>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : sharedDescriptions) {
|
||||
for (OperationMethodBinding sharedDescription : bindings) {
|
||||
if (isNotBlank(sharedDescription.getDescription())) {
|
||||
op.setDescription(sharedDescription.getDescription());
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
|
@ -37,6 +38,14 @@ import ca.uhn.fhir.rest.server.method.SearchParameter;
|
|||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind;
|
||||
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse;
|
||||
|
||||
public class ServerCapabilityStatementProviderDstu3Test {
|
||||
|
||||
|
@ -124,6 +133,9 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
|||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"));
|
||||
validate(opDef);
|
||||
assertEquals("everything", opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
assertThat(opDef.getType(), is(false));
|
||||
assertThat(opDef.getInstance(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -344,6 +356,10 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
|||
assertEquals("1", opDef.getParameter().get(2).getMinElement().getValueAsString());
|
||||
assertEquals("2", opDef.getParameter().get(2).getMaxElement().getValueAsString());
|
||||
assertEquals("string", opDef.getParameter().get(2).getTypeElement().getValueAsString());
|
||||
|
||||
assertThat(opDef.getSystem(), is(true));
|
||||
assertThat(opDef.getType(), is(false));
|
||||
assertThat(opDef.getInstance(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -622,6 +638,120 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
|||
assertTrue(result.getMessages().toString(), result.isSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
rs.setProviders(new NamedQueryPlainProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
assertThat(operationComponent.getName(), is(NamedQueryPlainProvider.QUERY_NAME));
|
||||
|
||||
String operationReference = operationComponent.getDefinition().getReference();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference));
|
||||
ourLog.info(ourCtx.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.getStatus(), is(PublicationStatus.ACTIVE));
|
||||
assertThat(operationDefinition.getKind(), is(OperationKind.QUERY));
|
||||
assertThat(operationDefinition.getDescription(), is(NamedQueryPlainProvider.DESCRIPTION));
|
||||
assertThat(operationDefinition.getIdempotent(), is(true));
|
||||
assertThat("A system level search has no target resources", operationDefinition.getResource(), is(empty()));
|
||||
assertThat(operationDefinition.getSystem(), is(true));
|
||||
assertThat(operationDefinition.getType(), is(false));
|
||||
assertThat(operationDefinition.getInstance(), is(false));
|
||||
List<OperationDefinitionParameterComponent> parameters = operationDefinition.getParameter();
|
||||
assertThat(parameters.size(), is(1));
|
||||
OperationDefinitionParameterComponent param = parameters.get(0);
|
||||
assertThat(param.getName(), is(NamedQueryPlainProvider.SP_QUANTITY));
|
||||
assertThat(param.getType(), is("string"));
|
||||
assertThat(param.getSearchTypeElement().asStringValue(), is(RestSearchParameterTypeEnum.QUANTITY.getCode()));
|
||||
assertThat(param.getMin(), is(1));
|
||||
assertThat(param.getMax(), is("1"));
|
||||
assertThat(param.getUse(), is(OperationParameterUse.IN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
rs.setProviders(new NamedQueryResourceProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
CapabilityStatementRestComponent restComponent = conformance.getRest().get(0);
|
||||
CapabilityStatementRestOperationComponent operationComponent = restComponent.getOperation().get(0);
|
||||
String operationReference = operationComponent.getDefinition().getReference();
|
||||
assertThat(operationReference, not(nullValue()));
|
||||
|
||||
OperationDefinition operationDefinition = sc.readOperationDefinition(new IdType(operationReference));
|
||||
ourLog.info(ourCtx.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));
|
||||
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));
|
||||
assertThat(operationDefinition.getType(), is(true));
|
||||
assertThat(operationDefinition.getInstance(), is(false));
|
||||
List<OperationDefinitionParameterComponent> parameters = operationDefinition.getParameter();
|
||||
assertThat(parameters.size(), is(1));
|
||||
OperationDefinitionParameterComponent param = parameters.get(0);
|
||||
assertThat(param.getName(), is(NamedQueryResourceProvider.SP_PARAM));
|
||||
assertThat(param.getType(), is("string"));
|
||||
assertThat(param.getSearchTypeElement().asStringValue(), is(RestSearchParameterTypeEnum.STRING.getCode()));
|
||||
assertThat(param.getMin(), is(0));
|
||||
assertThat(param.getMax(), is("1"));
|
||||
assertThat(param.getUse(), is(OperationParameterUse.IN));
|
||||
|
||||
CapabilityStatementRestResourceComponent patientResource = restComponent.getResource().stream()
|
||||
.filter(r -> patientResourceName.equals(r.getType()))
|
||||
.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);
|
||||
rs.setProviders(new TypeLevelOperationProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
List<CapabilityStatementRestOperationComponent> 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().getReference()));
|
||||
validate(opDef);
|
||||
assertEquals(TypeLevelOperationProvider.OPERATION_NAME, opDef.getCode());
|
||||
assertThat(opDef.getSystem(), is(false));
|
||||
assertThat(opDef.getType(), is(true));
|
||||
assertThat(opDef.getInstance(), is(false));
|
||||
}
|
||||
|
||||
private List<String> toOperationIdParts(List<CapabilityStatementRestOperationComponent> theOperation) {
|
||||
ArrayList<String> retVal = Lists.newArrayList();
|
||||
for (CapabilityStatementRestOperationComponent next : theOperation) {
|
||||
|
@ -933,5 +1063,51 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TypeLevelOperationProvider implements IResourceProvider {
|
||||
|
||||
public static final String OPERATION_NAME = "op";
|
||||
|
||||
@Operation(name = OPERATION_NAME, idempotent = true)
|
||||
public IBundleProvider op() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class NamedQueryPlainProvider {
|
||||
|
||||
public static final String QUERY_NAME = "testQuery";
|
||||
public static final String DESCRIPTION = "A query description";
|
||||
public static final String SP_QUANTITY = "quantity";
|
||||
|
||||
@Search(queryName = QUERY_NAME)
|
||||
@Description(formalDefinition = DESCRIPTION)
|
||||
public Bundle findAllGivenParameter(@RequiredParam(name = SP_QUANTITY) QuantityParam quantity) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NamedQueryResourceProvider implements IResourceProvider {
|
||||
|
||||
public static final String QUERY_NAME = "testQuery";
|
||||
public static final String SP_PARAM = "param";
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search(queryName = QUERY_NAME)
|
||||
public Bundle findAllGivenParameter(@OptionalParam(name = SP_PARAM) StringParam param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue