Skip to content
Merged
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
68 changes: 36 additions & 32 deletions src/SwaggerProvider.Runtime/RuntimeHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -85,38 +85,42 @@ module RuntimeHelpers =
| _ -> obj.ToString()

let toQueryParams (name: string) (obj: obj) (client: Swagger.ProvidedApiClientBase) =
match obj with
| :? array<byte> as xs -> [ name, (client.Serialize xs).Trim('\"') ] // TODO: Need to verify how servers parse byte[] from query string
| :? array<bool> as xs -> xs |> toStrArray name
| :? array<int32> as xs -> xs |> toStrArray name
| :? array<int64> as xs -> xs |> toStrArray name
| :? array<float32> as xs -> xs |> toStrArray name
| :? array<double> as xs -> xs |> toStrArray name
| :? array<string> as xs -> xs |> toStrArray name
| :? array<DateTime> as xs -> xs |> toStrArrayDateTime name
| :? array<DateTimeOffset> as xs -> xs |> toStrArrayDateTimeOffset name
| :? array<Guid> as xs -> xs |> toStrArray name
| :? array<Option<bool>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<int32>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<int64>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<float32>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<double>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<string>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<DateTime>> as xs -> xs |> toStrArrayDateTimeOpt name
| :? array<Option<DateTimeOffset>> as xs -> xs |> toStrArrayDateTimeOffsetOpt name
| :? array<Option<Guid>> as xs -> xs |> toStrArray name
| :? Option<bool> as x -> x |> toStrOpt name
| :? Option<int32> as x -> x |> toStrOpt name
| :? Option<int64> as x -> x |> toStrOpt name
| :? Option<float32> as x -> x |> toStrOpt name
| :? Option<double> as x -> x |> toStrOpt name
| :? Option<string> as x -> x |> toStrOpt name
| :? Option<DateTime> as x -> x |> toStrDateTimeOpt name
| :? Option<DateTimeOffset> as x -> x |> toStrDateTimeOffsetOpt name
| :? DateTime as x -> [ name, x.ToString("O") ]
| :? DateTimeOffset as x -> [ name, x.ToString("O") ]
| :? Option<Guid> as x -> x |> toStrOpt name
| _ -> [ name, obj.ToString() ]
if isNull obj then
[]
else

match obj with
| :? array<byte> as xs -> [ name, (client.Serialize xs).Trim('\"') ] // TODO: Need to verify how servers parse byte[] from query string
| :? array<bool> as xs -> xs |> toStrArray name
| :? array<int32> as xs -> xs |> toStrArray name
| :? array<int64> as xs -> xs |> toStrArray name
| :? array<float32> as xs -> xs |> toStrArray name
| :? array<double> as xs -> xs |> toStrArray name
| :? array<string> as xs -> xs |> toStrArray name
| :? array<DateTime> as xs -> xs |> toStrArrayDateTime name
| :? array<DateTimeOffset> as xs -> xs |> toStrArrayDateTimeOffset name
| :? array<Guid> as xs -> xs |> toStrArray name
| :? array<Option<bool>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<int32>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<int64>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<float32>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<double>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<string>> as xs -> xs |> toStrArrayOpt name
| :? array<Option<DateTime>> as xs -> xs |> toStrArrayDateTimeOpt name
| :? array<Option<DateTimeOffset>> as xs -> xs |> toStrArrayDateTimeOffsetOpt name
| :? array<Option<Guid>> as xs -> xs |> toStrArray name

Copilot AI Mar 10, 2026

Copy link

Choose a reason for hiding this comment

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

array<Option<Guid>> is currently serialized via toStrArray, which will call ToString() on Some g (producing e.g. Some ...) and will throw NullReferenceException for None elements (since None is represented as null). This should use the same option-array handling as the other option cases (e.g., toStrArrayOpt) so None values are skipped and Some values serialize as the underlying Guid.

Suggested change
| :? array<Option<Guid>> as xs -> xs |> toStrArray name
| :? array<Option<Guid>> as xs -> xs |> toStrArrayOpt name

Copilot uses AI. Check for mistakes.
| :? Option<bool> as x -> x |> toStrOpt name
| :? Option<int32> as x -> x |> toStrOpt name
| :? Option<int64> as x -> x |> toStrOpt name
| :? Option<float32> as x -> x |> toStrOpt name
| :? Option<double> as x -> x |> toStrOpt name
| :? Option<string> as x -> x |> toStrOpt name
| :? Option<DateTime> as x -> x |> toStrDateTimeOpt name
| :? Option<DateTimeOffset> as x -> x |> toStrDateTimeOffsetOpt name
| :? DateTime as x -> [ name, x.ToString("O") ]
| :? DateTimeOffset as x -> [ name, x.ToString("O") ]
| :? Option<Guid> as x -> x |> toStrOpt name
| _ -> [ name, obj.ToString() ]

let getPropertyNameAttribute name =
{ new Reflection.CustomAttributeData() with
Expand Down
4 changes: 2 additions & 2 deletions tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ module ToQueryParamsTests =

[<Fact>]
let ``toQueryParams returns empty list for null input (treated as Option None)``() =
// In F#, None for reference option types is compiled as null at the .NET level,
// so a null obj matches Option<string> None and returns an empty list.
// In F#, None for any option type is compiled as null at the .NET level,
// so a null obj is treated as Option None and returns an empty list.
let result = toQueryParams "q" null stubClient
result |> shouldEqual []

Expand Down
Loading