CMSgov/dpc-app

View on GitHub
dpc-api/src/main/java/gov/cms/dpc/api/converters/ChecksumConverterProvider.java

Summary

Maintainability
A
0 mins
Test Coverage
C
75%
package gov.cms.dpc.api.converters;

import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.ClassTypePair;

import javax.inject.Inject;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Implementation of Jersey's {@link ParamConverterProvider} which processes input values to remove any trailing characters from {@link HttpHeaders#IF_NONE_MATCH} header.
 * <p>
 * This class allows for both raw {@link String} values, as well as Strings wrapped in an {@link Optional}, the logic is borrowed from Dropwizard's optional handling.
 * We can't use their logic directly, because we need to do more advanced matching against header values
 *
 * @see <a href="https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/optional/OptionalParamConverterProvider.java">Dropwizard Optional Handling</a>
 */
@Provider
public class ChecksumConverterProvider implements ParamConverterProvider {

    @Inject
    public ChecksumConverterProvider() {
        // Not used
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {

        // Look to see if we have a matching header
        final boolean headerMatches = Arrays.stream(annotations)
                .filter(annotation -> annotation.annotationType().isAssignableFrom(HeaderParam.class))
                .map(annotation -> ((HeaderParam) annotation).value())
                .anyMatch(value -> value.equals(HttpHeaders.IF_NONE_MATCH));

        if (!headerMatches) {
            return null;
        }

        // Now, check to see if we have an optional value, in which case we have to wrap the result of the param conversion
        if (Optional.class.equals(rawType)) {
            return provideOptionalConverter(rawType, genericType);
        } else if (String.class.equals(rawType)) {
            return (ParamConverter<T>) new ChecksumParamConverter();
        }

        return null;
    }

    private <T> ParamConverter<T> provideOptionalConverter(Class<T> rawType, Type genericType) {
        final List<ClassTypePair> ctps = ReflectionHelper.getTypeArgumentAndClass(genericType);
        final ClassTypePair ctp = (ctps.size() == 1) ? ctps.get(0) : null;

        if (ctp == null || ctp.rawClass() == String.class) {
            return new ParamConverter<>() {
                @Override
                public T fromString(String value) {
                    return rawType.cast(Optional.ofNullable(ChecksumParamConverter.stringMatchLogic(value)));
                }

                @Override
                public String toString(T value) {
                    return value.toString();
                }
            };
        }

        return null;
    }
}