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

commonMain.com.apollographql.apollo.api.Adapters.kt Maven / Gradle / Ivy

The newest version!
@file:JvmName("Adapters")

package com.apollographql.apollo.api

import com.apollographql.apollo.annotations.ApolloDeprecatedSince
import com.apollographql.apollo.annotations.ApolloInternal
import com.apollographql.apollo.api.json.JsonReader
import com.apollographql.apollo.api.json.JsonWriter
import com.apollographql.apollo.api.json.MapJsonReader
import com.apollographql.apollo.api.json.MapJsonReader.Companion.buffer
import com.apollographql.apollo.api.json.MapJsonWriter
import com.apollographql.apollo.api.json.buildJsonString
import com.apollographql.apollo.api.json.readAny
import com.apollographql.apollo.api.json.writeAny
import com.apollographql.apollo.exception.ApolloException
import com.apollographql.apollo.exception.ApolloGraphQLException
import kotlin.jvm.JvmField
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmSuppressWildcards

/**
 * This file contains a list of [Adapter] for standard types
 *
 * They are mostly used from the generated code but could be useful in any other situations that requires adapting from
 * GraphQL to Kotlin.
 * In particular, [AnyAdapter] can be used to read/write a Kotlin representation from/to Json.
 */
class ListAdapter(private val wrappedAdapter: Adapter) : Adapter> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): List {
    reader.beginArray()
    val list = mutableListOf()
    while (reader.hasNext()) {
      list.add(wrappedAdapter.fromJson(reader, customScalarAdapters))
    }
    reader.endArray()
    return list
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: List) {
    writer.beginArray()
    value.forEach {
      wrappedAdapter.toJson(writer, customScalarAdapters, it)
    }
    writer.endArray()
  }
}

class NullableAdapter(private val wrappedAdapter: Adapter) : Adapter<@JvmSuppressWildcards T?> {
  init {
    check(wrappedAdapter !is NullableAdapter<*>) {
      "The adapter is already nullable"
    }
  }

  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): T? {
    return if (reader.peek() == JsonReader.Token.NULL) {
      reader.skipValue()
      null
    } else {
      wrappedAdapter.fromJson(reader, customScalarAdapters)
    }
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: T?) {
    if (value == null) {
      writer.nullValue()
    } else {
      wrappedAdapter.toJson(writer, customScalarAdapters, value)
    }
  }
}

@Deprecated("Use PresentAdapter instead")
class OptionalAdapter(private val wrappedAdapter: Adapter) : Adapter> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Optional.Present {
    return Optional.Present(wrappedAdapter.fromJson(reader, customScalarAdapters))
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Optional.Present) {
    wrappedAdapter.toJson(writer, customScalarAdapters, value.value)
  }
}

/**
 * PresentAdapter can only express something that's present. Absent values are handled outside the adapter.
 *
 * This adapter is used to handle optional arguments in operations and optional fields in Input objects.
 */
class PresentAdapter(private val wrappedAdapter: Adapter) : Adapter> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Optional.Present {
    return Optional.Present(wrappedAdapter.fromJson(reader, customScalarAdapters))
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Optional.Present) {
    wrappedAdapter.toJson(writer, customScalarAdapters, value.value)
  }
}


/**
 * This adapter is used to handle nullable fields when they are represented as [Optional].
 * `null` is deserialized as [Optional.Absent].
 */
class ApolloOptionalAdapter(private val wrappedAdapter: Adapter) : Adapter> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Optional {
    return if (reader.peek() == JsonReader.Token.NULL) {
      reader.skipValue()
      Optional.Absent
    } else {
      Optional.Present(wrappedAdapter.fromJson(reader, customScalarAdapters))
    }
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Optional) {
    if (value is Optional.Present) {
      wrappedAdapter.toJson(writer, customScalarAdapters, value.value)
    } else {
      writer.nullValue()
    }
  }
}

@JvmField
val StringAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): String {
    return reader.nextString()!!
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: String) {
    writer.value(value)
  }
}

@JvmField
val IntAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Int {
    return reader.nextInt()
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Int) {
    writer.value(value)
  }
}

