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

commonMain.com.apollographql.apollo3.api.test.TestResolver.kt Maven / Gradle / Ivy

There is a newer version: 4.0.0-beta.7
Show newest version
package com.apollographql.apollo3.api.test

import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.api.CompiledListType
import com.apollographql.apollo3.api.CompiledNamedType
import com.apollographql.apollo3.api.CompiledNotNullType
import com.apollographql.apollo3.api.CompiledType
import com.apollographql.apollo3.api.CustomScalarType
import com.apollographql.apollo3.api.EnumType
import kotlin.native.concurrent.ThreadLocal

/**
 * Implement [TestResolver] to generate fake data during tests.
 */
@ApolloExperimental
interface TestResolver {
  /**
   * Resolve the given field
   *
   * @param responseName the name of the field as seen in the json
   * @param compiledType the GraphQL type of the field
   * @param enumValues if [compiledType] is an enum, the list of possible values
   * @param ctors if [compiledType] is a composite type or any non-null or list combination of a composite type,
   * ctors contain a list of constructors for the possible shapes
   *
   * @return T the Kotlin value for the field. Can be Int, Double, String, List or Map or null
   */
  fun  resolve(responseName: String, compiledType: CompiledType, enumValues: List, ctors: Array Map>?): T
}

@ApolloExperimental
open class DefaultTestResolver : TestResolver {
  private val MAX_STACK_SIZE = 256

  private val stack = Array(MAX_STACK_SIZE) { 0 }
  private var stackSize = 0

  private var intCounter = 0
  private var floatCounter = 0.5
  private var compositeCounter = 0
  private var enumCounter = 0
  private var booleanCounter = false

  open fun resolveListSize(path: List): Int {
    return 3
  }

  open fun resolveInt(path: List): Int {
    return intCounter++
  }

  open fun resolveString(path: List): String {
    return path.subList(path.indexOfLast { it is String }, path.size).joinToString("")
  }

  open fun resolveFloat(path: List): Double {
    return floatCounter++
  }

  open fun resolveBoolean(path: List): Boolean {
    return booleanCounter.also {
      booleanCounter = !booleanCounter
    }
  }

  open fun resolveEnum(path: List, enumValues: List): String {
    return enumValues[(enumCounter++) % enumValues.size]
  }

  open fun resolveComposite(path: List, ctors: Array Map>): Map {
    return ctors[(compositeCounter++) % ctors.size]()
  }

  open fun resolveCustomScalar(path: List): String {
    error("Cannot resolve custom scalar at $path")
  }

  private fun push(v: Any) {
    check(stackSize < MAX_STACK_SIZE) {
      "Nesting too deep at ${stack.joinToString(".")}"
    }
    stack[stackSize++] = v
  }

  private fun pop() {
    stackSize--
    stack[stackSize] = 0 // Allow garbage collection
  }

  private fun  resolveInternal(responseName: String,
                                  compiledType: CompiledType,
                                  enumValues: List,
                                  ctors: Array Map>?): T {
    val path = stack.take(stackSize).toList()
    @Suppress("UNCHECKED_CAST")
    return when (compiledType) {
      is CompiledNotNullType -> resolve(responseName, compiledType.ofType, enumValues, ctors)
      is CompiledListType -> {
        0.until(resolveListSize(path)).map { i ->
          push(i)
          resolveInternal(responseName, compiledType.ofType, enumValues, ctors).also {
            pop()
          }
        }
      }
      is CustomScalarType -> {
        when (compiledType.name) {
          "Int" -> resolveInt(path)
          "Float" -> resolveFloat(path)
          "Boolean" -> resolveBoolean(path)
          "String" -> resolveString(path)
          "ID" -> resolveString(path)
          else -> {
            resolveCustomScalar(path)
          }
        }
      }
      is EnumType -> {
        resolveEnum(path, enumValues)
      }
      is CompiledNamedType -> {
        resolveComposite(path, ctors ?: error("no ctors for $responseName"))
      }
    } as T
  }

  override fun  resolve(
      responseName: String,
      compiledType: CompiledType,
      enumValues: List,
      ctors: Array Map>?,
  ): T {
    push(responseName)
    return resolveInternal(responseName, compiledType, enumValues, ctors).also {
      pop()
    }
  }
}

@ThreadLocal
@ApolloExperimental
internal var currentTestResolver: TestResolver? = null

@ApolloExperimental
fun  withTestResolver(testResolver: TestResolver, block: () -> T): T {
  currentTestResolver = testResolver
  return block().also {
    currentTestResolver = null
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy