Skip to content

Commit a557b1f

Browse files
committed
Add camelCase query parameter wire names
1 parent 6c39c39 commit a557b1f

5 files changed

Lines changed: 211 additions & 17 deletions

File tree

src/Bridge/Services/Clients/BranchClient.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ type BranchClient(transport: IHttpTransport) =
1414
[ match Option.ofObj queryParams.Id with
1515
| Some ids when not (Seq.isEmpty ids) ->
1616
for id in ids do
17-
yield "Id", (id: UrlPart)
17+
yield "id", (id: UrlPart)
1818
| _ -> ()
1919
if not (String.IsNullOrEmpty(queryParams.Search)) then
20-
yield "Search", (queryParams.Search: UrlPart)
20+
yield "search", (queryParams.Search: UrlPart)
2121
if queryParams.PageNumber.HasValue then
22-
yield "PageNumber", (queryParams.PageNumber.Value: UrlPart)
22+
yield "pageNumber", (queryParams.PageNumber.Value: UrlPart)
2323
if queryParams.PageSize.HasValue then
24-
yield "PageSize", (queryParams.PageSize.Value: UrlPart) ]
24+
yield "pageSize", (queryParams.PageSize.Value: UrlPart) ]
2525

2626
let endpointPath, securityRequirement = Endpoints.Branches
2727
composeRelativeUrl endpointPath securityRequirement [] parameters

src/Bridge/Services/Clients/DepartmentClient.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ type DepartmentClient(transport: IHttpTransport) =
1414
[ match Option.ofObj queryParams.Id with
1515
| Some ids when not (Seq.isEmpty ids) ->
1616
for id in ids do
17-
yield "Id", (id: UrlPart)
17+
yield "id", (id: UrlPart)
1818
| _ -> ()
1919
if not (String.IsNullOrEmpty(queryParams.Search)) then
20-
yield "Search", (queryParams.Search: UrlPart)
20+
yield "search", (queryParams.Search: UrlPart)
2121
if queryParams.PageNumber.HasValue then
22-
yield "PageNumber", (queryParams.PageNumber.Value: UrlPart)
22+
yield "pageNumber", (queryParams.PageNumber.Value: UrlPart)
2323
if queryParams.PageSize.HasValue then
24-
yield "PageSize", (queryParams.PageSize.Value: UrlPart) ]
24+
yield "pageSize", (queryParams.PageSize.Value: UrlPart) ]
2525

2626
let endpointPath, securityRequirement = Endpoints.Departments
2727
composeRelativeUrl endpointPath securityRequirement [] parameters

src/Bridge/Services/Clients/EmployeeClient.fs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,30 @@ type EmployeeClient(transport: IHttpTransport) =
1414
[ match Option.ofObj queryParams.Id with
1515
| Some ids when not (Seq.isEmpty ids) ->
1616
for id in ids do
17-
yield "Id", (id: UrlPart)
17+
yield "id", (id: UrlPart)
1818
| _ -> ()
1919
if not (String.IsNullOrEmpty(queryParams.Search)) then
20-
yield "Search", (queryParams.Search: UrlPart)
20+
yield "search", (queryParams.Search: UrlPart)
2121
if queryParams.PageNumber.HasValue then
22-
yield "PageNumber", (queryParams.PageNumber.Value: UrlPart)
22+
yield "pageNumber", (queryParams.PageNumber.Value: UrlPart)
2323
if queryParams.PageSize.HasValue then
24-
yield "PageSize", (queryParams.PageSize.Value: UrlPart)
24+
yield "pageSize", (queryParams.PageSize.Value: UrlPart)
2525
if queryParams.ReportsToId.HasValue then
26-
yield "ReportsToId", (queryParams.ReportsToId.Value: UrlPart)
26+
yield "reportsToId", (queryParams.ReportsToId.Value: UrlPart)
2727
if queryParams.TopLevelOnly.HasValue then
28-
yield "TopLevelOnly", (queryParams.TopLevelOnly.Value: UrlPart) ]
28+
yield "topLevelOnly", (queryParams.TopLevelOnly.Value: UrlPart) ]
2929

3030
let endpointPath, securityRequirement = Endpoints.Employees
3131
composeRelativeUrl endpointPath securityRequirement [] parameters
3232

