Skip to content

Commit

Permalink
Rename classes. Add annotations to caregaps and multimeasures. Start …
Browse files Browse the repository at this point in the history
…migrating OperationParameter and OperationParam to have parity with EmbeddedOperationParameter and EmbeddedOperationParam.
  • Loading branch information
lukedegruchy committed Jan 23, 2025
1 parent 69e1411 commit 365b746
Show file tree
Hide file tree
Showing 16 changed files with 179 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2025 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.rest.annotation;

import java.lang.annotation.ElementType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
* @return The range type associated with any type conversion. For instance, if we expect a start and end date.
* NOT_APPLICABLE is the default and indicates range conversion is not applicable.
*/
EmbeddedParameterRangeType rangeType() default EmbeddedParameterRangeType.NOT_APPLICABLE;
OperationParameterRangeType rangeType() default OperationParameterRangeType.NOT_APPLICABLE;

/**
* Optionally specifies the type of the parameter as a string, such as <code>Coding</code> or
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2025 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.rest.annotation;

import java.lang.annotation.ElementType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@
*/
Class<? extends IBase> type() default IBase.class;

/**
* The source type of the parameters if we're expecting to do a type conversion, such as String to ZonedDateTime.
* Void indicates that we don't want to do a type conversion.
*
* @return the source type of the parameter
*/
Class<?> sourceType() default Void.class;

/**
* @return The range type associated with any type conversion. For instance, if we expect a start and end date.
* NOT_APPLICABLE is the default and indicates range conversion is not applicable.
*/
OperationParameterRangeType rangeType() default OperationParameterRangeType.NOT_APPLICABLE;

/**
* Optionally specifies the type of the parameter as a string, such as <code>Coding</code> or
* <code>base64Binary</code>. This can be useful if you want to use a generic interface type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* Used to indicate whether an {@link EmbeddedOperationParam} should be considered as part of a range of values, and if
* so whether it's the start or end of the range.
*/
public enum EmbeddedParameterRangeType {
public enum OperationParameterRangeType {
START,
END,
NOT_APPLICABLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.annotation.EmbeddedOperationParam;
import ca.uhn.fhir.rest.annotation.EmbeddedParameterRangeType;
import ca.uhn.fhir.rest.annotation.OperationParameterRangeType;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
Expand Down Expand Up @@ -246,7 +246,7 @@ private Object convertParamIfNeeded(

final EmbeddedOperationParam embeddedParamAtIndex = (EmbeddedOperationParam) annotation;
final Class<?> paramClassAtIndex = paramAtIndex.getClass();
final EmbeddedParameterRangeType rangeType = embeddedParamAtIndex.rangeType();
final OperationParameterRangeType rangeType = embeddedParamAtIndex.rangeType();
final Parameter constructorParameter = theConstructorParameters[theIndex];
final Class<?> constructorParameterType = constructorParameter.getType();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.annotation.EmbeddedOperationParam;
import ca.uhn.fhir.rest.annotation.EmbeddedParameterRangeType;
import ca.uhn.fhir.rest.annotation.OperationParameterRangeType;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.QualifiedParamList;
Expand All @@ -54,7 +54,6 @@
import java.util.function.Consumer;
import java.util.*;

import static ca.uhn.fhir.rest.server.method.OperationParameter.REQUEST_CONTENTS_USERDATA_KEY;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

// LUKETODO: consider deleting whatever code may be unused
Expand Down Expand Up @@ -90,7 +89,7 @@ public class EmbeddedOperationParameter implements IParameter {
private final List<String> myExampleValues;
private final Class<?> mySourceType;
// LUKETODO: just pass the whole thing?
private final EmbeddedParameterRangeType myRengeType;
private final OperationParameterRangeType myRengeType;

EmbeddedOperationParameter(
FhirContext theCtx,
Expand All @@ -101,7 +100,7 @@ public class EmbeddedOperationParameter implements IParameter {
String theDescription,
List<String> theExampleValues,
Class<?> theSourceType,
EmbeddedParameterRangeType theRengeType) {
OperationParameterRangeType theRangeType) {
myOperationName = theOperationName;
myName = theParameterName;
myMin = theMin;
Expand All @@ -114,7 +113,7 @@ public class EmbeddedOperationParameter implements IParameter {
} else {
mySourceType = theSourceType;
}
myRengeType = theRengeType;
myRengeType = theRangeType;

List<String> exampleValues = new ArrayList<>();
if (theExampleValues != null) {
Expand Down Expand Up @@ -180,6 +179,11 @@ public Class<?> getSourceType() {
return mySourceType;
}

@VisibleForTesting
public OperationParameterRangeType getRangeType() {
return myRengeType;
}

@SuppressWarnings("unchecked")
@Override
public void initializeTypes(
Expand Down Expand Up @@ -262,7 +266,7 @@ public void initializeTypes(
// LUKETODO: test the rangeType NOT_APPLICABLE scenario
final String error = String.format(
"%sInvalid type for @OperationEmbeddedParam on method: %s with sourceType: %s, parameterType: %s, and rangeType: %s",
Msg.code(999991), theMethod.getName(), mySourceType, myParameterType, myRengeType);
Msg.code(999991), theMethod.getName(), mySourceType, myParameterType.getName(), myRengeType);
throw new ConfigurationException(error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.annotation.EmbeddedOperationParam;
import ca.uhn.fhir.rest.annotation.EmbeddedParameterRangeType;
import ca.uhn.fhir.rest.annotation.OperationParameterRangeType;
import ca.uhn.fhir.util.ReflectionUtil;
import jakarta.annotation.Nonnull;

Expand Down Expand Up @@ -118,9 +118,9 @@ private static Boolean isValidSourceTypeConversion(

if (annotation instanceof EmbeddedOperationParam) {
final EmbeddedOperationParam embeddedOperationParam = (EmbeddedOperationParam) annotation;
final EmbeddedParameterRangeType embeddedParameterRangeType = embeddedOperationParam.rangeType();
final OperationParameterRangeType operationParameterRangeType = embeddedOperationParam.rangeType();

if (isValidSourceTypeConversion(methodParamClass, constructorParamType, embeddedParameterRangeType)) {
if (isValidSourceTypeConversion(methodParamClass, constructorParamType, operationParameterRangeType)) {
return true;
}
}
Expand All @@ -134,14 +134,14 @@ private static Boolean isValidSourceTypeConversion(
*
* @param theSourceType The source type for the class, which can be different from the declared type
* @param theTargetType The target type for the class, which can be different from the source type
* @param theEmbeddedParameterRangeType Whether the embedded parameter is a range and if so, start or end
* @param theOperationParameterRangeType Whether the embedded parameter is a range and if so, start or end
* @return true if the type conversion is supported
*/
static boolean isValidSourceTypeConversion(
Class<?> theSourceType, Class<?> theTargetType, EmbeddedParameterRangeType theEmbeddedParameterRangeType) {
Class<?> theSourceType, Class<?> theTargetType, OperationParameterRangeType theOperationParameterRangeType) {
return String.class == theSourceType
&& ZonedDateTime.class == theTargetType
&& EmbeddedParameterRangeType.NOT_APPLICABLE != theEmbeddedParameterRangeType;
&& OperationParameterRangeType.NOT_APPLICABLE != theOperationParameterRangeType;
}

private static void validateConstructorArgs(Constructor<?> theConstructor, Field[] theDeclaredFields) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import ca.uhn.fhir.rest.annotation.Offset;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.OperationParameterRangeType;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Patch;
import ca.uhn.fhir.rest.annotation.RawParam;
Expand Down Expand Up @@ -200,54 +201,6 @@ public static List<IParameter> getResourceParameters(
param = new SearchTotalModeParameter();
} else {
final Operation op = methodToUse.getAnnotation(Operation.class);
// LUKETODO: delete this after all existing providers have migrated.
// There are no annotations on this parameter, so we check to see if the parameter class has fields
// annotated OperationEmbeddedParam
if (nextParameterAnnotations.length == 0) {
final List<Class<?>> operationEmbeddedTypes =
ReflectionUtil.getMethodParamsWithClassesWithFieldsWithAnnotation(
methodToUse, EmbeddedOperationParam.class);

if (op == null) {
throw new ConfigurationException(Msg.code(846192641)
+ "@OperationParam or OperationEmbeddedParam detected on method that is not annotated with @Operation: "
+ methodToUse.toGenericString());
}

if (operationEmbeddedTypes.size() > 1) {
throw new ConfigurationException(String.format(
"%sOnly one type with embedded params is supported for now for method: %s",
Msg.code(9999927), methodToUse.getName()));
}

if (!operationEmbeddedTypes.isEmpty()) {
final EmbeddedParameterConverter embeddedParameterConverter = new EmbeddedParameterConverter(
theContext, theMethod, op, operationEmbeddedTypes.get(0));

final List<EmbeddedParameterConverterContext> outerContexts =
embeddedParameterConverter.convert();

for (EmbeddedParameterConverterContext outerContext : outerContexts) {
if (outerContext.getParameter() != null) {
parameters.add(outerContext.getParameter());
}
final ParamInitializationContext paramContext = outerContext.getParamContext();

if (paramContext != null) {
paramContexts.add(paramContext);

// N.B. This a hack used only to pass the null check below, which is crucial to the
// non-embedded params logic
param = paramContext.getParam();
}
}
} else {
// More than likely this will result in the param == null Exception below
ourLog.warn(
"Method '{}' has no parameters with annotations. Don't know how to handle this parameter",
methodToUse.getName());
}
} // else there are no embedded params and let execution fall to the for loop below

for (int i = 0; i < nextParameterAnnotations.length && param == null; i++) {
Annotation nextAnnotation = nextParameterAnnotations[i];
Expand Down Expand Up @@ -375,7 +328,9 @@ public static List<IParameter> getResourceParameters(
operationParam.min(),
operationParam.max(),
description,
examples);
examples,
operationParam.sourceType(),
operationParam.rangeType());
if (isNotBlank(operationParam.typeName())) {
BaseRuntimeElementDefinition<?> elementDefinition =
theContext.getElementDefinition(operationParam.typeName());
Expand Down Expand Up @@ -455,7 +410,9 @@ public static List<IParameter> getResourceParameters(
0,
1,
description,
examples)
examples,
Void.class,
OperationParameterRangeType.NOT_APPLICABLE)
.setConverter(new IOperationParamConverter() {
@Override
public Object incomingServer(Object theObject) {
Expand Down Expand Up @@ -492,7 +449,9 @@ public Object outgoingClient(Object theObject) {
0,
1,
description,
examples)
examples,
Void.class,
OperationParameterRangeType.NOT_APPLICABLE)
.setConverter(new IOperationParamConverter() {
@Override
public Object incomingServer(Object theObject) {
Expand Down
Loading

0 comments on commit 365b746

Please sign in to comment.