Releases: sangria-graphql/sangria
v0.7.1
-
Provide
extendSchema
utility function (#113). This feature allows you to extend existing schema with additional types and existing types with additional fields. It may be very useful for client-side tools and for server implementations in cases where parts of a schema are dynamically generated or coming from external sources (like database).Here is a small example of how you can use it:
val schema: Schema[Ctx, Val] = Schema(...) val schemaExtensions = graphql""" extend type Human { pet: Animal @loadPetInfo } interface Animal { name: String! } type Dog implements Animal { name: String! nickname: String } type Cat implements Animal { name: String! age: Int } """ val myCustomBuilder = new DefaultAstSchemaBuilder[Ctx] {...} val extendedSchema = schema.extend(schemaExtensions, myCustomBuilder)
Just like with AST-based schema materialization, you can provide a custom schema builder which allows you to control most of the aspects of generated parts of the schema.
-
Handling of more than one
ProjectionName
for one field (#146). -
Updated context propagated only to siblings (#145).
v0.7.0
- Initial CATs (Compatibility Acceptance Tests) support (#142). The test suite itself is still work-in-progress, but sangria includes an integration which executes all currently available test cases.
- IDL (schema definition) syntax parsing and rendering (#137, #62)
- AST-based schema materializer (#139, #115). This feature may be very useful for different tools that need to create an executable schema based on IDL definitions. Almost any aspect of generated in-memory schema representation can be customized via custom implementation of
AstSchemaBuilder
. This feature is already used in sangria itself for CATs (Compatibility Acceptance Tests) integration. At the moment default implementation ofAstSchemaBuilder
treats comments that start with##
as a field/object/argument description. - Partial resolve
Action
s (#140). This change introduces 2 newAction
s that can be returned back from aresolve
function:PartialValue
andPartialFutureValue
. This allows you to return a list of errors in addition to a successfully resolved value (which may contain only partial result due to the errors). - Preserve comments during the AST parsing (#105). Most of the AST classes got comment
Option[Comment]
field. It can be very useful for query formatting becauseQueryRenderer
also got support for comments and able to render them. - Include execution path in error objects (#143). This may be helpful for client side tools that would like to analyze error messages and programmatically use them in some way. This is a minor braking change since
field
property on error is removed in favor of newpath
property which is a list. - Introspection-based schema materializer now also uses more advanced
IntrospectionSchemaBuilder
(similar to the AST-based one) instead ofMaterializationLogic
, which is now removed. This introduces a minor breaking change, but in a long runIntrospectionSchemaBuilder
will provide much more flexibility. - Add comment/directive support in the introspection-based schema renderer (#136).
- Validation: improving overlapping fields quality (#133)
- Deprecated directive (#132)
- New directive locations (#131)
- Default values should be in GraphQL format (introspection) (#141)
- Added support for case objects defined in companion object (#135). Big thanks to @joprice for contributing this improvement!
SchemaRenderer
now has improved default value rendering- Execution
path
now got it's own classExecutionPath
(which is now used instead of simpleVector[String]
). This introduces a minor breaking change.
v0.6.3
-
Marshaling for Amazon Ion data format is introduced. Amazon Ion is a richly-typed, self-describing, hierarchical data serialization format offering interchangeable binary and text representations.
You need following dependency to use it:
"org.sangria-graphql" %% "sangria-ion" % "0.1.0"
In order to use Ion marshalling, you need an implicit instance of
IonSystem
in scope as well:import sangria.marshalling.ion._ implicit val ionSystem = IonSystemBuilder.standard().build() val result: Future[IonValue] = Executor.execute(schema, query)
-
Marshalling API is updated to v0.2.1. It introduces a minor breaking change. This change introduces performance improvements to scalar value marshalling and gives much more flexibility in terms of the type of marshaled values.
ResultMarshaller
now able to communicate it's natively supported capabilities to aScalarType
viaMarshallerCapability
. A set of standard marshaller capabilities were introduced:DateSupport
- Marshaller supportsjava.util.Date
natively.CalendarSupport
- Marshaller supportsjava.util.Calendar
natively.BlobSupport
- Marshaller supports large binary objects in form ofArray[Byte]
natively.
This still requires you to create a custom scalar types (for dates, blobs, etc.), but it gives you an ability to generically use native features of underlying data format.
ScalarType
now also able to communicate back to marshaller viaScalarValueInfo
. This can be used, for instance, to represent anArray[Byte]
as aclob
type instead ofblob
in formats that support both of them (like Amazon Ion). -
Include possible field, argument, type names when validation fails (#126).
-
Deepen introspection query from 3 levels to 7 (#128).
-
Improve validation error message when field names conflict (#130).
-
Interface hierarchies are not correctly rendered with
SchemaRenderer
(#125). -
Updated parboiled to v2.1.3
v0.6.2
This version is fully compatible with "April 2016" version of the GraphQL specification.
v0.6.1
A minor maintenance release to keep up with the spec changes.
- Field order in the result now reflects field order in the query (according to the spec) for all marshalling libraries that support field ordering (#99) (spec change).
- Directive
locations
field replacesonOperation
,onFragment
andonField
(#119) (spec change). - Low-level marshalling API is improved: it's now possible to use efficient map builders (which also able to preserver an order of the fields). This improves serialization performance and minimizes memory footprint. All marshalling libraries already take advantage of this API.
SchemaRenderer
prints duplicated fields for a type that implements an interface (#122)
v0.6.0
-
Macro-Based GraphQL Type Derivation (#120). See "Macro-Based GraphQL Type Derivation" section of the documentation for more info.
-
Prepared Queries (#118). See "Prepared Queries" section of the documentation for more info.
-
Executor.execute
now returnsFuture
with failure if error happened before query execution (#109). It can be extremely helpful when you need to take some action or produce different result in case of error. Typical example is returning different HTTP status code.CAUTION: breaking change and action needed! Since things like validation errors and errors in query reducers are now explicitly returned as a
Future
failure and not as a successful result, you need to take some action to handle them. In order to migrate, all you need to do is to add followingrecover
:Executor.execute(schema, query).recover { case error: ErrorWithResolver ⇒ error.resolveError }
recover
function will make sure that all of the errors, that were previously handled internally inExecutor
, are now properly handled. Code above will produce exactly the same result as before.resolveError
produces a valid GraphQL response JSON and will use custom exception handler, if you have provided one.This new approach to error handling gives you much more flexibility. For example in most cases it makes a lot of sense to return 400 HTTP status code if query validation failed. It was not really possible to do this before. Now you able to do something like this (using playframefork in this particular example):
executor.execute(query, ...) .map(Ok(_)) .recover { case error: QueryAnalysisError ⇒ BadRequest(error.resolveError) case error: ErrorWithResolver ⇒ InternalServerError(error.resolveError) }
This code will produce status code 400 in case of any error caused by client (query validation, invalid operation name, etc.).
Errors that happened in a query reducer would be wrapped in
QueryReducingError
. Here is an example of returning custom status code in case of error in the query reducer:val authReducer = QueryReducer.collectTags[MyContext, String] { case Permission(name) ⇒ name } { (permissionNames, ctx) ⇒ if (ctx.isUserAuthorized(permissionNames)) ctx else throw AuthorizationException("User is not authorized!") } Executor.execute(schema, queryAst, userContext = new MyContext, queryReducers = authReducer :: Nil) .map(Ok(_)) .recover { case QueryReducingError(error: AuthorizationException) ⇒ Unauthorized(error.getMessage) case error: QueryAnalysisError ⇒ BadRequest(error.resolveError) case error: ErrorWithResolver ⇒ InternalServerError(error.resolveError) }
HTTP status code would be 401 for unauthorized users.
If you have issues with the migration, please raise an issue so that we can find a good solution together.
-
Minor breaking change.
userContext
androot
arguments ofExecutor
are moved inExecutor.execute
method. -
Detect name collisions with incompatible types during schema definition (#117)
-
Introduced a type alias
Executor.ExceptionHandler
for exception handler partial function
v0.5.2
-
Added introspection-based schema materializer (#21). This feature has a lot of potential for clint-side tools, testing, mocking,
creating facade GraphQL servers, etc.Here is simple example of how you can use this feature (Using circe in this particular example):
import io.circe._ import sangria.marshalling.circe._ val introspectionResults: Json = ??? // coming from other server or file val clientSchema: Schema[Unit, Unit] = Schema.buildFromIntrospection(introspectionResults)
It takes a results of full introspection query (loaded from the server, file, etc.) and recreates the schema definition with stubs for
resolve methods. You can customize a lot of aspects of materialization by providing customMaterializationLogic
implementation
(you can also extendDefaultMaterializationLogic
class). This means that you can, for instance, plug in some generic field resolution logic (resolveField
method) or
provide generic logic for custom scalars (coerceScalar*
methods). Without these customisations schema only would be able to execute introspection queries.By default, default values (for input object fields and arguments) would be ignored because it's just a string as far as introspection API is concerned. However you can enable default value
support if you know the format of the default values (in many cases it would be JSON). There is even a helper function for this:import spray.json._ import sangria.marshalling.sprayJson._ val clientSchema: Schema[Unit, Unit] = Schema.buildFromIntrospection(introspectionResults, MaterializationLogic.withDefaultValues[Unit, JsValue])
This will inform schema materializer that default values are serialized as JSON and that spray-json should be used to work with them (please note, that
circe does not have a built-in JSON parsing support, so it can't be used out-of-the-box here. On the other hand, it's pretty easy to add support for particular circe
parser by defining an implicit instance ofInputParser
type class). -
SchemaRenderer.renderSchema
is now able to renderSchema
objects and only introspection results (#114). This can be useful if you already
have schema in memory and don't want to execute an introspection query against the schema in order to render it. -
Query validation rule: Unique variable names (#112)
-
Add suggested types to incorrect field message (#111)
-
Introspection result now has a parser which deserializes a JSON (or any other format) to a set of case classes. This may simplify client-side tools that work with introspection queries.
Please usesangria.introspection.IntrospectionParser.parse
to parse an introspection query results. -
Introduced
InputParser
type class in order provide optional support for default value parsing in schema materialization. -
Updated descriptions of a scalar values
-
Updated dependencies
-
Minor improvements
v0.5.1
-
JSON library integration is extracted to separate libraries (#38). Evey integration library will have a separate and independent versioning and release cycle. Following new libraries were introduced:
-
sangria-marshalling-api now includes all of the interfaces that marshalling
library needs to implement. -
sangria-marshalling-testkit contains a set of generic test cases that can be used
to test a concrete marshalling library integration. -
sangria-spray-json contains an integration with spray-json library.
From now on, please use following dependency if you would like to use spray-json support:libraryDependencies += "org.sangria-graphql" %% "sangria-spray-json" % "0.1.0"
The package is changed for the sake of consistency. From now on please use following import:
import sangria.marshalling.sprayJson._
-
sangria-play-json contains an integration with play-json library.
From now on, please use following dependency if you would like to use play-json support:libraryDependencies += "org.sangria-graphql" %% "sangria-play-json" % "0.1.0"
The package is changed for the sake of consistency. From now on please use following import:
import sangria.marshalling.playJson._
-
sangria-json4s-native contains an integration with json4s-native library.
From now on, please use following dependency if you would like to use json4s-native support:libraryDependencies += "org.sangria-graphql" %% "sangria-json4s-native" % "0.1.0"
The package is changed for the sake of consistency. From now on please use following import:
import sangria.marshalling.json4s.native._
-
sangria-json4s-jackson contains an integration with json4s-jackson library.
From now on, please use following dependency if you would like to use json4s-jackson support:libraryDependencies += "org.sangria-graphql" %% "sangria-json4s-jackson" % "0.1.0"
The package is changed for the sake of consistency. From now on please use following import:
import sangria.marshalling.json4s.jackson._
-
sangria-circe contains an integration with circe library.
From now on, please use following dependency if you would like to use circe support:libraryDependencies += "org.sangria-graphql" %% "sangria-circe" % "0.1.0"
The package is changed for the sake of consistency. From now on please use following import:
import sangria.marshalling.circe._
-
-
Argonaut scala JSON library is now supported via sangria-argonaut (#59).
Please use following dependency if you would like to use argonaut support:libraryDependencies += "org.sangria-graphql" %% "sangria-argonaut" % "0.1.0"
And here is an import statement:
import sangria.marshalling.argonaut._
-
Added
operationType
andoperation
onast.Document
to easily identify the operation type (#110) -
Added a utility function to convert between different input representations (#108).
This functionality is available thoughsangria.marshalling.MarshallingUtil
.
v0.5.0
-
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 likeToInput
.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
forArticle
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 ofFromInput
are already provided out-of-the-box. For instanceFromInput[Map[String, Any]]
was added to support existing map-like
data-structure format. All supported Json libraries also provideFromInput[JsValue]
so that you can use Json AST instead of working withMap[String, Any]
.Moreover, play-json and spray-json integration provide support for
Reads
andJsonFormat
. This means that your domain objects are automatically
supported as long as you haveReads
orJsonFormat
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 newgraphqlInput
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
andResultMarshaller
are also now available for it, so you can useast.Value
as a variables or it can be a result
of GraphQL query execution (instead of more traditional JSON). -
ToInput
,InputUnmarshaller
andResultMarshaller
are moved tosangria.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
v0.4.3
-
QueryReducer
is introduced. It allows you to analyze a query and take an action before it's executed. It provides very similar functionality to
complexity analysis (introduced in previous release), but in much more generic form. That's because complexity analysis is now rewritten as
aQueryReducer
. In order to migrate, you need to replacemeasureComplexity
function withQueryReducer.measureComplexity
. Here is an example:val complReducer = QueryReducer.measureComplexity[MyCtx] { (c, ctx) ⇒ complexity = c ctx } Executor.execute(schema, query, userContext = new MyCtx, queryReducers = complReducer :: Nil)
Since rejection of complex queries is such a common use-case, there is now a helper function to create a reducer for it:
val rejectComplexQuery = QueryReducer.rejectComplexQueries[MyCtx](14, (c, ctx) ⇒ new IllegalArgumentException(s"Too complex query: max allowed complexity is 14.0, but got $c"))
-
Middleware
got a type parameter forCtx
. This is a minor breaking change. If you don't use theuserContext
inside of theMiddleware
,
then you can just parametrize it withAny
. -
Complexity function on field should also be given the
Ctx
(#87)