Skip to content

Commit

Permalink
Rename common method reflection test class. Add all MethodUtilTest me…
Browse files Browse the repository at this point in the history
…thods from the methodutil branch. Branch out MethodUtil generics context into an outer class.
  • Loading branch information
lukedegruchy committed Jan 28, 2025
1 parent c548b2c commit 2fba720
Show file tree
Hide file tree
Showing 6 changed files with 869 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public static void extractDescription(SearchParameter theParameter, Annotation[]
}
}

// LUKETODO: extract annotations method and make sure it works with embedded params
public static List<IParameter> getResourceParameters(
final FhirContext theContext, final Method theMethod, Object theProvider) {
// We mutate this variable so distinguish this from the argument to getResourceParameters
Expand All @@ -124,7 +123,7 @@ public static List<IParameter> getResourceParameters(
// TagList is handled directly within the method bindings
param = new NullParameter();
} else {
final GenericsContext genericsContext =
final MethodUtilGenericsContext genericsContext =
getGenericsContext(theContext, theMethod, parameterTypes, paramIndex);

parameterType = genericsContext.getParameterType();
Expand Down Expand Up @@ -565,7 +564,7 @@ private static ParamInitializationContext createOperationParamContext(
param, parameterTypeInner, theOuterCollectionType, theInnerCollectionType);
}

private static GenericsContext getGenericsContext(
private static MethodUtilGenericsContext getGenericsContext(
FhirContext theContext, Method theMethod, Class<?>[] theParameterTypes, int theParamIndex) {

Class<?> declaredParameterType = theParameterTypes[theParamIndex];
Expand Down Expand Up @@ -628,67 +627,12 @@ private static GenericsContext getGenericsContext(
}
}

return new GenericsContext(parameterType, declaredParameterType, outerCollectionType, innerCollectionType);
return new MethodUtilGenericsContext(
parameterType, declaredParameterType, outerCollectionType, innerCollectionType);
}

@SuppressWarnings("unchecked")
private static <T> T unsafeCast(Object theObject) {
return (T) theObject;
}

// LUKETODO: top level?
private static class GenericsContext {
private final Class<?> parameterType;
private final Class<?> declaredParameterType;
private final Class<? extends java.util.Collection<?>> outerCollectionType;
private final Class<? extends java.util.Collection<?>> innerCollectionType;

public GenericsContext(
Class<?> theParameterType,
Class<?> theDeclaredParameterType,
Class<? extends Collection<?>> theOuterCollectionType,
Class<? extends Collection<?>> theInnerCollectionType) {
parameterType = theParameterType;
declaredParameterType = theDeclaredParameterType;
outerCollectionType = theOuterCollectionType;
innerCollectionType = theInnerCollectionType;
}

public Class<?> getParameterType() {
return parameterType;
}

public Class<?> getDeclaredParameterType() {
return declaredParameterType;
}

public Class<? extends Collection<?>> getOuterCollectionType() {
return outerCollectionType;
}

public Class<? extends Collection<?>> getInnerCollectionType() {
return innerCollectionType;
}
}

// LUKETODO: refactor to use only one of the Context classes
private static class ParameterContext {
private final Class<?> myParameterType;

@Nullable
private final IParameter myParameter;

public ParameterContext(Class<?> theParameterType, @Nullable IParameter theParameter) {
myParameter = theParameter;
myParameterType = theParameterType;
}

public IParameter getParam() {
return myParameter;
}

public Class<?> getParameterType() {
return myParameterType;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* 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.server.method;

import java.util.Collection;
import java.util.Objects;
import java.util.StringJoiner;

/**
* Simple POJO to capture details of MethodUtil generic type information for method params, if there is any.
*/
class MethodUtilGenericsContext {
private final Class<?> parameterType;
private final Class<?> declaredParameterType;
private final Class<? extends java.util.Collection<?>> outerCollectionType;
private final Class<? extends java.util.Collection<?>> innerCollectionType;

public MethodUtilGenericsContext(
Class<?> theParameterType,
Class<?> theDeclaredParameterType,
Class<? extends Collection<?>> theOuterCollectionType,
Class<? extends Collection<?>> theInnerCollectionType) {
parameterType = theParameterType;
declaredParameterType = theDeclaredParameterType;
outerCollectionType = theOuterCollectionType;
innerCollectionType = theInnerCollectionType;
}

public Class<?> getParameterType() {
return parameterType;
}

public Class<?> getDeclaredParameterType() {
return declaredParameterType;
}

public Class<? extends Collection<?>> getOuterCollectionType() {
return outerCollectionType;
}

public Class<? extends Collection<?>> getInnerCollectionType() {
return innerCollectionType;
}

@Override
public boolean equals(Object theO) {
if (theO == null || getClass() != theO.getClass()) {
return false;
}
MethodUtilGenericsContext that = (MethodUtilGenericsContext) theO;
return Objects.equals(parameterType, that.parameterType) && Objects.equals(declaredParameterType, that.declaredParameterType) && Objects.equals(outerCollectionType, that.outerCollectionType) && Objects.equals(innerCollectionType, that.innerCollectionType);
}

@Override
public int hashCode() {
return Objects.hash(parameterType, declaredParameterType, outerCollectionType, innerCollectionType);
}

@Override
public String toString() {
return new StringJoiner(", ", MethodUtilGenericsContext.class.getSimpleName() + "[", "]")
.add("parameterType=" + parameterType)
.add("declaredParameterType=" + declaredParameterType)
.add("outerCollectionType=" + outerCollectionType)
.add("innerCollectionType=" + innerCollectionType)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SampleParams;
import ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SampleParams;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.IdType;
Expand All @@ -18,16 +18,16 @@
import java.time.ZoneOffset;
import java.util.List;

import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.ParamsWithTypeConversion;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.ParamsWithoutAnnotations;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_MULTIPLE_REQUEST_DETAILS;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST_WITH_ID_TYPE;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_LAST;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_PARAM_NO_EMBEDDED_TYPE;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SIMPLE_METHOD_WITH_PARAMS_CONVERSION;
import static ca.uhn.fhir.rest.server.method.EmbeddedParamsInnerClassesAndMethods.SampleParamsWithIdParam;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.ParamsWithTypeConversion;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.ParamsWithoutAnnotations;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_MULTIPLE_REQUEST_DETAILS;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST_WITH_ID_TYPE;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_LAST;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_PARAM_NO_EMBEDDED_TYPE;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SIMPLE_METHOD_WITH_PARAMS_CONVERSION;
import static ca.uhn.fhir.rest.server.method.MethodAndOperationParamsInnerClassesAndMethods.SampleParamsWithIdParam;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand All @@ -42,7 +42,7 @@ class BaseMethodBindingMethodParameterBuilderTest {

private static final RequestDetails REQUEST_DETAILS = new SystemRequestDetails();

private final EmbeddedParamsInnerClassesAndMethods myEmbeddedParamsInnerClassesAndMethods = new EmbeddedParamsInnerClassesAndMethods();
private final MethodAndOperationParamsInnerClassesAndMethods myMethodAndOperationParamsInnerClassesAndMethods = new MethodAndOperationParamsInnerClassesAndMethods();

// LUKETODO: wrong params
// LUKETODO: wrong param order
Expand All @@ -51,7 +51,7 @@ class BaseMethodBindingMethodParameterBuilderTest {

@Test
void happyPathOperationParamsEmptyParams() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(EmbeddedParamsInnerClassesAndMethods.SUPER_SIMPLE);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(MethodAndOperationParamsInnerClassesAndMethods.SUPER_SIMPLE);
final Object[] inputParams = new Object[]{};

final Object[] actualOutputParams = buildMethodParams(sampleMethod, REQUEST_DETAILS, inputParams);
Expand All @@ -61,7 +61,7 @@ void happyPathOperationParamsEmptyParams() {

@Test
void happyPathOperationParamsNonEmptyParams() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(EmbeddedParamsInnerClassesAndMethods.SAMPLE_METHOD_OPERATION_PARAMS, IIdType.class, String.class, List.class, BooleanType.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(MethodAndOperationParamsInnerClassesAndMethods.SAMPLE_METHOD_OPERATION_PARAMS, IIdType.class, String.class, List.class, BooleanType.class);
final Object[] inputParams = new Object[]{new IdDt(), "param1", List.of("param2")};

final Object[] actualOutputParams = buildMethodParams(sampleMethod, REQUEST_DETAILS, inputParams);
Expand All @@ -71,7 +71,7 @@ void happyPathOperationParamsNonEmptyParams() {

@Test
void happyPathOperationEmbeddedTypesNoRequestDetails() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS, SampleParams.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS, SampleParams.class);
final Object[] inputParams = new Object[]{"param1", List.of("param2")};
final Object[] expectedOutputParams = new Object[]{new SampleParams("param1", List.of("param2"))};

Expand All @@ -82,7 +82,7 @@ void happyPathOperationEmbeddedTypesNoRequestDetails() {

@Test
void happyPathOperationEmbeddedTypesNoRequestDetailsNullArguments() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS, SampleParams.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_NO_REQUEST_DETAILS, SampleParams.class);
final Object[] inputParams = new Object[]{null, null};
final Object[] expectedOutputParams = new Object[]{new SampleParams(null, null)};

Expand All @@ -93,7 +93,7 @@ void happyPathOperationEmbeddedTypesNoRequestDetailsNullArguments() {

@Test
void happyPathOperationEmbeddedTypesRequestDetailsFirst() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST, RequestDetails.class, SampleParams.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST, RequestDetails.class, SampleParams.class);
final Object[] inputParams = new Object[]{REQUEST_DETAILS, "param1", List.of("param2")};
final Object[] expectedOutputParams = new Object[]{REQUEST_DETAILS, new SampleParams("param1", List.of("param2"))};

Expand All @@ -104,7 +104,7 @@ void happyPathOperationEmbeddedTypesRequestDetailsFirst() {

@Test
void happyPathOperationEmbeddedTypesRequestDetailsLast() {
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_LAST, SampleParams.class, RequestDetails.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_LAST, SampleParams.class, RequestDetails.class);
final Object[] inputParams = new Object[]{"param1", List.of("param3"), REQUEST_DETAILS};
final Object[] expectedOutputParams = new Object[]{new SampleParams("param1", List.of("param3")), REQUEST_DETAILS};

Expand All @@ -117,7 +117,7 @@ void happyPathOperationEmbeddedTypesRequestDetailsLast() {
@Disabled
void happyPathOperationEmbeddedTypesWithIdType() {
final IdType id = new IdType();
final Method sampleMethod = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST_WITH_ID_TYPE, RequestDetails.class, SampleParamsWithIdParam.class);
final Method sampleMethod = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_REQUEST_DETAILS_FIRST_WITH_ID_TYPE, RequestDetails.class, SampleParamsWithIdParam.class);
final Object[] inputParams = new Object[]{REQUEST_DETAILS, id, "param1", List.of("param2"), new BooleanType(false)};
final Object[] expectedOutputParams = new Object[]{REQUEST_DETAILS, new SampleParamsWithIdParam(id, "param1", List.of("param2"), new BooleanType(false))};

Expand All @@ -135,7 +135,7 @@ void buildMethodParams_withNullMethod_shouldThrowInternalErrorException() {

@Test
void buildMethodParams_withNullParams_shouldThrowInternalErrorException() throws NoSuchMethodException {
final Method sampleMethod = EmbeddedParamsInnerClassesAndMethods.class.getDeclaredMethod(EmbeddedParamsInnerClassesAndMethods.SUPER_SIMPLE);
final Method sampleMethod = MethodAndOperationParamsInnerClassesAndMethods.class.getDeclaredMethod(MethodAndOperationParamsInnerClassesAndMethods.SUPER_SIMPLE);

assertThrows(InternalErrorException.class, () -> {
buildMethodParams(sampleMethod, REQUEST_DETAILS, null);
Expand All @@ -149,7 +149,7 @@ void buildMethodParams_withNullMethodAndParams_shouldThrowInternalErrorException

@Test
void buildMethodParams_multipleRequestDetails_shouldThrowInternalErrorException() {
final Method method = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_MULTIPLE_REQUEST_DETAILS,
final Method method = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_EMBEDDED_TYPE_MULTIPLE_REQUEST_DETAILS,
RequestDetails.class, SampleParams.class, RequestDetails.class);
final Object[] inputParams = new Object[]{REQUEST_DETAILS, new IdDt(), "param1", List.of("param2", REQUEST_DETAILS)};
assertThrows(InternalErrorException.class, () -> {
Expand All @@ -161,7 +161,7 @@ void buildMethodParams_multipleRequestDetails_shouldThrowInternalErrorException(
@Test
@Disabled
void buildMethodParams_withClassMiissingParameterAnnotations_shouldThrowInternalErrorException() {
final Method method = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_PARAM_NO_EMBEDDED_TYPE, ParamsWithoutAnnotations.class);
final Method method = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SAMPLE_METHOD_PARAM_NO_EMBEDDED_TYPE, ParamsWithoutAnnotations.class);

final Object[] inputParams = new Object[]{new IdDt(), "param1", 2, List.of("param3")};

Expand All @@ -172,7 +172,7 @@ void buildMethodParams_withClassMiissingParameterAnnotations_shouldThrowInternal

@Test
void paramsConversionZonedDateTime() {
final Method method = myEmbeddedParamsInnerClassesAndMethods.getDeclaredMethod(SIMPLE_METHOD_WITH_PARAMS_CONVERSION, ParamsWithTypeConversion.class);
final Method method = myMethodAndOperationParamsInnerClassesAndMethods.getDeclaredMethod(SIMPLE_METHOD_WITH_PARAMS_CONVERSION, ParamsWithTypeConversion.class);

final Object[] inputParams = new Object[]{"2024-01-01", "2025-01-01"};
final Object[] expectedOutputParams = new Object[]{
Expand Down
Loading

0 comments on commit 2fba720

Please sign in to comment.