Changelog History
Page 5
-
v0.5.0 Changes
December 03, 2015- ๐ A lot of performance improvements across the whole library
- โ Added basic subscription support as defined in the spec (https://github.com/facebook/graphql/pull/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 explicitFromInput
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: https://github.com/facebook/graphql/pull/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: ```scala 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: ```js { 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 Changes
October 16, 2015QueryReducer
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: ```scala 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: ```scala 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)
-
v0.4.2 Changes
October 12, 2015- Query complexity calculation mechanism is implemented (#85). This mechanism makes a rough estimation of the query complexity before it is executed.
Every field in the query gets a default score
1.0
. The "complexity" of the query is the sum of all field scores. You can customize the field score withcomplexity
argument:scala Field("pets", OptionType(ListType(PetType)), arguments = Argument("limit", IntType) :: Nil, complexity = Some((args, childrenScore) => 25.0D + args.arg[Int]("limit") * childrenScore), resolve = ctx => ...)
If you would like to use this feature, you need to providemeasureComplexity
argument to theExecutor
. For example: ```scala val rejectComplexQueries = (c: Double) => if (c > 1000) throw new IllegalArgumentException(s"Too complex query: max allowed complexity is 1000.0, but got $c") else ()
val exceptionHandler: Executor.ExceptionHandler = { case (m, e: IllegalArgumentException) => HandledException(e.getMessage) }
Executor.execute(schema, query, exceptionHandler = exceptionHandler, measureComplexity = Some(rejectComplexQueries))
The complexity of full introspection query (used by tools like GraphiQL) is `102.0`. * ๐ json4s-jackson is now supported in addition to native (#84). This results in minor import change: ```scala // before sangria.integration.json4s._ // after // either (same behaviour as before) sangria.integration.json4s.native._ //or sangria.integration.json4s.jackson._
- โก๏ธ json4s is updated to version 3.3.0 (#84)
- Provide a helpful error messages if schema has a broken circular references (which cause
fields
to be null) (#83)
- Query complexity calculation mechanism is implemented (#85). This mechanism makes a rough estimation of the query complexity before it is executed.
Every field in the query gets a default score
-
v0.4.1 Changes
October 03, 2015For the most part implemented spec changes. Now compatible with "October 2015" version of the GraphQL spec.
- Type condition optional on inline fragments. (#82) (spec change)
- ๐ Make operation name optional (#81) (spec change)
- Introspection descriptions for scalars and introspection (#80)
beforeField
now able to replace value and preventresolve
call (#79). This can be useful for things like caching. It contains minor breaking change - return type ofbeforeField
has changed. If you are implementing it, just returncontinue
if yourFieldVal
wasUnit
orcontinue(someFieldVal)
.Projection
andNoProjection
should be tags instead of resolve function wrappers (#78). Backwards-incompatible change: you need to replaceProjection
withProjectionName
tag andNoProjection
withProjectionExclude
tag. here is an example: ```scala // before
Field("id", StringType, Some("The id of the droid."), resolve = Projection("_id", _.value.id)),
// after
Field("id", StringType, Some("The id of the droid."), tags = ProjectionName("_id") :: Nil, resolve = _.value.id)
-
v0.4.0 Changes
September 27, 2015๐ This release contains quite a few backwards-incompatible changes, but fear not - all of them are renames and similar minor changes which should be easy to migrate. I collected all of them in the change list below. They were necessary in order to ensure consistent naming and improve the structure and flexibility of the library.
- 0๏ธโฃ #68 - Better handling of default input values. It's a part of ongoing effort to improve handling of input objects (#37). Default values should now have an instance
of
ToInput
type-class which is defined for all supported input types like scala map-like data structures, different json ASTs, etc. It even supports things likeWrites
from play-json orJsonFormat
from spray-json by default. This means that you can use your domain objects (likeUser
orApple
) as a default value for input fields or arguments as long as you haveWrites
orJsonFormat
defined for them. The mechanism is very extensible, of course: you just need to define implicitToInput[T]
for a class you want to use as a default value. This change makes it impossible to verify the default value type at compile time, since it can have any shape, like Json AST or maybe even some binary format. Don't worry though, at a schema creation time all default values would be validated according to the input type. - ๐ #77 - Middleware support. This addition has a huge potential: you can measure performance, collect metrics, enforce security, etc. on a field and query level. Moreover
it makes it much easier for people to share standard middleware in a libraries (e.g. sangria-security, sangria-graphite, sangria-influxdb, etc.). In order to ensure generic classification of
fields, every field now got a generic list or
FieldTag
s which allow to provide user-defined meta information about this field (just to highlight a few examples:Permission("ViewOrders")
,Authorized
,Measured
, etc.). You can find more info in docs and auth example - ๐ #76 - You can now provide
maxQueryDepth
toExecutor
. It will then enforce this constraint for all queries (very useful if query has recursive types) Docs - #69 -
DeferredResolver
now gotuserContext
as an argument. (breaking change: you need to provide a type parameter and one extra argument inresolve
for yourDeferredResolver
s. you you are not interested inuserContext
, you can just useAny
type) - ๐ Renamed Json support objects in order to make more concise import syntax (breaking change: you need to rename imports as well):
sangria.integration.CirceSupport
->sangria.integration.circe
sangria.integration.Json4sSupport
->sangria.integration.json4s
sangria.integration.PlayJsonSupport
->sangria.integration.playJson
sangria.integration.SprayJsonSupport
->sangria.integration.sprayJson
- ๐ฆ
ResultMarshaller
andInputUnmarshaller
are moved in theintegration
package - ๐ Renamed execution
arguments
tovariables
in order to be consistent with the spec (breaking change: you need to rename this argument as well, if you are using named arguments) - ๐จ Refactored variables and
InputUnmarshaller
. In order to avoid extra complexity it now does not have a dependent type. Instead it uses "type tagging" for scala map variables. It's a minor breaking change. If you are providing execution variables as a scala map, then you need to usemapVars
oremptyMapVars
which are defined inInputUnmarshaller
companion object (these functions do not wrapMap
- they only needed to ensure type constraints): ```scala Executor.execute(mySchema, query, variables = mapVars(Map("someId" -> "1000")))
// or
Executor.execute(mySchema, query, variables = mapVars("someId" -> "1000"))
* #72 - `scala.util.Try` now can be returned from `resolve` in order to indicate a successful or failed result * ๐ #65 - `DeprecationTracker` should be called even if deprecation is in the interface type * โก๏ธ #66 - `DeprecationTracker` should provide more contextual information (breaking change: the signature of `deprecatedFieldUsed` is changed. It now provides much more contextual information, but you need to update the code that implements it) * #74 - Improved unicode handling (spec change) * #67 - circe integration throws NoSuchElementException during execution * #75 - Identical documents should be equal * #73 - Verify input field uniqueness (spec change - new validation rule) * ๐ Minor bugfixes
- 0๏ธโฃ #68 - Better handling of default input values. It's a part of ongoing effort to improve handling of input objects (#37). Default values should now have an instance
of
-
v0.3.1 Changes
August 27, 2015- ๐ #58 - Implement CirceJsonSupport in order to be able to integrate with Circe
- #53 - Add
map
inAction
- #53 - Ensure Ctx proper inheritance behavior
- ๐ #33 -
graphql
string context macro to create parsed document and verify query at compile time (big thanks to @dlreeves for implementing this feature). Here is an example how you can use it: ```scala import sangria.macros._
val queryAst = graphql""" query FetchSomeIDQuery { human(id: "1000") { name } } """
If there is a syntax error in the query, you will see it at the compile time.
-
v0.3.0 Changes
August 16, 2015- #45 - Added
Long
scalar type - โก๏ธ #49 -
UpdateCtxAction
should also work for query types - #50 - Sanity check - fields should have unique name within the same type definition
- โ #31, #32 - More test coverage for "projections" and "deferred" features
- ๐ป #51 - Custom exception handler now should return
message
and list of additional filed - The
interfaces
property syntax changed. In order to ensure type safety, improve type inference and allow type-class based relations betweenInterfaceType
andObjectType
you now need to use following syntax:scala val PersonType = ObjectType("Person", interfaces = interfaces[Unit, Person](NamedType, BeingType), fields = ...)
instead of old syntax ```scala val PersonType = ObjectType[Unit, Person]("Person", interfaces = NamedType :: BeingType :: Nil, fields = ...)
// or
val PersonType = ObjectType[Unit, Person]("Person", interfaces = List(NamedType, BeingType), fields = ...)
* Fields in `ObjectType` and `InterfaceType` got small convenience method fields. You now can use it like this: ```scala val DogType = ObjectType("Dog", fields[Unit, Dog]( Field("name", OptionType(StringType), resolve = _.value.name), Field("barks", OptionType(BooleanType), resolve = _.value.barks)))
withPossibleTypes
was introduced onInterfaceType
andField
in order to provide a convenient way to the list of possible implementation types of interface- โ Added convenience method
Executor.execute
- Other minor improvements to make sangria-relay possible
- #45 - Added
-
v0.2.2 Changes
August 09, 2015- #44 - Add ability to add types explicitly in schema, for cases when they are not referenced anywhere else
Schema
now has additional argumentadditionalTypes
which can be used like this:Schema(HeroOnlyQuery, additionalTypes = Human :: Droid :: Nil)
- #44 - Add ability to add types explicitly in schema, for cases when they are not referenced anywhere else
-
v0.2.1 Changes
August 07, 2015- Spec changes - sew validation rules:
LoneAnonymousOperation
UniqueArgumentNames
UniqueFragmentNames
UniqueOperationNames
- Spec changes - sew validation rules:
-
v0.2.0 Changes
August 02, 2015- groupId is changed to org.sangria-graphql
- โ Added missing query validation rules (#30 #29 #28 #27 #26 #25 #24 #23)
- #36 - Change semantics of
Projector
featureProjector
now gets all of the fields, not only fields marked withProjection
Projection
now allows to customize the nameNoProjection
allows to remove field from projections listProjectior
allows to specify how deep it should look (the level arg)