Skip to content

Commit

Permalink
Merge pull request #2841 from apollographql/separate-parsing-and-norm…
Browse files Browse the repository at this point in the history
…alization

Separate parsing and normalization
  • Loading branch information
martinbonnin committed Jan 7, 2021
2 parents 1eeceb0 + cb747b7 commit 725ec72
Show file tree
Hide file tree
Showing 23 changed files with 257 additions and 265 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ fun Operation<*>.composeRequestBody(

/**
* Parses GraphQL operation raw response from the [source] with provided [customScalarAdapters] and returns result [Response]
*
* This will consume [source] so you don't need to close it. Also, you cannot reuse it
*/
@JvmOverloads
fun <D : Operation.Data> Operation<D>.parse(
Expand All @@ -128,5 +130,15 @@ fun <D : Operation.Data> Operation<D>.parse(
byteString: ByteString,
customScalarAdapters: CustomScalarAdapters = DEFAULT
): Response<D> {
return SimpleOperationResponseParser.parse(Buffer().write(byteString), this, customScalarAdapters)
return parse(Buffer().write(byteString), customScalarAdapters)
}

/**
* Parses GraphQL operation raw response from the [byteString] with provided [customScalarAdapters] and returns result [Response]
*/
fun <D : Operation.Data> Operation<D>.parse(
string: String,
customScalarAdapters: CustomScalarAdapters = DEFAULT
): Response<D> {
return parse(Buffer().writeUtf8(string), customScalarAdapters)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class RealResponseReader<R : Map<String, Any?>>(
private val recordSet: R,
internal val fieldValueResolver: FieldValueResolver<R>,
internal val customScalarAdapters: CustomScalarAdapters,
internal val resolveDelegate: ResolveDelegate<R>
) : ResponseReader {
private val variableValues: Map<String, Any?> = operationVariables.valueMap()
private var selectedFieldIndex = -1
Expand All @@ -31,120 +30,72 @@ class RealResponseReader<R : Map<String, Any?>>(
override fun readString(field: ResponseField): String? {
val value = fieldValueResolver.valueFor<String>(recordSet, field)
checkValue(field, value)
willResolve(field, value)
if (value == null) {
resolveDelegate.didResolveNull()
} else {
resolveDelegate.didResolveScalar(value)
}
didResolve(field)
return value
}

override fun readInt(field: ResponseField): Int? {
val value = fieldValueResolver.valueFor<BigDecimal>(recordSet, field)
checkValue(field, value)
willResolve(field, value)
if (value == null) {
resolveDelegate.didResolveNull()
} else {
resolveDelegate.didResolveScalar(value)
}
didResolve(field)

return value?.toNumber()?.toInt()
}

override fun readDouble(field: ResponseField): Double? {
val value = fieldValueResolver.valueFor<BigDecimal>(recordSet, field)
checkValue(field, value)
willResolve(field, value)
if (value == null) {
resolveDelegate.didResolveNull()
} else {
resolveDelegate.didResolveScalar(value)
}
didResolve(field)

return value?.toNumber()?.toDouble()
}

override fun readBoolean(field: ResponseField): Boolean? {
val value = fieldValueResolver.valueFor<Boolean>(recordSet, field)
checkValue(field, value)
willResolve(field, value)
if (value == null) {
resolveDelegate.didResolveNull()
} else {
resolveDelegate.didResolveScalar(value)
}
didResolve(field)

return value
}

override fun <T : Any> readObject(field: ResponseField, block: (ResponseReader) -> T): T? {
val value: R? = fieldValueResolver.valueFor(recordSet, field)
checkValue(field, value)
willResolve(field, value)
resolveDelegate.willResolveObject(field, value)
val parsedValue: T?
parsedValue = if (value == null) {
resolveDelegate.didResolveNull()
val parsedValue: T? = if (value == null) {
null
} else {
block(RealResponseReader(operationVariables, value, fieldValueResolver, customScalarAdapters, resolveDelegate))
block(RealResponseReader(operationVariables, value, fieldValueResolver, customScalarAdapters))
}
resolveDelegate.didResolveObject(field, value)
didResolve(field)
return parsedValue
}

override fun <T : Any> readList(field: ResponseField, block: (ResponseReader.ListItemReader) -> T): List<T?>? {
val values = fieldValueResolver.valueFor<List<*>>(recordSet, field)
checkValue(field, values)
willResolve(field, values)
val result = if (values == null) {
resolveDelegate.didResolveNull()
null
} else {
values.mapIndexed { index, value ->
resolveDelegate.willResolveElement(index)
if (value == null) {
resolveDelegate.didResolveNull()
null
} else {
block(ListItemReader(field, value))
}.also { resolveDelegate.didResolveElement(index) }
}.also { resolveDelegate.didResolveList(values) }
}
}
}
didResolve(field)
return result
}

override fun <T : Any> readCustomScalar(field: ResponseField.CustomScalarField): T? {
val value = fieldValueResolver.valueFor<Any>(recordSet, field)
checkValue(field, value)
willResolve(field, value)
val result: T?
if (value == null) {
resolveDelegate.didResolveNull()
result = null
} else {
val scalarTypeAdapter: CustomScalarAdapter<T> = customScalarAdapters.adapterFor(field.customScalar)
result = scalarTypeAdapter.decode(fromRawValue(value))
checkValue(field, result)
resolveDelegate.didResolveScalar(value)
}
didResolve(field)
return result
}

private fun willResolve(field: ResponseField, value: Any?) {
resolveDelegate.willResolve(field, operationVariables, value)
}

private fun didResolve(field: ResponseField) {
resolveDelegate.didResolve(field, operationVariables)
}

private fun checkValue(field: ResponseField, value: Any?) {
check(field.optional || value != null) {
"corrupted response reader, expected non null value for ${field.fieldName}"
Expand Down Expand Up @@ -177,52 +128,42 @@ class RealResponseReader<R : Map<String, Any?>>(
) : ResponseReader.ListItemReader {

override fun readString(): String {
resolveDelegate.didResolveScalar(value)
return value as String
}

override fun readInt(): Int {
resolveDelegate.didResolveScalar(value)
return (value as BigDecimal).toNumber().toInt()
}

override fun readDouble(): Double {
resolveDelegate.didResolveScalar(value)
return (value as BigDecimal).toNumber().toDouble()
}

override fun readBoolean(): Boolean {
resolveDelegate.didResolveScalar(value)
return value as Boolean
}

override fun <T : Any> readCustomScalar(customScalar: CustomScalar): T {
val scalarTypeAdapter: CustomScalarAdapter<T> = customScalarAdapters.adapterFor(customScalar)
resolveDelegate.didResolveScalar(value)
return scalarTypeAdapter.decode(fromRawValue(value))
}

@Suppress("UNCHECKED_CAST")
override fun <T : Any> readObject(block: (ResponseReader) -> T): T {
val value = value as R
resolveDelegate.willResolveObject(field, value)
val item = block(RealResponseReader(operationVariables, value, fieldValueResolver, customScalarAdapters, resolveDelegate))
resolveDelegate.didResolveObject(field, value)
val item = block(RealResponseReader(operationVariables, value, fieldValueResolver, customScalarAdapters))
return item
}

override fun <T : Any> readList(block: (ResponseReader.ListItemReader) -> T): List<T?> {
val values = value as List<*>
val result = values.mapIndexed { index, value ->
resolveDelegate.willResolveElement(index)
if (value == null) {
resolveDelegate.didResolveNull()
null
} else {
block(ListItemReader(field, value))
}.also { resolveDelegate.didResolveElement(index) }
}
}
resolveDelegate.didResolveList(values)
return result
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.apollographql.apollo.internal.response
package com.apollographql.apollo.api.internal.response

import com.apollographql.apollo.api.BigDecimal
import com.apollographql.apollo.api.Operation
Expand All @@ -7,6 +7,8 @@ import com.apollographql.apollo.api.CustomScalar
import com.apollographql.apollo.api.CustomScalarAdapters
import com.apollographql.apollo.api.internal.ResolveDelegate
import com.apollographql.apollo.api.internal.ResponseWriter
import com.apollographql.apollo.api.internal.Utils.shouldSkip
import com.apollographql.apollo.api.internal.json.JsonWriter.Companion.of

class RealResponseWriter(
private val operationVariables: Operation.Variables,
Expand All @@ -20,15 +22,15 @@ class RealResponseWriter(
}

override fun writeInt(field: ResponseField, value: Int?) {
writeScalarFieldValue(field, if (value != null) BigDecimal(value.toLong()) else null)
writeScalarFieldValue(field, if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeLong(field: ResponseField, value: Long?) {
writeScalarFieldValue(field, if (value != null) BigDecimal(value) else null)
writeScalarFieldValue(field, if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeDouble(field: ResponseField, value: Double?) {
writeScalarFieldValue(field, if (value != null) BigDecimal(value) else null)
writeScalarFieldValue(field, if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeBoolean(field: ResponseField, value: Boolean?) {
Expand All @@ -41,7 +43,11 @@ class RealResponseWriter(
}

override fun writeObject(field: ResponseField, block: ((ResponseWriter) -> Unit)?) {
if (field.shouldSkip(variableValues = operationVariables.valueMap())) {
return
}
checkFieldValue(field, block)

if (block == null) {
buffer[field.responseName] = FieldDescriptor(field, null)
return
Expand All @@ -55,7 +61,11 @@ class RealResponseWriter(
field: ResponseField, values: List<T>?,
block: (items: List<T>?, listItemWriter: ResponseWriter.ListItemWriter) -> Unit
) {
if (field.shouldSkip(variableValues = operationVariables.valueMap())) {
return
}
checkFieldValue(field, values)

if (values == null) {
buffer[field.responseName] = FieldDescriptor(field, null)
return
Expand All @@ -70,6 +80,9 @@ class RealResponseWriter(
}

private fun writeScalarFieldValue(field: ResponseField, value: Any?) {
if (field.shouldSkip(variableValues = operationVariables.valueMap())) {
return
}
checkFieldValue(field, value)
buffer[field.responseName] = FieldDescriptor(field, value)
}
Expand Down Expand Up @@ -181,15 +194,15 @@ class RealResponseWriter(
}

override fun writeInt(value: Int?) {
accumulator.add(if (value != null) BigDecimal(value.toLong()) else null)
accumulator.add(if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeLong(value: Long?) {
accumulator.add(if (value != null) BigDecimal(value) else null)
accumulator.add(if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeDouble(value: Double?) {
accumulator.add(if (value != null) BigDecimal(value) else null)
accumulator.add(if (value != null) BigDecimal(value.toString()) else null)
}

override fun writeBoolean(value: Boolean?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ object SimpleOperationResponseParser {
adapter: ResponseAdapter<D>,
variables: Operation.Variables,
customScalarAdapters: CustomScalarAdapters,
): D {
): D? {
if (peek() == JsonReader.Token.NULL) {
return nextNull<D>()
}

beginObject()
val data = adapter.fromResponse(
StreamResponseReader(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo.api.internal

import com.apollographql.apollo.api.ResponseField
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic

Expand Down Expand Up @@ -44,4 +45,24 @@ object Utils {
}
return reference
}

internal fun ResponseField.shouldSkip(variableValues: Map<String, Any?>): Boolean {
for (condition in conditions) {
if (condition is ResponseField.BooleanCondition) {
val conditionValue = variableValues[condition.variableName] as Boolean
if (condition.isInverted) {
// means it's a skip directive
if (conditionValue) {
return true
}
} else {
// means it's an include directive
if (!conditionValue) {
return true
}
}
}
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.apollographql.apollo.interceptor.ApolloRequest
import com.apollographql.apollo.interceptor.ApolloRequestInterceptor
import com.apollographql.apollo.interceptor.ApolloResponse
import com.apollographql.apollo.api.internal.RealResponseReader
import com.apollographql.apollo.internal.response.RealResponseWriter
import com.apollographql.apollo.api.internal.response.RealResponseWriter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
Expand Down Expand Up @@ -69,7 +69,7 @@ class ApolloCacheInterceptor<S>(private val store: S) : ApolloRequestInterceptor
responseNormalizer.willResolveRootQuery(operation);
writer.resolveFields(responseNormalizer)

store.merge(responseNormalizer.records()?.filterNotNull() ?: emptySet(), CacheHeaders.NONE)
store.merge(responseNormalizer.records().toList(), CacheHeaders.NONE)
}

private fun <D : Operation.Data> readFromCache(request: ApolloRequest<D>): Response<D>? {
Expand All @@ -82,7 +82,7 @@ class ApolloCacheInterceptor<S>(private val store: S) : ApolloRequestInterceptor
CacheHeaders.NONE,
RealCacheKeyBuilder()
)
val responseReader = RealResponseReader(operation.variables(), rootRecord, fieldValueResolver, request.customScalarAdapters, NoOpResolveDelegate())
val responseReader = RealResponseReader(operation.variables(), rootRecord, fieldValueResolver, request.customScalarAdapters)
val data = operation.adapter().fromResponse(responseReader)
return builder<D>(operation)
.data(data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query EpisodeHeroNameWithId($episode: Episode) {
hero(episode: $episode) {
id
name
}
}

0 comments on commit 725ec72

Please sign in to comment.