@JvmField
val DoubleAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Double {
    return reader.nextDouble()
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Double) {
    writer.value(value)
  }
}

/**
 * An [Adapter] that converts to/from a [Float]
 * Floats are not part of the GraphQL spec but this can be used in custom scalars
 */
@JvmField
val FloatAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Float {
    return reader.nextDouble().toFloat()
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Float) {
    writer.value(value.toDouble())
  }
}

/**
 * An [Adapter] that converts to/from a [Long]
 * Longs are not part of the GraphQL spec but this can be used in custom scalars
 *
 * If the Json number does not fit in a [Long], an exception will be thrown
 */
@JvmField
val LongAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Long {
    return reader.nextLong()
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Long) {
    writer.value(value)
  }
}

@JvmField
val BooleanAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Boolean {
    return reader.nextBoolean()
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Boolean) {
    writer.value(value)
  }
}

@OptIn(ApolloInternal::class)
@JvmField
val AnyAdapter = object : Adapter {
  fun fromJson(reader: JsonReader): Any {
    return reader.readAny()!!
  }

  fun toJson(writer: JsonWriter, value: Any) {
    writer.writeAny(value)
  }

  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Any {
    return fromJson(reader)
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Any) {
    toJson(writer, value)
  }
}

internal class PassThroughAdapter : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): T {
    check(reader is MapJsonReader) {
      "UnsafeAdapter only supports MapJsonReader"
    }

    @Suppress("UNCHECKED_CAST")
    return reader.nextValue() as T
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: T) {
    check(writer is MapJsonWriter) {
      "UnsafeAdapter only supports MapJsonWriter"
    }

    writer.value(value)
  }
}

@JvmField
val UploadAdapter = object : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Upload {
    error("File Upload used in output position")
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Upload) {
    writer.value(value)
  }
}

/*
 * Global instances of nullable adapters for built-in scalar types
 */
@JvmField
val NullableStringAdapter = StringAdapter.nullable()

@JvmField
val NullableDoubleAdapter = DoubleAdapter.nullable()

@JvmField
val NullableIntAdapter = IntAdapter.nullable()

@JvmField
val NullableBooleanAdapter = BooleanAdapter.nullable()

@JvmField
val NullableAnyAdapter = AnyAdapter.nullable()

/*
 * Global instances of optional adapters for built-in scalar types
 */
@JvmField
val ApolloOptionalStringAdapter = ApolloOptionalAdapter(StringAdapter)

@JvmField
val ApolloOptionalDoubleAdapter = ApolloOptionalAdapter(DoubleAdapter)

@JvmField
val ApolloOptionalIntAdapter = ApolloOptionalAdapter(IntAdapter)

@JvmField
val ApolloOptionalBooleanAdapter = ApolloOptionalAdapter(BooleanAdapter)

@JvmField
val ApolloOptionalAnyAdapter = ApolloOptionalAdapter(AnyAdapter)

/**
 * Note that Arrays require their type to be known at compile time, so we construct an anonymous object with reference to
 * function with reified type parameters as a workaround.
 *
 */
@JvmName("-array")
inline fun  Adapter.array() = object : Adapter> {

  private inline fun  arrayFromJson(
      wrappedAdapter: Adapter,
      reader: JsonReader,
      customScalarAdapters: CustomScalarAdapters,
  ): Array {
    reader.beginArray()
    val list = mutableListOf()
    while (reader.hasNext()) {
      list.add(wrappedAdapter.fromJson(reader, customScalarAdapters))
    }
    reader.endArray()
    return list.toTypedArray()
  }

  private inline fun  arrayToJson(
      wrappedAdapter: Adapter,
      writer: JsonWriter,
      customScalarAdapters: CustomScalarAdapters,
      value: Array,
  ) {
    writer.beginArray()
    value.forEach {
      wrappedAdapter.toJson(writer, customScalarAdapters, it)
    }
    writer.endArray()
  }

  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Array {
    return arrayFromJson(this@array, reader, customScalarAdapters)
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Array) {
    return arrayToJson(this@array, writer, customScalarAdapters, value)
  }
}

@JvmName("-optional")
@Deprecated("Use present instead", ReplaceWith("present()"))
fun  Adapter.optional() = PresentAdapter(this)

