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

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

The newest version!
package com.apollographql.apollo.api

import com.apollographql.apollo.annotations.ApolloExperimental
import kotlin.jvm.JvmField

/**
 * A wrapper around a Map used to retrieve custom scalar adapters at runtime.
 *
 * For historical reasons, it also contains other context used when parsing response.
 * See https://github.com/apollographql/apollo-kotlin/pull/3813
 */
class CustomScalarAdapters private constructor(
    customScalarAdapters: Map>,
    /**
     * Operation variables used to determine whether the parser must parse @skip/@include fragments
     *
     */
    @JvmField
    val falseVariables: Set?,
    /**
     * Defer identifiers used to determine whether the parser must parse @defer fragments
     */
    @JvmField
    val deferredFragmentIdentifiers: Set?,
    /**
     * Errors to use with @catch
     */
    @JvmField
    val errors: List?,

    private val unsafe: Boolean,
) : ExecutionContext.Element {

  private val adaptersMap: Map> = customScalarAdapters

  fun  adapterFor(name: String): Adapter? {
    @Suppress("UNCHECKED_CAST")
    return adaptersMap[name] as Adapter?
  }

  fun  responseAdapterFor(customScalar: CustomScalarType): Adapter {
    @Suppress("UNCHECKED_CAST")
    return when {
      adaptersMap[customScalar.name] != null -> {
        adaptersMap[customScalar.name]
      }
      /**
       * Below are shortcuts to save the users a call to `registerCustomScalarAdapter`
       */
      customScalar.className == "com.apollographql.apollo.api.Upload" -> {
        UploadAdapter
      }

      customScalar.className in listOf("kotlin.String", "java.lang.String") -> {
        StringAdapter
      }

      customScalar.className in listOf("kotlin.Boolean", "java.lang.Boolean") -> {
        BooleanAdapter
      }

      customScalar.className in listOf("kotlin.Int", "java.lang.Int") -> {
        IntAdapter
      }

      customScalar.className in listOf("kotlin.Double", "java.lang.Double") -> {
        DoubleAdapter
      }

      customScalar.className in listOf("kotlin.Long", "java.lang.Long") -> {
        LongAdapter
      }

      customScalar.className in listOf("kotlin.Float", "java.lang.Float") -> {
        FloatAdapter
      }

      customScalar.className in listOf("kotlin.Any", "java.lang.Object") -> {
        AnyAdapter
      }

      unsafe -> PassThroughAdapter()
      else -> error("Can't map GraphQL type: `${customScalar.name}` to: `${customScalar.className}`. Did you forget to add a scalar Adapter?")
    } as Adapter
  }

  override val key: ExecutionContext.Key<*>
    get() = Key

  companion object Key : ExecutionContext.Key {
    /**
     * An empty [CustomScalarAdapters]. If the models were generated with some custom scalars, parsing will fail
     */
    @JvmField
    val Empty = Builder().build()

    /**
     * Unsafe [CustomScalarAdapters]. They can only be used with `MapJsonReader` and `MapJsonWriter`. It will passthrough the values using
     * `MapJsonReader.nextValue` and `MapJsonWriter.value()`
     */
    @JvmField
    @ApolloExperimental
    val PassThrough = Builder().unsafe(true).build()
  }

  @ApolloExperimental
  fun firstErrorStartingWith(path: List): Error? {
    return errors?.firstOrNull {
      it.path?.startsWith(path) == true
    }
  }

  private fun List.startsWith(responsePath: List): Boolean {
    // start at 1 to drop the `data.`
    for (i in 1.until(responsePath.size)) {
      if (i - 1 >= this.size) {
        return false
      }
      if (responsePath[i] != this[i - 1]) {
        return false
      }
    }
    return true
  }

  fun newBuilder(): Builder {
    return Builder().addAll(this)
        .falseVariables(falseVariables)
        .deferredFragmentIdentifiers(deferredFragmentIdentifiers)
  }

  class Builder {
    private val adaptersMap: MutableMap> = mutableMapOf()
    private var unsafe = false
    private var falseVariables: Set? = null
    private var deferredFragmentIdentifiers: Set? = null
    private var errors: List? = null

    fun falseVariables(falseVariables: Set?) = apply {
      this.falseVariables = falseVariables
    }

    fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set?) = apply {
      this.deferredFragmentIdentifiers = deferredFragmentIdentifiers
    }

    fun errors(errors: List?) = apply {
      this.errors = errors
    }

    fun  add(
        name: String,
        adapter: Adapter,
    ) = apply {
      adaptersMap[name] = adapter
    }

    fun  add(
        customScalarType: CustomScalarType,
        customScalarAdapter: Adapter,
    ) = apply {
      adaptersMap[customScalarType.name] = customScalarAdapter
    }


    fun addAll(customScalarAdapters: CustomScalarAdapters) = apply {
      this.adaptersMap.putAll(customScalarAdapters.adaptersMap)
    }

    @ApolloExperimental
    fun unsafe(unsafe: Boolean) = apply {
      this.unsafe = unsafe
    }

    fun clear() {
      adaptersMap.clear()
    }

    fun build(): CustomScalarAdapters {
      return CustomScalarAdapters(
          adaptersMap,
          falseVariables,
          deferredFragmentIdentifiers,
          errors,
          unsafe,
      )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy