diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java
index c6068e314ee..fcd4b575262 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java
@@ -28,5 +28,11 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface IdParam {
- // just a marker
+
+ /**
+ * For {@link Operation extended operations}, any parameter with this value set to true
+ * (default is false) will also be invoked if the operation is invoked against the resource type.
+ */
+ boolean optional() default false;
+
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
index f0e13e587aa..5b95a4e5c77 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
@@ -50,11 +50,13 @@ 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 ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.context.IRuntimeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -1496,8 +1498,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
parameterElem.getChildByName("name").getMutator().setValue(parameter, name);
if (theValue instanceof IBaseDatatype) {
- String childElementName = "value" + StringUtils.capitalize(myContext.getElementDefinition(theValue.getClass()).getName());
- parameterElem.getChildByName(childElementName).getMutator().setValue(parameter, theValue);
+ BaseRuntimeElementDefinition> datatypeDef = myContext.getElementDefinition(theValue.getClass());
+ if (datatypeDef instanceof IRuntimeDatatypeDefinition) {
+ Class extends IBaseDatatype> profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf();
+ if (profileOf != null) {
+ datatypeDef = myContext.getElementDefinition(profileOf);
+ }
+ }
+ String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName());
+ BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName);
+ childByName.getMutator().setValue(parameter, theValue);
} else if (theValue instanceof IBaseResource) {
parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue);
} else {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java
index 153c1cead64..5a3ba9f65d0 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java
@@ -22,11 +22,12 @@ package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -43,6 +44,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
+import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@@ -67,6 +69,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
private final RestOperationTypeEnum myOtherOperatiopnType;
private List myReturnParams;
private final ReturnTypeEnum myReturnType;
+ private boolean myCanOperateAtTypeLevel;
protected OperationMethodBinding(Class> theReturnResourceType, Class extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class extends IBaseResource> theOperationType,
OperationParam[] theReturnParams) {
@@ -74,6 +77,16 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
myIdempotent = theIdempotent;
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
+ 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) {
myDescription = description.formalDefinition();
@@ -195,12 +208,22 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
return false;
}
- boolean requestHasId = theRequest.getId() != null;
- if (requestHasId != (myIdParamIndex != null)) {
+ if (!myName.equals(theRequest.getOperation())) {
return false;
}
+
+ boolean requestHasId = theRequest.getId() != null;
+ if (requestHasId) {
+ if (isCanOperateAtInstanceLevel() == false) {
+ return false;
+ }
+ } else {
+ if (myCanOperateAtTypeLevel == false) {
+ return false;
+ }
+ }
- return myName.equals(theRequest.getOperation());
+ return true;
}
@Override
@@ -289,7 +312,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
FhirTerser t = theContext.newTerser();
List