From fc4d2ae7e2234bd6c13146c2404bc36321f4503c Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 4 Jun 2015 22:23:57 -0400 Subject: [PATCH] Work on conformance generation for operations --- .../fhir/rest/annotation/OperationParam.java | 18 +++++ .../ca/uhn/fhir/rest/method/MethodUtil.java | 6 +- .../rest/method/OperationMethodBinding.java | 72 ++++++++++++------- .../fhir/rest/method/OperationParameter.java | 49 ++++++++++--- .../method/ValidateMethodBindingDstu2.java | 2 +- .../dstu2/ServerConformanceProvider.java | 28 +++++++- .../ServerConformanceProviderDstu2Test.java | 45 ++++++------ 7 files changed, 158 insertions(+), 62 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OperationParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OperationParam.java index 506397fd2a2..ad938edbc6c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OperationParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OperationParam.java @@ -33,6 +33,11 @@ import org.hl7.fhir.instance.model.api.IBase; @Target(value=ElementType.PARAMETER) public @interface OperationParam { + /** + * Value for {@link OperationParam#max()} indicating no maximum + */ + int MAX_UNLIMITED = -1; + /** * The name of the parameter */ @@ -44,4 +49,17 @@ public @interface OperationParam { */ Class type() default IBase.class; + /** + * The minimum number of repetitions allowed for this child + */ + int min() default 0; + + /** + * The maximum number of repetitions allowed for this child. Should be + * set to {@link #MAX_UNLIMITED} if there is no limit to the number of + * repetitions. + */ + int max() default MAX_UNLIMITED; + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index 88f66f3bd6b..dcc379bf7dc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -440,12 +440,12 @@ public class MethodUtil { param = new ConditionalParamBinder(theRestfulOperationTypeEnum); } else if (nextAnnotation instanceof OperationParam) { Operation op = theMethod.getAnnotation(Operation.class); - param = new OperationParameter(op.name(), ((OperationParam) nextAnnotation).name()); + param = new OperationParameter(op.name(), ((OperationParam) nextAnnotation)); } else if (nextAnnotation instanceof Validate.Mode) { if (parameterType.equals(ValidationModeEnum.class) == false) { throw new ConfigurationException("Parameter annotated with @" + Validate.class.getSimpleName() + "." + Validate.Mode.class.getSimpleName() + " must be of type " + ValidationModeEnum.class.getName()); } - param = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE).setConverter(new IConverter() { + param = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IConverter() { @Override public Object outgoingClient(Object theObject) { return new StringDt(((ValidationModeEnum)theObject).name().toLowerCase()); @@ -460,7 +460,7 @@ public class MethodUtil { if (parameterType.equals(String.class) == false) { throw new ConfigurationException("Parameter annotated with @" + Validate.class.getSimpleName() + "." + Validate.Profile.class.getSimpleName() + " must be of type " + String.class.getName()); } - param = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_PROFILE).setConverter(new IConverter() { + param = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_PROFILE, 0, 1).setConverter(new IConverter() { @Override public Object outgoingClient(Object theObject) { return new StringDt(theObject.toString()); 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 d1bcd732de9..4ce8358b86d 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 @@ -39,6 +39,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.primitive.IdDt; @@ -55,33 +56,37 @@ import ca.uhn.fhir.util.FhirTerser; public class OperationMethodBinding extends BaseResourceReturningMethodBinding { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class); - private final boolean myHttpGetPermitted; + private String myDescription; + private final boolean myIdempotent; private final Integer myIdParamIndex; private final String myName; - private final ReturnTypeEnum myReturnType; private final OtherOperationTypeEnum myOtherOperatiopnType; + private final ReturnTypeEnum myReturnType; - public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, - Operation theAnnotation) { - this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type()); - } - - public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, - Class theOperationType) { + public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class theOperationType) { super(theReturnResourceType, theMethod, theContext, theProvider); - myHttpGetPermitted = theIdempotent; + myIdempotent = theIdempotent; myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); - + Description description = theMethod.getAnnotation(Description.class); + if (description != null) { + myDescription = description.formalDefinition(); + if (isBlank(myDescription)) { + myDescription = description.shortDefinition(); + } + } + if (isBlank(myDescription)) { + myDescription = null; + } + 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"); + throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName() + " but this annotation has no name defined"); } if (theOperationName.startsWith("$") == false) { theOperationName = "$" + theOperationName; } myName = theOperationName; - + if (theContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU1)) { throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name()); } @@ -97,16 +102,15 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { } if (theMethod.getReturnType().isAssignableFrom(Bundle.class)) { - throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type " - + theMethod.getDeclaringClass().getName()); + throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type " + theMethod.getDeclaringClass().getName()); } - + if (theMethod.getReturnType().equals(IBundleProvider.class)) { myReturnType = ReturnTypeEnum.BUNDLE; } else { myReturnType = ReturnTypeEnum.RESOURCE; } - + if (getResourceName() == null) { myOtherOperatiopnType = OtherOperationTypeEnum.EXTENDED_OPERATION_SERVER; } else if (myIdParamIndex == null) { @@ -116,6 +120,18 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { } } + public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) { + this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type()); + } + + public String getDescription() { + return myDescription; + } + + public String getName() { + return myName; + } + @Override public OtherOperationTypeEnum getOtherOperationType() { return myOtherOperatiopnType; @@ -183,17 +199,16 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { if (theRequest.getRequestType() == RequestTypeEnum.POST) { // always ok } else if (theRequest.getRequestType() == RequestTypeEnum.GET) { - if (!myHttpGetPermitted) { + if (!myIdempotent) { String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); throw new MethodNotAllowedException(message, RequestTypeEnum.POST); } } else { - if (!myHttpGetPermitted) { + if (!myIdempotent) { String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); throw new MethodNotAllowedException(message, RequestTypeEnum.POST); } else { - String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), - RequestTypeEnum.POST.name()); + String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name()); throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST); } } @@ -207,8 +222,15 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { return retVal; } - public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, - boolean theUseHttpGet) { + public boolean isIdempotent() { + return myIdempotent; + } + + public boolean isInstanceLevel() { + return myIdParamIndex != null; + } + + public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) { StringBuilder b = new StringBuilder(); if (theResourceName != null) { b.append(theResourceName); @@ -242,7 +264,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { if (!params.containsKey(nextName)) { params.put(nextName, new ArrayList()); } - + IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]"); if (value == null) { continue; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java index e5b96b776d3..2b28b677f5b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java @@ -40,7 +40,9 @@ import ca.uhn.fhir.context.RuntimeChildPrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.HapiLocalizer; +import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.param.CollectionBinder; import ca.uhn.fhir.rest.param.ResourceParameter; @@ -48,18 +50,27 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -class OperationParameter implements IParameter { +public class OperationParameter implements IParameter { private IConverter myConverter; @SuppressWarnings("rawtypes") private Class myInnerCollectionType; + private int myMax; + private int myMin; private final String myName; private final String myOperationName; private Class myParameterType; + private RestSearchParameterTypeEnum myParamType; - OperationParameter(String theOperationName, String theParameterName) { + public OperationParameter(String theOperationName, OperationParam theOperationParam) { + this(theOperationName, theOperationParam.name(), theOperationParam.min(), theOperationParam.max()); + } + + OperationParameter(String theOperationName, String theParameterName, int theMin, int theMax) { myOperationName = theOperationName; myName = theParameterName; + myMin = theMin; + myMax = theMax; } private void addClientParameter(FhirContext theContext, Object theSourceClientArgument, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition paramChildElem) { @@ -78,7 +89,7 @@ class OperationParameter implements IParameter { throw new IllegalArgumentException("Don't know how to handle value of type " + theSourceClientArgument.getClass() + " for paramater " + myName); } } - + private IBase createParameterRepetition(FhirContext theContext, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition paramChildElem) { IBase parameter = paramChildElem.newInstance(); paramChild.getMutator().addValue(theTargetResource, parameter); @@ -92,11 +103,29 @@ class OperationParameter implements IParameter { return parameter; } + public int getMax() { + return myMax; + } + + public int getMin() { + return myMin; + } + + public String getName() { + return myName; + } + + public RestSearchParameterTypeEnum getParamType() { + return myParamType; + } + @Override public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { myParameterType = theParameterType; if (theInnerCollectionType != null) { myInnerCollectionType = CollectionBinder.getInstantiableCollectionType(theInnerCollectionType, myName); + } else { + myMax = 1; } } @@ -106,8 +135,7 @@ class OperationParameter implements IParameter { } @Override - public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) - throws InternalErrorException { + public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException { assert theTargetResource != null; Object sourceClientArgument = theSourceClientArgument; if (sourceClientArgument == null) { @@ -117,7 +145,7 @@ class OperationParameter implements IParameter { if (myConverter != null) { sourceClientArgument = myConverter.outgoingClient(sourceClientArgument); } - + RuntimeResourceDefinition def = theContext.getResourceDefinition(theTargetResource); BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); @@ -151,7 +179,7 @@ class OperationParameter implements IParameter { } else { FhirContext ctx = theRequest.getServer().getFhirContext(); - + if (theRequest.getRequestType() == RequestTypeEnum.GET) { return null; } @@ -223,19 +251,18 @@ class OperationParameter implements IParameter { nextValue = myConverter.incomingServer(nextValue); } if (!myParameterType.isAssignableFrom(nextValue.getClass())) { - throw new InvalidRequestException("Request has parameter " + myName + " of type " + nextValue.getClass().getSimpleName() + " but method expects type " - + myParameterType.getSimpleName()); + throw new InvalidRequestException("Request has parameter " + myName + " of type " + nextValue.getClass().getSimpleName() + " but method expects type " + myParameterType.getSimpleName()); } theMatchingParamValues.add(nextValue); } } public interface IConverter { - + Object incomingServer(Object theObject); Object outgoingClient(Object theObject); } - + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java index d44cc7507cf..87a77eaf75e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java @@ -27,7 +27,7 @@ public class ValidateMethodBindingDstu2 extends OperationMethodBinding { if (String.class.equals(parameterType) || EncodingEnum.class.equals(parameterType)) { newParams.add(next); } else { - OperationParameter parameter = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_RESOURCE); + OperationParameter parameter = new OperationParameter(Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_RESOURCE, 0, 1); parameter.initializeTypes(theMethod, null, null, parameterType); newParams.add(parameter); } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index ec1f8aaea0f..d90238a210d 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -41,6 +41,9 @@ import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceInteraction; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam; +import ca.uhn.fhir.model.dstu2.resource.OperationDefinition.Parameter; +import ca.uhn.fhir.model.dstu2.valueset.ConformanceResourceStatusEnum; +import ca.uhn.fhir.model.dstu2.valueset.OperationParameterUseEnum; import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.RestfulConformanceModeEnum; import ca.uhn.fhir.model.dstu2.valueset.SystemRestfulInteractionEnum; @@ -48,10 +51,12 @@ import ca.uhn.fhir.model.dstu2.valueset.TypeRestfulInteractionEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding; import ca.uhn.fhir.rest.method.IParameter; import ca.uhn.fhir.rest.method.OperationMethodBinding; +import ca.uhn.fhir.rest.method.OperationParameter; import ca.uhn.fhir.rest.method.SearchMethodBinding; import ca.uhn.fhir.rest.method.SearchParameter; import ca.uhn.fhir.rest.server.Constants; @@ -171,7 +176,28 @@ public class ServerConformanceProvider implements IServerConformanceProvider() { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java index a71a6e89603..a4e23c221fd 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java @@ -26,16 +26,19 @@ import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.SearchMethodBinding; import ca.uhn.fhir.rest.method.SearchParameter; +import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider; @@ -78,25 +81,6 @@ public class ServerConformanceProviderDstu2Test { } - @Test - public void testEverythingOperationDocumentation() throws Exception { - - RestfulServer rs = new RestfulServer(); - rs.setProviders(new ProviderWithOperations()); - - ServerConformanceProvider sc = new ServerConformanceProvider(rs); - rs.setServerConformanceProvider(sc); - - rs.init(createServletConfig()); - - Conformance conformance = sc.getServerConformance(createHttpServletRequest()); - - String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); - ourLog.info(conf); - - - } - @Test public void testOperationDocumentation() throws Exception { @@ -130,6 +114,23 @@ public class ServerConformanceProviderDstu2Test { } + @Test + public void testExtendedOperationReturningBundle() throws Exception { + + RestfulServer rs = new RestfulServer(); + rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); + + ServerConformanceProvider sc = new ServerConformanceProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + Conformance conformance = sc.getServerConformance(createHttpServletRequest()); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + } @Test public void testValidateGeneratedStatement() throws Exception { @@ -260,12 +261,14 @@ public class ServerConformanceProviderDstu2Test { } - public static class ProviderWithOperations implements IResourceProvider { + public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name="everything", idempotent=true) public ca.uhn.fhir.rest.server.IBundleProvider everything( javax.servlet.http.HttpServletRequest theServletRequest, - @IdParam ca.uhn.fhir.model.primitive.IdDt theId) { + @IdParam ca.uhn.fhir.model.primitive.IdDt theId, + @OperationParam(name="start") DateDt theStart, + @OperationParam(name="end") DateDt theEnd) { return null; }