Issue 430: Generate OperationDefinitions for named queries, fix minor errors in generation of OperationDefinition for operations. Closes #430.

This commit is contained in:
Stig Rohde Døssing 2018-08-23 13:14:01 +02:00 committed by James Agnew
parent 62ae71c1c6
commit f8b232bb67
5 changed files with 328 additions and 55 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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() {

View File

@ -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());
}

View File

@ -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;
}
}
}