Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>

<PropertyGroup Label="NuGet">
<Version>3.1.0</Version>
<Version>3.1.1</Version>
</PropertyGroup>

</Project>
7 changes: 5 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@
* Changed `GraphQLOptionsDefaults.WebSocketConnectionInitTimeoutInMs` const type to `double`
* Improved Relay XML documentation comments

### 3.1.1 - Unreleased
### 3.1.1 - 2026-04-03

* Fixed planning phase crash when inline fragments reference types not included in union or interface definitions
* Fixed GraphQL client provider handling for schema types that reuse reserved scalar names such as `Date`, so introspection kind now takes precedence over built-in scalar mappings.

### 3.1.2 - Unreleased
* Fixed planning phase crash when inline fragments reference types not included in union or interface definitions
Comment on lines +245 to +248
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Fixed GraphQL client provider handling for schema types that reuse reserved scalar names such as `Date`, so introspection kind now takes precedence over built-in scalar mappings.
### 3.1.2 - Unreleased
* Fixed planning phase crash when inline fragments reference types not included in union or interface definitions
* Fixed planning phase crash when inline fragments reference types not included in union or interface definitions
* Fixed GraphQL client provider handling for schema types that reuse reserved scalar names such as `Date`, so introspection kind now takes precedence over built-in scalar mappings.

12 changes: 4 additions & 8 deletions src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,6 @@ module internal ProvidedOperation =
tdef.AddXmlDoc("Represents a GraphQL operation on the server.")
tdef.AddMembersDelayed(fun _ ->
let operationResultDef = ProvidedOperationResult.makeProvidedType(operationType)
let isScalar (typeName: string) =
match schemaTypes.TryFind typeName with
| Some introspectionType -> introspectionType.Kind = TypeKind.SCALAR
| None -> false
let variables =
let rec mapVariable (variableName : string) (variableType : InputType) =
match variableType with
Expand All @@ -309,11 +305,11 @@ module internal ProvidedOperation =
| Some uploadInputTypeName when typeName = uploadInputTypeName ->
struct (variableName, typeName, TypeMapping.makeOption typeof<Upload>)
| _ ->
match TypeMapping.scalar.TryFind(typeName) with
match TypeMapping.tryFindScalarType schemaTypes typeName with
| Some t -> struct (variableName,typeName, TypeMapping.makeOption t)
| None when isScalar typeName -> struct (variableName, typeName, typeof<string option>)
| None when TypeMapping.isScalarTypeName schemaTypes typeName -> struct (variableName, typeName, typeof<string option>)
| None ->
match schemaProvidedTypes.TryFind(typeName) with
match schemaProvidedTypes.TryFind typeName with
| Some t -> struct (variableName, typeName, TypeMapping.makeOption t)
| None -> failwith $"""Unable to find variable type "%s{typeName}" in the schema definition."""
| ListType itype ->
Expand Down Expand Up @@ -381,7 +377,7 @@ module internal ProvidedOperation =
tdef.DeclaredProperties |> Seq.exists ((fun p -> p.PropertyType) >> existsUploadType)
variables |> Seq.exists (fun struct (_, _, t) -> existsUploadType t)
|| variables
|> Seq.where (fun struct (_, typeName, _) -> TypeMapping.scalar.TryGetValue typeName |> fst |> not)
|> Seq.where (fun struct (_, typeName, _) -> not (TypeMapping.isScalarTypeName schemaTypes typeName))
|> Seq.choose (fun struct (_, typeName, _) -> schemaProvidedTypes |> Map.tryFind typeName)
|> Seq.exists existsUploadTypeDefinition
let runMethodOverloads : MemberInfo list =
Expand Down
17 changes: 14 additions & 3 deletions src/FSharp.Data.GraphQL.Client/BaseTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ module internal TypeMapping =
"URI", typeof<Uri> |]
|> Map.ofArray

let isBuiltInScalarTypeName (name : string) =
scalar |> Map.containsKey name

