From cc1b014ee560e1180b8be514454f1404cbff8a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Thu, 10 Sep 2026 22:23:15 -0300 Subject: [PATCH 1/3] carve out parser as an artifact --- .github/workflows/ci.yml | 4 +- README.md | 18 ++--- build.sbt | 24 ++++++- .../scala/syntax/DocumentLiteralSuite.scala | 71 +++++++++++++++++++ .../{core => parser}/src/main/scala/ast.scala | 0 .../src/main/scala/parser.scala | 0 .../src/main/scala/problem.scala | 0 .../src/main/scala/result.scala | 0 .../src/test/scala/laws/ResultSuite.scala | 0 .../src/test/scala/parser/ParserSuite.scala | 8 ++- .../test/scala/problem}/ProblemSuite.scala | 2 +- 11 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala rename modules/{core => parser}/src/main/scala/ast.scala (100%) rename modules/{core => parser}/src/main/scala/parser.scala (100%) rename modules/{core => parser}/src/main/scala/problem.scala (100%) rename modules/{core => parser}/src/main/scala/result.scala (100%) rename modules/{core => parser}/src/test/scala/laws/ResultSuite.scala (100%) rename modules/{core => parser}/src/test/scala/parser/ParserSuite.scala (99%) rename modules/{core/src/test/scala/compiler => parser/src/test/scala/problem}/ProblemSuite.scala (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d359b93e..14c6cc46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,11 +106,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p modules/skunk/js/target modules/sql-core/.js/target modules/circe/.jvm/target modules/generic/.jvm/target modules/doobie-mysql/target modules/doobie-pg/target unidocs/target modules/core/.native/target modules/skunk/jvm/target modules/core/.js/target modules/doobie-core/target modules/circe/.js/target modules/doobie-h2/target modules/skunk/native/target modules/generic/.js/target modules/doobie-oracle/target modules/sql-core/.jvm/target modules/core/.jvm/target modules/sql-pg/native/target modules/doobie-mssql/target modules/sql-pg/js/target modules/doobie-sqlite/target modules/circe/.native/target modules/generic/.native/target modules/sql-pg/jvm/target modules/doobie-mariadb/target modules/sql-core/.native/target project/target + run: mkdir -p modules/skunk/js/target modules/sql-core/.js/target modules/circe/.jvm/target modules/generic/.jvm/target modules/doobie-mysql/target modules/doobie-pg/target modules/parser/.jvm/target unidocs/target modules/parser/.js/target modules/core/.native/target modules/skunk/jvm/target modules/parser/.native/target modules/core/.js/target modules/doobie-core/target modules/circe/.js/target modules/doobie-h2/target modules/skunk/native/target modules/generic/.js/target modules/doobie-oracle/target modules/sql-core/.jvm/target modules/core/.jvm/target modules/sql-pg/native/target modules/doobie-mssql/target modules/sql-pg/js/target modules/doobie-sqlite/target modules/circe/.native/target modules/generic/.native/target modules/sql-pg/jvm/target modules/doobie-mariadb/target modules/sql-core/.native/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar modules/skunk/js/target modules/sql-core/.js/target modules/circe/.jvm/target modules/generic/.jvm/target modules/doobie-mysql/target modules/doobie-pg/target unidocs/target modules/core/.native/target modules/skunk/jvm/target modules/core/.js/target modules/doobie-core/target modules/circe/.js/target modules/doobie-h2/target modules/skunk/native/target modules/generic/.js/target modules/doobie-oracle/target modules/sql-core/.jvm/target modules/core/.jvm/target modules/sql-pg/native/target modules/doobie-mssql/target modules/sql-pg/js/target modules/doobie-sqlite/target modules/circe/.native/target modules/generic/.native/target modules/sql-pg/jvm/target modules/doobie-mariadb/target modules/sql-core/.native/target project/target + run: tar cf targets.tar modules/skunk/js/target modules/sql-core/.js/target modules/circe/.jvm/target modules/generic/.jvm/target modules/doobie-mysql/target modules/doobie-pg/target modules/parser/.jvm/target unidocs/target modules/parser/.js/target modules/core/.native/target modules/skunk/jvm/target modules/parser/.native/target modules/core/.js/target modules/doobie-core/target modules/circe/.js/target modules/doobie-h2/target modules/skunk/native/target modules/generic/.js/target modules/doobie-oracle/target modules/sql-core/.jvm/target modules/core/.jvm/target modules/sql-pg/native/target modules/doobie-mssql/target modules/sql-pg/js/target modules/doobie-sqlite/target modules/circe/.native/target modules/generic/.native/target modules/sql-pg/jvm/target modules/doobie-mariadb/target modules/sql-core/.native/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/README.md b/README.md index 1bb14397..a7c9eb9b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ [![codecov](https://codecov.io/gh/typelevel/grackle/branch/main/graph/badge.svg)](https://codecov.io/gh/typelevel/grackle) [![Discord](https://img.shields.io/discord/632277896739946517.svg?label=&logo=discord&logoColor=ffffff&color=404244&labelColor=6A7EC2)][grackle-dev] - ## Overview Grackle is a [GraphQL](https://graphql.org) server written in functional [Scala](https://www.scala-lang.org), built on @@ -43,25 +42,28 @@ To add Grackle to your project you should add the following to your `build.sbt`, ```scala // Required: Scala 2.13/3.3+ -libraryDependencies += "org.typelevel" %% "grackle-core" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-core" % "0.31.0" // Optional: support for in-memory Json backend using circe -libraryDependencies += "org.typelevel" %% "grackle-circe" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-circe" % "0.31.0" // Optional: support for in-memory generic Scala backend using shapeless -libraryDependencies += "org.typelevel" %% "grackle-generic" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-generic" % "0.31.0" // Optional: support for Postgres backend via Doobie (JVM only) -libraryDependencies += "org.typelevel" %% "grackle-doobie-pg" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-doobie-pg" % "0.31.0" // Optional: support for Postgres backend via Skunk -libraryDependencies += "org.typelevel" %% "grackle-skunk" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-skunk" % "0.31.0" // Optional: support for Oracle backend via Doobie (JVM only) -libraryDependencies += "org.typelevel" %% "grackle-doobie-oracle" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-doobie-oracle" % "0.31.0" // Optional: support for SQL Server backend via Doobie (JVM only) -libraryDependencies += "org.typelevel" %% "grackle-doobie-mssql" % "0.26.0" +libraryDependencies += "org.typelevel" %% "grackle-doobie-mssql" % "0.31.0" + +// Optional: the GraphQL document parser, AST and Result/Problem types on their own, for tools that only need to parse +libraryDependencies += "org.typelevel" %% "grackle-parser" % "0.31.0" ``` ## Running tests for database backed mappings diff --git a/build.sbt b/build.sbt index 7bd3f458..f2ca08d9 100644 --- a/build.sbt +++ b/build.sbt @@ -240,6 +240,7 @@ lazy val dbTestSettings = Seq( ) lazy val modules: List[CompositeProject] = List( + parser, core, circe, sqlcore, @@ -263,17 +264,37 @@ lazy val modules: List[CompositeProject] = List( lazy val root = tlCrossRootProject.aggregate(modules: _*).disablePlugins(RevolverPlugin) +lazy val parser = crossProject(JVMPlatform, JSPlatform, NativePlatform) + .crossType(CrossType.Pure) + .in(file("modules/parser")) + .enablePlugins(AutomateHeaderPlugin) + .disablePlugins(RevolverPlugin) + .settings(commonSettings) + .settings( + name := "grackle-parser", + // New in 0.31.0 (previous releases had no such artifact). + tlVersionIntroduced := Map("2.13" -> "0.31.0", "3" -> "0.31.0"), + libraryDependencies ++= Seq( + "org.typelevel" %%% "cats-parse" % catsParseVersion, + "org.typelevel" %%% "cats-core" % catsVersion, + "io.circe" %%% "circe-core" % circeVersion + ) + ) + .jsSettings(scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))) + .nativeSettings(nativeSettings) + .nativeSettings(tlVersionIntroduced := Map("2.13" -> "0.31.0", "3" -> "0.31.0")) + lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) .in(file("modules/core")) .enablePlugins(AutomateHeaderPlugin) .disablePlugins(RevolverPlugin) + .dependsOn(parser) .settings(commonSettings) .settings( name := "grackle-core", libraryDependencies ++= Seq( - "org.typelevel" %%% "cats-parse" % catsParseVersion, "org.typelevel" %%% "cats-core" % catsVersion, "org.typelevel" %%% "literally" % literallyVersion, "io.circe" %%% "circe-core" % circeVersion, @@ -627,6 +648,7 @@ lazy val unidocs = project name := "grackle-docs", coverageEnabled := false, ScalaUnidoc / unidoc / unidocProjectFilter := inProjects( + parser.jvm, core.jvm, circe.jvm, sqlcore.jvm, diff --git a/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala new file mode 100644 index 00000000..4a3e9695 --- /dev/null +++ b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala @@ -0,0 +1,71 @@ +// Copyright (c) 2016-2025 Association of Universities for Research in Astronomy, Inc. (AURA) +// Copyright (c) 2016-2025 Grackle Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package syntax + +import munit.FunSuite + +import grackle.GraphQLParser +import grackle.Ast._ +import grackle.Ast.OperationDefinition._ +import grackle.Ast.OperationType._ +import grackle.Ast.Selection._ +import grackle.Ast.Value._ +import grackle.syntax._ + +final class DocumentLiteralSuite extends FunSuite { + + test("doc literal matches runtime parse of the same query") { + val queryText = + """|query { + | character(id: 1000) { + | name + | } + |} + |""".stripMargin + + val literal: Document = doc""" + query { + character(id: 1000) { + name + } + } + """ + + val expected = + Operation( + Query, + None, + Nil, + Nil, + List( + Field( + None, + Name("character"), + List((Name("id"), IntValue(1000))), + Nil, + List( + Field(None, Name("name"), Nil, Nil, Nil) + ) + ) + ) + ) + + assertEquals(literal, List(expected)) + + val parsed = GraphQLParser(GraphQLParser.defaultConfig).parseText(queryText).toOption + assertEquals(parsed, Some(literal)) + } +} diff --git a/modules/core/src/main/scala/ast.scala b/modules/parser/src/main/scala/ast.scala similarity index 100% rename from modules/core/src/main/scala/ast.scala rename to modules/parser/src/main/scala/ast.scala diff --git a/modules/core/src/main/scala/parser.scala b/modules/parser/src/main/scala/parser.scala similarity index 100% rename from modules/core/src/main/scala/parser.scala rename to modules/parser/src/main/scala/parser.scala diff --git a/modules/core/src/main/scala/problem.scala b/modules/parser/src/main/scala/problem.scala similarity index 100% rename from modules/core/src/main/scala/problem.scala rename to modules/parser/src/main/scala/problem.scala diff --git a/modules/core/src/main/scala/result.scala b/modules/parser/src/main/scala/result.scala similarity index 100% rename from modules/core/src/main/scala/result.scala rename to modules/parser/src/main/scala/result.scala diff --git a/modules/core/src/test/scala/laws/ResultSuite.scala b/modules/parser/src/test/scala/laws/ResultSuite.scala similarity index 100% rename from modules/core/src/test/scala/laws/ResultSuite.scala rename to modules/parser/src/test/scala/laws/ResultSuite.scala diff --git a/modules/core/src/test/scala/parser/ParserSuite.scala b/modules/parser/src/test/scala/parser/ParserSuite.scala similarity index 99% rename from modules/core/src/test/scala/parser/ParserSuite.scala rename to modules/parser/src/test/scala/parser/ParserSuite.scala index 07515f00..66da850b 100644 --- a/modules/core/src/test/scala/parser/ParserSuite.scala +++ b/modules/parser/src/test/scala/parser/ParserSuite.scala @@ -24,13 +24,12 @@ import grackle.Ast.OperationType._ import grackle.Ast.Selection._ import grackle.Ast.Type.Named import grackle.Ast.Value._ -import grackle.syntax._ final class ParserSuite extends CatsEffectSuite { val parser = mkParser() test("simple query") { - val query = doc""" + val query = """ query { character(id: 1000) { name @@ -57,7 +56,10 @@ final class ParserSuite extends CatsEffectSuite { ) ) - assertEquals(query, List(expected)) + parser.parseText(query).toOption match { + case Some(xs) => assertEquals(xs, List(expected)) + case _ => assert(false) + } } test("multiple parameters (commas)") { diff --git a/modules/core/src/test/scala/compiler/ProblemSuite.scala b/modules/parser/src/test/scala/problem/ProblemSuite.scala similarity index 99% rename from modules/core/src/test/scala/compiler/ProblemSuite.scala rename to modules/parser/src/test/scala/problem/ProblemSuite.scala index 65e08625..a264eea8 100644 --- a/modules/core/src/test/scala/compiler/ProblemSuite.scala +++ b/modules/parser/src/test/scala/problem/ProblemSuite.scala @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package compiler +package problem import io.circe.JsonObject import io.circe.literal._ From 0efa020f1c2a78f8233bedfee745ea4f09317054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Fri, 11 Sep 2026 10:05:46 -0300 Subject: [PATCH 2/3] fixes in build for new project --- build.sbt | 5 ++--- .../core/src/test/scala/syntax/DocumentLiteralSuite.scala | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index f2ca08d9..67bc4fe4 100644 --- a/build.sbt +++ b/build.sbt @@ -272,7 +272,8 @@ lazy val parser = crossProject(JVMPlatform, JSPlatform, NativePlatform) .settings(commonSettings) .settings( name := "grackle-parser", - // New in 0.31.0 (previous releases had no such artifact). + // New in 0.31.0 on every platform, so `nativeSettings` (which dates the native artifact to + // 0.26.1) doesn't apply here. tlVersionIntroduced := Map("2.13" -> "0.31.0", "3" -> "0.31.0"), libraryDependencies ++= Seq( "org.typelevel" %%% "cats-parse" % catsParseVersion, @@ -281,8 +282,6 @@ lazy val parser = crossProject(JVMPlatform, JSPlatform, NativePlatform) ) ) .jsSettings(scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))) - .nativeSettings(nativeSettings) - .nativeSettings(tlVersionIntroduced := Map("2.13" -> "0.31.0", "3" -> "0.31.0")) lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) diff --git a/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala index 4a3e9695..6a0c7cf3 100644 --- a/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala +++ b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala @@ -17,12 +17,12 @@ package syntax import munit.FunSuite -import grackle.GraphQLParser import grackle.Ast._ import grackle.Ast.OperationDefinition._ import grackle.Ast.OperationType._ import grackle.Ast.Selection._ import grackle.Ast.Value._ +import grackle.GraphQLParser import grackle.syntax._ final class DocumentLiteralSuite extends FunSuite { From 55d62011895df7dd2bc64a2f36c809c2f93ca16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Fri, 11 Sep 2026 11:17:27 -0300 Subject: [PATCH 3/3] Fix doc literal test against located fields --- .../scala/syntax/DocumentLiteralSuite.scala | 50 +++---------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala index 6a0c7cf3..d03d9807 100644 --- a/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala +++ b/modules/core/src/test/scala/syntax/DocumentLiteralSuite.scala @@ -17,55 +17,21 @@ package syntax import munit.FunSuite -import grackle.Ast._ -import grackle.Ast.OperationDefinition._ -import grackle.Ast.OperationType._ -import grackle.Ast.Selection._ -import grackle.Ast.Value._ +import grackle.Ast.Document import grackle.GraphQLParser import grackle.syntax._ final class DocumentLiteralSuite extends FunSuite { - test("doc literal matches runtime parse of the same query") { - val queryText = - """|query { - | character(id: 1000) { - | name - | } - |} - |""".stripMargin + // The `doc` interpolator parses at compile time; this checks it agrees with the runtime parser. + // Both are given exactly the same text, so the `location`s recorded in the AST match too. + test("doc literal matches the runtime parse of the same text") { + val text = "query { character(id: 1000) { name } }" - val literal: Document = doc""" - query { - character(id: 1000) { - name - } - } - """ + val literal: Document = doc"query { character(id: 1000) { name } }" - val expected = - Operation( - Query, - None, - Nil, - Nil, - List( - Field( - None, - Name("character"), - List((Name("id"), IntValue(1000))), - Nil, - List( - Field(None, Name("name"), Nil, Nil, Nil) - ) - ) - ) - ) + val parsed = GraphQLParser(GraphQLParser.defaultConfig).parseText(text) - assertEquals(literal, List(expected)) - - val parsed = GraphQLParser(GraphQLParser.defaultConfig).parseText(queryText).toOption - assertEquals(parsed, Some(literal)) + assertEquals(parsed.toOption, Some(literal)) } }