@JvmName("-present")
fun  Adapter.present() = PresentAdapter(this)


@JvmName("-toJson")
@JvmOverloads
fun  Adapter.toJsonString(
    value: T,
    customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
    indent: String? = null,
): String = buildJsonString(indent) {
  [email protected](this, customScalarAdapters, value)
}

class ObjectAdapter(
    private val wrappedAdapter: Adapter,
    private val buffered: Boolean,
) : Adapter<@JvmSuppressWildcards T> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): T {
    val actualReader = if (buffered) {
      reader.buffer()
    } else {
      reader
    }
    actualReader.beginObject()
    return wrappedAdapter.fromJson(actualReader, customScalarAdapters).also {
      actualReader.endObject()
    }
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: T) {
    if (buffered && writer !is MapJsonWriter) {
      /**
       * Convert to a Map first
       */
      val mapWriter = MapJsonWriter()
      mapWriter.beginObject()
      wrappedAdapter.toJson(mapWriter, customScalarAdapters, value)
      mapWriter.endObject()

      /**
       * And write to the original writer
       */
      writer.writeAny(mapWriter.root()!!)
    } else {
      writer.beginObject()
      wrappedAdapter.toJson(writer, customScalarAdapters, value)
      writer.endObject()
    }
  }
}

private class ErrorAwareAdapter(private val wrappedAdapter: Adapter) : Adapter {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): T {
    if (reader.peek() == JsonReader.Token.NULL) {
      val error = customScalarAdapters.firstErrorStartingWith(reader.getPath())
      if (error != null) {
        reader.skipValue()
        throw ApolloGraphQLException(error)
      }
    }

    return wrappedAdapter.fromJson(reader, customScalarAdapters)
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: T) {
    wrappedAdapter.toJson(writer, customScalarAdapters, value)
  }
}

private class CatchToResultAdapter(private val wrappedAdapter: Adapter) : Adapter> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): FieldResult {
    return try {
      FieldResult.Success(wrappedAdapter.fromJson(reader, customScalarAdapters))
    } catch (e: ApolloException) {
      FieldResult.Failure(e)
    }
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: FieldResult) {
    when (value) {
      is FieldResult.Success -> wrappedAdapter.toJson(writer, customScalarAdapters, value.getOrThrow())
      else -> Unit // ignore errors
    }
  }
}

private class CatchToNullAdapter(private val wrappedAdapter: Adapter) : Adapter<@JvmSuppressWildcards T?> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): T? {
    return try {
      wrappedAdapter.fromJson(reader, customScalarAdapters)
    } catch (e: ApolloException) {
      null
    }
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: T?) {
    if (value == null) {
      // XXX: this potentially writes null instead of an error
      writer.nullValue()
    } else {
      wrappedAdapter.toJson(writer, customScalarAdapters, value)
    }
  }
}

@JvmName("-nullable")
fun  Adapter.nullable() = NullableAdapter(this)

@JvmName("-list")
fun  Adapter.list() = ListAdapter(this)

@JvmName("-obj")
fun  Adapter.obj(buffered: Boolean = false) = ObjectAdapter(this, buffered)

@JvmName("-catchToResult")
fun  Adapter.catchToResult(): Adapter> = CatchToResultAdapter(this)

@JvmName("-errorAware")
fun  Adapter.errorAware(): Adapter = ErrorAwareAdapter(this)

@JvmName("-catchToNull")
fun  Adapter.catchToNull(): Adapter = CatchToNullAdapter(this)

/**
 * A replica of Apollo Android v2's CustomTypeAdapter, to ease migration from v2 to v3.
 *
 * Make your CustomTypeAdapters implement this interface by updating the imports
 * from `com.apollographql.apollo.api` to `com.apollographql.apollo.api`.
 *
 * **Note**: [Adapter]s are called from multiple threads and implementations must be thread safe.
 */
@Deprecated("CustomTypeAdapter was used for backward compatibility with 2.x. Use Adapter instead", level = DeprecationLevel.ERROR)
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v3_0_0)
interface CustomTypeAdapter




© 2015 - 2024 Weber Informatics LLC | Privacy Policy