Skip to content

Commit

Permalink
fix: implementation of fromType
Browse files Browse the repository at this point in the history
  • Loading branch information
tusharmath committed Apr 9, 2023
1 parent e3bcc88 commit 4a215e9
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 24 deletions.
11 changes: 11 additions & 0 deletions runtime/src/main/scala/tailcall/runtime/model/Blueprint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ object Blueprint {
case NamedType(_, nonNull) => NamedType(name, nonNull)
case ListType(ofType, nonNull) => ListType(ofType.withName(name), nonNull)
}

final def render: String = {
def renderNonNull(tpe: Type): String =
tpe match {
case NamedType(name, true) => s"$name!"
case ListType(ofType, true) => s"[${renderNonNull(ofType)}]!"
case NamedType(name, false) => name
case ListType(ofType, false) => s"[${renderNonNull(ofType)}]"
}
renderNonNull(self)
}
}

final case class NamedType(name: String, nonNull: Boolean) extends Type
Expand Down
39 changes: 23 additions & 16 deletions runtime/src/main/scala/tailcall/runtime/service/StepGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ object StepGenerator {
final case class BlueprintGenerator(rtm: EvaluationRuntime, document: Blueprint) {
val rootContext: Context = Context(DynamicValue(()))

val stepRef: Map[String, Context => Step[HttpDataLoader]] = document.definitions
// A map of all the object types and a way to construct an instance of them.
val objectStepRef: Map[String, Context => Step[HttpDataLoader]] = document.definitions
.collect { case obj @ Blueprint.ObjectTypeDefinition(_, _, _) => (obj.name, ctx => fromObjectDef(obj, ctx)) }
.toMap

def resolve: StepResult[HttpDataLoader] = {

val queryStep = for {
query <- document.schema.flatMap(_.query)
qStep <- stepRef.get(query)
qStep <- objectStepRef.get(query)
} yield qStep(rootContext)

val mutationStep = for {
mutation <- document.schema.flatMap(_.mutation)
mStep <- stepRef.get(mutation)
mStep <- objectStepRef.get(mutation)
} yield mStep(rootContext)

StepResult(queryStep, mutationStep)
Expand Down Expand Up @@ -78,23 +79,29 @@ object StepGenerator {
Step.ObjectStep(obj.name, obj.fields.map(field => field.name -> fromFieldDefinition(field, ctx)).toMap)
}

/**
* This method converts create a step from a type. There
* is an implicit assumption that the type and the
* actual value, which is available in the ctx.value are
* compatible. We bailout if the types are not
* compatible with the value.
*/
def fromType(tpe: model.Blueprint.Type, ctx: Context): Step[HttpDataLoader] = {
tpe match {
case model.Blueprint.NamedType(name, _) => stepRef.get(name) match {
case Some(stepFunction) => ctx.value match {
case DynamicValue.Sequence(chunks) => Step
.ListStep(chunks.toList.map(value => stepFunction(ctx.copy(value = value))))
// TODO: add unit test for some value
// case DynamicValue.SomeValue(value) => stepFunction(ctx.copy(value = value))
case _ => stepFunction(ctx)
}
case model.Blueprint.NamedType(name, _) => objectStepRef.get(name) match {
case Some(stepFunction) => stepFunction(ctx)
// This is a case for scalar values
case None => Step.PureStep(Transcoder.toResponseValue(ctx.value).getOrElse(Value.NullValue))
}
case model.Blueprint.ListType(ofType, _) => ctx.value match {
case DynamicValue.Sequence(values) => Step
.ListStep(values.map(value => fromType(ofType, ctx.copy(value = value))).toList)
case DynamicValue.SomeValue(value) => fromType(ofType, ctx.copy(value = value))
case _ => Step.ListStep(List(fromType(ofType, ctx)))
case model.Blueprint.ListType(ofType, nonNull) =>
val isNullable = !nonNull
ctx.value match {
// This should be a guarantee we should be able to typecast it safely
case DynamicValue.Sequence(values) => Step
.ListStep(values.toList.map(value => fromType(ofType, ctx.copy(value = value))))
case DynamicValue.SomeValue(DynamicValue.Sequence(values)) if isNullable =>
Step.ListStep(values.toList.map(value => fromType(ofType, ctx.copy(value = value))))
case _ => throw new RuntimeException(s"Unexpected value received for type ${tpe.render}")
}
}
}
Expand Down
23 changes: 15 additions & 8 deletions runtime/src/test/scala/tailcall/runtime/Config2GraphQLSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import tailcall.runtime.model.Config.{Arg, Field, Type}
import tailcall.runtime.model.{Config, Step}
import tailcall.runtime.service.DataLoader.HttpDataLoader
import tailcall.runtime.service._
import zio.json._
import zio.json.ast.Json
import zio.test.Assertion.equalTo
import zio.test.TestAspect.timeout
import zio.test.{ZIOSpecDefault, assertZIO}
Expand Down Expand Up @@ -122,16 +122,23 @@ object Config2GraphQLSpec extends ZIOSpecDefault {
assertZIO(program)(equalTo(expected))
},
test("nested type") {
val value = Map("a" -> "abc".toJsonAST.toOption.get, "b" -> List(Map("bar" -> "bar")).toJsonAST.toOption.get)
.toJsonAST.toOption.get
val value = Json.Obj(
"b" -> Json.Arr(
//
Json.Obj("c" -> Json.Num(1)),
Json.Obj("c" -> Json.Num(2)),
Json.Obj("c" -> Json.Num(3)),
)
)

val config = Config.empty.withQuery("Query").withType(
"Query" -> Type("foo" -> Field.ofType("Foo").withSteps(Step.Constant(value))),
"Foo" -> Type("a" -> Field.ofType("String"), "b" -> Field.ofType("Bar").asList),
"Bar" -> Type("bar" -> Field.ofType("String")),
"Query" -> Type("a" -> Field.ofType("A").withSteps(Step.Constant(value))),
"A" -> Type("b" -> Field.ofType("B").asList),
"B" -> Type("c" -> Field.int),
)

val program = execute(config)("""{foo {a b {bar}}}""")
assertZIO(program)(equalTo("""{"foo":{"a":"abc","b":[{"bar":"bar"}]}}"""))
val program = execute(config)("""{a {b {c}}}""")
assertZIO(program)(equalTo("""{"a":{"b":[{"c":1},{"c":2},{"c":3}]}}"""))
},
).provide(GraphQLGenerator.default, HttpClient.default, DataLoader.http) @@ timeout(10 seconds)
}

0 comments on commit 4a215e9

Please sign in to comment.