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

io.github.jpmorganchase.fusion.parsing.GsonAPIResponseParser Maven / Gradle / Ivy

There is a newer version: 0.0.14
Show newest version
package io.github.jpmorganchase.fusion.parsing;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import io.github.jpmorganchase.fusion.api.response.UploadedPart;
import io.github.jpmorganchase.fusion.model.*;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GsonAPIResponseParser implements APIResponseParser {

    private static final Logger logger =
            LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private final Gson gson;

    public GsonAPIResponseParser() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        // TODO: need to add a serializer as well once we get to update operations
        gsonBuilder.registerTypeAdapter(LocalDate.class, new LocalDateDeserializer());
        gson = gsonBuilder.create();
    }

    public GsonAPIResponseParser(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Map parseCatalogResponse(String json) {
        return parseResourcesFromResponse(json, Catalog.class);
    }

    @Override
    public Map parseDatasetResponse(String json) {
        return parseResourcesFromResponse(json, Dataset.class);
    }

    @Override
    public Map parseAttributeResponse(String json) {
        return parseResourcesFromResponse(json, Attribute.class);
    }

    @Override
    public Map parseDataProductResponse(String json) {
        return parseResourcesFromResponse(json, DataProduct.class);
    }

    @Override
    public Map parseDatasetSeriesResponse(String json) {
        return parseResourcesFromResponse(json, DatasetSeries.class);
    }

    @Override
    public Map parseDistributionResponse(String json) {
        return parseResourcesFromResponse(json, Distribution.class);
    }

    @Override
    public Operation parseOperationResponse(String json) {
        return gson.fromJson(json, Operation.class);
    }

    @Override
    public UploadedPart parseUploadPartResponse(String json) {
        return new GsonBuilder().create().fromJson(json, UploadedPart.class);
    }

    @Override
    public  Map parseResourcesFromResponse(String json, Class resourceClass) {
        // TODO: handle varArgs

        JsonArray resources = getResources(json);

        Type listType = TypeToken.getParameterized(List.class, resourceClass).getType();
        List resourceList = gson.fromJson(resources, listType);
        return resourceList.stream()
                .collect(Collectors.toMap(
                        T::getIdentifier,
                        Function.identity(),
                        // resolve any duplicate keys, for now just skip the duplicates
                        (r1, r2) -> {
                            logger.warn("Duplicate key '{}' found, will be ignored", r2.getIdentifier());
                            return r1;
                        }));
    }

    @Override
    public Map> parseResourcesUntyped(String json) {
        Type mapType = new TypeToken>() {}.getType();
        Map responseMap = gson.fromJson(json, mapType);

        Object resources = responseMap.get("resources");
        if (resources instanceof List) {
            @SuppressWarnings("unchecked") // List is always safe, compiler disagrees
            List resourceList = (List) resources;

            if (resourceList.size() == 0) throw generateNoResourceException();
            Map> resourcesMap = new HashMap<>();
            resourceList.forEach((o -> {
                @SuppressWarnings("unchecked") // Output of GSON parsing will always be in this format
                Map resource = (Map) o;

                String identifier = (String) resource.get("identifier");
                resourcesMap.put(identifier, resource);
            }));
            return resourcesMap;
        } else {
            throw generateNoResourceException();
        }
    }

    private JsonArray getResources(String json) {
        JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
        JsonArray array = obj.getAsJsonArray("resources");
        if (array == null || array.size() == 0) {
            throw generateNoResourceException();
        }
        return array;
    }

    private ParsingException generateNoResourceException() {
        String message = "Failed to parse resources from JSON, none found";
        logger.error(message);
        return new ParsingException(message);
    }

    private static final class LocalDateDeserializer implements JsonDeserializer {

        private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        @Override
        public LocalDate deserialize(
                JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext)
                throws JsonParseException {
            try {
                return LocalDate.parse(jsonElement.getAsString(), dateTimeFormatter);
            } catch (DateTimeParseException e) {
                String message = "Failed to deserialize date field with value " + jsonElement.getAsString();
                logger.warn(message);
                return null;
            }
        }
    }
}