Skip to content

v0.5.0

Compare
Choose a tag to compare
@OlegIlyenko OlegIlyenko released this 03 Dec 02:56
· 1939 commits to main since this release
  • A lot of performance improvements across the whole library

  • Added basic subscription support as defined in the spec (graphql/graphql-spec#109) and reference implementation (#89).
    At the moment subscriptions are pretty basic, so it's meant more for experiments rather than for use in real applications.
    It is very likely that this feature will experience breaking changes in the near future (spec change)

  • Much better handling of input objects (#37, #70). A new type-class is introduced: FromInput. It provides high-level and low-level
    way to deserialize arbitrary input objects, just like ToInput.

    In order to use this feature, you need to provide a type parameter to the InputObjectType:

    case class Article(title: String, text: Option[String])
    
    val ArticleType = InputObjectType[Article]("Article", List(
      InputField("title", StringType),
      InputField("text", OptionInputType(StringType))))
    
    val arg = Argument("article", ArticleType)

    This code will not compile unless you define an implicit instance of FromInput for Article case class:

    implicit val manual = new FromInput[Article] {
      val marshaller = CoercedScalaResultMarshaller.default
      def fromResult(node: marshaller.Node) = {
        val ad = node.asInstanceOf[Map[String, Any]]
    
        Article(
          title = ad("title").asInstanceOf[String],
          text = ad.get("text").flatMap(_.asInstanceOf[Option[String]])
      }
    }

    As you can see, you need to provide a ResultMarshaller for desired format and then use a marshaled value to create a domain object based on it.
    Many instances of FromInput are already provided out-of-the-box. For instance FromInput[Map[String, Any]] was added to support existing map-like
    data-structure format. All supported Json libraries also provide FromInput[JsValue] so that you can use Json AST instead of working with Map[String, Any].

    Moreover, play-json and spray-json integration provide support for Reads and JsonFormat. This means that your domain objects are automatically
    supported as long as you have Reads or JsonFormat defined for them. For instance this example should compile and work just fine without explicit
    FromInput declaration:

    import sangria.integration.playJson._
    import play.api.libs.json._
    
    case class Article(title: String, text: Option[String])
    
    implicit val articleFormat = Json.format[Article]
    
    val ArticleType = InputObjectType[Article]("Article", List(
      InputField("title", StringType),
      InputField("text", OptionInputType(StringType))))
    
    val arg = Argument("article", ArticleType)

    CAUTION: this is minor breaking change. Together with null value support, this feature changes the way input objects are
    deserialized into map-like structures (which still happens by default). Optional input fields will now produce input
    objects like:

    // for JSON input: {"op1": "foo", "opt2": null} 
    Map("opt1"  Some("foo"), "opt2"  None)
    
    // for JSON input: {"op1": "foo"} 
    Map("opt1"  Some("foo"))

    instead of (old format):

    // for JSON input: {"op1": "foo", "opt2": null} 
    Map("opt1"  "foo")
    
    // for JSON input: {"op1": "foo"} 
    Map("opt1"  "foo")

    As you can see, this allows you to distinguish between "undefined" json object fields and json object fields that are set to null.

  • null value support (as defined in the spec change: graphql/graphql-spec#83) (#55) (spec change)

  • Extracted input value parsing and made it a first-class citizen (#103). So now you can parse and render any ast.Value independently from
    GraphQL query. There is even a new graphqlInput macros available:

    import sangria.renderer.QueryRenderer
    import sangria.macros._
    import sangria.ast
    
    val parsed: ast.Value =
      graphqlInput"""
        {
          id: "1234345"
          version: 2 # changed 2 times
          deliveries: [
            {id: 123, received: false, note: null, state: OPEN}
          ]
        }
      """
    
    val rendered: String =
      QueryRenderer.render(parsed, QueryRenderer.PrettyInput)
    
    println(rendered)

    It will print something like this:

    {
      id: "1234345"
      version: 2
      deliveries: [{
        id: 123
        received: false
        note: null
        state: OPEN
      }]
    }

    InputUnmarshaller and ResultMarshaller are also now available for it, so you can use ast.Value as a variables or it can be a result
    of GraphQL query execution (instead of more traditional JSON).

  • ToInput, InputUnmarshaller and ResultMarshaller are moved to sangria.marshalling package.

  • Improved error messages for input values (#86). Now they will contain the reason why particular value is invalid.

  • Implementations of interfaces can include additional field args (#90) (spec change)

  • Loosen overlapping field validation rules (#94) (spec change)

  • False positive validation error from fragment cycle when unknown fragment (#95)

  • Interfaces with covariant return types (#96)

  • A lot of minor changes and performance improvements in validation rules and query validator (#97) (spec change)

  • Add error handling in the SchemaRenderer (#100)

  • Ambiguous implicit when using a UnionType bug (#101)

  • A lot of internal refactorings (especially in variable and argument processing) to make everything above possible