git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on main help sign in
opt helper: partial query params without Just/Nothing boilerplate
c78cf65Mark
src/Yoga/Fetch/Om.purs+14−20
@@ -13,7 +13,6 @@ module Yoga.Fetch.Om
13 , module Yoga.HTTP.API.Route
13 , module Yoga.HTTP.API.Route
14 , module Yoga.HTTP.API.Path
14 , module Yoga.HTTP.API.Path
15 , plainText
15 , plainText
16− , opt
17 , module Yoga.Fetch.Om.StreamDecode
16 , module Yoga.Fetch.Om.StreamDecode
18 , module Yoga.Fetch.Om.Simple
17 , module Yoga.Fetch.Om.Simple
19 ) where
18 ) where
@@ -35,7 +34,7 @@ import Yoga.Fetch.Om.BuildUrl (class BuildUrl, buildUrl)
35import Yoga.Fetch.Om.Simple (class DecodeResponse, decodeResponse, FetchError, FetchResponse, get, getWithHeaders, delete, deleteWithHeaders, delete_, post, postWithHeaders, post_, put, putWithHeaders, put_, patch, patchWithHeaders, patch_)
34import Yoga.Fetch.Om.Simple (class DecodeResponse, decodeResponse, FetchError, FetchResponse, get, getWithHeaders, delete, deleteWithHeaders, delete_, post, postWithHeaders, post_, put, putWithHeaders, put_, patch, patchWithHeaders, patch_)
36import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
35import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
37import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
36import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
38−import Yoga.Fetch.Om.QueryDefaults (class BuildQueryFromPartial, buildQueryFromPartial)
37+import Yoga.Fetch.Om.ReadOptional (class ReadPartialQuery, readPartialQuery)
39import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractBodyEncoding, class ExtractRequestHeaders)
38import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractBodyEncoding, class ExtractRequestHeaders)
40import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class BodyEncoding, makeRequest, encodingContentType, encodeBody)
39import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class BodyEncoding, makeRequest, encodingContentType, encodeBody)
41import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
40import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
@@ -74,11 +73,11 @@ else instance
74 rest = toHeaders (Proxy :: Proxy tail) (unsafeCoerce headers :: Record tailRow)
73 rest = toHeaders (Proxy :: Proxy tail) (unsafeCoerce headers :: Record tailRow)
75
74
76class DeriveClientFn
75class DeriveClientFn
77− :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
76+ :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
78class
77class
79− DeriveClientFn method segments request response ctx extraErr routeErrors result fn
78+ DeriveClientFn method segments request response ctx extraErr given routeErrors result fn
80 | method segments request response -> routeErrors result
79 | method segments request response -> routeErrors result
81− , method segments request response ctx extraErr -> fn where
80+ , method segments request response ctx extraErr given -> fn where
82 deriveClientFn :: String -> Proxy (Route method segments request response) -> fn
81 deriveClientFn :: String -> Proxy (Route method segments request response) -> fn
83
82
84instance
83instance
@@ -98,18 +97,23 @@ instance
98 , Row.Union pathParams queryParams pathQuery
97 , Row.Union pathParams queryParams pathQuery
99 , Row.Nub pathQuery pathQuery
98 , Row.Nub pathQuery pathQuery
100 , RowToList pathQuery pathQueryRL
99 , RowToList pathQuery pathQueryRL
100+ , RowToList queryParams queryParamsRL
101+ , ReadPartialQuery queryParamsRL queryParams
101 , RowToList headers headersRL
102 , RowToList headers headersRL
102 , ToHeaders headersRL headers
103 , ToHeaders headersRL headers
103 , CheckBodyIsUnit body bodyFlag
104 , CheckBodyIsUnit body bodyFlag
104 , Row.Union routeErrors extraErr errRow
105 , Row.Union routeErrors extraErr errRow
105− , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers { | ctx } errRow result fn
106+ , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers given { | ctx } errRow result fn
106 ) =>
107 ) =>
107− DeriveClientFn method segments request response ctx extraErr routeErrors result fn where
108+ DeriveClientFn method segments request response ctx extraErr given routeErrors result fn where
108 deriveClientFn baseUrl _ =
109 deriveClientFn baseUrl _ =
109− buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) impl
110+ buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) convert impl
110 where
111 where
112+ convert :: Record given -> Record pathQuery
113+ convert = unsafeCoerce
111 impl :: Record pathQuery -> Record headers -> body -> Om { | ctx } errRow result
114 impl :: Record pathQuery -> Record headers -> body -> Om { | ctx } errRow result
112 impl pathQueryRec headersRec bodyVal = widenOm do
115 impl pathQueryRec headersRec bodyVal = widenOm do
116+ let queryParamsRec = readPartialQuery @queryParamsRL pathQueryRec
113 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
117 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
114 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
118 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
115 let ct = encodingContentType (Proxy :: _ encoding)
119 let ct = encodingContentType (Proxy :: _ encoding)
@@ -118,7 +122,6 @@ instance
118 variantOrValue (Proxy :: _ successRL) variant # pure
122 variantOrValue (Proxy :: _ successRL) variant # pure
119 where
123 where
120 pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
124 pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
121− queryParamsRec = unsafeCoerce pathQueryRec :: Record queryParams
122 widenOm :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
125 widenOm :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
123 widenOm = unsafeCoerce
126 widenOm = unsafeCoerce
124
127
@@ -146,15 +149,6 @@ deriveClient baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | r
146plainText :: PlainText -> String
149plainText :: PlainText -> String
147plainText = unsafeCoerce
150plainText = unsafeCoerce
148
151
149−-- | Convert a partial record to a full query params record.
150−-- | Provided fields get `Just`, missing fields get `Nothing`.
151−-- |
152−-- | ```purescript
153−-- | api.listUsers $ opt { limit: 10 }
154−-- | -- instead of: api.listUsers { limit: Just 10, offset: Nothing }
155−-- | ```
156−opt :: forall @queryParams provided. BuildQueryFromPartial queryParams provided => Record provided -> Record queryParams
157−opt = buildQueryFromPartial
158
152
159instance
153instance
160 ( RowToList routesRow rl
154 ( RowToList routesRow rl
@@ -172,7 +166,7 @@ instance DeriveClientRL ctx extraErr RL.Nil acc acc where
172
166
173instance
167instance
174 ( IsSymbol label
168 ( IsSymbol label
175− , DeriveClientFn method segments request response ctx extraErr routeErrors result fn
169+ , DeriveClientFn method segments request response ctx extraErr given routeErrors result fn
176 , DeriveClientRL ctx extraErr tail acc1 acc2
170 , DeriveClientRL ctx extraErr tail acc1 acc2
177 , Row.Cons label fn acc2 out
171 , Row.Cons label fn acc2 out
178 , Row.Lacks label acc2
172 , Row.Lacks label acc2
@@ -181,5 +175,5 @@ instance
181 DeriveClientRL ctx extraErr (RL.Cons label (Route method segments request response) tail) acc1 out where
175 DeriveClientRL ctx extraErr (RL.Cons label (Route method segments request response) tail) acc1 out where
182 deriveClientRL baseUrl _ acc = Record.insert (Proxy :: _ label) clientFn rest
176 deriveClientRL baseUrl _ acc = Record.insert (Proxy :: _ label) clientFn rest
183 where
177 where
184− clientFn = deriveClientFn @_ @_ @_ @_ @ctx @extraErr baseUrl (Proxy :: _ (Route method segments request response))
178+ clientFn = deriveClientFn @_ @_ @_ @_ @ctx @extraErr @given baseUrl (Proxy :: _ (Route method segments request response))
185 rest = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ tail) acc
179 rest = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ tail) acc
src/Yoga/Fetch/Om/ClientFunction.purs+24−15
@@ -22,16 +22,17 @@ instance CheckBodyIsUnit Unit IsUnit
22else instance CheckBodyIsUnit body IsNotUnit
22else instance CheckBodyIsUnit body IsNotUnit
23
23
24class BuildClientFn
24class BuildClientFn
25− :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Type -> Row Type -> Type -> Type -> Constraint
25+ :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Row Type -> Type -> Row Type -> Type -> Type -> Constraint
26class
26class
27− BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers ctx errorRow result fn
27+ BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers given ctx errorRow result fn
28 | pathQueryRL -> pathQuery
28 | pathQueryRL -> pathQuery
29 , headersRL -> headers
29 , headersRL -> headers
30− , pathQueryRL headersRL bodyFlag body ctx errorRow result -> fn where
30+ , pathQueryRL headersRL bodyFlag body given ctx errorRow result -> fn where
31 buildClientFn
31 buildClientFn
32 :: Proxy pathQueryRL
32 :: Proxy pathQueryRL
33 -> Proxy headersRL
33 -> Proxy headersRL
34 -> Proxy bodyFlag
34 -> Proxy bodyFlag
35+ -> (Record given -> Record pathQuery)
35 -> (Record pathQuery -> Record headers -> body -> Om ctx errorRow result)
36 -> (Record pathQuery -> Record headers -> body -> Om ctx errorRow result)
36 -> fn
37 -> fn
37
38
@@ -43,11 +44,12 @@ instance
43 Unit
44 Unit
44 ()
45 ()
45 ()
46 ()
47+ given
46 ctx
48 ctx
47 errorRow
49 errorRow
48 result
50 result
49 (Om ctx errorRow result) where
51 (Om ctx errorRow result) where
50− buildClientFn _ _ _ f = f {} {} unit
52+ buildClientFn _ _ _ _ f = f {} {} unit
51
53
52-- No path/query, no headers, with body
54-- No path/query, no headers, with body
53instance
55instance
@@ -57,11 +59,12 @@ instance
57 body
59 body
58 ()
60 ()
59 ()
61 ()
62+ given
60 ctx
63 ctx
61 errorRow
64 errorRow
62 result
65 result
63 (body -> Om ctx errorRow result) where
66 (body -> Om ctx errorRow result) where
64− buildClientFn _ _ _ f b = f {} {} b
67+ buildClientFn _ _ _ _ f b = f {} {} b
65
68
66-- No path/query, with headers, no body
69-- No path/query, with headers, no body
67instance
70instance
@@ -71,11 +74,12 @@ instance
71 Unit
74 Unit
72 ()
75 ()
73 h
76 h
77+ given
74 ctx
78 ctx
75 errorRow
79 errorRow
76 result
80 result
77 (Record h -> Om ctx errorRow result) where
81 (Record h -> Om ctx errorRow result) where
78− buildClientFn _ _ _ f hdrs = f {} hdrs unit
82+ buildClientFn _ _ _ _ f hdrs = f {} hdrs unit
79
83
80-- No path/query, with headers, with body
84-- No path/query, with headers, with body
81instance
85instance
@@ -85,11 +89,12 @@ instance
85 body
89 body
86 ()
90 ()
87 h
91 h
92+ given
88 ctx
93 ctx
89 errorRow
94 errorRow
90 result
95 result
91 (Record h -> body -> Om ctx errorRow result) where
96 (Record h -> body -> Om ctx errorRow result) where
92− buildClientFn _ _ _ f hdrs b = f {} hdrs b
97+ buildClientFn _ _ _ _ f hdrs b = f {} hdrs b
93
98
94-- With path/query, no headers, no body
99-- With path/query, no headers, no body
95instance
100instance
@@ -99,11 +104,12 @@ instance
99 Unit
104 Unit
100 pq
105 pq
101 ()
106 ()
107+ given
102 ctx
108 ctx
103 errorRow
109 errorRow
104 result
110 result
105− (Record pq -> Om ctx errorRow result) where
111+ (Record given -> Om ctx errorRow result) where
106− buildClientFn _ _ _ f pqr = f pqr {} unit
112+ buildClientFn _ _ _ convert f pqr = f (convert pqr) {} unit
107
113
108-- With path/query, no headers, with body
114-- With path/query, no headers, with body
109instance
115instance
@@ -113,11 +119,12 @@ instance
113 body
119 body
114 pq
120 pq
115 ()
121 ()
122+ given
116 ctx
123 ctx
117 errorRow
124 errorRow
118 result
125 result
119− (Record pq -> body -> Om ctx errorRow result) where
126+ (Record given -> body -> Om ctx errorRow result) where
120− buildClientFn _ _ _ f pqr b = f pqr {} b
127+ buildClientFn _ _ _ convert f pqr b = f (convert pqr) {} b
121
128
122-- With path/query, with headers, no body
129-- With path/query, with headers, no body
123instance
130instance
@@ -127,11 +134,12 @@ instance
127 Unit
134 Unit
128 pq
135 pq
129 h
136 h
137+ given
130 ctx
138 ctx
131 errorRow
139 errorRow
132 result
140 result
133− (Record pq -> Record h -> Om ctx errorRow result) where
141+ (Record given -> Record h -> Om ctx errorRow result) where
134− buildClientFn _ _ _ f pqr hdrs = f pqr hdrs unit
142+ buildClientFn _ _ _ convert f pqr hdrs = f (convert pqr) hdrs unit
135
143
136-- With path/query, with headers, with body
144-- With path/query, with headers, with body
137instance
145instance
@@ -141,8 +149,9 @@ instance
141 body
149 body
142 pq
150 pq
143 h
151 h
152+ given
144 ctx
153 ctx
145 errorRow
154 errorRow
146 result
155 result
147− (Record pq -> Record h -> body -> Om ctx errorRow result) where
156+ (Record given -> Record h -> body -> Om ctx errorRow result) where
148− buildClientFn _ _ _ f = f
157+ buildClientFn _ _ _ convert f pqr hdrs b = f (convert pqr) hdrs b
test/Roundtrip.Spec.purs+15−15
@@ -12,7 +12,7 @@ import Yoga.Fastify.Fastify as F
12import Yoga.Fastify.Fastify (Port(..), Host(..))
12import Yoga.Fastify.Fastify (Port(..), Host(..))
13import Yoga.Fastify.Om.API (registerAPI)
13import Yoga.Fastify.Om.API (registerAPI)
14import Yoga.Fastify.Om.Route (Handler, handle, respond, reject, BearerToken(..))
14import Yoga.Fastify.Om.Route (Handler, handle, respond, reject, BearerToken(..))
15−import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, type (/), type (:), type (:?), client, opt)
15+import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, type (/), type (:), type (:?), client)
16import Yoga.Om (Om, ask, handleErrors, runOm)
16import Yoga.Om (Om, ask, handleErrors, runOm)
17
17
18-- Types
18-- Types
@@ -168,12 +168,12 @@ spec = around withServer $ describe "server ↔ client roundtrip" do
168 describe "GET with query params" do
168 describe "GET with query params" do
169 it "passes query params correctly" \_ ->
169 it "passes query params correctly" \_ ->
170 runOm {} { exception: \_ -> pure unit } do
170 runOm {} { exception: \_ -> pure unit } do
171− users <- api.listUsers { limit: Just 1, offset: Nothing }
171+ users <- api.listUsers { limit: 1 }
172 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
172 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
173
173
174 it "handles no query params" \_ ->
174 it "handles no query params" \_ ->
175 runOm {} { exception: \_ -> pure unit } do
175 runOm {} { exception: \_ -> pure unit } do
176− users <- api.listUsers { limit: Nothing, offset: Nothing }
176+ users <- api.listUsers {}
177 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
177 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
178
178
179 describe "POST with JSON body" do
179 describe "POST with JSON body" do
@@ -230,37 +230,37 @@ spec = around withServer $ describe "server ↔ client roundtrip" do
230 describe "GET with Boolean and Number query params" do
230 describe "GET with Boolean and Number query params" do
231 it "passes Boolean and Number query params" \_ ->
231 it "passes Boolean and Number query params" \_ ->
232 runOm {} { exception: \_ -> pure unit } do
232 runOm {} { exception: \_ -> pure unit } do
233− result <- api.search { active: Just true, minScore: Just 3.14 }
233+ result <- api.search { active: true, minScore: 3.14 }
234 liftAff do
234 liftAff do
235 result.active `shouldEqual` true
235 result.active `shouldEqual` true
236 result.minScore `shouldEqual` 3.14
236 result.minScore `shouldEqual` 3.14
237
237
238− it "handles Nothing for optional Boolean/Number" \_ ->
238+ it "handles missing optional query params" \_ ->
239 runOm {} { exception: \_ -> pure unit } do
239 runOm {} { exception: \_ -> pure unit } do
240− result <- api.search { active: Nothing, minScore: Nothing }
240+ result <- api.search {}
241 liftAff do
241 liftAff do
242 result.active `shouldEqual` false
242 result.active `shouldEqual` false
243 result.minScore `shouldEqual` 0.0
243 result.minScore `shouldEqual` 0.0
244
244
245− describe "opt helper for partial query params" do
245+ describe "partial query params — no wrapper needed" do
246− it "passes subset of query params via opt" \_ ->
246+ it "passes subset of query params directly" \_ ->
247 runOm {} { exception: \_ -> pure unit } do
247 runOm {} { exception: \_ -> pure unit } do
248− users <- api.listUsers $ opt { limit: 1 }
248+ users <- api.listUsers { limit: 1 }
249 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
249 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
250
250
251− it "passes all query params via opt" \_ ->
251+ it "passes all query params directly" \_ ->
252 runOm {} { exception: \_ -> pure unit } do
252 runOm {} { exception: \_ -> pure unit } do
253− users <- api.listUsers $ opt { limit: 2, offset: 0 }
253+ users <- api.listUsers { limit: 2, offset: 0 }
254 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob"]
254 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob"]
255
255
256− it "passes empty record via opt" \_ ->
256+ it "passes empty record for all defaults" \_ ->
257 runOm {} { exception: \_ -> pure unit } do
257 runOm {} { exception: \_ -> pure unit } do
258− users <- api.listUsers $ opt {}
258+ users <- api.listUsers {}
259 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
259 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
260
260
261− it "passes Boolean via opt" \_ ->
261+ it "passes partial Boolean query param" \_ ->
262 runOm {} { exception: \_ -> pure unit } do
262 runOm {} { exception: \_ -> pure unit } do
263− result <- api.search $ opt { active: true }
263+ result <- api.search { active: true }
264 liftAff do
264 liftAff do
265 result.active `shouldEqual` true
265 result.active `shouldEqual` true
266 result.minScore `shouldEqual` 0.0
266 result.minScore `shouldEqual` 0.0