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

com.apollographql.apollo.internal.response.OperationResponseParser Maven / Gradle / Ivy

/**
 * Copyright 2018-2019 Amazon.com,
 * Inc. or its affiliates. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package com.apollographql.apollo.internal.response;

import com.apollographql.apollo.api.Error;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.api.ResponseFieldMapper;
import com.apollographql.apollo.internal.cache.normalized.ResponseNormalizer;
import com.apollographql.apollo.internal.field.MapFieldValueResolver;
import com.apollographql.apollo.internal.json.BufferedSourceJsonReader;
import com.apollographql.apollo.internal.json.ResponseJsonStreamReader;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;

import okio.BufferedSource;

import static com.apollographql.apollo.api.internal.Utils.checkNotNull;
import static com.apollographql.apollo.internal.json.ApolloJsonReader.responseJsonStreamReader;

@SuppressWarnings("WeakerAccess")
public class OperationResponseParser {
  private final Operation operation;
  private final ResponseFieldMapper responseFieldMapper;
  private final ScalarTypeAdapters scalarTypeAdapters;
  private final ResponseNormalizer> responseNormalizer;

  @SuppressWarnings("unchecked") public OperationResponseParser(Operation operation,
      ResponseFieldMapper responseFieldMapper, ScalarTypeAdapters scalarTypeAdapters) {
    this(operation, responseFieldMapper, scalarTypeAdapters, ResponseNormalizer.NO_OP_NORMALIZER);
  }

  public OperationResponseParser(Operation operation, ResponseFieldMapper responseFieldMapper,
      ScalarTypeAdapters scalarTypeAdapters, ResponseNormalizer> responseNormalizer) {
    this.operation = operation;
    this.responseFieldMapper = responseFieldMapper;
    this.scalarTypeAdapters = scalarTypeAdapters;
    this.responseNormalizer = responseNormalizer;
  }

  @SuppressWarnings("unchecked")
  public Response parse(@Nonnull Map payload) {
    checkNotNull(payload, "payload == null");

    D data = null;
    if (payload.containsKey("data")) {
      Map buffer = (Map) payload.get("data");
      RealResponseReader> realResponseReader = new RealResponseReader<>(operation.variables(),
              buffer, new MapFieldValueResolver(), scalarTypeAdapters, responseNormalizer);
      data = (D) responseFieldMapper.map(realResponseReader);
    }

    List errors = null;
    if (payload.containsKey("errors")) {
      List> errorPayloads = (List>) payload.get("errors");
      if (errorPayloads != null) {
        errors = new ArrayList<>();
        for (Map errorPayload : errorPayloads) {
          errors.add(readError(errorPayload));
        }
      }
    }

    return Response.builder(operation)
            .data(operation.wrapData(data))
            .errors(errors)
            .dependentKeys(responseNormalizer.dependentKeys())
            .build();
  }

  public Response parse(BufferedSource source) throws IOException {
    responseNormalizer.willResolveRootQuery(operation);
    BufferedSourceJsonReader jsonReader = null;
    try {
      jsonReader = new BufferedSourceJsonReader(source);
      jsonReader.beginObject();

      D data = null;
      List errors = null;
      ResponseJsonStreamReader responseStreamReader = responseJsonStreamReader(jsonReader);
      while (responseStreamReader.hasNext()) {
        String name = responseStreamReader.nextName();
        if ("data".equals(name)) {
          //noinspection unchecked
          data = (D) responseStreamReader.nextObject(true, new ResponseJsonStreamReader.ObjectReader() {
            @Override public Object read(ResponseJsonStreamReader reader) throws IOException {
              Map buffer = reader.toMap();
              RealResponseReader> realResponseReader = new RealResponseReader<>(
                  operation.variables(), buffer, new MapFieldValueResolver(), scalarTypeAdapters, responseNormalizer);
              return responseFieldMapper.map(realResponseReader);
            }
          });
        } else if ("errors".equals(name)) {
          errors = readResponseErrors(responseStreamReader);
        } else {
          responseStreamReader.skipNext();
        }
      }
      jsonReader.endObject();
      return Response.builder(operation)
          .data(operation.wrapData(data))
          .errors(errors)
          .dependentKeys(responseNormalizer.dependentKeys())
          .build();
    } finally {
      if (jsonReader != null) {
        jsonReader.close();
      }
    }
  }

  private List readResponseErrors(ResponseJsonStreamReader reader) throws IOException {
    return reader.nextList(true, new ResponseJsonStreamReader.ListReader() {
      @Override public Error read(ResponseJsonStreamReader reader) throws IOException {
        return reader.nextObject(true, new ResponseJsonStreamReader.ObjectReader() {
          @Override public Error read(ResponseJsonStreamReader reader) throws IOException {
            return readError(reader.toMap());
          }
        });
      }
    });
  }

  @SuppressWarnings("unchecked")
  private Error readError(Map payload) {
    String message = null;
    final List locations = new ArrayList<>();
    final Map customAttributes = new HashMap<>();
    for (Map.Entry entry : payload.entrySet()) {
      if ("message".equals(entry.getKey())) {
        Object value = entry.getValue();
        message = value != null ? value.toString() : null;
      } else if ("locations".equals(entry.getKey())) {
        List> locationItems = (List>) entry.getValue();
        if (locationItems != null) {
          for (Map item : locationItems) {
            locations.add(readErrorLocation(item));
          }
        }
      } else {
        if (entry.getValue() != null) {
          customAttributes.put(entry.getKey(), entry.getValue());
        }
      }
    }
    return new Error(message, locations, customAttributes);
  }

  @SuppressWarnings("ConstantConditions")
  private Error.Location readErrorLocation(Map data) {
    long line = -1;
    long column = -1;
    if (data != null) {
      for (Map.Entry entry : data.entrySet()) {
        if ("line".equals(entry.getKey())) {
          line = ((BigDecimal) entry.getValue()).longValue();
        } else if ("column".equals(entry.getKey())) {
          column = ((BigDecimal) entry.getValue()).longValue();
        }
      }
    }
    return new Error.Location(line, column);
  }
}