Skip to content

Commit 5b448ee

Browse files
committed
Implemented TimeOnly GraphQL type (#546)
1 parent 689908d commit 5b448ee

File tree

5 files changed

+65
-2
lines changed

5 files changed

+65
-2
lines changed

RELEASE_NOTES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,8 @@
230230
* Fixed ability to override `GraphQLRequestHandler<'Root>`
231231
* Fixed input object CLR property type validation against GraphQL scalar definitions
232232
* Migrated all solutions to SLNX format
233-
* Various performance optimizations and bug fixes
233+
* Various performance optimizations and bug fixes
234+
235+
### Unreleased
236+
237+
* Added `TimeOnly` GraphQL type

src/FSharp.Data.GraphQL.Server/Schema.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ type Schema<'Root> (query: ObjectDef<'Root>, ?mutation: ObjectDef<'Root>, ?subsc
186186
IDType
187187
DateTimeOffsetType
188188
DateOnlyType
189+
TimeOnlyType
189190
UriType
190191
__Schema
191192
query ]

src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ module SchemaDefinitions =
159159
| false, _ -> None
160160
| other -> None
161161

162+
/// Tries to convert any value to TimeOnly.
163+
let coerceTimeOnlyValue (x : obj) : TimeOnly option =
164+
match x with
165+
| null -> None
166+
| :? TimeOnly as d -> Some d
167+
| :? DateTime as d -> Some (TimeOnly.FromDateTime d)
168+
| :? string as s ->
169+
match TimeOnly.TryParse(s) with
170+
| true, time -> Some time
171+
| false, _ -> None
172+
| other -> None
173+
162174
/// Tries to convert any value to Guid.
163175
let coerceGuidValue (x : obj) : Guid option =
164176
match x with
@@ -348,6 +360,22 @@ module SchemaDefinitions =
348360
| false, _ -> getParseRangeError(destinationType, DateOnly.MinValue, DateOnly.MaxValue) s
349361
| InlineConstant value -> value.GetCoerceRangeError(destinationType, DateOnly.MinValue, DateOnly.MaxValue)
350362

363+
/// Tries to resolve AST query input to TimeOnly.
364+
let coerceTimeOnlyInput =
365+
let destinationType = "time"
366+
function
367+
| Variable e when e.ValueKind = JsonValueKind.String ->
368+
let s = e.GetString()
369+
match TimeOnly.TryParse(s) with
370+
| true, time -> Ok time
371+
| false, _ -> e.GetDeserializeError destinationType
372+
| Variable e -> e.GetDeserializeError destinationType
373+
| InlineConstant (StringValue s) ->
374+
match TimeOnly.TryParse(s) with
375+
| true, time -> Ok time
376+
| false, _ -> getParseRangeError(destinationType, TimeOnly.MinValue, TimeOnly.MaxValue) s
377+
| InlineConstant value -> value.GetCoerceRangeError(destinationType, TimeOnly.MinValue, TimeOnly.MaxValue)
378+
351379
/// Tries to resolve AST query input to Guid.
352380
let coerceGuidInput =
353381
let destinationType = "GUID"
@@ -473,6 +501,15 @@ module SchemaDefinitions =
473501
CoerceInput = coerceDateOnlyInput
474502
CoerceOutput = coerceDateOnlyValue }
475503

504+
/// GraphQL type for System.TimeOnly
505+
let TimeOnlyType : ScalarDefinition<TimeOnly> =
506+
{ Name = "TimeOnly"
507+
Description =
508+
Some
509+
"The `TimeOnly` scalar type represents a Time value without Date component. The `TimeOnly` type appears in a JSON response as a `String` representation of full-time value as specified by [IETF 3339](https://www.ietf.org/rfc/rfc3339.txt)."
510+
CoerceInput = coerceTimeOnlyInput
511+
CoerceOutput = coerceTimeOnlyValue }
512+
476513
/// GraphQL type for System.Guid
477514
let GuidType : ScalarDefinition<Guid> =
478515
{ Name = "Guid"
@@ -759,6 +796,7 @@ module SchemaDefinitions =
759796
/// </summary>
760797
/// <param name="name">Type name. Must be unique in scope of the current schema.</param>
761798
/// <param name="fields">List of input fields defined by the current input object. </param>
799+
/// <param name="validator">Object validator.</param>
762800
/// <param name="description">Optional input object description. Useful for generating documentation.</param>
763801
static member InputObject(name : string, fields : InputFieldDef list, validator: GQLValidator<'Out>, ?description : string) : InputObjectDefinition<'Out> =
764802
{ Name = name

tests/FSharp.Data.GraphQL.IntegrationTests/introspection.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"documentId": 1481480240,
2+
"documentId": -128167532,
33
"data": {
44
"__schema": {
55
"queryType": {
@@ -82,6 +82,16 @@
8282
"enumValues": null,
8383
"possibleTypes": null
8484
},
85+
{
86+
"kind": "SCALAR",
87+
"name": "TimeOnly",
88+
"description": "The `TimeOnly` scalar type represents a Time value without Date component. The `TimeOnly` type appears in a JSON response as a `String` representation of full-time value as specified by [IETF 3339](https://www.ietf.org/rfc/rfc3339.txt).",
89+
"fields": null,
90+
"inputFields": null,
91+
"interfaces": null,
92+
"enumValues": null,
93+
"possibleTypes": null
94+
},
8595
{
8696
"kind": "SCALAR",
8797
"name": "URI",

tests/FSharp.Data.GraphQL.Tests/IntrospectionTests.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,16 @@ let ``Introspection executes an introspection query`` () =
691691
"enumValues", null
692692
"possibleTypes", null
693693
]
694+
box <| NameValueLookup.ofList [
695+
"kind", upcast "SCALAR"
696+
"name", upcast "TimeOnly"
697+
"description", upcast "The `TimeOnly` scalar type represents a Time value without Date component. The `TimeOnly` type appears in a JSON response as a `String` representation of full-time value as specified by [IETF 3339](https://www.ietf.org/rfc/rfc3339.txt)."
698+
"fields", null
699+
"inputFields", null
700+
"interfaces", null
701+
"enumValues", null
702+
"possibleTypes", null
703+
]
694704
box <| NameValueLookup.ofList [
695705
"kind", upcast "SCALAR"
696706
"name", upcast "URI"

0 commit comments

Comments
 (0)