let isScalarTypeName (schemaTypes : Map<TypeName, IntrospectionType>) (name : string) =
match schemaTypes.TryFind name with
| Some schemaType -> schemaType.Kind = TypeKind.SCALAR
| None -> isBuiltInScalarTypeName name

let tryFindScalarType (schemaTypes : Map<TypeName, IntrospectionType>) (name : string) =
if isScalarTypeName schemaTypes name
then scalar |> Map.tryFind name
else None

let getSchemaTypes (introspection : IntrospectionSchema) =
let schemaTypeNames =
[| "__TypeKind"
Expand All @@ -179,13 +192,11 @@ module internal TypeMapping =
"__EnumValue"
"__Directive"
"__Schema" |]
let isScalarType (name : string) =
scalar |> Map.containsKey name
let isIntrospectionType (name : string) =
schemaTypeNames |> Array.contains name
introspection.Types
|> Array.choose (fun t ->
if not (isIntrospectionType t.Name) && not (isScalarType t.Name)
if not (isIntrospectionType t.Name) && not (t.Kind = TypeKind.SCALAR && isBuiltInScalarTypeName t.Name)
then Some(t.Name, t)
else None)
|> Map.ofArray
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

<ItemGroup>
<Content Include="introspection.json" />
<Content Include="reserved_scalar_input_date_introspection.json" />
<Content Include="reserved_scalar_object_date_introspection.json" />
<None Include="operation.graphql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
<Compile Include="Helpers.fs" />
<Compile Include="ReservedScalarNameProviderTests.fs" />
<Compile Include="LocalProviderTests.fs" />
<Compile Include="LocalProviderWithOptionalParametersOnlyTests.fs" />
<Compile Include="SwapiLocalProviderTests.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module FSharp.Data.GraphQL.IntegrationTests.ReservedScalarNameProviderTests

open Xunit
open FSharp.Data.GraphQL

type ObjectDateProvider = GraphQLProvider<"reserved_scalar_object_date_introspection.json">
type InputDateProvider = GraphQLProvider<"reserved_scalar_input_date_introspection.json">

module ObjectDateSchema =
type SchemaDate = ObjectDateProvider.Types.Date

let operation =
ObjectDateProvider.Operation<"""query Q {
dateInfo {
value
category
}
}""">()

let compileSmoke () =
let schemaDate = SchemaDate(value = "2026-04-03", category = "default")
let operationInstance : ObjectDateProvider.Operations.Q = operation
schemaDate |> ignore
operationInstance |> ignore

module InputDateSchema =
type SchemaDate = InputDateProvider.Types.Date

let operation =
InputDateProvider.Operation<"""query Q($input: Date) {
echoDate(input: $input)
}""">()

let compileSmoke () =
let schemaDate = SchemaDate(value = "2026-04-03", category = "default")
let deferredRun : unit -> _ =
fun () -> operation.Run(Unchecked.defaultof<GraphQLProviderRuntimeContext>, schemaDate)
schemaDate |> ignore
deferredRun |> ignore

[<Fact>]
let ``Should allow object types that reuse reserved scalar names`` () =
ObjectDateSchema.compileSmoke ()

[<Fact>]
let ``Should allow input object types that reuse reserved scalar names`` () =
InputDateSchema.compileSmoke ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
},
"mutationType": null,
"subscriptionType": null,
"types": [
{
"kind": "SCALAR",
"name": "String",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Query",
"description": null,
"fields": [
{
"name": "echoDate",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "Date",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "Date",
"description": null,
"fields": null,
"inputFields": [
{
"name": "value",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "category",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
}
],
"directives": []
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
},
"mutationType": null,
"subscriptionType": null,
"types": [
{
"kind": "SCALAR",
"name": "String",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Query",
"description": null,
"fields": [
{
"name": "dateInfo",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "Date",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Date",
"description": null,
"fields": [
{
"name": "value",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "category",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
}
],
"directives": []
}
}
}