diff --git a/README.md b/README.md index 9463f26..b71af40 100644 --- a/README.md +++ b/README.md @@ -5,28 +5,36 @@ [![Latest Snapshot](https://img.shields.io/maven-central/v/com.graphql-java/graphql-java-extended-scalars?label=maven-central%20snapshot)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-scalars/) [![MIT licensed](https://img.shields.io/badge/license-MIT-green)](https://github.com/graphql-java/graphql-java-extended-scalars/blob/master/LICENSE.md) +## Overview + This library provides extended scalars for [graphql-java](https://github.com/graphql-java/graphql-java) -Scalars in graphql are the leaf nodes of a query, the non-compound values that can't be queried further via sub-field selections. +[GraphQL Scalars](https://spec.graphql.org/draft/#sec-Scalars) are the primitive leaf values in the GraphQL type system which cannot be queried further via sub-field selections. -The graphql standard specifies that the `String`, `Int`, `Float`, `Boolean` and `ID` scalars must be present in a graphql type -system but after that it is up to an implementation about what custom scalars are present. +The GraphQL Specification defines `String`, `Int`, `Float`, `Boolean` and `ID` as well-defined [built-in scalars](https://spec.graphql.org/draft/#sec-Scalars.Built-in-Scalars) that must be present in a graphql type +system. Beyond these, it is up to an implementation to decide what [custom scalars](https://spec.graphql.org/draft/#sec-Scalars.Custom-Scalars) are present. You would use custom scalars when you want to describe more meaningful behavior or ranges of values. +# Getting started + ## How to install To use this library put the following into your gradle config - implementation 'com.graphql-java:graphql-java-extended-scalars:20.0' +```java +implementation 'com.graphql-java:graphql-java-extended-scalars:20.0' +``` or the following into your Maven config - - com.graphql-java - graphql-java-extended-scalars - 20.0 - +```xml + + com.graphql-java + graphql-java-extended-scalars + 20.2 + +``` > Note: > @@ -34,106 +42,142 @@ or the following into your Maven config > > use 20.0 or above for graphql-java 20.x and above -It's currently available from Maven Central. - ## How to use extended scalars -Register the scalar with graphql-java +### Direct use - RuntimeWiring.newRuntimeWiring().scalar(ExtendedScalars.DateTime) +Register the scalar with `graphql-java` -Or if using [Spring for GraphQL](https://docs.spring.io/spring-graphql/docs/current/reference/html/), register the scalar with `RuntimeWiringConfigurer` +```java +RuntimeWiring.newRuntimeWiring().scalar(ExtendedScalars.DateTime) +``` - @Configuration - public class GraphQlConfig { - @Bean - public RuntimeWiringConfigurer runtimeWiringConfigurer() { - return wiringBuilder -> wiringBuilder.scalar(ExtendedScalars.DateTime); - } - } +### Spring for GraphQL -And use the scalar in your schema +If you are using [Spring for GraphQL](https://docs.spring.io/spring-graphql/docs/current/reference/html/), register the scalar with `RuntimeWiringConfigurer` - scalar DateTime - type Something { - someDateTime: DateTime +```java +@Configuration +public class GraphQlConfig { + @Bean + public RuntimeWiringConfigurer runtimeWiringConfigurer() { + return wiringBuilder -> wiringBuilder.scalar(ExtendedScalars.DateTime); } +} +``` -## DateTime Scalars - -- `DateTime` - [specification](https://scalars.graphql.org/andimarek/date-time.html) - - See specification - - An RFC-3339 compliant date time scalar that accepts string values like `1996-12-19T16:39:57-08:00` and produces - `java.time.OffsetDateTime` objects at runtime. -- `Time` - - An RFC-3339 compliant time scalar that accepts string values like `16:39:57-08:00` and produces - `java.time.OffsetTime` objects at runtime -- `LocalTime` - - 24-hour clock time string in the format `hh:mm:ss.sss` or `hh:mm:ss` if partial seconds is zero and - produces `java.time.LocalTime` objects at runtime. -- `Date` - - An RFC-3339 compliant date scalar that accepts string values like `1996-12-19` and produces - `java.time.LocalDate` objects at runtime +### Netflix DGS -An example declaration in SDL might be: +If you are using [Netflix DGS](https://netflix.github.io/dgs), please see their [configuration documentation](https://netflix.github.io/dgs/configuration/#dgs-extended-scalars-graphql-dgs-extended-scalars). -```graphql -type Customer { - birthDay: Date - workStartTime: Time - bornAt: DateTime -} +## How to add extended scalars to your schema -type Query { - customers(bornAfter: DateTime): [Customers] -} +The GraphQL Specification recommends the use of the [@specifiedBy](https://spec.graphql.org/October2021/#sec--specifiedBy) built-in schema directive to provide a scalar specification URL for specifying the behavior of custom scalar types. + +```graphql +directive @specifiedBy(url: String!) on SCALAR ``` -And example query might look like: +To use a extended scalar in your schema, define the scalar like shown below for `DateTime` ```graphql -query { - customers(bornAfter: "1996-12-19T16:39:57-08:00") { - birthDay - bornAt - } +scalar DateTime + @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time.html") + +type Something { + someDateTime: DateTime } ``` -## ID Scalars +# Custom Scalars -- `UUID` - - A universally unique identifier scalar that accepts uuid values like `2423f0a0-3b81-4115-a189-18df8b35e8fc` and produces - `java.util.UUID` instances at runtime. +## Alias Scalars -## Object / JSON Scalars + + + + + + + + + +
Scalar DefinitionDescription
+scalar AliasedScalar
+
You can create aliases for existing scalars to add more semantic meaning to them.
-- `Object` +For example a link to a social media post could be representing by a `String` but the name `SocialMediaLink` is a +more semantically meaningful name for that scalar type. - - An object scalar that accepts any object as a scalar value +For example, you would build it like this: -- `JSON` - - A synonym for the `Object` scalar, it will accept any object as a scalar value +```java +AliasedScalar socialMediaLink = ExtendedScalars.newAliasedScalar("SocialMediaLink") + .aliasedScalar(Scalars.GraphQLString) + .build() +``` -One of the design goals of graphql, is that the type system describes the shape of the data returned. +And use it in a SDL schema like this : -The `Object` / `JSON` scalars work against this some what because they can return compound values outside the type system. As such -they should be used sparingly. In general your should aim to describe the data via the graphql type system where you can and only -resort to the `Object` / `JSON` scalars in very rare circumstances. +```graphql +type Customer { + name: String + socialMediaLink: SocialMediaLink +} +``` -An example might be an extensible graphql system where systems can input custom metadata objects that cant be known at -schema type design time. +## Date & Time Scalars + + + + + + + + + + + + + + + + + + + + + + +
Scalar DefinitionDescription
+scalar DateTime
+  @specifiedBy(url: 
+    "https://scalars.graphql.org/andimarek/date-time.html"
+  )
+
A RFC-3339 compliant date time scalar that accepts string values like 1996-12-19T16:39:57-08:00 and produces java.time.OffsetDateTime objects at runtime.
+scalar Date
+  @specifiedBy(url: 
+    "https://tools.ietf.org/html/rfc3339"
+  )
A RFC-3339 compliant date scalar that accepts string values like 1996-12-19 and produces java.time.LocalDate objects at runtime.
+scalar Time
+  @specifiedBy(url: 
+    "https://tools.ietf.org/html/rfc3339"
+  )
+
A RFC-3339 compliant time scalar that accepts string values like 16:39:57-08:00 and produces java.time.OffsetTime objects at runtime.
+scalar LocalTime
+
24-hour clock time string in the format hh:mm:ss.sss or hh:mm:ss if partial seconds is zero and produces java.time.LocalTime objects at runtime.
An example declaration in SDL might be: ```graphql type Customer { - name: String - associatedMetaData: JSON + birthDay: Date + workStartTime: Time + bornAt: DateTime } type Query { - customers(filterSyntax: JSON): [Customers] + customers(bornAfter: DateTime): [Customers] } ``` @@ -141,42 +185,73 @@ And example query might look like: ```graphql query { - customers( - filterSyntax: { - startSpan: "First" - matchCriteria: { countryCode: "AU", isoCodes: ["27B-34R", "95A-E23"] } - } - ) { - name - associatedMetaData + customers(bornAfter: "1996-12-19T16:39:57-08:00") { + birthDay + bornAt } } ``` -Note : The `JSON` scalar is a simple alias type to the `Object` scalar because often the returned data is a blob of JSON. They are -all just objects at runtime in graphql-java terms and what network serialisation protocol is up to you. Choose whichever name you think -adds more semantic readers to your schema consumers. +## ID Scalars + + + + + + + + + + +
Scalar DefinitionDescription
+scalar UUID
+  @specifiedBy(url: 
+    "https://tools.ietf.org/html/rfc4122"
+  )
+
A universally unique identifier scalar that accepts uuid values like 2423f0a0-3b81-4115-a189-18df8b35e8fc and produces java.util.UUID instances at runtime.
## Numeric Scalars -- `PositiveInt` - - An `Int` scalar that MUST be greater than zero -- `NegativeInt` - - An `Int` scalar that MUST be less than zero -- `NonPositiveInt` - - An `Int` scalar that MUST be less than or equal to zero -- `NonNegativeInt` - - An `Int` scalar that MUST be greater than or equal to zero -- `PositiveFloat` - - An `Float` scalar that MUST be greater than zero -- `NegativeFloat` - - An `Float` scalar that MUST be less than zero -- `NonPositiveFloat` - - An `Float` scalar that MUST be less than or equal to zero -- `NonNegativeFloat` - - An `Float` scalar that MUST be greater than or equal to zero - -The numeric scalars are derivations of the standard graphql `Int` and `Float` scalars that enforce range limits. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scalar DefinitionDescription
scalar PositiveInt
An Int scalar that MUST be greater than zero.
scalar NegativeInt
An Int scalar that MUST be less than zero.
scalar NonPositiveInt
An Int scalar that MUST be less than or equal to zero.
scalar NonNegativeInt
An Int scalar that MUST be greater than or equal to zero.
scalar PositiveFloat
An Float scalar that MUST be greater than zero.
scalar NegativeFloat
An Float scalar that MUST be less than zero.
scalar NonPositiveFloat
An Float scalar that MUST be less than or equal to zero.
scalar NonNegativeFloat
An Float scalar that MUST be greater than or equal to zero.
+ +The numeric scalars are derivations of the standard GraphQL `Int` and `Float` scalars that enforce range limits. An example declaration in SDL might be: @@ -205,27 +280,56 @@ query { } ``` -## Regex Scalars - -The RegexScalar has a builder where you provide one or more regex patterns that control the acceptable values -for a new scalar. - -You name the scalar and it provides an implementation. - -For example, imagine a `phoneNumber` scalar like this : - -```java - - RegexScalar phoneNumberScalar = ExtendedScalars.newRegexScalar("phoneNumber") - .addPattern(Pattern.compile("\\([0-9]*\\)[0-9]*")) - .build() +## Java Primitives -``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scalar DefinitionDescription
scalar GraphQLLong
A scalar which represents java.lang.Long
scalar GraphQLShort
A scalar which represents java.lang.Short
scalar GraphQLByte
A scalar which represents java.lang.Byte
scalar GraphQLBigDecimal
A scalar which represents java.lang.BigDecimal
scalar GraphQLBigInteger
A scalar which represents java.lang.BigInteger
scalar GraphQLChar
A scalar which represents java.lang.Character
## Locale Scalar -The Locale scalar handles [IETF BCP 47](https://tools.ietf.org/html/bcp47) language tags via the -JDK method [Locale.forLanguageTag]() + + + + + + + + + +
Scalar DefinitionDescription
+scalar Locale
+  @specifiedBy(url: 
+    "https://tools.ietf.org/html/bcp47"
+  )
+
The Locale scalar handles IETF BCP 47 language tags via the JDK method Locale.forLanguageTag.
```graphql type Customer { @@ -251,7 +355,21 @@ query { ## Country Code Scalar -The CountryCode scalar type as defined by [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + + + + + + + + + +
Scalar DefinitionDescription
+scalar CountryCode 
+  @specifiedBy(url: 
+    "https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"
+  )
+
The CountryCode scalar type as defined by ISO 3166-1 alpha-2.
An example declaration in SDL might be: @@ -277,7 +395,21 @@ query { ## Currency Scalar -A field whose value is an [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217) currency. + + + + + + + + + +
Scalar DefinitionDescription
+scalar Currency
+  @specifiedBy(url: 
+    "https://en.wikipedia.org/wiki/ISO_4217"
+  )
+
A field whose value is an ISO-4217 currency.
An example declaration in SDL might be: @@ -303,51 +435,107 @@ query { } ``` -## Alias Scalars +## URL Scalars + + + + + + + + + + +
Scalar DefinitionDescription
+scalar URL
+  @specifiedBy(url: 
+    "https://www.w3.org/Addressing/URL/url-spec.txt"
+  )
+
An url scalar that accepts string values like https://www.w3.org/Addressing/URL/url-spec.txt and produces java.net.URL objects at runtime.
-You can create aliases for existing scalars to add more semantic meaning to them. +## Object / JSON Scalars -For example a link to a social media post could be representing by a `String` but the name `SocialMediaLink` is a -more semantically meaningful name for that scalar type. + + + + + + + + + + + + + +
Scalar DefinitionDescription
scalar Object
An object scalar that accepts any object as a scalar value.
scalar JSON
A synonym for the Object scalar, it will accept any object as a scalar value.
+ +One of the design goals of GraphQL, is that the type system describes the shape of the data returned. -For example, you would build it like this: +The `Object` / `JSON` scalars work against this some what because they can return compound values outside the type system. As such +they should be used sparingly. In general your should aim to describe the data via the GraphQL type system where you can and only +resort to the `Object` / `JSON` scalars in very rare circumstances. -```java +An example might be an extensible GraphQL system where systems can input custom metadata objects that cant be known at +schema type design time. - AliasedScalar socialMediaLink = ExtendedScalars.newAliasedScalar("SocialMediaLink") - .aliasedScalar(Scalars.GraphQLString) - .build() +An example declaration in SDL might be: +```graphql +type Customer { + name: String + associatedMetaData: JSON +} + +type Query { + customers(filterSyntax: JSON): [Customers] +} ``` -And use it in a SDL schema like this : +And example query might look like: ```graphql -type Customer { - name: String - socialMediaLink: SocialMediaLink +query { + customers( + filterSyntax: { + startSpan: "First" + matchCriteria: { countryCode: "AU", isoCodes: ["27B-34R", "95A-E23"] } + } + ) { + name + associatedMetaData + } } ``` -Note: A future version of the graphql specification may add this capability but in the meantime you can use this facility. +Note : The `JSON` scalar is a simple alias type to the `Object` scalar because often the returned data is a blob of JSON. They are +all just objects at runtime in `graphql-java` terms and what network serialization protocol is up to you. Choose whichever name you think +adds more semantic readers to your schema consumers. -## Java Primitives +## Regex Scalars + + + + + + + + + + +
Scalar NameDescription
RegexScalarAllows you to build a new scalar via a builder pattern using regular expressions.
-- `GraphQLLong` - - A scalar which represents `java.lang.Long` -- `GraphQLShort` - - A scalar which represents `java.lang.Short` -- `GraphQLByte` - - A scalar which represents `java.lang.Byte` -- `GraphQLBigDecimal` - - A scalar which represents `java.math.BigDecimal` -- `GraphQLBigInteger` - - A scalar which represents `java.math.BigInteger` -- `GraphQLChar` - - A scalar which represents `java.lang.Character` - -## Other Scalars - -- `Url` - - An url scalar that accepts string values like `https://www.w3.org/Addressing/URL/url-spec.txt` and produces - `java.net.URL` objects at runtime +The RegexScalar has a builder where you provide one or more regex patterns that control the acceptable values +for a new scalar. + +You name the scalar and it provides an implementation. + +For example, imagine a `phoneNumber` scalar like this : + +```java + + RegexScalar phoneNumberScalar = ExtendedScalars.newRegexScalar("phoneNumber") + .addPattern(Pattern.compile("\\([0-9]*\\)[0-9]*")) + .build() + +```