Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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>
5 changes: 4 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 3.1.1 - 2026-04-03

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

The release notes now contain two separate sections for version 3.1.1 (one dated and one 'Unreleased'), which is confusing and can lead to incorrect changelog generation. Please merge these entries under a single 3.1.1 section, or bump the 'Unreleased' header to the next version (e.g., 3.1.2 - Unreleased) so each version appears only once.

Suggested change
### 3.1.1 - 2026-04-03
### 3.1.2 - Unreleased

Copilot uses AI. Check for mistakes.
* 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.

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.

this must go to the bottom of the file

#### 0.0.1-beta - 2016-04-19
* Initial release

Expand Down Expand Up @@ -242,4 +245,4 @@

### 3.1.1 - Unreleased

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

The release notes now contain two separate sections for version 3.1.1 (one dated and one 'Unreleased'), which is confusing and can lead to incorrect changelog generation. Please merge these entries under a single 3.1.1 section, or bump the 'Unreleased' header to the next version (e.g., 3.1.2 - Unreleased) so each version appears only once.

Suggested change
### 3.1.1 - Unreleased
### 3.1.2 - Unreleased

Copilot uses AI. Check for mistakes.

* 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
10 changes: 3 additions & 7 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,9 +305,9 @@ 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

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
match schemaProvidedTypes.TryFind(typeName) with
match schemaProvidedTypes.TryFind typeName with

| Some t -> struct (variableName, typeName, TypeMapping.makeOption t)
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,50 @@
module FSharp.Data.GraphQL.IntegrationTests.ReservedScalarNameProviderTests

open Xunit
open Helpers
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 ()
true |> equals true

[<Fact>]
let ``Should allow input object types that reuse reserved scalar names`` () =
InputDateSchema.compileSmoke ()
true |> equals true

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

These assertions are no-ops (they always compare true to true) and don’t add signal beyond ‘test did not throw’. Since xUnit will already fail the test on any exception from compileSmoke, consider removing the true |> equals true lines (or replace with an assertion that reflects an observable outcome if you want runtime validation).

Suggested change
true |> equals true
[<Fact>]
let ``Should allow input object types that reuse reserved scalar names`` () =
InputDateSchema.compileSmoke ()
true |> equals true
[<Fact>]
let ``Should allow input object types that reuse reserved scalar names`` () =
InputDateSchema.compileSmoke ()

Copilot uses AI. Check for mistakes.
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": []
}
}
}
Loading