3333
let buildPagingUrl (queryParams: EmployeePagingRequestModel) =
3434
let parameters =
3535
[ if not (String.IsNullOrEmpty(queryParams.Search)) then
36-
yield "Search", (queryParams.Search: UrlPart)
36+
yield "search", (queryParams.Search: UrlPart)
3737
if queryParams.PageNumber.HasValue then
38-
yield "PageNumber", (queryParams.PageNumber.Value: UrlPart)
38+
yield "pageNumber", (queryParams.PageNumber.Value: UrlPart)
3939
if queryParams.PageSize.HasValue then
40-
yield "PageSize", (queryParams.PageSize.Value: UrlPart) ]
40+
yield "pageSize", (queryParams.PageSize.Value: UrlPart) ]
4141

4242
let endpointPath, securityRequirement = Endpoints.Employees
4343
composeRelativeUrl endpointPath securityRequirement [] parameters

tests/Bridge.Tests/Bridge.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<Compile Include="ClientResultTests.fs" />
1010
<Compile Include="ClientUnitResultTests.fs" />
1111
<Compile Include="UrlHelpersTests.fs" />
12+
<Compile Include="ClientQueryParameterTests.fs" />
1213
<Compile Include="StatusCodeHelpersTests.fs" />
1314
<Compile Include="HttpTransportTests.fs" />
1415
<Compile Include="Main.fs" />
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
module ClientQueryParameterTests
2+
3+
open System
4+
open System.Collections.Generic
5+
open System.Threading
6+
open System.Threading.Tasks
7+
open Expecto
8+
open Fossa.Bridge.Models.ApiModels
9+
open Fossa.Bridge.Services
10+
open Fossa.Bridge.Services.Clients
11+
12+
type private CapturingTransport() =
13+
let mutable lastUrl: string | null = null
14+
15+
member _.LastUrl = lastUrl
16+
17+
interface IHttpTransport with
18+
member _.GetAsync<'TResponse when 'TResponse: not struct and 'TResponse: not null>
19+
(endpointUrl: string, _endpointSecurity: EndpointSecurity, _cancellationToken: CancellationToken)
20+
: Task<ClientResult<'TResponse>> =
21+
lastUrl <- endpointUrl
22+
23+
Task.FromResult(
24+
{ Succeeded = true
25+
Value = Unchecked.defaultof<'TResponse>
26+
Problem = null }
27+
)
28+
29+
member _.PostAsync<'TRequest when 'TRequest: not null>
30+
(
31+
endpointUrl: string,
32+
_endpointSecurity: EndpointSecurity,
33+
_request: 'TRequest,
34+
_cancellationToken: CancellationToken
35+
) : Task<ClientUnitResult> =
36+
lastUrl <- endpointUrl
37+
Task.FromResult({ Succeeded = true; Problem = null })
38+
39+
member _.PutAsync<'TRequest when 'TRequest: not null>
40+
(
41+
endpointUrl: string,
42+
_endpointSecurity: EndpointSecurity,
43+
_request: 'TRequest,
44+
_cancellationToken: CancellationToken
45+
) : Task<ClientUnitResult> =
46+
lastUrl <- endpointUrl
47+
Task.FromResult({ Succeeded = true; Problem = null })
48+
49+
member _.PatchAsync<'TRequest when 'TRequest: not null>
50+
(
51+
endpointUrl: string,
52+
_endpointSecurity: EndpointSecurity,
53+
_request: 'TRequest,
54+
_cancellationToken: CancellationToken
55+
) : Task<ClientUnitResult> =
56+
lastUrl <- endpointUrl
57+
Task.FromResult({ Succeeded = true; Problem = null })
58+
59+
member _.DeleteAsync
60+
(endpointUrl: string, _endpointSecurity: EndpointSecurity, _cancellationToken: CancellationToken)
61+
: Task<ClientUnitResult> =
62+
lastUrl <- endpointUrl
63+
Task.FromResult({ Succeeded = true; Problem = null })
64+
65+
let private idList (values: int64 seq) =
66+
ResizeArray<int64>(values) :> IReadOnlyList<int64>
67+
68+
let private capturedUrl (transport: CapturingTransport) =
69+
match transport.LastUrl with
70+
| null -> failtest "Client should send a request through the transport"
71+
| url -> url
72+
73+
let private queryPart (url: string) =
74+
let queryStart = url.IndexOf("?")
75+
Expect.isGreaterThanOrEqual queryStart 0 "URL should include query parameters"
76+
url.Substring(queryStart + 1)
77+
78+
let private expectNoPascalCaseQueryNames (url: string) =
79+
let queryKeys =
80+
(queryPart url).Split('&')
81+
|> Array.map (fun parameter -> parameter.Split('=')[0])
82+
83+
[ "Id"; "Search"; "PageNumber"; "PageSize"; "ReportsToId"; "TopLevelOnly" ]
84+
|> List.iter (fun key -> Expect.isFalse (Array.contains key queryKeys) $"Query should not contain {key}")
85+
86+
[<Tests>]
87+
let tests =
88+
testList
89+
"ClientQueryParameterTests"
90+
[ testCase "Branch query uses camelCase query parameter names"
91+
<| fun _ ->
92+
let transport = CapturingTransport()
93+
let client = BranchClient(transport)
94+
95+
client
96+
.GetBranchesAsync(
97+
{ Id = idList [ 1L; 2L ]
98+
Search = "north"
99+
PageNumber = Nullable 3
100+
PageSize = Nullable 25 },
101+
CancellationToken.None
102+
)
103+
.GetAwaiter()
104+
.GetResult()
105+
|> ignore
106+
107+
let url = capturedUrl transport
108+
109+
Expect.equal
110+
url
111+
"api/1.0/Branches?id=1&id=2&search=north&pageNumber=3&pageSize=25"
112+
"Branch query should use camelCase wire names"
113+
114+
expectNoPascalCaseQueryNames url
115+
116+
testCase "Department query uses camelCase query parameter names"
117+
<| fun _ ->
118+
let transport = CapturingTransport()
119+
let client = DepartmentClient(transport)
120+
121+
client
122+
.GetDepartmentsAsync(
123+
{ Id = idList [ 10L; 20L ]
124+
Search = "ops"
125+
PageNumber = Nullable 2
126+
PageSize = Nullable 50 },
127+
CancellationToken.None
128+
)
129+
.GetAwaiter()
130+
.GetResult()
131+
|> ignore
132+
133+
let url = capturedUrl transport
134+
135+
Expect.equal
136+
url
137+
"api/1.0/Departments?id=10&id=20&search=ops&pageNumber=2&pageSize=50"
138+
"Department query should use camelCase wire names"
139+
140+
expectNoPascalCaseQueryNames url
141+
142+
testCase "Employee query uses camelCase query parameter names"
143+
<| fun _ ->
144+
let transport = CapturingTransport()
145+
let client = EmployeeClient(transport)
146+
147+
client
148+
.GetEmployeesAsync(
149+
{ Id = idList [ 99L ]
150+
Search = "casey"
151+
PageNumber = Nullable 4
152+
PageSize = Nullable 15
153+
ReportsToId = Nullable 7L
154+
TopLevelOnly = Nullable true },
155+
CancellationToken.None
156+
)
157+
.GetAwaiter()
158+
.GetResult()
159+
|> ignore
160+
161+
let url = capturedUrl transport
162+
163+
Expect.equal
164+
url
165+
"api/1.0/Employees?id=99&search=casey&pageNumber=4&pageSize=15&reportsToId=7&topLevelOnly=true"
166+
"Employee query should use camelCase wire names"
167+
168+
expectNoPascalCaseQueryNames url
169+
170+
testCase "Employee paging query uses camelCase query parameter names"
171+
<| fun _ ->
172+
let transport = CapturingTransport()
173+
let client = EmployeeClient(transport)
174+
175+
client
176+
.GetEmployeesPagingAsync(
177+
{ Search = "casey"
178+
PageNumber = Nullable 5
179+
PageSize = Nullable 30 },
180+
CancellationToken.None
181+
)
182+
.GetAwaiter()
183+
.GetResult()
184+
|> ignore
185+
186+
let url = capturedUrl transport
187+
188+
Expect.equal
189+
url
190+
"api/1.0/Employees?search=casey&pageNumber=5&pageSize=30"
191+
"Employee paging query should use camelCase wire names"
192+
193+
expectNoPascalCaseQueryNames url ]

0 commit comments

Comments
 (0)