All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.awsjavakit.eventbridge.handlers.EventParser Maven / Gradle / Ivy

There is a newer version: 0.18.4
Show newest version
package com.github.awsjavakit.eventbridge.handlers;

import static com.github.awsjavakit.misc.exceptions.ExceptionUtils.stackTraceInSingleLine;
import static com.gtihub.awsjavakit.attempt.Try.attempt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.awsjavakit.eventbridge.models.AwsEventBridgeEvent;
import com.gtihub.awsjavakit.attempt.Failure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EventParser {

  public static final String ERROR_PARSING_INPUT = "Could not parse input: ";
  public static final int SKIP_BOTTOM_TYPE = 2;
  public static final String RAWTYPES = "rawtypes";
  private static final Logger logger = LoggerFactory.getLogger(EventParser.class);
  private final String input;
  private final ObjectMapper objectMapper;

  public EventParser(String input, ObjectMapper objectMapper) {
    this.input = input;
    this.objectMapper = objectMapper;
  }

  public AwsEventBridgeEvent parse(Class iclass) {
    return attempt(() -> parseJson(iclass)).orElseThrow(this::handleParsingError);
  }

  /**
   * Given the nested parameter classes ClassA,ClassB,ClassC,...,ClassZ, this method returns the an
   * AwsEventBridgeEvent where the detail object in of type
   * {@literal ClassA>..>}.
   *
   * @param nestedParameterClasses the classes of the nested generic type of the detail object.
   * @return an AwsEventBridgeEvent with a nested generic type as detail object.
   */
    /*
      Using raw types because of Java's type erasure for nested generic classes.
      I.e. one cannot retrieve the java.lang.Class instance for the type ClassA.
    */
  @SuppressWarnings(RAWTYPES)
  public AwsEventBridgeEvent parse(Class... nestedParameterClasses) {
    return attempt(() -> parseJson(nestedParameterClasses)).orElseThrow(this::handleParsingError);
  }

  private AwsEventBridgeEvent parseJson(Class iclass) throws JsonProcessingException {
    JavaType javaType =
      objectMapper.getTypeFactory().constructParametricType(AwsEventBridgeEvent.class, iclass);
    return objectMapper.readValue(input, javaType);
  }

  private AwsEventBridgeEvent parseJson(Class... nestedClasses)
    throws JsonProcessingException {
    JavaType nestedJavaTypes = nestedGenericTypesToJavaType(nestedClasses);
    JavaType eventBridgeJavaType = constructAwsEventBridgeDataTypeWithAllNestedTypes(
      nestedJavaTypes);
    return objectMapper.readValue(input, eventBridgeJavaType);
  }

  private  RuntimeException handleParsingError(Failure fail) {
    logger.error(ERROR_PARSING_INPUT + input);
    logger.error(stackTraceInSingleLine(fail.getException()));
    return new RuntimeException(fail.getException());
  }

  /*
   * Given an array of {@link Class} generic classes ClassA,ClassB,ClassC,...ClassZ,
   * it creates a {@link JavaType} for the object  ClassA>>>
   */
  @SuppressWarnings(RAWTYPES)
  private JavaType nestedGenericTypesToJavaType(Class... classes) {
    //Variables not inlined for readability purposes.
    JavaType mostRecentType = objectMapper.getTypeFactory().constructType(innermostType(classes));
    for (int index = classes.length - SKIP_BOTTOM_TYPE; index >= 0; index--) {
      Class currentClass = classes[index];
      mostRecentType = createGenericClassContainingAllPreviousTypes(mostRecentType, currentClass);
    }
    return mostRecentType;
  }

  private JavaType createGenericClassContainingAllPreviousTypes(JavaType mostRecentType,
    Class currentClass) {
    return objectMapper.getTypeFactory().constructParametricType(currentClass, mostRecentType);
  }

  @SafeVarargs
  private  T innermostType(T... classes) {
    return classes[classes.length - 1];
  }

  private JavaType constructAwsEventBridgeDataTypeWithAllNestedTypes(JavaType mostRecentType) {
    return objectMapper.getTypeFactory()
      .constructParametricType(AwsEventBridgeEvent.class, mostRecentType);
  }
}