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 (returnTypeFromRp != null) {
|
||||||
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
|
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
|
||||||
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
|
if (returnTypeFromMethod != null && !returnTypeFromRp.isAssignableFrom(returnTypeFromMethod)) {
|
||||||
//FIXME potential null access on retunrTypeFromMethod
|
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
|
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");
|
+ 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) {
|
if (read != null) {
|
||||||
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
|
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
|
||||||
} else if (search != null) {
|
} else if (search != null) {
|
||||||
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
|
return new SearchMethodBinding(returnType, returnTypeFromRp, theMethod, theContext, theProvider);
|
||||||
} else if (conformance != null) {
|
} else if (conformance != null) {
|
||||||
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
||||||
} else if (create != null) {
|
} else if (create != null) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
private final boolean myIdempotent;
|
private final boolean myIdempotent;
|
||||||
private final Integer myIdParamIndex;
|
private final Integer myIdParamIndex;
|
||||||
private final String myName;
|
private final String myName;
|
||||||
private final RestOperationTypeEnum myOtherOperatiopnType;
|
private final RestOperationTypeEnum myOtherOperationType;
|
||||||
private final ReturnTypeEnum myReturnType;
|
private final ReturnTypeEnum myReturnType;
|
||||||
private BundleTypeEnum myBundleType;
|
private BundleTypeEnum myBundleType;
|
||||||
private boolean myCanOperateAtInstanceLevel;
|
private boolean myCanOperateAtInstanceLevel;
|
||||||
|
@ -75,16 +75,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
myBundleType = theBundleType;
|
myBundleType = theBundleType;
|
||||||
myIdempotent = theIdempotent;
|
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);
|
Description description = theMethod.getAnnotation(Description.class);
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
|
@ -108,13 +98,11 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
if (theReturnTypeFromRp != null) {
|
if (theReturnTypeFromRp != null) {
|
||||||
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
|
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
|
||||||
} else {
|
} else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
|
||||||
if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
|
|
||||||
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
|
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
|
||||||
} else {
|
} else {
|
||||||
setResourceName(null);
|
setResourceName(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (theMethod.getReturnType().equals(IBundleProvider.class)) {
|
if (theMethod.getReturnType().equals(IBundleProvider.class)) {
|
||||||
myReturnType = ReturnTypeEnum.BUNDLE;
|
myReturnType = ReturnTypeEnum.BUNDLE;
|
||||||
|
@ -122,12 +110,24 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
myReturnType = ReturnTypeEnum.RESOURCE;
|
myReturnType = ReturnTypeEnum.RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
|
||||||
if (getResourceName() == null) {
|
if (getResourceName() == null) {
|
||||||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER;
|
myOtherOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER;
|
||||||
|
myCanOperateAtServerLevel = true;
|
||||||
|
if (myIdParamIndex != null) {
|
||||||
|
myCanOperateAtInstanceLevel = true;
|
||||||
|
}
|
||||||
} else if (myIdParamIndex == null) {
|
} else if (myIdParamIndex == null) {
|
||||||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
|
myOtherOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
|
||||||
|
myCanOperateAtTypeLevel = true;
|
||||||
} else {
|
} 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<>();
|
myReturnParams = new ArrayList<>();
|
||||||
|
@ -149,14 +149,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
myReturnParams.add(type);
|
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,
|
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
|
||||||
|
@ -188,7 +180,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public RestOperationTypeEnum getRestOperationType() {
|
public RestOperationTypeEnum getRestOperationType() {
|
||||||
return myOtherOperatiopnType;
|
return myOtherOperationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ReturnType> getReturnParams() {
|
public List<ReturnType> getReturnParams() {
|
||||||
|
@ -230,16 +222,12 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
boolean requestHasId = theRequest.getId() != null;
|
boolean requestHasId = theRequest.getId() != null;
|
||||||
if (requestHasId) {
|
if (requestHasId) {
|
||||||
if (isCanOperateAtInstanceLevel() == false) {
|
return myCanOperateAtInstanceLevel;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
if (isNotBlank(theRequest.getResourceName())) {
|
||||||
if (myCanOperateAtTypeLevel == false) {
|
return myCanOperateAtTypeLevel;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
return myCanOperateAtServerLevel;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
private Integer myIdParamIndex;
|
private Integer myIdParamIndex;
|
||||||
private String myQueryName;
|
private String myQueryName;
|
||||||
private boolean myAllowUnknownParams;
|
private boolean myAllowUnknownParams;
|
||||||
|
private final String myResourceProviderResourceName;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HashSet<String> specialSearchParams = new HashSet<>();
|
HashSet<String> specialSearchParams = new HashSet<>();
|
||||||
|
@ -64,7 +65,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
SPECIAL_SEARCH_PARAMS = Collections.unmodifiableSet(specialSearchParams);
|
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);
|
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||||
Search search = theMethod.getAnnotation(Search.class);
|
Search search = theMethod.getAnnotation(Search.class);
|
||||||
this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null);
|
this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null);
|
||||||
|
@ -89,12 +90,26 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
throw new ConfigurationException(msg);
|
throw new ConfigurationException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theResourceProviderResourceType != null) {
|
||||||
|
this.myResourceProviderResourceName = theContext.getResourceDefinition(theResourceProviderResourceType).getName();
|
||||||
|
} else {
|
||||||
|
this.myResourceProviderResourceName = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return myDescription;
|
return myDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getQueryName() {
|
||||||
|
return myQueryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResourceProviderResourceName() {
|
||||||
|
return myResourceProviderResourceName;
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public RestOperationTypeEnum getRestOperationType() {
|
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 static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
|
||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile CapabilityStatement myCapabilityStatement;
|
private volatile CapabilityStatement myCapabilityStatement;
|
||||||
|
private IdentityHashMap<SearchMethodBinding, String> myNamedSearchMethodBindingToName;
|
||||||
|
private HashMap<String, List<SearchMethodBinding>> mySearchNameToBindings;
|
||||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||||
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
||||||
private String myPublisher = "Not provided";
|
private String myPublisher = "Not provided";
|
||||||
|
@ -151,6 +153,17 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
return DateTimeType.now();
|
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) {
|
private String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||||
StringBuilder retVal = new StringBuilder();
|
StringBuilder retVal = new StringBuilder();
|
||||||
if (theMethodBinding.getResourceName() != null) {
|
if (theMethodBinding.getResourceName() != null) {
|
||||||
|
@ -297,7 +310,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
|
||||||
|
|
||||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
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) {
|
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||||
String opName = myOperationBindingToName.get(methodBinding);
|
String opName = myOperationBindingToName.get(methodBinding);
|
||||||
|
@ -349,7 +370,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
return retVal;
|
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) {
|
SearchMethodBinding searchMethodBinding) {
|
||||||
includes.addAll(searchMethodBinding.getIncludes());
|
includes.addAll(searchMethodBinding.getIncludes());
|
||||||
|
|
||||||
|
@ -438,8 +459,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
|
|
||||||
@Initialize
|
@Initialize
|
||||||
public void initializeOperations() {
|
public void initializeOperations() {
|
||||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
myNamedSearchMethodBindingToName = new IdentityHashMap<>();
|
||||||
myOperationNameToBindings = new HashMap<String, List<OperationMethodBinding>>();
|
mySearchNameToBindings = new HashMap<>();
|
||||||
|
myOperationBindingToName = new IdentityHashMap<>();
|
||||||
|
myOperationNameToBindings = new HashMap<>();
|
||||||
|
|
||||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||||
|
@ -456,9 +479,23 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
|
|
||||||
myOperationBindingToName.put(methodBinding, name);
|
myOperationBindingToName.put(methodBinding, name);
|
||||||
if (myOperationNameToBindings.containsKey(name) == false) {
|
if (myOperationNameToBindings.containsKey(name) == false) {
|
||||||
myOperationNameToBindings.put(name, new ArrayList<OperationMethodBinding>());
|
myOperationNameToBindings.put(name, new ArrayList<>());
|
||||||
}
|
}
|
||||||
myOperationNameToBindings.get(name).add(methodBinding);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,11 +506,69 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
if (theId == null || theId.hasIdPart() == false) {
|
if (theId == null || theId.hasIdPart() == false) {
|
||||||
throw new ResourceNotFoundException(theId);
|
throw new ResourceNotFoundException(theId);
|
||||||
}
|
}
|
||||||
List<OperationMethodBinding> sharedDescriptions = myOperationNameToBindings.get(theId.getIdPart());
|
List<OperationMethodBinding> operationBindings = myOperationNameToBindings.get(theId.getIdPart());
|
||||||
if (sharedDescriptions == null || sharedDescriptions.isEmpty()) {
|
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);
|
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();
|
OperationDefinition op = new OperationDefinition();
|
||||||
op.setStatus(PublicationStatus.ACTIVE);
|
op.setStatus(PublicationStatus.ACTIVE);
|
||||||
op.setKind(OperationKind.OPERATION);
|
op.setKind(OperationKind.OPERATION);
|
||||||
|
@ -484,10 +579,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
|
||||||
op.setType(false);
|
op.setType(false);
|
||||||
op.setInstance(false);
|
op.setInstance(false);
|
||||||
|
|
||||||
Set<String> inParams = new HashSet<String>();
|
Set<String> inParams = new HashSet<>();
|
||||||
Set<String> outParams = new HashSet<String>();
|
Set<String> outParams = new HashSet<>();
|
||||||
|
|
||||||
for (OperationMethodBinding sharedDescription : sharedDescriptions) {
|
for (OperationMethodBinding sharedDescription : bindings) {
|
||||||
if (isNotBlank(sharedDescription.getDescription())) {
|
if (isNotBlank(sharedDescription.getDescription())) {
|
||||||
op.setDescription(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.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.rest.annotation.*;
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
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.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
import ca.uhn.fhir.rest.server.*;
|
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.util.TestUtil;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
import ca.uhn.fhir.validation.ValidationResult;
|
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 {
|
public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
|
|
||||||
|
@ -124,6 +133,9 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"));
|
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"));
|
||||||
validate(opDef);
|
validate(opDef);
|
||||||
assertEquals("everything", opDef.getCode());
|
assertEquals("everything", opDef.getCode());
|
||||||
|
assertThat(opDef.getSystem(), is(false));
|
||||||
|
assertThat(opDef.getType(), is(false));
|
||||||
|
assertThat(opDef.getInstance(), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -344,6 +356,10 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
assertEquals("1", opDef.getParameter().get(2).getMinElement().getValueAsString());
|
assertEquals("1", opDef.getParameter().get(2).getMinElement().getValueAsString());
|
||||||
assertEquals("2", opDef.getParameter().get(2).getMaxElement().getValueAsString());
|
assertEquals("2", opDef.getParameter().get(2).getMaxElement().getValueAsString());
|
||||||
assertEquals("string", opDef.getParameter().get(2).getTypeElement().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
|
@Test
|
||||||
|
@ -622,6 +638,120 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
assertTrue(result.getMessages().toString(), result.isSuccessful());
|
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) {
|
private List<String> toOperationIdParts(List<CapabilityStatementRestOperationComponent> theOperation) {
|
||||||
ArrayList<String> retVal = Lists.newArrayList();
|
ArrayList<String> retVal = Lists.newArrayList();
|
||||||
for (CapabilityStatementRestOperationComponent next : theOperation) {
|
for (CapabilityStatementRestOperationComponent next : theOperation) {
|
||||||
|
@ -934,4 +1064,50 @@ 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