Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(QueryAST marshaller/unmarshaller)!: make ObjectValue a scalar #566

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/core/src/main/scala/sangria/ast/QueryAst.scala
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ case class ObjectValue(
fields: Vector[ObjectField],
comments: Vector[Comment] = Vector.empty,
location: Option[AstLocation] = None)
extends Value {
extends ScalarValue {
lazy val fieldsByName =
fields.foldLeft(ListMap.empty[String, Value]) { case (acc, field) =>
acc + (field.name -> field.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class QueryAstInputUnmarshaller extends InputUnmarshaller[ast.Value] {

def isScalarNode(node: ast.Value) = node.isInstanceOf[ast.ScalarValue]
def getScalarValue(node: ast.Value) = node

import sangria.marshalling.MarshallingUtil._
import sangria.util.tag.@@
import sangria.marshalling.scalaMarshalling._
import queryAst._

def getScalaScalarValue(node: ast.Value) = node match {
case ast.BooleanValue(b, _, _) => b
case ast.BigIntValue(i, _, _) => i
Expand All @@ -59,6 +65,7 @@ class QueryAstInputUnmarshaller extends InputUnmarshaller[ast.Value] {
case ast.IntValue(i, _, _) => i
case ast.StringValue(s, _, _, _, _) => s
case ast.EnumValue(s, _, _) => s
case obj: ast.ObjectValue => convert[ast.Value, Any @@ ScalaInput](obj)
case node => throw new IllegalStateException("Unsupported scalar node: " + node)
}

Expand All @@ -85,6 +92,7 @@ class QueryAstResultMarshaller extends ResultMarshaller {
case v: Double => ast.FloatValue(v)
case v: BigInt => ast.BigIntValue(v)
case v: BigDecimal => ast.BigDecimalValue(v)
case v: ast.ObjectValue => v
case v => throw new IllegalArgumentException("Unsupported scalar value: " + v)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package sangria.marshalling

import scala.util.Success

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import sangria.execution.Executor
import sangria.macros._
import sangria.parser.QueryParser
import sangria.renderer.QueryRenderer
import sangria.util.FutureResultSupport

class ObjectValueAsScalarSpec extends AnyWordSpec with Matchers with FutureResultSupport {

import sangria.schema._

"Unmarshaller" should {
"accept an object value as a scalar parameter" in {
val ast =
graphql"""
schema {
query: Query
}

type Query {
entities(representation: _Any!): _Any
}

union _Entity = State

type State {
id: Int!
value: String!
}
"""

val representationArg = Argument("representation", _Any.Type)

val schema: Schema[Any, Any] = Schema.buildFromAst(
ast,
AstSchemaBuilder.resolverBased[Any](
FieldResolver.map(
"Query" -> Map(
"entities" -> (ctx => ctx.withArgs(representationArg)(arg => arg))
)
),
AdditionalTypes(_Any.Type)))

val Success(query) = QueryParser.parse("""
query FetchState {
entities(representation: { __typename: "State", id: 1 })
}
""")

import sangria.marshalling.queryAst._
import scala.concurrent.ExecutionContext.Implicits.global

val result = Executor
.execute(
schema,
query)
.await

QueryRenderer.render(result, QueryRenderer.PrettyInput) should be(
"""{
| data: {
| entities: "ListMap(__typename -> State, id -> 1)"
| }
|}""".stripMargin)
}
}
}

case class _Any(fields: Map[String, Any])

object _Any {

import sangria.validation.ValueCoercionViolation

case object AnyCoercionViolation extends
ValueCoercionViolation("_Any value expected")

import sangria.ast._
import sangria.marshalling.MarshallingUtil._
import sangria.util.tag.@@
import sangria.marshalling.scalaMarshalling._
import sangria.schema.ScalarType
import queryAst._

val Type = ScalarType[_Any](
name = "_Any",
coerceOutput = {
case (_Any(output), _) => output.toString
},
coerceUserInput = {
case _ => Left(AnyCoercionViolation)
},
coerceInput = {
case obj: ObjectValue =>
Right(_Any(convert[Value, Any @@ ScalaInput](obj).asInstanceOf[Map[String, Any]]))
case _ => Left(AnyCoercionViolation)
})
}