From 84e8e9e1242ce32b8006c5c0e32cad9276141ec6 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 23 Jun 2022 09:56:03 +0200 Subject: [PATCH 1/6] Allow sangria-core to compile --- build.sbt | 24 +- .../sangria/macros/AstLiftable.scala | 0 .../sangria/macros/ParseMacro.scala | 1 - .../sangria/macros/package.scala | 0 .../scala-3/sangria/macros/ParseMacro.scala | 36 ++ .../scala-3/sangria/macros/ToExprGivens.scala | 414 ++++++++++++++++++ .../main/scala-3/sangria/macros/package.scala | 16 + .../execution/DeprecationTracker.scala | 8 +- .../execution/QueryReducerExecutor.scala | 4 +- .../scala/sangria/execution/Resolver.scala | 17 +- .../execution/ValueCoercionHelper.scala | 29 +- .../sangria/execution/ValueCollector.scala | 7 +- .../execution/batch/BatchExecutor.scala | 4 +- .../sangria/execution/deferred/Fetcher.scala | 11 +- .../FetcherBasedDeferredResolver.scala | 50 ++- .../scala/sangria/introspection/package.scala | 12 +- .../scala/sangria/marshalling/queryAst.scala | 6 +- .../schema/AstSchemaMaterializer.scala | 5 +- .../sangria/schema/AstSchemaResolver.scala | 14 +- .../main/scala/sangria/schema/Context.scala | 2 +- .../ResolverBasedAstSchemaBuilder.scala | 14 +- .../main/scala/sangria/schema/Schema.scala | 10 +- .../main/scala/sangria/schema/package.scala | 2 +- .../sangria/validation/QueryValidator.scala | 4 +- .../scala/sangria/validation/TypeInfo.scala | 4 +- .../rules/ValuesOfCorrectType.scala | 6 +- .../overlappingfields/SortedArraySet.scala | 5 +- .../scala/sangria/parser/QueryParser.scala | 40 +- 28 files changed, 632 insertions(+), 113 deletions(-) rename modules/core/src/main/{scala => scala-2}/sangria/macros/AstLiftable.scala (100%) rename modules/core/src/main/{scala => scala-2}/sangria/macros/ParseMacro.scala (99%) rename modules/core/src/main/{scala => scala-2}/sangria/macros/package.scala (100%) create mode 100644 modules/core/src/main/scala-3/sangria/macros/ParseMacro.scala create mode 100644 modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala create mode 100644 modules/core/src/main/scala-3/sangria/macros/package.scala diff --git a/build.sbt b/build.sbt index 32ec9b49..c6557547 100644 --- a/build.sbt +++ b/build.sbt @@ -3,8 +3,12 @@ import sbt.Keys._ import com.typesafe.tools.mima.core._ +val isScala3 = Def.setting( + CrossVersion.partialVersion(scalaVersion.value).exists(_._1 == 3) +) + // sbt-github-actions needs configuration in `ThisBuild` -ThisBuild / crossScalaVersions := Seq("2.12.16", "2.13.8") +ThisBuild / crossScalaVersions := Seq("2.12.16", "2.13.8", "3.1.3") ThisBuild / scalaVersion := crossScalaVersions.value.last ThisBuild / githubWorkflowBuildPreamble ++= List( WorkflowStep.Sbt(List("mimaReportBinaryIssues"), name = Some("Check binary compatibility")), @@ -114,13 +118,12 @@ lazy val core = project Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), libraryDependencies ++= Seq( // AST Visitor - "org.sangria-graphql" %% "macro-visit" % "0.1.3", + if (isScala3.value) "org.sangria-graphql" %% "macro-visit" % "0.1.0-SNAPSHOT" + else "org.sangria-graphql" %% "macro-visit" % "0.1.3", // TODO needs release // Marshalling "org.sangria-graphql" %% "sangria-marshalling-api" % "1.0.8", // Streaming "org.sangria-graphql" %% "sangria-streaming-api" % "1.0.3", - // Macros - "org.scala-lang" % "scala-reflect" % scalaVersion.value, // Testing "co.fs2" %% "fs2-core" % "2.5.11" % Test, "org.scalatest" %% "scalatest" % "3.2.13" % Test, @@ -131,9 +134,11 @@ lazy val core = project "org.sangria-graphql" %% "sangria-monix" % "2.0.1" % Test, "eu.timepit" %% "refined" % "0.10.1" % Test, // CATs - "net.jcazevedo" %% "moultingyaml" % "0.4.2" % Test, + ("net.jcazevedo" %% "moultingyaml" % "0.4.2" % Test).cross(CrossVersion.for3Use2_13), "io.github.classgraph" % "classgraph" % "4.8.149" % Test - ), + ) ++ (if (isScala3.value) Seq.empty + else Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value)), // Macros + apiURL := { val ver = CrossVersion.binaryScalaVersion(scalaVersion.value) Some(url(s"https://www.javadoc.io/doc/org.sangria-graphql/sangria-core_$ver/latest/")) @@ -149,10 +154,9 @@ lazy val derivation = project name := "sangria-derivation", Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), - libraryDependencies ++= Seq( - // Macros - "org.scala-lang" % "scala-reflect" % scalaVersion.value - ), + // Macros + libraryDependencies ++= (if (isScala3.value) Seq.empty + else Seq( "org.scala-lang" % "scala-reflect" % scalaVersion.value)), apiURL := { val ver = CrossVersion.binaryScalaVersion(scalaVersion.value) Some(url(s"https://www.javadoc.io/doc/org.sangria-graphql/sangria-derivation_$ver/latest/")) diff --git a/modules/core/src/main/scala/sangria/macros/AstLiftable.scala b/modules/core/src/main/scala-2/sangria/macros/AstLiftable.scala similarity index 100% rename from modules/core/src/main/scala/sangria/macros/AstLiftable.scala rename to modules/core/src/main/scala-2/sangria/macros/AstLiftable.scala diff --git a/modules/core/src/main/scala/sangria/macros/ParseMacro.scala b/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala similarity index 99% rename from modules/core/src/main/scala/sangria/macros/ParseMacro.scala rename to modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala index d524531b..e703b98b 100644 --- a/modules/core/src/main/scala/sangria/macros/ParseMacro.scala +++ b/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala @@ -9,7 +9,6 @@ class ParseMacro(context: blackbox.Context) val c = context } with MacroAstLiftable { - import c.universe._ def impl(args: Expr[Any]*) = diff --git a/modules/core/src/main/scala/sangria/macros/package.scala b/modules/core/src/main/scala-2/sangria/macros/package.scala similarity index 100% rename from modules/core/src/main/scala/sangria/macros/package.scala rename to modules/core/src/main/scala-2/sangria/macros/package.scala diff --git a/modules/core/src/main/scala-3/sangria/macros/ParseMacro.scala b/modules/core/src/main/scala-3/sangria/macros/ParseMacro.scala new file mode 100644 index 00000000..e244207b --- /dev/null +++ b/modules/core/src/main/scala-3/sangria/macros/ParseMacro.scala @@ -0,0 +1,36 @@ +package sangria.macros + +import scala.quoted._ +import sangria.parser.{QueryParser, SyntaxError} + +object ParseMacro extends ToExprGivens { + def impl(using Quotes)(strCtxExpr: Expr[StringContext]): Expr[sangria.ast.Document] = + val parts = strCtxExpr.valueOrAbort.parts + if parts.length > 1 then + throw new Exception( + "String interpolation is not supported for `graphql`/`gql` macro at the moment.") + parts.headOption match + case Some(str) => + Expr(QueryParser.parse(str).get) + case None => throw new Exception("Invalid `graphql` invocation syntax.") + + def implInput(using Quotes)(strCtxExpr: Expr[StringContext]): Expr[sangria.ast.Value] = + val parts = strCtxExpr.valueOrAbort.parts + if parts.length > 1 then + throw new Exception( + "String interpolation is not supported for `graphqlInput`/`gqlInp` macro at the moment.") + parts.headOption match + case Some(str) => + Expr(QueryParser.parseInput(str).get) + case None => throw new Exception("Invalid `graphql` invocation syntax.") + + def implInputDoc(using Quotes)(strCtxExpr: Expr[StringContext]): Expr[sangria.ast.InputDocument] = + val parts = strCtxExpr.valueOrAbort.parts + if parts.length > 1 then + throw new Exception( + "String interpolation is not supported for `gqlInpDoc` macro at the moment.") + parts.headOption match + case Some(str) => + Expr(QueryParser.parseInputDocument(str).get) + case None => throw new Exception("Invalid `graphql` invocation syntax.") +} diff --git a/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala new file mode 100644 index 00000000..6c991c6f --- /dev/null +++ b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala @@ -0,0 +1,414 @@ +package sangria.macros + +import scala.quoted._ +import sangria.ast._ + +trait ToExprGivens { + + given [T: scala.quoted.Type: ToExpr]: ToExpr[Vector[T]] with + def apply(vector: Vector[T])(using Quotes): Expr[Vector[T]] = + '{ ${ Expr.ofSeq(vector.map(summon[ToExpr[T]].apply)) }.toVector } + + given ToExpr[AstLocation] with + def apply(position: AstLocation)(using Quotes) = position match + case sangria.ast.AstLocation(id, i, l, c) => + '{ sangria.ast.AstLocation(${ Expr(id) }, ${ Expr(i) }, ${ Expr(l) }, ${ Expr(c) }) } + + given ToExpr[OperationType] with + def apply(operationType: OperationType)(using Quotes) = operationType match + case OperationType.Query => '{ sangria.ast.OperationType.Query } + case OperationType.Mutation => '{ sangria.ast.OperationType.Mutation } + case OperationType.Subscription => '{ sangria.ast.OperationType.Subscription } + + given ToExpr[sangria.ast.Type] with + def apply(tpe: sangria.ast.Type)(using Quotes): Expr[sangria.ast.Type] = tpe match + case NamedType(n, p) => '{ sangria.ast.NamedType(${ Expr(n) }, ${ Expr(p) }) } + case NotNullType(o, p) => '{ sangria.ast.NotNullType(${ Expr(o) }, ${ Expr(p) }) } + case ListType(o, p) => '{ sangria.ast.ListType(${ Expr(o) }, ${ Expr(p) }) } + + given ToExpr[sangria.ast.NamedType] with + def apply(tpe: sangria.ast.NamedType)(using Quotes): Expr[sangria.ast.NamedType] = tpe match + case NamedType(n, p) => '{ sangria.ast.NamedType(${ Expr(n) }, ${ Expr(p) }) } + + given ToExpr[Comment] with + def apply(comment: Comment)(using Quotes) = comment match + case Comment(l, p) => + '{ sangria.ast.Comment(${ Expr(l) }, ${ Expr(p) }) } + + given ToExpr[VariableDefinition] with + def apply(varDef: VariableDefinition)(using Quotes) = varDef match + case VariableDefinition(n, t, d, dirs, c, p) => + '{ + sangria.ast.VariableDefinition( + ${ Expr(n) }, + ${ Expr(t) }, + ${ Expr(d) }, + ${ Expr(dirs) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[InputValueDefinition] with + def apply(inpValDef: InputValueDefinition)(using Quotes) = inpValDef match + case InputValueDefinition(n, v, de, di, desc, c, p) => + '{ + sangria.ast.InputValueDefinition( + ${ Expr(n) }, + ${ Expr(v) }, + ${ Expr(de) }, + ${ Expr(di) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[OperationTypeDefinition] with + def apply(inpOpTpeDef: OperationTypeDefinition)(using Quotes) = inpOpTpeDef match + case OperationTypeDefinition(o, t, c, p) => + '{ OperationTypeDefinition(${ Expr(o) }, ${ Expr(t) }, ${ Expr(c) }, ${ Expr(p) }) } + + given ToExpr[EnumValueDefinition] with + def apply(enumValDef: EnumValueDefinition)(using Quotes) = enumValDef match + case EnumValueDefinition(n, d, desc, c, p) => + '{ + EnumValueDefinition( + ${ Expr(n) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[FieldDefinition] with + def apply(liftFieldDef: FieldDefinition)(using Quotes) = liftFieldDef match + case FieldDefinition(n, f, a, d, desc, c, p) => + '{ + FieldDefinition( + ${ Expr(n) }, + ${ Expr(f) }, + ${ Expr(a) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[DirectiveLocation] with + def apply(dirLocDef: DirectiveLocation)(using Quotes) = dirLocDef match + case DirectiveLocation(n, c, p) => + '{ DirectiveLocation(${ Expr(n) }, ${ Expr(c) }, ${ Expr(p) }) } + + given ToExpr[Definition] with + def apply(definition: Definition)(using Quotes) = definition match + case OperationDefinition(o, n, v, d, s, c, tc, p) => + '{ + sangria.ast.OperationDefinition( + ${ Expr(o) }, + ${ Expr(n) }, + ${ Expr(v) }, + ${ Expr(d) }, + ${ Expr(s) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case FragmentDefinition(n, t, d, s, v, c, tc, p) => + '{ + sangria.ast.FragmentDefinition( + ${ Expr(n) }, + ${ Expr(t) }, + ${ Expr(d) }, + ${ Expr(s) }, + ${ Expr(v) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + + case DirectiveDefinition(n, a, l, desc, c, p) => + '{ + sangria.ast.DirectiveDefinition( + ${ Expr(n) }, + ${ Expr(a) }, + ${ Expr(l) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + case SchemaDefinition(o, d, desc, c, tc, p) => + '{ + sangria.ast.SchemaDefinition( + ${ Expr(o) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + + case ObjectTypeExtensionDefinition(n, i, f, d, c, tc, p) => + '{ + sangria.ast.ObjectTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(i) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case InterfaceTypeExtensionDefinition(n, f, d, c, tc, p) => + '{ + sangria.ast.InterfaceTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case InputObjectTypeExtensionDefinition(n, f, d, c, tc, p) => + '{ + sangria.ast.InputObjectTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case UnionTypeExtensionDefinition(n, t, d, c, p) => + '{ + sangria.ast.UnionTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(t) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + case EnumTypeExtensionDefinition(n, v, d, c, tc, p) => + '{ + sangria.ast.EnumTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(v) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case ScalarTypeExtensionDefinition(n, d, c, p) => + '{ + sangria.ast.ScalarTypeExtensionDefinition( + ${ Expr(n) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + case SchemaExtensionDefinition(o, d, c, tc, p) => + '{ + sangria.ast.SchemaExtensionDefinition( + ${ Expr(o) }, + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + + case EnumTypeDefinition(n, v, d, desc, c, tc, p) => + '{ + sangria.ast.EnumTypeDefinition( + ${ Expr(n) }, + ${ Expr(v) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case InputObjectTypeDefinition(n, f, d, desc, c, tc, p) => + '{ + sangria.ast.InputObjectTypeDefinition( + ${ Expr(n) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case InterfaceTypeDefinition(n, f, d, desc, c, tc, p) => + '{ + sangria.ast.InterfaceTypeDefinition( + ${ Expr(n) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case ObjectTypeDefinition(n, i, f, d, desc, c, tc, p) => + '{ + sangria.ast.ObjectTypeDefinition( + ${ Expr(n) }, + ${ Expr(i) }, + ${ Expr(f) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case ScalarTypeDefinition(n, d, desc, c, p) => + '{ + sangria.ast.ScalarTypeDefinition( + ${ Expr(n) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + case UnionTypeDefinition(n, t, d, desc, c, p) => + '{ + sangria.ast.UnionTypeDefinition( + ${ Expr(n) }, + ${ Expr(t) }, + ${ Expr(d) }, + ${ Expr(desc) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[Argument] with + def apply(argument: Argument)(using Quotes) = argument match + case Argument(n, v, c, p) => + '{ sangria.ast.Argument(${ Expr(n) }, ${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + + given ToExpr[ObjectField] with + def apply(namedValue: ObjectField)(using Quotes) = namedValue match + case ObjectField(n, v, c, p) => + '{ sangria.ast.ObjectField(${ Expr(n) }, ${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + + given ToExpr[sangria.ast.Value] with + def apply(value: sangria.ast.Value)(using Quotes): Expr[Value] = value match + case IntValue(v, c, p) => '{ sangria.ast.IntValue(${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + case FloatValue(v, c, p) => + '{ sangria.ast.FloatValue(${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + case StringValue(v, b, r, c, p) => + '{ + sangria.ast.StringValue( + ${ Expr(v) }, + ${ Expr(b) }, + ${ Expr(r) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + case BooleanValue(v, c, p) => + '{ sangria.ast.BooleanValue(${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + case NullValue(c, p) => '{ sangria.ast.NullValue(${ Expr(c) }, ${ Expr(p) }) } + case EnumValue(v, c, p) => + '{ sangria.ast.EnumValue(${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + case ListValue(v, c, p) => + '{ sangria.ast.ListValue(${ Expr(v) }, ${ Expr(c) }, ${ Expr(p) }) } + case ObjectValue(f, c, p) => + '{ sangria.ast.ObjectValue(${ Expr(f) }, ${ Expr(c) }, ${ Expr(p) }) } + case VariableValue(n, c, p) => + '{ sangria.ast.VariableValue(${ Expr(n) }, ${ Expr(c) }, ${ Expr(p) }) } + case BigIntValue(v, c, p) => + '{ + sangria.ast.BigIntValue( + scala.math.BigInt(${ Expr(v.toByteArray) }), + ${ Expr(c) }, + ${ Expr(p) }) + } + case sangria.ast.BigDecimalValue(v, c, p) => + '{ + sangria.ast.BigDecimalValue( + scala.math.BigDecimal(${ Expr(v.toString()) }), + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[sangria.ast.StringValue] with + def apply(value: sangria.ast.StringValue)(using Quotes): Expr[StringValue] = value match + case StringValue(v, b, r, c, p) => + '{ + sangria.ast.StringValue( + ${ Expr(v) }, + ${ Expr(b) }, + ${ Expr(r) }, + ${ Expr(c) }, + ${ Expr(p) }) + } + + given ToExpr[Directive] with + def apply(directive: Directive)(using Quotes) = directive match + case Directive(n, a, c, p) => + '{ sangria.ast.Directive(${ Expr(n) }, ${ Expr(a) }, ${ Expr(c) }, ${ Expr(p) }) } + + given ToExpr[Selection] with + def apply(selection: Selection)(using Quotes) = selection match + case Field(a, n, arg, d, s, c, tc, p) => + '{ + sangria.ast.Field( + ${ Expr(a) }, + ${ Expr(n) }, + ${ Expr(arg) }, + ${ Expr(d) }, + ${ Expr(s) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + case FragmentSpread(n, d, c, p) => + '{ sangria.ast.FragmentSpread(${ Expr(n) }, ${ Expr(d) }, ${ Expr(c) }, ${ Expr(p) }) } + case InlineFragment(t, d, s, c, tc, p) => + '{ + sangria.ast.InlineFragment( + ${ Expr(t) }, + ${ Expr(d) }, + ${ Expr(s) }, + ${ Expr(c) }, + ${ Expr(tc) }, + ${ Expr(p) }) + } + + given ToExpr[Document] with + def apply(document: Document)(using Quotes) = document match + case doc @ Document(d, c, p, _) => + '{ + Document( + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(p) }, + Some( + new DefaultSourceMapper( + ${ Expr(document.sourceMapper.get.id) }, + sangria.parser.ParserConfig.parboiledToSourceMapper(org.parboiled2.ParserInput(${ + Expr(doc.source.get) + })) + ) + ) + ) + } + + given ToExpr[InputDocument] with + def apply(inputDocument: InputDocument)(using Quotes) = inputDocument match + case doc @ InputDocument(d, c, p, _) => + '{ + sangria.ast.InputDocument( + ${ Expr(d) }, + ${ Expr(c) }, + ${ Expr(p) }, + _root_.scala.Some( + new _root_.sangria.ast.DefaultSourceMapper( + ${ Expr { doc.sourceMapper.get.id } }, + _root_.sangria.parser.ParserConfig + .parboiledToSourceMapper(_root_.org.parboiled2.ParserInput(${ + Expr(doc.source.get) + })) + ) + ) + ) + } +} diff --git a/modules/core/src/main/scala-3/sangria/macros/package.scala b/modules/core/src/main/scala-3/sangria/macros/package.scala new file mode 100644 index 00000000..9dd01bda --- /dev/null +++ b/modules/core/src/main/scala-3/sangria/macros/package.scala @@ -0,0 +1,16 @@ +package sangria + +import sangria.ast.{Document, InputDocument, Value} + +package object macros { + extension (inline sc: StringContext) { + inline def gql(inline args: Any*): Document = ${ ParseMacro.impl('sc) } + inline def gqlInp(inline args: Any*): Value = ${ ParseMacro.implInput('sc) } + inline def gqlInpDoc(inline args: Any*): InputDocument = ${ ParseMacro.implInputDoc('sc) } + + inline def graphql(inline args: Any*): Document = ${ ParseMacro.impl('sc) } + inline def graphqlInput(inline args: Any*): Value = ${ ParseMacro.implInput('sc) } + inline def graphqlInputDoc(inline args: Any*): InputDocument = ${ ParseMacro.implInputDoc('sc) } + } + +} diff --git a/modules/core/src/main/scala/sangria/execution/DeprecationTracker.scala b/modules/core/src/main/scala/sangria/execution/DeprecationTracker.scala index 13336dc3..5aa5ed66 100644 --- a/modules/core/src/main/scala/sangria/execution/DeprecationTracker.scala +++ b/modules/core/src/main/scala/sangria/execution/DeprecationTracker.scala @@ -4,7 +4,7 @@ import sangria.schema.{Context, EnumType} trait DeprecationTracker { def deprecatedFieldUsed[Ctx](ctx: Context[Ctx, _]): Unit - def deprecatedEnumValueUsed[T, Ctx](enum: EnumType[T], value: T, userContext: Ctx): Unit + def deprecatedEnumValueUsed[T, Ctx](`enum`: EnumType[T], value: T, userContext: Ctx): Unit } object DeprecationTracker { @@ -14,7 +14,7 @@ object DeprecationTracker { object NilDeprecationTracker extends DeprecationTracker { def deprecatedFieldUsed[Ctx](ctx: Context[Ctx, _]) = () - def deprecatedEnumValueUsed[T, Ctx](enum: EnumType[T], value: T, userContext: Ctx) = () + def deprecatedEnumValueUsed[T, Ctx](`enum`: EnumType[T], value: T, userContext: Ctx) = () } class LoggingDeprecationTracker(logFn: String => Unit) extends DeprecationTracker { @@ -22,6 +22,6 @@ class LoggingDeprecationTracker(logFn: String => Unit) extends DeprecationTracke logFn( s"Deprecated field '${ctx.parentType.name}.${ctx.field.name}' used at path '${ctx.path}'.") - def deprecatedEnumValueUsed[T, Ctx](enum: EnumType[T], value: T, userContext: Ctx) = - logFn(s"Deprecated enum value '$value' used of enum '${enum.name}'.") + def deprecatedEnumValueUsed[T, Ctx](`enum`: EnumType[T], value: T, userContext: Ctx) = + logFn(s"Deprecated enum value '$value' used of enum '${`enum`.name}'.") } diff --git a/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala b/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala index 2675d568..8f7fb555 100644 --- a/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala +++ b/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala @@ -27,7 +27,7 @@ object QueryReducerExecutor { Future.failed(ValidationError(violations, exceptionHandler)) else { val scalarMiddleware = Middleware.composeFromScalarMiddleware(middleware, userContext) - val valueCollector = new ValueCollector[Ctx, _ @@ ScalaInput]( + val valueCollector = new ValueCollector[Ctx, Any @@ ScalaInput]( schema, InputUnmarshaller.emptyMapVars, queryAst.sourceMapper, @@ -35,7 +35,7 @@ object QueryReducerExecutor { userContext, exceptionHandler, scalarMiddleware, - true)(InputUnmarshaller.scalaInputUnmarshaller[_ @@ ScalaInput]) + true)(InputUnmarshaller.scalaInputUnmarshaller[Any]) val executionResult = for { operation <- Executor.getOperation(exceptionHandler, queryAst, operationName) diff --git a/modules/core/src/main/scala/sangria/execution/Resolver.scala b/modules/core/src/main/scala/sangria/execution/Resolver.scala index bb3a89b6..b8c69189 100644 --- a/modules/core/src/main/scala/sangria/execution/Resolver.scala +++ b/modules/core/src/main/scala/sangria/execution/Resolver.scala @@ -1166,7 +1166,7 @@ class Resolver[Ctx]( value: Future[Any]): Future[(Any, Vector[Throwable])] = deferred match { case MappingDeferred(d, fn) => mapAllDeferred(d, value).map { case (v, errors) => - val (mappedV, newErrors) = fn(v) + val (mappedV, newErrors) = fn.asInstanceOf[Any => (Any, Seq[Throwable])](v) mappedV -> (errors ++ newErrors) } case _ => value.map(_ -> Vector.empty) @@ -1303,19 +1303,19 @@ class Resolver[Ctx]( scalar.toScalar(value), userCtx, Some(scalar)) - case enum: EnumType[Any @unchecked] => + case enumT: EnumType[Any @unchecked] => try Result( ErrorRegistry.empty, if (isUndefinedValue(value)) None else { - val coerced = enum.coerceOutput(value) + val coerced = enumT.coerceOutput(value) if (isUndefinedValue(coerced)) None else - Some(marshalEnumValue(coerced, marshaller, enum.name)) + Some(marshalEnumValue(coerced, marshaller, enumT.name)) } ) catch { @@ -1507,9 +1507,10 @@ class Resolver[Ctx]( case Some(action) => action case None => field.resolve match { - case pfn: Projector[Ctx, _, _] => - pfn(updatedCtx, collectProjections(path, field, astFields, pfn.maxLevel)) - case fn => fn(updatedCtx) + case pfn: Projector[Ctx, Any, _] => + pfn(updatedCtx.asInstanceOf[Context[Ctx, Any]], collectProjections(path, field, astFields, pfn.maxLevel)) + case fn => + fn(updatedCtx) } } @@ -1879,7 +1880,7 @@ object Resolver { case ast.BigDecimalValue(f, _, _) => marshaller.scalarNode(f, typeName, scalarInfo) case ast.BooleanValue(b, _, _) => marshaller.scalarNode(b, typeName, scalarInfo) case ast.NullValue(_, _) => marshaller.nullNode - case ast.EnumValue(enum, _, _) => marshaller.enumNode(enum, typeName) + case ast.EnumValue(enumT, _, _) => marshaller.enumNode(enumT, typeName) case ast.ListValue(values, _, _) => marshaller.arrayNode(values.map(marshalAstValue(_, marshaller, typeName, scalarInfo))) case ast.ObjectValue(values, _, _) => diff --git a/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala b/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala index 0b9b809d..38fa038d 100644 --- a/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala +++ b/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala @@ -51,14 +51,14 @@ class ValueCoercionHelper[Ctx]( isArgument: Boolean, fromScalarMiddleware: Option[(Any, InputType[_]) => Option[Either[Violation, Any]]], allowErrorsOnDefault: Boolean = false, - valueMap: Nothing => Any = defaultValueMapFn, + valueMap: Any => Any = defaultValueMapFn, defaultValueInfo: Option[Cache[String, Any]] = None, undefinedValues: Option[VectorBuilder[String]] = None )( acc: marshaller.MapBuilder, value: Option[Either[Vector[Violation], Trinary[marshaller.Node]]] ): marshaller.MapBuilder = { - val valueMapTyped = valueMap.asInstanceOf[Any => marshaller.Node] + val valueMapTyped: Any => marshaller.Node = valueMap.asInstanceOf[Any => marshaller.Node] def locations = inputFor match { @@ -508,10 +508,10 @@ class ValueCoercionHelper[Ctx]( case (scalar: ScalarAlias[_, _], value) => nullScalarViolation(scalar.aliasFor, value) - case (enum: EnumType[_], value) if iu.isEnumNode(value) => + case (enumT: EnumType[_], value) if iu.isEnumNode(value) => val coerced = iu.getScalarValue(value) match { - case node: ast.Value => enum.coerceInput(node) - case other => enum.coerceUserInput(other) + case node: ast.Value => enumT.coerceInput(node) + case other => enumT.coerceUserInput(other) } coerced.fold( @@ -527,18 +527,19 @@ class ValueCoercionHelper[Ctx]( isArgument))), { case (v, deprecated) => if (deprecated && userContext.isDefined) - deprecationTracker.deprecatedEnumValueUsed(enum, v, userContext.get) + deprecationTracker.deprecatedEnumValueUsed(enumT, v, userContext.get) val prepared = firstKindMarshaller match { case raw: RawResultMarshaller => raw.rawScalarNode(v) - case standard => Resolver.marshalEnumValue(enum.coerceOutput(v), standard, enum.name) + case standard => + Resolver.marshalEnumValue(enumT.coerceOutput(v), standard, enumT.name) } Right(defined(prepared.asInstanceOf[marshaller.Node])) } ) - case (enum: EnumType[_], value) if iu.isDefined(value) => + case (enumT: EnumType[_], value) if iu.isDefined(value) => Left( Vector( FieldCoercionViolation( @@ -549,14 +550,14 @@ class ValueCoercionHelper[Ctx]( errorPrefix, isArgument))) - case (enum: EnumType[_], value) => + case (enumT: EnumType[_], value) => Left( Vector( FieldCoercionViolation( fieldPath, NullValueForNotNullTypeViolation( fieldPath, - SchemaRenderer.renderTypeName(enum), + SchemaRenderer.renderTypeName(enumT), sourceMapper, valuePosition(inputFor, value)), sourceMapper, @@ -655,10 +656,10 @@ class ValueCoercionHelper[Ctx]( } } - case (enum: EnumType[_], Some(value)) if um.isEnumNode(value) => + case (enumT: EnumType[_], Some(value)) if um.isEnumNode(value) => val coerced = um.getScalarValue(value) match { - case node: ast.Value => enum.coerceInput(node) - case other => enum.coerceUserInput(other) + case node: ast.Value => enumT.coerceInput(node) + case other => enumT.coerceUserInput(other) } coerced match { @@ -666,7 +667,7 @@ class ValueCoercionHelper[Ctx]( case _ => Vector.empty } - case (enum: EnumType[_], Some(value)) => + case (enumT: EnumType[_], Some(value)) => Vector(EnumCoercionViolation) case _ => diff --git a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala index 1f8d842e..110b5fdc 100644 --- a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala +++ b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala @@ -119,8 +119,7 @@ object ValueCollector { ): Try[Args] = { import coercionHelper._ - if (argumentDefs.isEmpty) - emptyArgs + if (argumentDefs.isEmpty) emptyArgs else { val astArgMap = argumentAsts.groupBy(_.name).map { case (k, v) => (k, v.head) } val marshaller = CoercedScalaResultMarshaller.default @@ -147,7 +146,9 @@ object ValueCollector { marshaller, fromInput.marshaller, errors = errors, - valueMap = fromInput.fromResult, + valueMap = + ((a: Any) => fromInput.fromResult(a.asInstanceOf[fromInput.marshaller.Node])) + .asInstanceOf[Any => Any], defaultValueInfo = defaultInfo, undefinedValues = undefinedArgs, isArgument = true, diff --git a/modules/core/src/main/scala/sangria/execution/batch/BatchExecutor.scala b/modules/core/src/main/scala/sangria/execution/batch/BatchExecutor.scala index f08dc678..f59d49cd 100644 --- a/modules/core/src/main/scala/sangria/execution/batch/BatchExecutor.scala +++ b/modules/core/src/main/scala/sangria/execution/batch/BatchExecutor.scala @@ -277,8 +277,8 @@ object BatchExecutor { } } - exports.foreach { export => - visitPath(export.exportedName, result, "data" +: export.path) + exports.foreach { `export` => + visitPath(`export`.exportedName, result, "data" +: `export`.path) } } diff --git a/modules/core/src/main/scala/sangria/execution/deferred/Fetcher.scala b/modules/core/src/main/scala/sangria/execution/deferred/Fetcher.scala index 849ed9c8..28839290 100644 --- a/modules/core/src/main/scala/sangria/execution/deferred/Fetcher.scala +++ b/modules/core/src/main/scala/sangria/execution/deferred/Fetcher.scala @@ -70,11 +70,14 @@ class Fetcher[Ctx, Res, RelRes, Id]( allIds.getOrElseUpdate(rel, MutableSet[Any]()) += id deferred.foreach { - case FetcherDeferredRel(s, rel, relId) if s eq this => addToSet(rel, relId) - case FetcherDeferredRelOpt(s, rel, relId) if s eq this => addToSet(rel, relId) - case FetcherDeferredRelSeq(s, rel, relId) if s eq this => addToSet(rel, relId) + case FetcherDeferredRel(s, rel, relId) if s eq this => + addToSet(rel.asInstanceOf[Relation[Any, Any, Any]], relId) + case FetcherDeferredRelOpt(s, rel, relId) if s eq this => + addToSet(rel.asInstanceOf[Relation[Any, Any, Any]], relId) + case FetcherDeferredRelSeq(s, rel, relId) if s eq this => + addToSet(rel.asInstanceOf[Relation[Any, Any, Any]], relId) case FetcherDeferredRelSeqMany(s, rel, relIds) if s eq this => - relIds.foreach(addToSet(rel, _)) + relIds.foreach(addToSet(rel.asInstanceOf[Relation[Any, Any, Any]], _)) case _ => // skip } diff --git a/modules/core/src/main/scala/sangria/execution/deferred/FetcherBasedDeferredResolver.scala b/modules/core/src/main/scala/sangria/execution/deferred/FetcherBasedDeferredResolver.scala index 418556f2..1f7a74c1 100644 --- a/modules/core/src/main/scala/sangria/execution/deferred/FetcherBasedDeferredResolver.scala +++ b/modules/core/src/main/scala/sangria/execution/deferred/FetcherBasedDeferredResolver.scala @@ -117,48 +117,66 @@ class FetcherBasedDeferredResolver[-Ctx]( val f = ctx.fetcher.asInstanceOf[Fetcher[Any, Any, Any, Any]] deferred match { - case FetcherDeferredRel(_, rel, relId) - if cachedResults.contains(rel) && cachedResults(rel).contains(relId) => - cachedResults(rel)(relId).headOption match { + case FetcherDeferredRel(_, rel: Relation[Any, Any, Any], relId) + if cachedResults.contains(rel.asInstanceOf[Relation[Any, Any, Any]]) && cachedResults( + rel.asInstanceOf[Relation[Any, Any, Any]]).contains(relId) => + cachedResults(rel.asInstanceOf[Relation[Any, Any, Any]])(relId).headOption match { case Some(head) => head case None => throw AbsentDeferredRelValueError(f, deferred, rel, relId) } - case FetcherDeferredRel(_, rel, relId) if m.contains(rel) && m(rel).contains(relId) => - m(rel)(relId).headOption match { + case FetcherDeferredRel(_, rel, relId) + if m.contains(rel.asInstanceOf[Relation[Any, Any, Any]]) && m( + rel.asInstanceOf[Relation[Any, Any, Any]]).contains(relId) => + m(rel.asInstanceOf[Relation[Any, Any, Any]])(relId).headOption match { case Some(head) => head - case None => throw AbsentDeferredRelValueError(f, deferred, rel, relId) + case None => + throw AbsentDeferredRelValueError( + f, + deferred, + rel.asInstanceOf[Relation[Any, Any, Any]], + relId) } case FetcherDeferredRel(_, rel, relId) => - throw AbsentDeferredRelValueError(f, deferred, rel, relId) + throw AbsentDeferredRelValueError( + f, + deferred, + rel.asInstanceOf[Relation[Any, Any, Any]], + relId) - case FetcherDeferredRelOpt(_, rel, relId) - if cachedResults.contains(rel) && cachedResults(rel).contains(relId) => - cachedResults(rel)(relId).headOption + case FetcherDeferredRelOpt(_, rel: Relation[Any, Any, Any], relId) + if cachedResults.contains(rel.asInstanceOf[Relation[Any, Any, Any]]) && cachedResults( + rel.asInstanceOf[Relation[Any, Any, Any]]).contains(relId) => + cachedResults(rel.asInstanceOf[Relation[Any, Any, Any]])(relId).headOption - case FetcherDeferredRelOpt(_, rel, relId) if m.contains(rel) && m(rel).contains(relId) => - m(rel)(relId).headOption + case FetcherDeferredRelOpt(_, rel, relId) + if m.contains(rel.asInstanceOf[Relation[Any, Any, Any]]) && m( + rel.asInstanceOf[Relation[Any, Any, Any]]).contains(relId) => + m(rel.asInstanceOf[Relation[Any, Any, Any]])(relId).headOption case FetcherDeferredRelOpt(_, _, _) => None - case FetcherDeferredRelSeq(_, rel, relId) + case FetcherDeferredRelSeq(_, rel: Relation[Any, Any, Any], relId) if cachedResults.contains(rel) && cachedResults(rel).contains(relId) => cachedResults(rel)(relId) - case FetcherDeferredRelSeq(_, rel, relId) if m.contains(rel) && m(rel).contains(relId) => + case FetcherDeferredRelSeq(_, rel: Relation[Any, Any, Any], relId) + if m.contains(rel) && m(rel).contains(relId) => m(rel)(relId) case FetcherDeferredRelSeq(_, _, _) => Vector.empty - case FetcherDeferredRelSeqMany(_, rel, relIds) if cachedResults.contains(rel) => + case FetcherDeferredRelSeqMany(_, rel: Relation[Any, Any, Any], relIds) + if cachedResults.contains(rel) => removeDuplicates( f, relIds.flatMap(relId => cachedResults(rel).getOrElse(relId, Vector.empty))) - case FetcherDeferredRelSeqMany(_, rel, relIds) if m.contains(rel) => + case FetcherDeferredRelSeqMany(_, rel: Relation[Any, Any, Any], relIds) + if m.contains(rel) => removeDuplicates(f, relIds.flatMap(relId => m(rel).getOrElse(relId, Vector.empty))) case FetcherDeferredRelSeqMany(_, _, _) => diff --git a/modules/core/src/main/scala/sangria/introspection/package.scala b/modules/core/src/main/scala/sangria/introspection/package.scala index ecc2af9b..6730bcb7 100644 --- a/modules/core/src/main/scala/sangria/introspection/package.scala +++ b/modules/core/src/main/scala/sangria/introspection/package.scala @@ -3,6 +3,9 @@ package sangria import sangria.parser.QueryParser import sangria.schema._ +import sangria.util.tag.@@ // Scala 3 workaround +import sangria.marshalling.FromInput.CoercedScalaResult + package object introspection { object TypeKind extends Enumeration { val Scalar, Object, Interface, Union, Enum, InputObject, List, NonNull = Value @@ -172,7 +175,10 @@ package object introspection { ) ) - val includeDeprecated = Argument("includeDeprecated", OptionInputType(BooleanType), false) + val includeDeprecated = Argument[Option[Boolean @@ CoercedScalaResult], Boolean]( + "includeDeprecated", + OptionInputType(BooleanType), + false) private def getKind(value: (Boolean, Type)) = { def identifyKind(t: Type, optional: Boolean): TypeKind.Value = t match { @@ -294,8 +300,8 @@ package object introspection { val incDep = ctx.arg(includeDeprecated) ctx.value._2 match { - case enum: EnumType[_] if incDep => Some(enum.values) - case enum: EnumType[_] => Some(enum.values.filter(_.deprecationReason.isEmpty)) + case enumT: EnumType[_] if incDep => Some(enumT.values) + case enumT: EnumType[_] => Some(enumT.values.filter(_.deprecationReason.isEmpty)) case _ => None } } diff --git a/modules/core/src/main/scala/sangria/marshalling/queryAst.scala b/modules/core/src/main/scala/sangria/marshalling/queryAst.scala index 2470b6a0..990e64cb 100644 --- a/modules/core/src/main/scala/sangria/marshalling/queryAst.scala +++ b/modules/core/src/main/scala/sangria/marshalling/queryAst.scala @@ -5,9 +5,9 @@ import sangria.parser.QueryParser import sangria.renderer.QueryRenderer object queryAst { - implicit val queryAstInputUnmarshaller = new QueryAstInputUnmarshaller + implicit val queryAstInputUnmarshaller: QueryAstInputUnmarshaller = new QueryAstInputUnmarshaller - implicit val queryAstResultMarshaller = new QueryAstResultMarshaller + implicit val queryAstResultMarshaller: QueryAstResultMarshaller = new QueryAstResultMarshaller implicit object QueryAstMarshallerForType extends ResultMarshallerForType[ast.Value] { val marshaller = queryAstResultMarshaller @@ -22,7 +22,7 @@ object queryAst { private object QueryAstFromInput extends FromInput[ast.Value] { val marshaller = queryAstResultMarshaller - def fromResult(node: marshaller.Node) = node + def fromResult(node: marshaller.Node) = node.asInstanceOf[sangria.ast.Value] } implicit def queryAstFromInput[T <: ast.Value]: FromInput[T] = diff --git a/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala b/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala index 076c0364..86df640f 100644 --- a/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala +++ b/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala @@ -393,7 +393,7 @@ class AstSchemaMaterializer[Ctx] private ( var newCount = 0 var iteration = 0 - do { + while ({ prevCount = typeDefCache.size iteration += 1 @@ -404,7 +404,8 @@ class AstSchemaMaterializer[Ctx] private ( } newCount = typeDefCache.size - } while (prevCount != newCount && iteration < 20) + (prevCount != newCount && iteration < 20) + }) () } def getTypeFromExistingType(origin: MatOrigin, tpe: OutputType[_]): OutputType[Any] = tpe match { diff --git a/modules/core/src/main/scala/sangria/schema/AstSchemaResolver.scala b/modules/core/src/main/scala/sangria/schema/AstSchemaResolver.scala index 4e2dd397..b223e756 100644 --- a/modules/core/src/main/scala/sangria/schema/AstSchemaResolver.scala +++ b/modules/core/src/main/scala/sangria/schema/AstSchemaResolver.scala @@ -103,11 +103,13 @@ object FieldResolver { config: (String, Map[String, Context[Ctx, _] => Action[Ctx, Any]])*): FieldResolver[Ctx] = { val configMap = config.toMap - FieldResolver { - case (TypeName(name), field) - if configMap.contains(name) && configMap(name).contains(field.name) => - configMap(name)(field.name) - } + FieldResolver[Ctx]( + { + case (TypeName(name), field) + if configMap.contains(name) && configMap(name).contains(field.name) => + configMap(name)(field.name) + }, + PartialFunction.empty) } def defaultInput[Ctx, In: InputUnmarshaller] = @@ -223,7 +225,7 @@ case class GenericDynamicDirectiveResolver[T, A]( directiveName: String, locations: Set[DirectiveLocation.Value] = Set.empty, resolve: GenericDynamicDirectiveContext[A] => Option[T])(implicit - val marshaller: ResultMarshallerForType[T]) + val marshaller: ResultMarshallerForType[A]) extends AstSchemaGenericResolver[T] case class AstDirectiveInputTypeContext[Ctx]( diff --git a/modules/core/src/main/scala/sangria/schema/Context.scala b/modules/core/src/main/scala/sangria/schema/Context.scala index 878429d4..a4fc26a2 100644 --- a/modules/core/src/main/scala/sangria/schema/Context.scala +++ b/modules/core/src/main/scala/sangria/schema/Context.scala @@ -164,7 +164,7 @@ object UpdateCtx { } private[sangria] case class SubscriptionValue[Ctx, Val, S[_]]( - source: Val, + source: S[Any], stream: SubscriptionStream[S]) extends LeafAction[Ctx, Val] { override def map[NewVal](fn: Val => NewVal)(implicit diff --git a/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala b/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala index 5d894417..f4745287 100644 --- a/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala +++ b/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala @@ -186,7 +186,7 @@ class ResolverBasedAstSchemaBuilder[Ctx](val resolvers: Seq[AstSchemaResolver[Ct Some( fn( - DynamicDirectiveContext[Ctx, Any]( + DynamicDirectiveContext( d, typeDefinition, definition, @@ -377,7 +377,7 @@ class ResolverBasedAstSchemaBuilder[Ctx](val resolvers: Seq[AstSchemaResolver[Ct Some( complexity( - ComplexityDynamicDirectiveContext[Ctx, Any]( + ComplexityDynamicDirectiveContext( d, typeDefinition, definition, @@ -419,7 +419,7 @@ class ResolverBasedAstSchemaBuilder[Ctx](val resolvers: Seq[AstSchemaResolver[Ct implicit val marshaller = ddfp.marshaller resolve( - DynamicDirectiveFieldProviderContext[Ctx, Any]( + DynamicDirectiveFieldProviderContext( origin, astDir, typeDefinition, @@ -635,9 +635,9 @@ object ResolverBasedAstSchemaBuilder { } def defaultInputResolver[Ctx, In: InputUnmarshaller] = - FieldResolver[Ctx] { case (_, _) => + FieldResolver[Ctx]({ case (_, _) => extractFieldValue[Ctx, In] - } + }, PartialFunction.empty) def defaultExistingInputResolver[Ctx, In: InputUnmarshaller] = ExistingFieldResolver[Ctx] { case (_, _, _) => @@ -670,10 +670,10 @@ object ResolverBasedAstSchemaBuilder { case GenericDirectiveResolver(directive, _, resolve) => resolve(GenericDirectiveContext(astDir, node, Args(directive, astDir))) case gd @ GenericDynamicDirectiveResolver(_, _, resolve) => - implicit val marshaller: ResultMarshallerForType[T] = gd.marshaller + implicit val marshaller = gd.marshaller resolve( - GenericDynamicDirectiveContext(astDir, node, createDynamicArgs(astDir))) + GenericDynamicDirectiveContext(astDir, node, createDynamicArgs(astDir)(marshaller))) } } diff --git a/modules/core/src/main/scala/sangria/schema/Schema.scala b/modules/core/src/main/scala/sangria/schema/Schema.scala index 8e2e39f6..6982e76c 100644 --- a/modules/core/src/main/scala/sangria/schema/Schema.scala +++ b/modules/core/src/main/scala/sangria/schema/Schema.scala @@ -70,7 +70,7 @@ sealed trait AbstractType extends Type with Named { .flatMap(_.find(_.isInstanceOf(value)).asInstanceOf[Option[ObjectType[Ctx, _]]]) } -sealed trait MappedAbstractType[T] extends Type with AbstractType with OutputType[T] { +sealed trait MappedAbstractType[T] extends Type with AbstractType{ def contraMap(value: T): Any } @@ -554,20 +554,20 @@ case class UnionType[Ctx]( object UnionType { def apply[Ctx](name: String, types: List[ObjectType[Ctx, _]]): UnionType[Ctx] = - UnionType[Ctx](name, None, () => types) + UnionType(name, None, () => types) def apply[Ctx]( name: String, description: Option[String], types: List[ObjectType[Ctx, _]]): UnionType[Ctx] = - UnionType[Ctx](name, description, () => types) + UnionType(name, description, () => types) def apply[Ctx]( name: String, description: Option[String], types: List[ObjectType[Ctx, _]], astDirectives: Vector[ast.Directive]): UnionType[Ctx] = - UnionType[Ctx](name, description, () => types, astDirectives) + UnionType(name, description, () => types, astDirectives) def apply[Ctx]( name: String, @@ -652,7 +652,7 @@ object Field { fieldType, description, arguments, - ctx => SubscriptionValue[Ctx, StreamSource, stream.StreamSource](resolve(ctx), s), + ctx => SubscriptionValue[Ctx, StreamSource, stream.StreamSource](resolve(ctx).asInstanceOf[stream.StreamSource[Any]], s), deprecationReason, SubscriptionField[stream.StreamSource](s) +: tags, complexity, diff --git a/modules/core/src/main/scala/sangria/schema/package.scala b/modules/core/src/main/scala/sangria/schema/package.scala index d01bb9b3..1db93034 100644 --- a/modules/core/src/main/scala/sangria/schema/package.scala +++ b/modules/core/src/main/scala/sangria/schema/package.scala @@ -245,7 +245,7 @@ package object schema { val DefaultDeprecationReason = "No longer supported" - val ReasonArg: Argument[String] = Argument( + val ReasonArg: Argument[Option[String]] = Argument( "reason", OptionInputType(StringType), description = "Explains why this element was deprecated, usually also including a " + diff --git a/modules/core/src/main/scala/sangria/validation/QueryValidator.scala b/modules/core/src/main/scala/sangria/validation/QueryValidator.scala index 4f124a16..c776cf49 100644 --- a/modules/core/src/main/scala/sangria/validation/QueryValidator.scala +++ b/modules/core/src/main/scala/sangria/validation/QueryValidator.scala @@ -239,8 +239,8 @@ object ValidationContext { case _ => Vector.empty } } - case (enum: EnumType[_], v) => - enum.coerceInput(v) match { + case (enumT: EnumType[_], v) => + enumT.coerceInput(v) match { case Left(violation) => Vector(violation) case _ => Vector.empty } diff --git a/modules/core/src/main/scala/sangria/validation/TypeInfo.scala b/modules/core/src/main/scala/sangria/validation/TypeInfo.scala index a85cb631..90760552 100644 --- a/modules/core/src/main/scala/sangria/validation/TypeInfo.scala +++ b/modules/core/src/main/scala/sangria/validation/TypeInfo.scala @@ -131,8 +131,8 @@ class TypeInfo(schema: Schema[_, _], initialType: Option[Type] = None) { case ast.EnumValue(name, _, _) => enumValue = inputType .map(_.namedType) - .collect { case enum: EnumType[_] => - enum.byName.get(name) + .collect { case enumT: EnumType[_] => + enumT.byName.get(name) } .flatten case _ => // ignore diff --git a/modules/core/src/main/scala/sangria/validation/rules/ValuesOfCorrectType.scala b/modules/core/src/main/scala/sangria/validation/rules/ValuesOfCorrectType.scala index 34e35193..5c16f644 100644 --- a/modules/core/src/main/scala/sangria/validation/rules/ValuesOfCorrectType.scala +++ b/modules/core/src/main/scala/sangria/validation/rules/ValuesOfCorrectType.scala @@ -116,11 +116,11 @@ class ValuesOfCorrectType extends ValidationRule { )) def enumTypeSuggestion(tpe: Type, node: ast.Value): Option[Violation] = tpe match { - case enum: EnumType[_] => + case enumT: EnumType[_] => val name = QueryRenderer.render(node) - val suggestions = StringUtil.suggestionList(name, enum.values.map(_.name)) + val suggestions = StringUtil.suggestionList(name, enumT.values.map(_.name)) - if (suggestions.nonEmpty) Some(EnumValueCoercionViolation(name, enum.name, suggestions)) + if (suggestions.nonEmpty) Some(EnumValueCoercionViolation(name, enumT.name, suggestions)) else None case _ => None diff --git a/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala b/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala index 3defd824..11f168db 100644 --- a/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala +++ b/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala @@ -80,7 +80,10 @@ object SortedArraySet { val first_from = members.get(from) members.set(into, first_from) into += 1 - do from += 1 while (from < members.size() && members.get(from) == first_from) + while({ + from += 1 + (from < members.size() && members.get(from) == first_from) + }) () } members.subList(into, members.size()).clear() } diff --git a/modules/parser/src/main/scala/sangria/parser/QueryParser.scala b/modules/parser/src/main/scala/sangria/parser/QueryParser.scala index c8e791ec..596124d6 100644 --- a/modules/parser/src/main/scala/sangria/parser/QueryParser.scala +++ b/modules/parser/src/main/scala/sangria/parser/QueryParser.scala @@ -153,24 +153,28 @@ private[parser] sealed trait Ignored extends PositionTracking { this: Parser => private[parser] sealed trait Document { this: Parser + with Tokens with Operations with Ignored with Fragments with Operations with Values + with Types + with Directives with TypeSystemDefinitions => - protected def Document = rule { - IgnoredNoComment.* ~ trackPos ~ Definition.+ ~ IgnoredNoComment.* ~ Comments ~ EOI ~> - ((location, d, comments) => ast.Document(d.toVector, comments, location)) - } + protected[parser] def Document = + rule { + IgnoredNoComment.* ~ trackPos ~ Definition.+ ~ IgnoredNoComment.* ~ Comments ~ EOI ~> + ((location, d, comments) => ast.Document(d.toVector, comments, location)) + } - protected def InputDocument = rule { + protected[parser] def InputDocument = rule { IgnoredNoComment.* ~ trackPos ~ ValueConst.+ ~ IgnoredNoComment.* ~ Comments ~ EOI ~> ((location, vs, comments) => ast.InputDocument(vs.toVector, comments, location)) } - protected def InputDocumentWithVariables = rule { + protected[parser] def InputDocumentWithVariables = rule { IgnoredNoComment.* ~ trackPos ~ Value.+ ~ IgnoredNoComment.* ~ Comments ~ EOI ~> ((location, vs, comments) => ast.InputDocument(vs.toVector, comments, location)) } @@ -333,7 +337,7 @@ private[parser] sealed trait TypeSystemDefinitions { } private[this] def EnumTypeExtensionDefinition = rule { - (Comments ~ trackPos ~ extend ~ enum ~ Name ~ (DirectivesConst.? ~> (_.getOrElse( + (Comments ~ trackPos ~ extend ~ `enum` ~ Name ~ (DirectivesConst.? ~> (_.getOrElse( Vector.empty))) ~ EnumValuesDefinition ~> ((comment, location, name, dirs, values) => ast.EnumTypeExtensionDefinition( name, @@ -342,7 +346,7 @@ private[parser] sealed trait TypeSystemDefinitions { comment, values._2, location))) | - (Comments ~ trackPos ~ extend ~ enum ~ Name ~ DirectivesConst ~> ( + (Comments ~ trackPos ~ extend ~ `enum` ~ Name ~ DirectivesConst ~> ( (comment, location, name, dirs) => ast.EnumTypeExtensionDefinition( name, @@ -431,7 +435,7 @@ private[parser] sealed trait TypeSystemDefinitions { private[this] def UnionMembers = rule(ws('|').? ~ NamedType.+(ws('|')) ~> (_.toVector)) private[this] def EnumTypeDefinition = rule { - Description ~ Comments ~ trackPos ~ enum ~ Name ~ (DirectivesConst.? ~> (_.getOrElse( + Description ~ Comments ~ trackPos ~ `enum` ~ Name ~ (DirectivesConst.? ~> (_.getOrElse( Vector.empty))) ~ EnumValuesDefinition.? ~> ((descr, comment, location, name, dirs, values) => ast.EnumTypeDefinition( name, @@ -616,7 +620,7 @@ private[parser] sealed trait Operations extends PositionTracking { } private[parser] sealed trait Fragments { - this: Parser with Tokens with Ignored with Directives with Types with Operations => + this: Parser with Tokens with Ignored with Directives with Types with Operations with Values => protected[this] def experimentalFragmentVariables: Boolean @@ -659,7 +663,15 @@ private[parser] sealed trait Fragments { private[this] def TypeCondition = rule(on ~ NamedType) } -private[parser] sealed trait Values { this: Parser with Tokens with Ignored with Operations => +private[parser] sealed trait Values { + this: Parser + with Tokens + with Ignored + with Operations + with Fragments + with Values + with Types + with Directives => protected[this] def ValueConst: Rule1[ast.Value] = rule { NumberValue | StringValue | BooleanValue | NullValue | EnumValue | ListValueConst | ObjectValueConst } @@ -729,7 +741,8 @@ private[parser] sealed trait Values { this: Parser with Tokens with Ignored with } } -private[parser] sealed trait Directives { this: Parser with Tokens with Operations with Ignored => +private[parser] sealed trait Directives { + this: Parser with Tokens with Operations with Ignored with Fragments with Values with Types => protected[this] def Directives = rule(Directive.+ ~> (_.toVector)) protected[this] def DirectivesConst = rule(DirectiveConst.+ ~> (_.toVector)) @@ -803,7 +816,8 @@ object QueryParser { config.parseComments) parser.Document.run() match { - case Success(res) => Success(res.copy(sourceMapper = config.sourceMapperFn(id, input))) + case Success(res) => + Success(res.copy(sourceMapper = config.sourceMapperFn(id, input))) case Failure(e: ParseError) => Failure(SyntaxError(parser, input, e)) case f @ Failure(_) => f } From 010bbfcba1236115a0492e7fad8c199b42515e5d Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 23 Jun 2022 11:14:51 +0200 Subject: [PATCH 2/6] Allow to compile and pass sangria core tests in Scala 3 --- .scalafmt.conf | 6 ++ build.sbt | 7 +- .../scala-2/sangria/macros/ParseMacro.scala | 1 + .../scala-3/sangria/macros/ToExprGivens.scala | 2 +- .../scala/sangria/execution/Resolver.scala | 2 +- .../execution/ValueCoercionHelper.scala | 2 +- .../sangria/execution/ValueCollector.scala | 4 +- .../scala/sangria/introspection/package.scala | 2 +- .../schema/AstSchemaMaterializer.scala | 2 +- .../ResolverBasedAstSchemaBuilder.scala | 13 ++-- .../main/scala/sangria/schema/Schema.scala | 7 +- .../main/scala/sangria/schema/package.scala | 2 +- .../overlappingfields/SortedArraySet.scala | 6 +- .../execution/DeprecationTrackerSpec.scala | 10 +-- .../execution/ExceptionHandlingSpec.scala | 2 +- .../execution/ExecutorSchemaSpec.scala | 15 ++-- .../sangria/execution/ExecutorSpec.scala | 13 +++- .../InputDocumentMaterializerSpec.scala | 4 +- .../sangria/execution/ScalarAliasSpec.scala | 15 ++-- .../execution/ScalarMiddlewareSpec.scala | 13 +++- .../execution/UnionInterfaceSpec.scala | 7 +- .../sangria/execution/VariablesSpec.scala | 66 ++++++++++++----- .../execution/batch/BatchExecutorSpec.scala | 9 ++- .../execution/deferred/FetcherSpec.scala | 25 ++++--- .../sangria/marshalling/FromInputSpec.scala | 34 +++++---- .../sangria/marshalling/IonSupportSpec.scala | 2 +- .../sangria/renderer/SchemaRenderSpec.scala | 56 ++++++++++----- .../test/scala/sangria/schema/ArgsSpec.scala | 5 +- .../schema/DefaultValueApplicationSpec.scala | 11 ++- .../sangria/schema/DefaultValuesSpec.scala | 4 +- .../scala/sangria/schema/EnumTypeSpec.scala | 11 +-- .../IntrospectionSchemaMaterializerSpec.scala | 59 ++++++++++------ .../ResolverBasedAstSchemaBuilderSpec.scala | 54 ++++++++------ .../sangria/schema/SchemaDefinitionSpec.scala | 4 +- .../sangria/schema/SchemaExtensionSpec.scala | 6 +- .../schema/TypeFieldConstraintsSpec.scala | 13 ++-- .../sangria/starWars/StarWarsQuerySpec.scala | 2 +- .../test/scala/sangria/util/CatsSupport.scala | 18 ++--- .../test/scala/sangria/util/DebugUtil.scala | 2 +- .../sangria/util/ValidationSupport.scala | 70 ++++++++++--------- .../validation/DocumentAnalyzerSpec.scala | 10 ++- 41 files changed, 384 insertions(+), 212 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 910a9ee8..d7c87dd8 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -2,6 +2,12 @@ version = 3.5.8 runner.dialect = scala213 +fileOverride { + "glob:**/src/main/scala-3/**" { + runner.dialect = scala3 + } +} + maxColumn = 100 // Vertical alignment is pretty, but leads to bigger diffs diff --git a/build.sbt b/build.sbt index c6557547..cfcbba90 100644 --- a/build.sbt +++ b/build.sbt @@ -118,8 +118,7 @@ lazy val core = project Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), libraryDependencies ++= Seq( // AST Visitor - if (isScala3.value) "org.sangria-graphql" %% "macro-visit" % "0.1.0-SNAPSHOT" - else "org.sangria-graphql" %% "macro-visit" % "0.1.3", // TODO needs release + "org.sangria-graphql" %% "macro-visit" % "0.2.0-RC1", // Marshalling "org.sangria-graphql" %% "sangria-marshalling-api" % "1.0.8", // Streaming @@ -154,9 +153,9 @@ lazy val derivation = project name := "sangria-derivation", Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), - // Macros + // Macros libraryDependencies ++= (if (isScala3.value) Seq.empty - else Seq( "org.scala-lang" % "scala-reflect" % scalaVersion.value)), + else Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value)), apiURL := { val ver = CrossVersion.binaryScalaVersion(scalaVersion.value) Some(url(s"https://www.javadoc.io/doc/org.sangria-graphql/sangria-derivation_$ver/latest/")) diff --git a/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala b/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala index e703b98b..d524531b 100644 --- a/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala +++ b/modules/core/src/main/scala-2/sangria/macros/ParseMacro.scala @@ -9,6 +9,7 @@ class ParseMacro(context: blackbox.Context) val c = context } with MacroAstLiftable { + import c.universe._ def impl(args: Expr[Any]*) = diff --git a/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala index 6c991c6f..ab76bbd5 100644 --- a/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala +++ b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala @@ -402,7 +402,7 @@ trait ToExprGivens { ${ Expr(p) }, _root_.scala.Some( new _root_.sangria.ast.DefaultSourceMapper( - ${ Expr { doc.sourceMapper.get.id } }, + ${ Expr(doc.sourceMapper.get.id) }, _root_.sangria.parser.ParserConfig .parboiledToSourceMapper(_root_.org.parboiled2.ParserInput(${ Expr(doc.source.get) diff --git a/modules/core/src/main/scala/sangria/execution/Resolver.scala b/modules/core/src/main/scala/sangria/execution/Resolver.scala index b8c69189..17d266df 100644 --- a/modules/core/src/main/scala/sangria/execution/Resolver.scala +++ b/modules/core/src/main/scala/sangria/execution/Resolver.scala @@ -1508,7 +1508,7 @@ class Resolver[Ctx]( case None => field.resolve match { case pfn: Projector[Ctx, Any, _] => - pfn(updatedCtx.asInstanceOf[Context[Ctx, Any]], collectProjections(path, field, astFields, pfn.maxLevel)) + pfn(updatedCtx, collectProjections(path, field, astFields, pfn.maxLevel)) case fn => fn(updatedCtx) } diff --git a/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala b/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala index 38fa038d..289ece6e 100644 --- a/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala +++ b/modules/core/src/main/scala/sangria/execution/ValueCoercionHelper.scala @@ -58,7 +58,7 @@ class ValueCoercionHelper[Ctx]( acc: marshaller.MapBuilder, value: Option[Either[Vector[Violation], Trinary[marshaller.Node]]] ): marshaller.MapBuilder = { - val valueMapTyped: Any => marshaller.Node = valueMap.asInstanceOf[Any => marshaller.Node] + val valueMapTyped = valueMap.asInstanceOf[Any => marshaller.Node] def locations = inputFor match { diff --git a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala index 110b5fdc..c878cf7d 100644 --- a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala +++ b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala @@ -146,9 +146,7 @@ object ValueCollector { marshaller, fromInput.marshaller, errors = errors, - valueMap = - ((a: Any) => fromInput.fromResult(a.asInstanceOf[fromInput.marshaller.Node])) - .asInstanceOf[Any => Any], + valueMap = (v: Any) => fromInput.fromResult(v.asInstanceOf[fromInput.marshaller.Node]), defaultValueInfo = defaultInfo, undefinedValues = undefinedArgs, isArgument = true, diff --git a/modules/core/src/main/scala/sangria/introspection/package.scala b/modules/core/src/main/scala/sangria/introspection/package.scala index 6730bcb7..e588147a 100644 --- a/modules/core/src/main/scala/sangria/introspection/package.scala +++ b/modules/core/src/main/scala/sangria/introspection/package.scala @@ -3,7 +3,7 @@ package sangria import sangria.parser.QueryParser import sangria.schema._ -import sangria.util.tag.@@ // Scala 3 workaround +import sangria.util.tag.@@ // Scala 3 issue workaround import sangria.marshalling.FromInput.CoercedScalaResult package object introspection { diff --git a/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala b/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala index 86df640f..0201662b 100644 --- a/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala +++ b/modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala @@ -404,7 +404,7 @@ class AstSchemaMaterializer[Ctx] private ( } newCount = typeDefCache.size - (prevCount != newCount && iteration < 20) + prevCount != newCount && iteration < 20 }) () } diff --git a/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala b/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala index f4745287..d738f362 100644 --- a/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala +++ b/modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala @@ -635,9 +635,11 @@ object ResolverBasedAstSchemaBuilder { } def defaultInputResolver[Ctx, In: InputUnmarshaller] = - FieldResolver[Ctx]({ case (_, _) => - extractFieldValue[Ctx, In] - }, PartialFunction.empty) + FieldResolver[Ctx]( + { case (_, _) => + extractFieldValue[Ctx, In] + }, + PartialFunction.empty) def defaultExistingInputResolver[Ctx, In: InputUnmarshaller] = ExistingFieldResolver[Ctx] { case (_, _, _) => @@ -673,7 +675,10 @@ object ResolverBasedAstSchemaBuilder { implicit val marshaller = gd.marshaller resolve( - GenericDynamicDirectiveContext(astDir, node, createDynamicArgs(astDir)(marshaller))) + GenericDynamicDirectiveContext( + astDir, + node, + createDynamicArgs(astDir)(marshaller))) } } diff --git a/modules/core/src/main/scala/sangria/schema/Schema.scala b/modules/core/src/main/scala/sangria/schema/Schema.scala index 6982e76c..1c13158b 100644 --- a/modules/core/src/main/scala/sangria/schema/Schema.scala +++ b/modules/core/src/main/scala/sangria/schema/Schema.scala @@ -70,7 +70,7 @@ sealed trait AbstractType extends Type with Named { .flatMap(_.find(_.isInstanceOf(value)).asInstanceOf[Option[ObjectType[Ctx, _]]]) } -sealed trait MappedAbstractType[T] extends Type with AbstractType{ +sealed trait MappedAbstractType[T] extends Type with AbstractType { def contraMap(value: T): Any } @@ -652,7 +652,10 @@ object Field { fieldType, description, arguments, - ctx => SubscriptionValue[Ctx, StreamSource, stream.StreamSource](resolve(ctx).asInstanceOf[stream.StreamSource[Any]], s), + ctx => + SubscriptionValue[Ctx, StreamSource, stream.StreamSource]( + resolve(ctx).asInstanceOf[stream.StreamSource[Any]], + s), deprecationReason, SubscriptionField[stream.StreamSource](s) +: tags, complexity, diff --git a/modules/core/src/main/scala/sangria/schema/package.scala b/modules/core/src/main/scala/sangria/schema/package.scala index 1db93034..d01bb9b3 100644 --- a/modules/core/src/main/scala/sangria/schema/package.scala +++ b/modules/core/src/main/scala/sangria/schema/package.scala @@ -245,7 +245,7 @@ package object schema { val DefaultDeprecationReason = "No longer supported" - val ReasonArg: Argument[Option[String]] = Argument( + val ReasonArg: Argument[String] = Argument( "reason", OptionInputType(StringType), description = "Explains why this element was deprecated, usually also including a " + diff --git a/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala b/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala index 11f168db..3d011a5a 100644 --- a/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala +++ b/modules/core/src/main/scala/sangria/validation/rules/overlappingfields/SortedArraySet.scala @@ -80,9 +80,9 @@ object SortedArraySet { val first_from = members.get(from) members.set(into, first_from) into += 1 - while({ - from += 1 - (from < members.size() && members.get(from) == first_from) + while ({ + from += 1 + from < members.size() && members.get(from) == first_from }) () } members.subList(into, members.size()).clear() diff --git a/modules/core/src/test/scala/sangria/execution/DeprecationTrackerSpec.scala b/modules/core/src/test/scala/sangria/execution/DeprecationTrackerSpec.scala index 49e1801d..7165da99 100644 --- a/modules/core/src/test/scala/sangria/execution/DeprecationTrackerSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/DeprecationTrackerSpec.scala @@ -19,7 +19,7 @@ class DeprecationTrackerSpec var ctx: Option[Context[_, _]] = None var enumValue: Option[Any] = None - var enum: Option[String] = None + var `enum`: Option[String] = None def deprecatedFieldUsed[Ctx](ctx: Context[Ctx, _]) = { times.incrementAndGet() @@ -27,10 +27,10 @@ class DeprecationTrackerSpec this.ctx = Some(ctx) } - def deprecatedEnumValueUsed[T, Ctx](enum: EnumType[T], value: T, userContext: Ctx) = { + def deprecatedEnumValueUsed[T, Ctx](`enum`: EnumType[T], value: T, userContext: Ctx) = { times.incrementAndGet() this.enumValue = Some(value) - this.enum = Some(enum.name) + this.`enum` = Some(`enum`.name) } } @@ -179,7 +179,7 @@ class DeprecationTrackerSpec Executor.execute(schema, query, deprecationTracker = deprecationTracker).await deprecationTracker.times.get should be(1) - deprecationTracker.enum should be(Some("TestEnum")) + deprecationTracker.`enum` should be(Some("TestEnum")) deprecationTracker.enumValue should be(Some(2)) } @@ -216,7 +216,7 @@ class DeprecationTrackerSpec Executor.execute(schema, query, deprecationTracker = deprecationTracker).await deprecationTracker.times.get should be(0) - deprecationTracker.enum should be(None) + deprecationTracker.`enum` should be(None) deprecationTracker.enumValue should be(None) } } diff --git a/modules/core/src/test/scala/sangria/execution/ExceptionHandlingSpec.scala b/modules/core/src/test/scala/sangria/execution/ExceptionHandlingSpec.scala index ee52bbc5..d82cf1a7 100644 --- a/modules/core/src/test/scala/sangria/execution/ExceptionHandlingSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/ExceptionHandlingSpec.scala @@ -38,7 +38,7 @@ class ExceptionHandlingSpec Field( "success", OptionType(StringType), - arguments = Argument("num", OptionInputType(IntType)) :: Nil, + arguments = List(Argument("num", OptionInputType(IntType))), resolve = _ => "Yay"), Field( "errorInScalar", diff --git a/modules/core/src/test/scala/sangria/execution/ExecutorSchemaSpec.scala b/modules/core/src/test/scala/sangria/execution/ExecutorSchemaSpec.scala index 74c0d89d..2a0ff05f 100644 --- a/modules/core/src/test/scala/sangria/execution/ExecutorSchemaSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/ExecutorSchemaSpec.scala @@ -13,6 +13,9 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ExecutorSchemaSpec extends AnyWordSpec with Matchers with FutureResultSupport { case class Image(url: Option[String], width: Option[Int], height: Option[Int]) @@ -49,9 +52,11 @@ class ExecutorSchemaSpec extends AnyWordSpec with Matchers with FutureResultSupp Field( "pic", OptionType(BlogImageType), - arguments = Argument("width", OptionInputType(IntType)) :: Argument( - "height", - OptionInputType(IntType)) :: Nil, + arguments = Argument[Option[Int @@ CoercedScalaResult]]( + "width", + OptionInputType(IntType)) :: Argument[Option[ + Int @@ CoercedScalaResult + ]]("height", OptionInputType(IntType)) :: Nil, resolve = ctx => for { w <- ctx.argOpt[Int]("width") @@ -87,7 +92,7 @@ class ExecutorSchemaSpec extends AnyWordSpec with Matchers with FutureResultSupp Field( "article", OptionType(BlogArticleType), - arguments = Argument("id", OptionInputType(IDType)) :: Nil, + arguments = List(Argument("id", OptionInputType(IDType))), resolve = ctx => ctx.argOpt[String]("id").flatMap(id => article(id.toInt)) ), Field( @@ -103,7 +108,7 @@ class ExecutorSchemaSpec extends AnyWordSpec with Matchers with FutureResultSupp Field( "articleSubscribe", OptionType(BlogArticleType), - arguments = Argument("id", OptionInputType(IDType)) :: Nil, + arguments = List(Argument("id", OptionInputType(IDType))), resolve = ctx => ctx.argOpt[String]("id").flatMap(id => article(id.toInt)) )) ) diff --git a/modules/core/src/test/scala/sangria/execution/ExecutorSpec.scala b/modules/core/src/test/scala/sangria/execution/ExecutorSpec.scala index d4c12c72..0807dad0 100644 --- a/modules/core/src/test/scala/sangria/execution/ExecutorSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/ExecutorSpec.scala @@ -18,6 +18,9 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ExecutorSpec extends AnyWordSpec with Matchers with FutureResultSupport { class TestSubject { def a: Option[String] = Some("Apple") @@ -123,8 +126,10 @@ class ExecutorSpec extends AnyWordSpec with Matchers with FutureResultSupport { Field( "pic", OptionType(StringType), - arguments = Argument("size", OptionInputType(IntType)) :: Nil, - resolve = ctx => ctx.value.pic(ctx.argOpt[Int]("size"))), + arguments = + Argument[Option[Int @@ CoercedScalaResult]]("size", OptionInputType(IntType)) :: Nil, + resolve = ctx => ctx.value.pic(ctx.argOpt[Int]("size")) + ), Field("deep", OptionType(DeepDataType), resolve = _.value.deep), Field("future", OptionType(DataType), resolve = _.value.future) ) @@ -512,7 +517,9 @@ class ExecutorSpec extends AnyWordSpec with Matchers with FutureResultSupport { fields[Unit, Unit](Field( "b", OptionType(StringType), - arguments = Argument("numArg", OptionInputType(IntType)) :: Argument( + arguments = Argument[Option[Int @@ CoercedScalaResult]]( + "numArg", + OptionInputType(IntType)) :: Argument[Option[String @@ CoercedScalaResult]]( "stringArg", OptionInputType(StringType)) :: Nil, resolve = ctx => { resolvedArgs = ctx.args.raw; None } diff --git a/modules/core/src/test/scala/sangria/execution/InputDocumentMaterializerSpec.scala b/modules/core/src/test/scala/sangria/execution/InputDocumentMaterializerSpec.scala index 6b69a7cd..30d7fc17 100644 --- a/modules/core/src/test/scala/sangria/execution/InputDocumentMaterializerSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/InputDocumentMaterializerSpec.scala @@ -22,8 +22,8 @@ class InputDocumentMaterializerSpec extends AnyWordSpec with Matchers with Strin comments: Vector[Option[Comment]]) object MyJsonProtocol extends DefaultJsonProtocol { - implicit val commentFormat = jsonFormat2(Comment.apply) - implicit val articleFormat = jsonFormat4(Article.apply) + implicit val commentFormat: JsonFormat[Comment] = jsonFormat2(Comment.apply) + implicit val articleFormat: JsonFormat[Article] = jsonFormat4(Article.apply) } import MyJsonProtocol._ diff --git a/modules/core/src/test/scala/sangria/execution/ScalarAliasSpec.scala b/modules/core/src/test/scala/sangria/execution/ScalarAliasSpec.scala index e01a9f3c..a356ad5a 100644 --- a/modules/core/src/test/scala/sangria/execution/ScalarAliasSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/ScalarAliasSpec.scala @@ -16,6 +16,9 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ScalarAliasSpec extends AnyWordSpec with Matchers with FutureResultSupport { import ScalarAliasSpec._ @@ -23,12 +26,14 @@ class ScalarAliasSpec extends AnyWordSpec with Matchers with FutureResultSupport case class RefineViolation(error: String) extends ValueCoercionViolation(error) - implicit val UserIdType = ScalarAlias[UserId, String](StringType, _.id, id => Right(UserId(id))) + implicit val UserIdType: ScalarAlias[UserId, String] = + ScalarAlias[UserId, String](StringType, _.id, id => Right(UserId(id))) - implicit val PositiveIntType = ScalarAlias[Int Refined Positive, Int]( - IntType, - _.value, - i => refineV[Positive](i).left.map(RefineViolation)) + implicit val PositiveIntType: ScalarAlias[Int Refined Positive, Int] = + ScalarAlias[Int Refined Positive, Int]( + IntType, + _.value, + i => refineV[Positive](i).left.map(RefineViolation)) case object IDViolation extends ValueCoercionViolation("Invalid ID") diff --git a/modules/core/src/test/scala/sangria/execution/ScalarMiddlewareSpec.scala b/modules/core/src/test/scala/sangria/execution/ScalarMiddlewareSpec.scala index 3ec1a6dd..101fb8c9 100644 --- a/modules/core/src/test/scala/sangria/execution/ScalarMiddlewareSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/ScalarMiddlewareSpec.scala @@ -10,6 +10,9 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ScalarMiddlewareSpec extends AnyWordSpec with Matchers with FutureResultSupport { import ScalarMiddlewareSpec._ @@ -43,9 +46,15 @@ class ScalarMiddlewareSpec extends AnyWordSpec with Matchers with FutureResultSu InputField("name", StringType))) val IdArg = Argument("id", EncodedIdType) - val IdArgWithDefault = Argument("id", OptionInputType(EncodedIdType), defaultValue = "SOME_ID") + val IdArgWithDefault = Argument[Option[String @@ CoercedScalaResult], String]( + "id", + OptionInputType(EncodedIdType), + defaultValue = "SOME_ID") val IdArgWithValidDefault = - Argument("id", OptionInputType(EncodedIdType), defaultValue = "test-SOME_ID") + Argument[Option[String @@ CoercedScalaResult], String]( + "id", + OptionInputType(EncodedIdType), + defaultValue = "test-SOME_ID") val ComplexArg = Argument("c", ComplexInputType) val ComplexArgWithDefault = Argument("c", ComplexInputWithDefaultType) val ComplexArgWithValidDefault = Argument("c", ComplexInputWithValidDefaultType) diff --git a/modules/core/src/test/scala/sangria/execution/UnionInterfaceSpec.scala b/modules/core/src/test/scala/sangria/execution/UnionInterfaceSpec.scala index e1a14a40..3bfb4b76 100644 --- a/modules/core/src/test/scala/sangria/execution/UnionInterfaceSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/UnionInterfaceSpec.scala @@ -7,6 +7,9 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class UnionInterfaceSpec extends AnyWordSpec with Matchers @@ -369,7 +372,9 @@ class UnionInterfaceSpec Field( "quz", OptionType(ListType(OptionType(QuzType))), - arguments = Argument("id", OptionInputType(ListInputType(StringType))) :: Nil, + arguments = Argument[Option[Seq[String @@ CoercedScalaResult]]]( + "id", + OptionInputType(ListInputType(StringType))) :: Nil, resolve = c => c.argOpt[Seq[String]]("id") .map(queried => c.value.quz.filter(quz => queried.contains(quz.id))) diff --git a/modules/core/src/test/scala/sangria/execution/VariablesSpec.scala b/modules/core/src/test/scala/sangria/execution/VariablesSpec.scala index e965691c..3a6fae25 100644 --- a/modules/core/src/test/scala/sangria/execution/VariablesSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/VariablesSpec.scala @@ -15,6 +15,10 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult +import sangria.marshalling.ScalaInput + class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { val TestInputObject = InputObjectType( "TestInputObject", @@ -42,7 +46,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "fieldWithNullableStringInput", OptionType(StringType), - arguments = Argument("input", OptionInputType(StringType)) :: Nil, + arguments = Argument[Option[String @@ CoercedScalaResult]]( + "input", + OptionInputType(StringType)) :: Nil, resolve = ctx => ctx .argOpt[Any]("input") @@ -52,7 +58,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "fieldWithNullableStringInputDefined", BooleanType, - arguments = Argument("input", OptionInputType(StringType)) :: Nil, + arguments = Argument[Option[String @@ CoercedScalaResult]]( + "input", + OptionInputType(StringType)) :: Nil, resolve = ctx => ctx.argDefinedInQuery("input") ), Field( @@ -65,8 +73,10 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "fieldWithDefaultArgumentValue", OptionType(StringType), - arguments = - Argument("input", OptionInputType(StringType), defaultValue = "Hello World") :: Nil, + arguments = Argument[Option[String @@ CoercedScalaResult], String]( + "input", + OptionInputType(StringType), + defaultValue = "Hello World") :: Nil, resolve = ctx => DefaultValueRenderer.renderCoercedInputValueCompact( ctx.arg[Any]("input"), @@ -84,8 +94,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "list", OptionType(StringType), - arguments = - Argument("input", OptionInputType(ListInputType(OptionInputType(StringType)))) :: Nil, + arguments = Argument[Option[Seq[Option[String @@ CoercedScalaResult]]]]( + "input", + OptionInputType(ListInputType(OptionInputType(StringType)))) :: Nil, resolve = ctx => ctx .argOpt[Any]("input") @@ -97,7 +108,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "nnList", OptionType(StringType), - arguments = Argument("input", ListInputType(OptionInputType(StringType))) :: Nil, + arguments = Argument[Seq[Option[String @@ CoercedScalaResult]]]( + "input", + ListInputType(OptionInputType(StringType))) :: Nil, resolve = ctx => DefaultValueRenderer.renderCoercedInputValueCompact( ctx.arg[Any]("input"), @@ -106,7 +119,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "listNN", OptionType(StringType), - arguments = Argument("input", OptionInputType(ListInputType(StringType))) :: Nil, + arguments = Argument[Option[Seq[String @@ CoercedScalaResult]]]( + "input", + OptionInputType(ListInputType(StringType))) :: Nil, resolve = ctx => ctx .argOpt[Any]("input") @@ -116,7 +131,8 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { Field( "nnListNN", OptionType(StringType), - arguments = Argument("input", ListInputType(StringType)) :: Nil, + arguments = + Argument[Seq[String @@ CoercedScalaResult]]("input", ListInputType(StringType)) :: Nil, resolve = ctx => DefaultValueRenderer.renderCoercedInputValueCompact( ctx.arg[Any]("input"), @@ -125,7 +141,7 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { ) ) - def schema = Schema(TestType) + def schema = Schema[Unit, Unit](TestType) "Execute: Handles inputs" when { "Handles objects and nullability" when { @@ -395,7 +411,13 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { "executes with complex input (scala input)" in { val args = Map("input" -> Map("a" -> "foo", "b" -> List("bar"), "c" -> "baz")) - Executor.execute(schema, testQuery, variables = mapVars(args)).await should be( + Executor + .execute( + schema.asInstanceOf[Schema[Unit, Unit]], + testQuery, + variables = mapVars(args) + ) + .await should be( Map( "data" -> Map( "fieldWithObjectInput" -> """{a:"foo",b:["bar"],c:"baz"}""" @@ -405,7 +427,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { "executes with complex input (json input)" in { val args = """{"input": {"a": "foo", "b": ["bar"], "c": "baz"}}""".parseJson - Executor.execute(schema, testQuery, variables = args).await should be( + Executor + .execute(schema.asInstanceOf[Schema[Unit, Unit]], testQuery, variables = args) + .await should be( Map( "data" -> Map( "fieldWithObjectInput" -> """{a:"foo",b:["bar"],c:"baz"}""" @@ -428,7 +452,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { "properly coerces single value to array (scala input)" in { val args = Map("input" -> Map("a" -> "foo", "b" -> "bar", "c" -> "baz")) - Executor.execute(schema, testQuery, variables = mapVars(args)).await should be( + Executor + .execute(schema.asInstanceOf[Schema[Unit, Unit]], testQuery, variables = mapVars(args)) + .await should be( Map( "data" -> Map( "fieldWithObjectInput" -> """{a:"foo",b:["bar"],c:"baz"}""" @@ -438,7 +464,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { "properly coerces single value to array (json input)" in { val args = """{"input": {"a": "foo", "b": "bar", "c": "baz"}}""".parseJson - Executor.execute(schema, testQuery, variables = args).await should be( + Executor + .execute(schema.asInstanceOf[Schema[Unit, Unit]], testQuery, variables = args) + .await should be( Map( "data" -> Map( "fieldWithObjectInput" -> """{a:"foo",b:["bar"],c:"baz"}""" @@ -447,7 +475,7 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { def assertErrorResult[T: InputUnmarshaller](args: T, expectedError: String) = { val result = Executor - .execute(schema, testQuery, variables = args) + .execute(schema.asInstanceOf[Schema[Unit, Unit]], testQuery, variables = args) .awaitAndRecoverQueryAnalysisScala .asInstanceOf[Map[String, AnyRef]] @@ -532,7 +560,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { } """) - Executor.execute(schema, query, variables = args).await should be( + Executor + .execute(schema.asInstanceOf[Schema[Unit, Unit]], query, variables = args) + .await should be( Map( "data" -> Map( "fieldWithNullableStringInput" -> null @@ -548,7 +578,9 @@ class VariablesSpec extends AnyWordSpec with Matchers with GraphQlSupport { } """) - Executor.execute(schema, query, variables = args).await should be( + Executor + .execute(schema.asInstanceOf[Schema[Unit, Unit]], query, variables = args) + .await should be( Map( "data" -> Map( "fieldWithNullableStringInput" -> "\"a\"" diff --git a/modules/core/src/test/scala/sangria/execution/batch/BatchExecutorSpec.scala b/modules/core/src/test/scala/sangria/execution/batch/BatchExecutorSpec.scala index 3a843aaf..d027fcf6 100644 --- a/modules/core/src/test/scala/sangria/execution/batch/BatchExecutorSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/batch/BatchExecutorSpec.scala @@ -13,14 +13,17 @@ import sangria.util.SimpleGraphQlSupport._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class BatchExecutorSpec extends AnyWordSpec with Matchers with FutureResultSupport { implicit val ec: ExecutionContext = ExecutionContext.global implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global) - val IdsArg = Argument("ids", ListInputType(IntType)) + val IdsArg = Argument[Seq[Int @@ CoercedScalaResult]]("ids", ListInputType(IntType)) val IdArg = Argument("id", IntType) val NameArg = Argument("name", StringType) - val NamesArg = Argument("names", ListInputType(StringType)) + val NamesArg = Argument[Seq[String @@ CoercedScalaResult]]("names", ListInputType(StringType)) val DataType = ObjectType( "Data", @@ -294,7 +297,7 @@ class BatchExecutorSpec extends AnyWordSpec with Matchers with FutureResultSuppo val res = BatchExecutor.executeBatch(schema, query, operationNames = List("q1", "q2")) - res.compile.toVector.unsafeRunSync.toSet should be( + res.compile.toVector.unsafeRunSync().toSet should be( Set( """ { diff --git a/modules/core/src/test/scala/sangria/execution/deferred/FetcherSpec.scala b/modules/core/src/test/scala/sangria/execution/deferred/FetcherSpec.scala index b2183d9b..45594191 100644 --- a/modules/core/src/test/scala/sangria/execution/deferred/FetcherSpec.scala +++ b/modules/core/src/test/scala/sangria/execution/deferred/FetcherSpec.scala @@ -8,6 +8,8 @@ import sangria.macros._ import sangria.schema._ import sangria.util.{FutureResultSupport, Pos} import sangria.util.SimpleGraphQlSupport._ +import sangria.util.tag.@@ +import sangria.marshalling.FromInput.CoercedScalaResult import scala.concurrent.{ExecutionContext, Future} import org.scalatest.matchers.should.Matchers @@ -23,11 +25,11 @@ class FetcherSpec extends AnyWordSpec with Matchers with FutureResultSupport { case class ColorDeferred(id: String) extends Deferred[String] object Category { - implicit val hasId = HasId[Category, String](_.id) + implicit val hasId: HasId[Category, String] = HasId[Category, String](_.id) } object Product { - implicit val hasId = HasId[Product, Int](_.id) + implicit val hasId: HasId[Product, Int] = HasId[Product, Int](_.id) } val prodCat = Relation[Product, String]("product-category", _.inCategories) @@ -101,13 +103,13 @@ class FetcherSpec extends AnyWordSpec with Matchers with FutureResultSupport { lazy val ProductType: ObjectType[Repo, Product] = ObjectType( "Product", () => - fields( - Field("id", IntType, resolve = c => c.value.id), - Field("name", StringType, resolve = c => c.value.name), + fields[Repo, Product]( + Field("id", IntType, resolve = (c: Context[_, Product]) => c.value.id), + Field("name", StringType, resolve = (c: Context[_, Product]) => c.value.name), Field( "categories", ListType(CategoryType), - resolve = c => fetcherCat.deferSeqOpt(c.value.inCategories)), + resolve = (c: Context[_, Product]) => fetcherCat.deferSeqOpt(c.value.inCategories)), Field( "categoryRel", CategoryType, @@ -126,7 +128,7 @@ class FetcherSpec extends AnyWordSpec with Matchers with FutureResultSupport { lazy val CategoryType: ObjectType[Repo, Category] = ObjectType( "Category", () => - fields( + fields[Repo, Category]( Field("id", StringType, resolve = c => c.value.id), Field("name", StringType, resolve = c => c.value.name), Field("color", StringType, resolve = c => ColorDeferred("red")), @@ -200,12 +202,15 @@ class FetcherSpec extends AnyWordSpec with Matchers with FutureResultSupport { Field( "productOpt", OptionType(ProductType), - arguments = Argument("id", OptionInputType(IntType)) :: Nil, - resolve = c => fetcherProd.deferOpt(c.argOpt[Int]("id"))), + arguments = + Argument[Option[Int @@ CoercedScalaResult]]("id", OptionInputType(IntType)) :: Nil, + resolve = c => fetcherProd.deferOpt(c.argOpt[Int]("id")) + ), Field( "productsOptExplicit", ListType(OptionType(ProductType)), - arguments = Argument("ids", ListInputType(IntType)) :: Nil, + arguments = + Argument[Seq[Int @@ CoercedScalaResult]]("ids", ListInputType(IntType)) :: Nil, resolve = c => fetcherProd.deferSeqOptExplicit(c.arg[Seq[Int]]("ids")) ), Field("root", CategoryType, resolve = _ => fetcherCat.defer("1")), diff --git a/modules/core/src/test/scala/sangria/marshalling/FromInputSpec.scala b/modules/core/src/test/scala/sangria/marshalling/FromInputSpec.scala index b5bd8b22..629ab013 100644 --- a/modules/core/src/test/scala/sangria/marshalling/FromInputSpec.scala +++ b/modules/core/src/test/scala/sangria/marshalling/FromInputSpec.scala @@ -6,6 +6,9 @@ import spray.json._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.InputObjectResult + class FromInputSpec extends AnyWordSpec with Matchers { case class Comment(author: String, text: Option[String]) case class Article( @@ -15,8 +18,8 @@ class FromInputSpec extends AnyWordSpec with Matchers { comments: Vector[Option[Comment]]) object MyJsonProtocol extends DefaultJsonProtocol { - implicit val commentFormat = jsonFormat2(Comment.apply) - implicit val articleFormat = jsonFormat4(Article.apply) + implicit val commentFormat: JsonFormat[Comment] = jsonFormat2(Comment.apply) + implicit val articleFormat: JsonFormat[Article] = jsonFormat4(Article.apply) } import sangria.marshalling.sprayJson.sprayJsonWriterToInput @@ -130,20 +133,21 @@ class FromInputSpec extends AnyWordSpec with Matchers { "" + value }) }, { - val arg = Argument( - "articles", - OptionInputType(ListInputType(OptionInputType(Article1Type))), - Vector( - Some( - Article( - "def1", - None, - Some(Vector("c", "d")), - Vector(Some(Comment("c1", None)), None))), - None, - Some(Article("def2", Some("some text"), None, Vector.empty)) + val arg = + Argument[Option[Seq[Option[Article @@ InputObjectResult]]], Seq[Option[Article]]]( + "articles", + OptionInputType(ListInputType(OptionInputType(Article1Type))), + Vector( + Some( + Article( + "def1", + None, + Some(Vector("c", "d")), + Vector(Some(Comment("c1", None)), None))), + None, + Some(Article("def2", Some("some text"), None, Vector.empty)) + ) ) - ) Field( "optListOpt", diff --git a/modules/core/src/test/scala/sangria/marshalling/IonSupportSpec.scala b/modules/core/src/test/scala/sangria/marshalling/IonSupportSpec.scala index 65ac3c75..ee0154fc 100644 --- a/modules/core/src/test/scala/sangria/marshalling/IonSupportSpec.scala +++ b/modules/core/src/test/scala/sangria/marshalling/IonSupportSpec.scala @@ -20,7 +20,7 @@ class IonSupportSpec extends AnyWordSpec with Matchers with FutureResultSupport import sangria.marshalling.ion._ - implicit val ionSystem = IonSystemBuilder.standard().build() + implicit val ionSystem: software.amazon.ion.IonSystem = IonSystemBuilder.standard().build() val dateFormat = new SimpleDateFormat("yyyy-MM-dd") diff --git a/modules/core/src/test/scala/sangria/renderer/SchemaRenderSpec.scala b/modules/core/src/test/scala/sangria/renderer/SchemaRenderSpec.scala index 77cc420e..e3070dd9 100644 --- a/modules/core/src/test/scala/sangria/renderer/SchemaRenderSpec.scala +++ b/modules/core/src/test/scala/sangria/renderer/SchemaRenderSpec.scala @@ -12,10 +12,14 @@ import sangria.validation.IntCoercionViolation import scala.concurrent.ExecutionContext.Implicits.global import sangria.marshalling.sprayJson._ import sangria.marshalling.ScalaInput.scalaInput +import sangria.marshalling.ScalaInput import sangria.parser.QueryParser import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class SchemaRenderSpec extends AnyWordSpec with Matchers @@ -139,7 +143,8 @@ class SchemaRenderSpec "Prints String Field With Int Arg" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType)) :: Nil + args = + Argument[Option[Int @@ CoercedScalaResult]]("argOne", OptionInputType(IntType)) :: Nil ) should equal(""" |schema { | query: Root @@ -154,7 +159,10 @@ class SchemaRenderSpec "Prints String Field With Int Arg With Default" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType), 2) :: Nil + args = Argument[Option[Int @@ CoercedScalaResult], Int]( + "argOne", + OptionInputType(IntType), + 2) :: Nil ) should equal(""" |schema { | query: Root @@ -184,7 +192,9 @@ class SchemaRenderSpec "Prints String Field With Multiple Args" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType)) :: Argument( + args = Argument[Option[Int @@ CoercedScalaResult]]( + "argOne", + OptionInputType(IntType)) :: Argument[Option[String @@ CoercedScalaResult]]( "argTwo", OptionInputType(StringType)) :: Nil ) should equal(""" @@ -201,10 +211,13 @@ class SchemaRenderSpec "Prints String Field With Multiple Args, First is Default" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType), 1) :: - Argument("argTwo", OptionInputType(StringType)) :: - Argument("argThree", OptionInputType(BooleanType)) :: - Nil + args = + Argument[Option[Int @@ CoercedScalaResult], Int]("argOne", OptionInputType(IntType), 1) :: + Argument[Option[String @@ CoercedScalaResult]]("argTwo", OptionInputType(StringType)) :: + Argument[Option[Boolean @@ CoercedScalaResult]]( + "argThree", + OptionInputType(BooleanType)) :: + Nil ) should equal(""" |schema { | query: Root @@ -219,9 +232,14 @@ class SchemaRenderSpec "Prints String Field With Multiple Args, Second is Default" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType)) :: - Argument("argTwo", OptionInputType(StringType), defaultValue = "foo") :: - Argument("argThree", OptionInputType(BooleanType)) :: + args = Argument[Option[Int @@ CoercedScalaResult]]("argOne", OptionInputType(IntType)) :: + Argument[Option[String @@ CoercedScalaResult], String]( + "argTwo", + OptionInputType(StringType), + defaultValue = "foo") :: + Argument[Option[Boolean @@ CoercedScalaResult]]( + "argThree", + OptionInputType(BooleanType)) :: Nil ) should equal(""" |schema { @@ -237,9 +255,12 @@ class SchemaRenderSpec "Prints String Field With Multiple Args, Last is Default" in { renderSingleFieldSchema( tpe = OptionType(StringType), - args = Argument("argOne", OptionInputType(IntType)) :: - Argument("argTwo", OptionInputType(StringType)) :: - Argument("argThree", OptionInputType(BooleanType), false) :: + args = Argument[Option[Int @@ CoercedScalaResult]]("argOne", OptionInputType(IntType)) :: + Argument[Option[String @@ CoercedScalaResult]]("argTwo", OptionInputType(StringType)) :: + Argument[Option[Boolean @@ CoercedScalaResult], Boolean]( + "argThree", + OptionInputType(BooleanType), + false) :: Nil ) should equal(""" |schema { @@ -703,17 +724,20 @@ class SchemaRenderSpec "myDirective", description = Some( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec posuere ornare nulla, non bibendum nisi dictum at. Etiam consequat velit ut leo fringilla mollis. Integer ut fringilla ante. Curabitur sagittis malesuada nibh sed vestibulum.\nNunc eu metus felis. Cras tellus nibh, porta nec lorem quis, elementum egestas tellus. Etiam vitae tellus vitae dui varius lobortis."), - arguments = Argument( + arguments = Argument[Option[Seq[String @@ CoercedScalaResult]], List[String] @@ ScalaInput]( "first", OptionInputType(ListInputType(StringType)), "Some descr", scalaInput(List("foo", "bar", "baz"))) :: - Argument( + Argument[Option[Seq[String @@ CoercedScalaResult]], Int @@ ScalaInput]( "middle", OptionInputType(ListInputType(StringType)), "Several\n lines\nof \"description\"", scalaInput(123)) :: - Argument("last", OptionInputType(IntType), "Another descr") :: + Argument[Option[Int @@ CoercedScalaResult]]( + "last", + OptionInputType(IntType), + "Another descr") :: Nil, locations = Set(DirectiveLocation.FieldDefinition, DirectiveLocation.InputFieldDefinition), shouldInclude = _ => true diff --git a/modules/core/src/test/scala/sangria/schema/ArgsSpec.scala b/modules/core/src/test/scala/sangria/schema/ArgsSpec.scala index 071b0efb..a72e7bc5 100644 --- a/modules/core/src/test/scala/sangria/schema/ArgsSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/ArgsSpec.scala @@ -7,6 +7,9 @@ import spray.json._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ArgsSpec extends AnyWordSpec with Matchers { val NonDefaultArgumentName = "nonDefaultArgument" val DefaultArgumentName = "defaultArgument" @@ -19,7 +22,7 @@ class ArgsSpec extends AnyWordSpec with Matchers { description = "Argument without default value" ) - val defaultArgument = Argument( + val defaultArgument = Argument[Option[Int @@ CoercedScalaResult], Int]( name = DefaultArgumentName, argumentType = OptionInputType(IntType), defaultValue = 10, diff --git a/modules/core/src/test/scala/sangria/schema/DefaultValueApplicationSpec.scala b/modules/core/src/test/scala/sangria/schema/DefaultValueApplicationSpec.scala index c653bdcf..70f5e462 100644 --- a/modules/core/src/test/scala/sangria/schema/DefaultValueApplicationSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/DefaultValueApplicationSpec.scala @@ -13,6 +13,9 @@ import sangria.marshalling.sprayJson._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class DefaultValueApplicationSpec extends AnyWordSpec with Matchers with FutureResultSupport { "Default value application" should { "use default value if argument is not provided" in { @@ -181,10 +184,14 @@ class DefaultValueApplicationSpec extends AnyWordSpec with Matchers with FutureR InputField("f", OptionInputType(StringType), defaultValue = "default"), InputField("fo", OptionInputType(StringType)))) - private[this] val AArg = Argument("a", OptionInputType(StringType), defaultValue = "default") + private[this] val AArg = Argument[Option[String @@ CoercedScalaResult], String]( + "a", + OptionInputType(StringType), + defaultValue = "default") private[this] val InpArg = Argument("inp", TestInputType) private[this] val InpJsonArg = Argument("inpJson", TestInputJsonType) - private[this] val SizeArg = Argument("size", OptionInputType(IntType), 42) + private[this] val SizeArg = + Argument[Option[Int @@ CoercedScalaResult], Int]("size", OptionInputType(IntType), 42) private[this] val QueryType = ObjectType( "Query", diff --git a/modules/core/src/test/scala/sangria/schema/DefaultValuesSpec.scala b/modules/core/src/test/scala/sangria/schema/DefaultValuesSpec.scala index abc6750f..59710507 100644 --- a/modules/core/src/test/scala/sangria/schema/DefaultValuesSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/DefaultValuesSpec.scala @@ -339,8 +339,8 @@ class DefaultValuesSpec extends AnyWordSpec with Matchers with FutureResultSuppo "generated typeclass-based serialisation" in { object MyJsonProtocol extends DefaultJsonProtocol { - implicit val sharesFormat = jsonFormat2(Shares.apply) - implicit val commentFormat = jsonFormat3(Comment.apply) + implicit val sharesFormat: JsonFormat[Shares] = jsonFormat2(Shares.apply) + implicit val commentFormat: JsonFormat[Comment] = jsonFormat3(Comment.apply) } import MyJsonProtocol._ diff --git a/modules/core/src/test/scala/sangria/schema/EnumTypeSpec.scala b/modules/core/src/test/scala/sangria/schema/EnumTypeSpec.scala index f97238c0..d944f6cc 100644 --- a/modules/core/src/test/scala/sangria/schema/EnumTypeSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/EnumTypeSpec.scala @@ -15,8 +15,8 @@ class EnumTypeSpec extends AnyWordSpec with Matchers with GraphQlSupport { )) object Args { - val fromEnum = Argument("fromEnum", OptionInputType(ColorType)) - val fromInt = Argument("fromInt", OptionInputType(IntType)) + val fromEnum: Argument[Option[Int]] = Argument("fromEnum", OptionInputType(ColorType)) + val fromInt: Argument[Option[Int]] = Argument("fromInt", OptionInputType(IntType)) } val QueryType = ObjectType( @@ -43,7 +43,7 @@ class EnumTypeSpec extends AnyWordSpec with Matchers with GraphQlSupport { "favoriteEnum", OptionType(ColorType), arguments = Args.fromEnum :: Nil, - resolve = _.args.arg(Args.fromEnum)) + resolve = _.args.arg[Option[Int]](Args.fromEnum)) )) val SubscriptionType = ObjectType( @@ -53,8 +53,9 @@ class EnumTypeSpec extends AnyWordSpec with Matchers with GraphQlSupport { "subscribeToEnum", OptionType(ColorType), arguments = Args.fromEnum :: Nil, - resolve = _.args.arg(Args.fromEnum)) - )) + resolve = _.args.arg[Option[Int]](Args.fromEnum)) + ) + ) val schema = Schema(QueryType, Some(MutationType), Some(SubscriptionType)) diff --git a/modules/core/src/test/scala/sangria/schema/IntrospectionSchemaMaterializerSpec.scala b/modules/core/src/test/scala/sangria/schema/IntrospectionSchemaMaterializerSpec.scala index f20eb58b..e3ce7e3a 100644 --- a/modules/core/src/test/scala/sangria/schema/IntrospectionSchemaMaterializerSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/IntrospectionSchemaMaterializerSpec.scala @@ -19,6 +19,10 @@ import spray.json._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ +import sangria.marshalling.FromInput.CoercedScalaResult +import sangria.marshalling.ScalaInput + class IntrospectionSchemaMaterializerSpec extends AnyWordSpec with Matchers @@ -134,13 +138,15 @@ class IntrospectionSchemaMaterializerSpec mutation = Some(ObjectType( "MutationType", "This is a simple mutation type", - fields[Any, Any]( - Field( - "setString", - OptionType(StringType), - Some("Set the string field"), - arguments = Argument("value", OptionInputType(StringType)) :: Nil, - resolve = _ => "foo")) + fields[Any, Any](Field( + "setString", + OptionType(StringType), + Some("Set the string field"), + arguments = Argument[Option[String @@ CoercedScalaResult]]( + "value", + OptionInputType(StringType)) :: Nil, + resolve = _ => "foo" + )) )), subscription = Some(ObjectType( "SubscriptionType", @@ -242,7 +248,7 @@ class IntrospectionSchemaMaterializerSpec "one", OptionType(StringType), Some("A field with a single arg"), - arguments = Argument( + arguments = Argument[Option[Int @@ CoercedScalaResult]]( "intArg", OptionInputType(IntType), description = "This is an int arg") :: Nil, @@ -252,7 +258,7 @@ class IntrospectionSchemaMaterializerSpec "two", OptionType(StringType), Some("A field with a two args"), - arguments = Argument( + arguments = Argument[Option[Seq[Option[Int @@ CoercedScalaResult]]]]( "listArg", OptionInputType(ListInputType(OptionInputType(IntType))), description = "This is an list of int arg") :: @@ -283,7 +289,7 @@ class IntrospectionSchemaMaterializerSpec "food", OptionType(foodType), Some("Repeats the arg you give it"), - arguments = Argument( + arguments = Argument[Option[Int @@ CoercedScalaResult]]( "kind", OptionInputType(foodType), description = "what kind of food?") :: Nil, @@ -347,15 +353,20 @@ class IntrospectionSchemaMaterializerSpec Field( "defaultInt", OptionType(StringType), - arguments = Argument("intArg", OptionInputType(IntType), 10) :: Nil, - resolve = _ => None), + arguments = Argument[Option[Int @@ CoercedScalaResult], Int]( + "intArg", + OptionInputType(IntType), + 10) :: Nil, + resolve = _ => None + ), Field( "defaultList", OptionType(StringType), - arguments = Argument( - "listArg", - OptionInputType(ListInputType(OptionInputType(IntType))), - scalaInput(Vector(1, 2, 3))) :: Nil, + arguments = + Argument[Option[Seq[Option[Int @@ CoercedScalaResult]]], Vector[Int] @@ ScalaInput]( + "listArg", + OptionInputType(ListInputType(OptionInputType(IntType))), + scalaInput(Vector(1, 2, 3))) :: Nil, resolve = _ => None ), Field( @@ -449,8 +460,12 @@ class IntrospectionSchemaMaterializerSpec fields[Any, Any](Field( "foo", OptionType(StringType), - arguments = Argument("custom1", OptionInputType(CustomScalar)) :: - Argument("custom2", OptionInputType(CustomScalar)) :: + arguments = Argument[Option[Int @@ CoercedScalaResult]]( + "custom1", + OptionInputType(CustomScalar)) :: + Argument[Option[Int @@ CoercedScalaResult]]( + "custom2", + OptionInputType(CustomScalar)) :: Nil, resolve = _ => None )) @@ -477,8 +492,12 @@ class IntrospectionSchemaMaterializerSpec fields[Any, Any](Field( "foo", OptionType(IntType), - arguments = Argument("custom1", OptionInputType(CustomScalar)) :: - Argument("custom2", OptionInputType(CustomScalar)) :: + arguments = Argument[Option[Int @@ CoercedScalaResult]]( + "custom1", + OptionInputType(CustomScalar)) :: + Argument[Option[Int @@ CoercedScalaResult]]( + "custom2", + OptionInputType(CustomScalar)) :: Nil, resolve = _ => None )) diff --git a/modules/core/src/test/scala/sangria/schema/ResolverBasedAstSchemaBuilderSpec.scala b/modules/core/src/test/scala/sangria/schema/ResolverBasedAstSchemaBuilderSpec.scala index 0ee009ce..cbf6e785 100644 --- a/modules/core/src/test/scala/sangria/schema/ResolverBasedAstSchemaBuilderSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/ResolverBasedAstSchemaBuilderSpec.scala @@ -18,6 +18,9 @@ import sangria.util.SimpleGraphQlSupport._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with FutureResultSupport { case object UUIDViolation extends BaseViolation("Invalid UUID") @@ -44,7 +47,7 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F "provide basic resolution capabilities" in { import sangria.marshalling.sprayJson._ - val ValueArg = Argument("value", StringType) + val ValueArg: Argument[String] = Argument("value", StringType) val TestDir = Directive("test", arguments = ValueArg :: Nil, locations = Set(DL.FieldDefinition)) @@ -101,11 +104,13 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F c.withArgs(MinArg, MaxArg)((min, max) => c.inputType(c.definition.valueType, intValidationAlias(min, max)))), DirectiveScalarResolver(CoolDir, _ => StringType), - DirectiveResolver(TestDir, resolve = _.arg(ValueArg)), + DirectiveResolver(TestDir, resolve = _.arg[String](ValueArg)), DynamicDirectiveResolver[Any, JsValue]("json", resolve = _.args), - FieldResolver { case (TypeName("Query"), FieldName("id")) => - _ => UUID.fromString("a26bdfd4-0fcf-484f-b363-585091b3319f") - }, + FieldResolver( + { case (TypeName("Query"), FieldName("id")) => + _ => UUID.fromString("a26bdfd4-0fcf-484f-b363-585091b3319f") + }, + PartialFunction.empty), AnyFieldResolver.defaultInput[Any, JsValue] ) @@ -269,7 +274,7 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F } "resolve fields based on the directives" in { - val ValueArg = Argument("value", StringType) + val ValueArg: Argument[String] = Argument("value", StringType) val ConstDir = Directive("const", arguments = ValueArg :: Nil, locations = Set(DL.FieldDefinition)) @@ -278,7 +283,7 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F val AddFinalDir = Directive("addFinal", locations = Set(DL.Schema, DL.FieldDefinition)) val builder = resolverBased[Any]( - DirectiveResolver(ConstDir, c => c.arg(ValueArg)), + DirectiveResolver(ConstDir, c => c.arg[String](ValueArg)), DirectiveResolver( AddDir, c => @@ -336,11 +341,14 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F case (TypeName("Color"), v) if v.name == "GREEN" => "#00FF00" case (TypeName("Color"), v) if v.name == "BLUE" => "#0000FF" }, - FieldResolver { case (TypeName("Mutation"), FieldName("eat")) => - ctx => - "tasty " + ctx.arg[String]("color") + " " + ctx.arg[InputObjectType.DefaultInput]( - "fruit")("color") - } + FieldResolver( + { case (TypeName("Mutation"), FieldName("eat")) => + ctx => + "tasty " + ctx.arg[String]("color") + " " + ctx.arg[InputObjectType.DefaultInput]( + "fruit")("color") + }, + PartialFunction.empty + ) ) val schemaAst = @@ -464,10 +472,13 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F "resolve fields based on names" in { val builder = resolverBased[Unit]( - FieldResolver { - case (TypeName("Query"), field @ FieldName(fieldName)) if fieldName.startsWith("test") => - c => c.arg[Int](field.arguments.head.name) + 1 - }, + FieldResolver( + { + case (TypeName("Query"), field @ FieldName(fieldName)) + if fieldName.startsWith("test") => + c => c.arg[Int](field.arguments.head.name) + 1 + }, + PartialFunction.empty), FieldResolver.map("Query" -> Map("a" -> (_ => "a value"), "b" -> (_ => "b value"))), ExistingFieldResolver { case (_, _, field) if field.name.startsWith("existing") => @@ -736,10 +747,13 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F } """ - val builder = resolverBased[Any](FieldResolver { - case (_, FieldName("name")) => _ => "test" - case (_, _) => _ => () - }) + val builder = resolverBased[Any]( + FieldResolver( + { + case (_, FieldName("name")) => _ => "test" + case (_, _) => _ => () + }, + PartialFunction.empty)) val resolverBuilder = builder.validateSchemaWithException(schemaDocument) diff --git a/modules/core/src/test/scala/sangria/schema/SchemaDefinitionSpec.scala b/modules/core/src/test/scala/sangria/schema/SchemaDefinitionSpec.scala index 794caf5e..5dc8289f 100644 --- a/modules/core/src/test/scala/sangria/schema/SchemaDefinitionSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/SchemaDefinitionSpec.scala @@ -71,7 +71,7 @@ class SchemaDefinitionSpec extends AnyWordSpec with Matchers with FutureResultSu "Foo", interfaces = Nil, () => - fields( + fields[Unit, Foo]( Field("name", OptionType(StringType), resolve = _.value.name), Field("city", OptionType(StringType), resolve = _.value.name) ) @@ -81,7 +81,7 @@ class SchemaDefinitionSpec extends AnyWordSpec with Matchers with FutureResultSu val FooType1: ObjectType[Unit, Foo] = ObjectType( "Foo", interfaces = Nil, - () => fields(Field("name", OptionType(StringType), resolve = _.value.name)) + () => fields[Unit, Foo](Field("name", OptionType(StringType), resolve = _.value.name)) ) val field1 = diff --git a/modules/core/src/test/scala/sangria/schema/SchemaExtensionSpec.scala b/modules/core/src/test/scala/sangria/schema/SchemaExtensionSpec.scala index 57ce8011..b4566444 100644 --- a/modules/core/src/test/scala/sangria/schema/SchemaExtensionSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/SchemaExtensionSpec.scala @@ -30,7 +30,7 @@ class SchemaExtensionSpec val SomeInterfaceType: InterfaceType[Unit, SomeInterface] = InterfaceType( "SomeInterface", () => - fields( + fields[Unit, SomeInterface]( Field("name", OptionType(StringType), resolve = _.value.name), Field("some", OptionType(SomeInterfaceType), resolve = _.value.some) ) @@ -40,7 +40,7 @@ class SchemaExtensionSpec "Foo", interfaces = interfaces(SomeInterfaceType), () => - fields( + fields[Unit, Foo]( Field("name", OptionType(StringType), resolve = _.value.name), Field("some", OptionType(SomeInterfaceType), resolve = _.value.some), Field("tree", ListType(OptionType(FooType)), resolve = _.value.tree) @@ -51,7 +51,7 @@ class SchemaExtensionSpec "Bar", interfaces = interfaces(SomeInterfaceType), () => - fields( + fields[Unit, Bar]( Field("name", OptionType(StringType), resolve = _.value.name), Field("some", OptionType(SomeInterfaceType), resolve = _.value.some), Field("foo", OptionType(FooType), resolve = _.value.foo) diff --git a/modules/core/src/test/scala/sangria/schema/TypeFieldConstraintsSpec.scala b/modules/core/src/test/scala/sangria/schema/TypeFieldConstraintsSpec.scala index c6b6643a..b832aef6 100644 --- a/modules/core/src/test/scala/sangria/schema/TypeFieldConstraintsSpec.scala +++ b/modules/core/src/test/scala/sangria/schema/TypeFieldConstraintsSpec.scala @@ -5,6 +5,9 @@ import sangria.util.SimpleGraphQlSupport._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class TypeFieldConstraintsSpec extends AnyWordSpec with Matchers { "ObjectType" should { @@ -425,10 +428,12 @@ class TypeFieldConstraintsSpec extends AnyWordSpec with Matchers { Field( "slice", IntType, - arguments = Argument("parts", IntType) :: Argument( - "careful", - OptionInputType(BooleanType)) :: Nil, - resolve = _.args.arg[Int]("parts")), + arguments = + Argument("parts", IntType) :: Argument[Option[Boolean @@ CoercedScalaResult]]( + "careful", + OptionInputType(BooleanType)) :: Nil, + resolve = _.args.arg[Int]("parts") + ), Field("color", StringType, resolve = _.value.color) ) ) diff --git a/modules/core/src/test/scala/sangria/starWars/StarWarsQuerySpec.scala b/modules/core/src/test/scala/sangria/starWars/StarWarsQuerySpec.scala index 5162861f..2656f17c 100644 --- a/modules/core/src/test/scala/sangria/starWars/StarWarsQuerySpec.scala +++ b/modules/core/src/test/scala/sangria/starWars/StarWarsQuerySpec.scala @@ -523,7 +523,7 @@ class StarWarsQuerySpec extends AnyWordSpec with Matchers with FutureResultSuppo lazy val A: ObjectType[Unit, Any] = ObjectType( "A", () => - fields( + fields[Unit, Any]( Field("nullableA", OptionType(A), resolve = _ => ""), Field("nonNullA", A, resolve = _ => ""), Field("throws", A, resolve = _ => throw PrivacyError("Catch me if you can")) diff --git a/modules/core/src/test/scala/sangria/util/CatsSupport.scala b/modules/core/src/test/scala/sangria/util/CatsSupport.scala index 49538f25..2becec38 100644 --- a/modules/core/src/test/scala/sangria/util/CatsSupport.scala +++ b/modules/core/src/test/scala/sangria/util/CatsSupport.scala @@ -62,11 +62,11 @@ trait CatsSupport { this: AnyWordSpec with Matchers => val testData = testTestData.orElse(bgTestData).getOrElse(JsObject.empty) - val given = getGiven(test, testSchema) + val `given` = getGiven(test, testSchema) val action = getAction(test, testName, testData) val assertions = getAssertions(test, testName) - val result = executeAction(given, action) + val result = executeAction(`given`, action) assertions.foreach { a => assertActionResult(result, a) @@ -213,26 +213,26 @@ object CatsScenarioExecutor extends FutureResultSupport { value.asInstanceOf[JsValue].get("type").exists(_.stringValue == c.definition.name)) ) - def executeAction(given: Given[Any, Any], action: Action) = action match { + def executeAction(`given`: Given[Any, Any], action: Action) = action match { case Parse => ParsingResult( - QueryParser.parse(given.query).toEither.left.map(_.asInstanceOf[SangriaSyntaxError])) + QueryParser.parse(`given`.query).toEither.left.map(_.asInstanceOf[SangriaSyntaxError])) case Validate(rules) => ValidationResult( new RuleBasedQueryValidator(rules.toList) - .validateQuery(given.schema, QueryParser.parse(given.query).get)) + .validateQuery(`given`.schema, QueryParser.parse(`given`.query).get)) case Execute(validate, value, vars, op) => val validator = if (validate) QueryValidator.default else QueryValidator.empty ExecutionResult( QueryParser - .parse(given.query) + .parse(`given`.query) .map(queryAst => Executor .execute( - given.schema, + `given`.schema, queryAst, root = value, queryValidator = validator, @@ -586,8 +586,8 @@ object CatsScenarioData { } def getGiven(value: YamlValue, schema: Option[Schema[Any, Any]]) = { - val given = value("given") - val query = given("query").stringValue + val `given` = value("given") + val query = `given`("query").stringValue Given(query, schema) } diff --git a/modules/core/src/test/scala/sangria/util/DebugUtil.scala b/modules/core/src/test/scala/sangria/util/DebugUtil.scala index aa5309fb..d7f71495 100644 --- a/modules/core/src/test/scala/sangria/util/DebugUtil.scala +++ b/modules/core/src/test/scala/sangria/util/DebugUtil.scala @@ -83,7 +83,7 @@ object DebugUtil { } } - private implicit val conf = myPrettifier + private implicit val conf: Prettifier = myPrettifier def prettyRender[T](obj: T): String = obj.pretty diff --git a/modules/core/src/test/scala/sangria/util/ValidationSupport.scala b/modules/core/src/test/scala/sangria/util/ValidationSupport.scala index 9587d99b..2529d745 100644 --- a/modules/core/src/test/scala/sangria/util/ValidationSupport.scala +++ b/modules/core/src/test/scala/sangria/util/ValidationSupport.scala @@ -8,6 +8,9 @@ import sangria.util.SimpleGraphQlSupport._ import scala.util.Success import org.scalatest.matchers.should.Matchers +import sangria.util.tag.@@ +import sangria.marshalling.FromInput.CoercedScalaResult + trait ValidationSupport extends Matchers { type TestField = Field[Unit, Unit] @@ -29,9 +32,12 @@ trait ValidationSupport extends Matchers { Field( "name", OptionType(StringType), - arguments = Argument("surname", OptionInputType(BooleanType)) :: Nil, + arguments = Argument[Option[Boolean @@ CoercedScalaResult]]( + "surname", + OptionInputType(BooleanType)) :: Nil, resolve = _ => None) - )) + ) + ) val DogCommand = EnumType( "DogCommand", @@ -57,7 +63,9 @@ trait ValidationSupport extends Matchers { Field( "name", OptionType(StringType), - arguments = Argument("surname", OptionInputType(BooleanType)) :: Nil, + arguments = Argument[Option[Boolean @@ CoercedScalaResult]]( + "surname", + OptionInputType(BooleanType)) :: Nil, resolve = _ => None), Field("nickname", OptionType(StringType), resolve = _ => None), Field("barks", OptionType(BooleanType), resolve = _ => None), @@ -65,18 +73,18 @@ trait ValidationSupport extends Matchers { Field( "doesKnowCommand", OptionType(BooleanType), - arguments = Argument("dogCommand", OptionInputType(DogCommand)) :: Nil, + arguments = List(Argument("dogCommand", OptionInputType(DogCommand))), resolve = _ => None), Field( "isHousetrained", OptionType(BooleanType), - arguments = Argument("atOtherHomes", OptionInputType(BooleanType), true) :: Nil, + arguments = List(Argument("atOtherHomes", OptionInputType(BooleanType), true)), resolve = _ => None), Field( "isAtLocation", OptionType(BooleanType), arguments = - Argument("x", OptionInputType(IntType)) :: Argument("y", OptionInputType(IntType)) :: Nil, + List(Argument("x", OptionInputType(IntType)), Argument("y", OptionInputType(IntType))), resolve = _ => None) ) ) @@ -109,7 +117,7 @@ trait ValidationSupport extends Matchers { Field( "name", OptionType(StringType), - arguments = Argument("surname", OptionInputType(BooleanType)) :: Nil, + arguments = List(Argument("surname", OptionInputType(BooleanType))), resolve = _ => None), Field("pets", OptionType(ListType(OptionType(Pet))), resolve = _ => None), Field("relatives", OptionType(ListType(OptionType(Human))), resolve = _ => None) @@ -143,88 +151,86 @@ trait ValidationSupport extends Matchers { Field( "intArgField", OptionType(StringType), - arguments = Argument("intArg", OptionInputType(IntType)) :: Nil, + arguments = List(Argument("intArg", OptionInputType(IntType))), resolve = _ => None), Field( "bigIntArgField", OptionType(StringType), - arguments = Argument("bigIntArg", OptionInputType(BigIntType)) :: Nil, + arguments = List(Argument("bigIntArg", OptionInputType(BigIntType))), resolve = _ => None), Field( "nonNullIntArgField", OptionType(StringType), - arguments = Argument("nonNullIntArg", IntType) :: Nil, + arguments = List(Argument("nonNullIntArg", IntType)), resolve = _ => None), Field( "stringArgField", OptionType(StringType), - arguments = Argument("stringArg", OptionInputType(StringType)) :: Nil, + arguments = List(Argument("stringArg", OptionInputType(StringType))), resolve = _ => None), Field( "booleanArgField", OptionType(StringType), - arguments = Argument("booleanArg", OptionInputType(BooleanType)) :: Nil, + arguments = List(Argument("booleanArg", OptionInputType(BooleanType))), resolve = _ => None), Field( "enumArgField", OptionType(StringType), - arguments = Argument("enumArg", OptionInputType(FurColor)) :: Nil, + arguments = List(Argument("enumArg", OptionInputType(FurColor))), resolve = _ => None), Field( "floatArgField", OptionType(StringType), - arguments = Argument("floatArg", OptionInputType(FloatType)) :: Nil, + arguments = List(Argument("floatArg", OptionInputType(FloatType))), resolve = _ => None), Field( "bigDecimalArgField", OptionType(StringType), - arguments = Argument("bigDecimalArg", OptionInputType(BigDecimalType)) :: Nil, + arguments = List(Argument("bigDecimalArg", OptionInputType(BigDecimalType))), resolve = _ => None), Field( "idArgField", OptionType(StringType), - arguments = Argument("idArg", OptionInputType(IDType)) :: Nil, + arguments = List(Argument("idArg", OptionInputType(IDType))), resolve = _ => None), Field( "stringListArgField", OptionType(StringType), - arguments = Argument( - "stringListArg", - OptionInputType(ListInputType(OptionInputType(StringType)))) :: Nil, + arguments = List( + Argument("stringListArg", OptionInputType(ListInputType(OptionInputType(StringType))))), resolve = _ => None ), Field( "complexArgField", OptionType(StringType), - arguments = Argument("complexArg", OptionInputType(ComplexInput)) :: Nil, + arguments = List(Argument("complexArg", OptionInputType(ComplexInput))), resolve = _ => None), Field( "multipleReqs", OptionType(StringType), - arguments = Argument("req1", IntType) :: Argument("req2", IntType) :: Nil, + arguments = List(Argument("req1", IntType), Argument("req2", IntType)), resolve = _ => None), Field( "nonNullFieldWithDefault", OptionType(StringType), - arguments = Argument("arg", IntType, 0) :: Nil, + arguments = List(Argument("arg", IntType, 0)), resolve = _ => None), Field( "multipleOpts", OptionType(StringType), - arguments = Argument("opt1", OptionInputType(IntType), 0) :: Argument( - "opt2", - OptionInputType(IntType), - 0) :: Nil, + arguments = List( + Argument("opt1", OptionInputType(IntType), 0), + Argument("opt2", OptionInputType(IntType), 0)), resolve = _ => None ), Field( "multipleOptAndReq", OptionType(StringType), - arguments = Argument("req1", IntType) :: - Argument("req2", IntType) :: - Argument("opt1", OptionInputType(IntType), 0) :: - Argument("opt2", OptionInputType(IntType), 0) :: - Nil, + arguments = List( + Argument("req1", IntType), + Argument("req2", IntType), + Argument("opt1", OptionInputType(IntType), 0), + Argument("opt2", OptionInputType(IntType), 0)), resolve = _ => None ) ) @@ -236,7 +242,7 @@ trait ValidationSupport extends Matchers { Field( "human", OptionType(Human), - arguments = Argument("id", OptionInputType(IDType)) :: Nil, + arguments = List(Argument("id", OptionInputType(IDType))), resolve = _ => None), Field("alien", OptionType(Alien), resolve = _ => None), Field("dog", OptionType(Dog), resolve = _ => None), diff --git a/modules/core/src/test/scala/sangria/validation/DocumentAnalyzerSpec.scala b/modules/core/src/test/scala/sangria/validation/DocumentAnalyzerSpec.scala index e6897428..3c2b2622 100644 --- a/modules/core/src/test/scala/sangria/validation/DocumentAnalyzerSpec.scala +++ b/modules/core/src/test/scala/sangria/validation/DocumentAnalyzerSpec.scala @@ -7,6 +7,9 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import sangria.renderer.QueryRenderer +import sangria.util.tag.@@ // Scala 3 issue workaround +import sangria.marshalling.FromInput.CoercedScalaResult + class DocumentAnalyzerSpec extends AnyWordSpec with Matchers with StringMatchers { val NumberType = EnumType( "Number", @@ -20,8 +23,11 @@ class DocumentAnalyzerSpec extends AnyWordSpec with Matchers with StringMatchers Field( "normalField", OptionType(NumberType), - arguments = Argument("enumArg", OptionInputType(NumberType)) :: Nil, - resolve = ctx => ctx.argOpt[Int]("enumArg")), + arguments = Argument[Option[Int @@ CoercedScalaResult]]( + "enumArg", + OptionInputType(NumberType)) :: Nil, + resolve = ctx => ctx.argOpt[Int]("enumArg") + ), Field( "deprecatedField", OptionType(StringType), From 76a3180e3e09084fb67a16f31f83c14f826482e7 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 4 Aug 2022 10:18:15 +0200 Subject: [PATCH 3/6] Adjust CI to not test derivation for Scala 3 --- .github/workflows/ci.yml | 17 +++++++++++++---- build.sbt | 10 ++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea4716e6..7ed4e28b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.16, 2.13.8] + scala: [2.12.16, 2.13.8, 3.1.3] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -60,8 +60,7 @@ jobs: - name: Check that workflows are up to date run: sbt ++${{ matrix.scala }} githubWorkflowCheck - - name: Build project - run: sbt ++${{ matrix.scala }} test + - run: sbt ++${{ matrix.scala }} testAll - name: Compress target directories run: tar cf targets.tar modules/core/target modules/parser/target modules/benchmarks/target modules/derivation/target modules/ast/target target modules/sangria/target project/target @@ -79,7 +78,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.8] + scala: [3.1.3] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -127,6 +126,16 @@ jobs: tar xf targets.tar rm targets.tar + - name: Download target directories (3.1.3) + uses: actions/download-artifact@v2 + with: + name: target-${{ matrix.os }}-3.1.3-${{ matrix.java }} + + - name: Inflate target directories (3.1.3) + run: | + tar xf targets.tar + rm targets.tar + - env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} PGP_SECRET: ${{ secrets.PGP_SECRET }} diff --git a/build.sbt b/build.sbt index cfcbba90..4149dc2f 100644 --- a/build.sbt +++ b/build.sbt @@ -15,6 +15,16 @@ ThisBuild / githubWorkflowBuildPreamble ++= List( WorkflowStep.Sbt(List("scalafmtCheckAll"), name = Some("Check formatting")) ) +// Workaround for not running derivation tests for Scala 3 yet. +// Should be deleted and githubWorkflowBuild should be restored to default when +// derivation is compiling for all versions +lazy val testAll = taskKey[Unit]("Test all available modules depending on the Scala version") +testAll := { + if (isScala3.value) Def.sequential(parser / Test / test, core / Test / test).value + else Def.sequential(parser / Test / test, core / Test / test, derivation / Test / test).value +} +ThisBuild / githubWorkflowBuild := Seq(WorkflowStep.Sbt(List("testAll"))) + // Release ThisBuild / githubWorkflowTargetTags ++= Seq("v*") ThisBuild / githubWorkflowPublishTargetBranches := From dfb739f4c07da9126434b254ef90d55d1c7f0e92 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 4 Aug 2022 10:32:57 +0200 Subject: [PATCH 4/6] Deactivate unused options in Scala 3 --- build.sbt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 4149dc2f..02a783d6 100644 --- a/build.sbt +++ b/build.sbt @@ -223,11 +223,12 @@ lazy val projectInfo = Seq( ) lazy val scalacSettings = Seq( - scalacOptions ++= Seq("-deprecation", "-feature", "-Xlint:-missing-interpolator,_"), + scalacOptions ++= Seq("-deprecation", "-feature"), + scalacOptions ++= { if (!isScala3.value) Seq("-Xlint:-missing-interpolator,_") else Seq.empty }, scalacOptions ++= { if (scalaVersion.value.startsWith("2.12")) Seq("-language:higherKinds") else List.empty[String] }, - scalacOptions += "-target:jvm-1.8", + scalacOptions += { if (isScala3.value) "-Xtarget:8" else "-target:jvm-1.8" }, autoAPIMappings := true, Compile / doc / scalacOptions ++= // scaladoc options Opts.doc.title("Sangria") ++ Seq( From 1169ca1b2e56701b502af5d5fc95d28ed6484161 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 4 Aug 2022 11:00:42 +0200 Subject: [PATCH 5/6] After rebase fix and apply review suggestions --- build.sbt | 15 ++++++++++----- .../scala-3/sangria/macros/ToExprGivens.scala | 3 ++- .../sangria/execution/QueryReducerExecutor.scala | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index 02a783d6..f7217a08 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ ThisBuild / githubWorkflowBuildPreamble ++= List( ) // Workaround for not running derivation tests for Scala 3 yet. -// Should be deleted and githubWorkflowBuild should be restored to default when +// Should be deleted and githubWorkflowBuild should be restored to default when // derivation is compiling for all versions lazy val testAll = taskKey[Unit]("Test all available modules depending on the Scala version") testAll := { @@ -42,6 +42,10 @@ ThisBuild / githubWorkflowPublish := Seq( ) ) +def emptyForScala3(isScala3: Boolean, module: ModuleID): Set[ModuleID] = + if (isScala3) Set.empty + else Set(module) + lazy val root = project .in(file(".")) .withId("sangria-root") @@ -59,7 +63,7 @@ lazy val ast = project .settings( name := "sangria-ast", description := "Scala GraphQL AST representation", - mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-ast" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-ast" % "3.0.0"), mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[IncompatibleResultTypeProblem]("sangria.ast.DirectiveDefinition.*"), ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.ast.DirectiveDefinition.apply"), @@ -81,7 +85,8 @@ lazy val parser = project .settings( name := "sangria-parser", description := "Scala GraphQL parser", - mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-parser" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-parser" % "3.0.0"), + libraryDependencies ++= Seq( // AST Parser "org.parboiled" %% "parboiled" % "2.4.0", @@ -101,7 +106,7 @@ lazy val core = project .settings( name := "sangria-core", description := "Scala GraphQL implementation", - mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-core" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-core" % "3.0.0"), mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[DirectMissingMethodProblem]( "sangria.introspection.IntrospectionDirective.apply"), @@ -162,7 +167,7 @@ lazy val derivation = project .settings( name := "sangria-derivation", Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), - mimaPreviousArtifacts := Set("org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), // Macros libraryDependencies ++= (if (isScala3.value) Seq.empty else Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value)), diff --git a/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala index ab76bbd5..d08c2d26 100644 --- a/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala +++ b/modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala @@ -125,13 +125,14 @@ trait ToExprGivens { ${ Expr(p) }) } - case DirectiveDefinition(n, a, l, desc, c, p) => + case DirectiveDefinition(n, a, l, desc, r, c, p) => '{ sangria.ast.DirectiveDefinition( ${ Expr(n) }, ${ Expr(a) }, ${ Expr(l) }, ${ Expr(desc) }, + ${ Expr(r) }, ${ Expr(c) }, ${ Expr(p) }) } diff --git a/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala b/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala index 8f7fb555..c086c064 100644 --- a/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala +++ b/modules/core/src/main/scala/sangria/execution/QueryReducerExecutor.scala @@ -35,7 +35,7 @@ object QueryReducerExecutor { userContext, exceptionHandler, scalarMiddleware, - true)(InputUnmarshaller.scalaInputUnmarshaller[Any]) + true)(InputUnmarshaller.scalaInputUnmarshaller[Any @@ ScalaInput]) val executionResult = for { operation <- Executor.getOperation(exceptionHandler, queryAst, operationName) From 10c82e18fdb22ca1db847bce1a3746a9b7136fc7 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 4 Aug 2022 13:10:17 +0200 Subject: [PATCH 6/6] Setup GHA manually for Scala 3, run scalafmt and filter mima checks --- .github/workflows/ci-scala3.yml | 55 +++++++++++++++++++ .github/workflows/ci.yml | 17 ++---- build.sbt | 33 +++++------ .../sangria/execution/ValueCollector.scala | 3 +- 4 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/ci-scala3.yml diff --git a/.github/workflows/ci-scala3.yml b/.github/workflows/ci-scala3.yml new file mode 100644 index 00000000..37caaf0d --- /dev/null +++ b/.github/workflows/ci-scala3.yml @@ -0,0 +1,55 @@ +# This file was based on ci.yml file, but is unmanaged by the sbt plugin +# and was edited manually. Should be removed when sangria-derivation will +# be merged for Scala 3 + +name: Continuous Integration (Scala 3) + +on: + pull_request: + branches: ['**'] + push: + branches: ['**'] + tags: [v*] + +jobs: + build: + name: Build and Test on Scala 3 + strategy: + matrix: + os: [ubuntu-latest] + scala: [3.1.3] + java: [temurin@11] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v2 + with: + distribution: temurin + java-version: 11 + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Check binary compatibility + run: sbt ++${{ matrix.scala }} "sangria-ast/mimaReportBinaryIssues;sangria-parser/mimaReportBinaryIssues;sangria-core/mimaReportBinaryIssues" + + - name: Check formatting + run: sbt ++${{ matrix.scala }} scalafmtCheckAll + + - name: Build and test project + run: sbt ++${{ matrix.scala }} "sangria-parser/test;sangria-core/test" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ed4e28b..ea4716e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.16, 2.13.8, 3.1.3] + scala: [2.12.16, 2.13.8] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -60,7 +60,8 @@ jobs: - name: Check that workflows are up to date run: sbt ++${{ matrix.scala }} githubWorkflowCheck - - run: sbt ++${{ matrix.scala }} testAll + - name: Build project + run: sbt ++${{ matrix.scala }} test - name: Compress target directories run: tar cf targets.tar modules/core/target modules/parser/target modules/benchmarks/target modules/derivation/target modules/ast/target target modules/sangria/target project/target @@ -78,7 +79,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [3.1.3] + scala: [2.13.8] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -126,16 +127,6 @@ jobs: tar xf targets.tar rm targets.tar - - name: Download target directories (3.1.3) - uses: actions/download-artifact@v2 - with: - name: target-${{ matrix.os }}-3.1.3-${{ matrix.java }} - - - name: Inflate target directories (3.1.3) - run: | - tar xf targets.tar - rm targets.tar - - env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} PGP_SECRET: ${{ secrets.PGP_SECRET }} diff --git a/build.sbt b/build.sbt index f7217a08..0d966624 100644 --- a/build.sbt +++ b/build.sbt @@ -9,21 +9,14 @@ val isScala3 = Def.setting( // sbt-github-actions needs configuration in `ThisBuild` ThisBuild / crossScalaVersions := Seq("2.12.16", "2.13.8", "3.1.3") -ThisBuild / scalaVersion := crossScalaVersions.value.last +ThisBuild / scalaVersion := "2.13.8" ThisBuild / githubWorkflowBuildPreamble ++= List( WorkflowStep.Sbt(List("mimaReportBinaryIssues"), name = Some("Check binary compatibility")), WorkflowStep.Sbt(List("scalafmtCheckAll"), name = Some("Check formatting")) ) -// Workaround for not running derivation tests for Scala 3 yet. -// Should be deleted and githubWorkflowBuild should be restored to default when -// derivation is compiling for all versions -lazy val testAll = taskKey[Unit]("Test all available modules depending on the Scala version") -testAll := { - if (isScala3.value) Def.sequential(parser / Test / test, core / Test / test).value - else Def.sequential(parser / Test / test, core / Test / test, derivation / Test / test).value -} -ThisBuild / githubWorkflowBuild := Seq(WorkflowStep.Sbt(List("testAll"))) +// For now we set up GH Actions manually for Scala 3 +ThisBuild / githubWorkflowScalaVersions := crossScalaVersions.value.filterNot(_.startsWith("3.")) // Release ThisBuild / githubWorkflowTargetTags ++= Seq("v*") @@ -63,7 +56,9 @@ lazy val ast = project .settings( name := "sangria-ast", description := "Scala GraphQL AST representation", - mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-ast" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3( + isScala3.value, + "org.sangria-graphql" %% "sangria-ast" % "3.0.0"), mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[IncompatibleResultTypeProblem]("sangria.ast.DirectiveDefinition.*"), ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.ast.DirectiveDefinition.apply"), @@ -85,8 +80,9 @@ lazy val parser = project .settings( name := "sangria-parser", description := "Scala GraphQL parser", - mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-parser" % "3.0.0"), - + mimaPreviousArtifacts := emptyForScala3( + isScala3.value, + "org.sangria-graphql" %% "sangria-parser" % "3.0.0"), libraryDependencies ++= Seq( // AST Parser "org.parboiled" %% "parboiled" % "2.4.0", @@ -106,7 +102,9 @@ lazy val core = project .settings( name := "sangria-core", description := "Scala GraphQL implementation", - mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-core" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3( + isScala3.value, + "org.sangria-graphql" %% "sangria-core" % "3.0.0"), mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[DirectMissingMethodProblem]( "sangria.introspection.IntrospectionDirective.apply"), @@ -128,7 +126,8 @@ lazy val core = project "sangria.schema.Directive.copy$default$*"), ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Directive.apply"), ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Directive.this"), - ProblemFilters.exclude[MissingTypesProblem]("sangria.schema.Directive$") + ProblemFilters.exclude[MissingTypesProblem]("sangria.schema.Directive$"), + ProblemFilters.exclude[MissingTypesProblem]("sangria.schema.MappedAbstractType") ), Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), libraryDependencies ++= Seq( @@ -167,7 +166,9 @@ lazy val derivation = project .settings( name := "sangria-derivation", Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"), - mimaPreviousArtifacts := emptyForScala3(isScala3.value, "org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), + mimaPreviousArtifacts := emptyForScala3( + isScala3.value, + "org.sangria-graphql" %% "sangria-derivation" % "3.0.0"), // Macros libraryDependencies ++= (if (isScala3.value) Seq.empty else Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value)), diff --git a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala index c878cf7d..7e56139e 100644 --- a/modules/core/src/main/scala/sangria/execution/ValueCollector.scala +++ b/modules/core/src/main/scala/sangria/execution/ValueCollector.scala @@ -146,7 +146,8 @@ object ValueCollector { marshaller, fromInput.marshaller, errors = errors, - valueMap = (v: Any) => fromInput.fromResult(v.asInstanceOf[fromInput.marshaller.Node]), + valueMap = + (v: Any) => fromInput.fromResult(v.asInstanceOf[fromInput.marshaller.Node]), defaultValueInfo = defaultInfo, undefinedValues = undefinedArgs, isArgument = true,