git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on main help sign in
v0.6.0: Add Streaming response support, update deps
f26d55aMark
spago.lock+279−23
@@ -8,6 +8,9 @@
8 {
8 {
9 "aff": ">=7.0.0 <9.0.0"
9 "aff": ">=7.0.0 <9.0.0"
10 },
10 },
11+ {
12+ "arraybuffer-types": ">=3.0.2 <4.0.0"
13+ },
11 {
14 {
12 "arrays": ">=7.3.0 <8.0.0"
15 "arrays": ">=7.3.0 <8.0.0"
13 },
16 },
@@ -57,14 +60,103 @@
57 "variant": ">=8.0.0 <9.0.0"
60 "variant": ">=8.0.0 <9.0.0"
58 },
61 },
59 {
62 {
60− "yoga-http-api": ">=0.1.0 <0.2.0"
63+ "yoga-http-api": ">=0.1.0 <0.3.0"
61 },
64 },
62 {
65 {
63 "yoga-json": ">=5.2.0 <6.0.0"
66 "yoga-json": ">=5.2.0 <6.0.0"
64 },
67 },
65 {
68 {
66 "yoga-om": ">=1.2.2 <2.0.0"
69 "yoga-om": ">=1.2.2 <2.0.0"
70+ },
71+ {
72+ "yoga-om-strom": ">=0.2.0 <0.5.0"
67 }
73 }
74+ ],
75+ "build_plan": [
76+ "aff",
77+ "aff-retry",
78+ "ansi",
79+ "arraybuffer-types",
80+ "arrays",
81+ "avar",
82+ "bifunctors",
83+ "catenable-lists",
84+ "console",
85+ "const",
86+ "contravariant",
87+ "control",
88+ "datetime",
89+ "distributive",
90+ "effect",
91+ "either",
92+ "enums",
93+ "exceptions",
94+ "exists",
95+ "foldable-traversable",
96+ "foreign",
97+ "foreign-object",
98+ "fork",
99+ "free",
100+ "functions",
101+ "functors",
102+ "gen",
103+ "heterogeneous",
104+ "http-methods",
105+ "identity",
106+ "integers",
107+ "invariant",
108+ "js-bigints",
109+ "js-date",
110+ "js-fetch",
111+ "js-promise",
112+ "js-promise-aff",
113+ "lazy",
114+ "lists",
115+ "literals",
116+ "maybe",
117+ "media-types",
118+ "mmorph",
119+ "newtype",
120+ "nonempty",
121+ "now",
122+ "nullable",
123+ "numbers",
124+ "ordered-collections",
125+ "orders",
126+ "parallel",
127+ "partial",
128+ "pipes",
129+ "prelude",
130+ "profunctor",
131+ "random",
132+ "record",
133+ "record-studio",
134+ "ref-lifted",
135+ "refs",
136+ "safe-coerce",
137+ "spec",
138+ "st",
139+ "strings",
140+ "tailrec",
141+ "transformers",
142+ "tuples",
143+ "type-equality",
144+ "typelevel-prelude",
145+ "uncurried-transformers",
146+ "undefined-is-not-a-problem",
147+ "unfoldable",
148+ "unsafe-coerce",
149+ "untagged-union",
150+ "variant",
151+ "web-events",
152+ "web-file",
153+ "web-streams",
154+ "yoga-http-api",
155+ "yoga-json",
156+ "yoga-om",
157+ "yoga-om-strom",
158+ "yoga-options",
159+ "yoga-tree"
68 ]
160 ]
69 },
161 },
70 "test": {
162 "test": {
@@ -82,8 +174,107 @@
82 "spec-discovery": ">=8.0.0 <9.0.0"
174 "spec-discovery": ">=8.0.0 <9.0.0"
83 },
175 },
84 {
176 {
85− "yoga-fastify-om": ">=0.1.0 <0.2.0"
177+ "yoga-fastify-om": ">=0.1.0 <0.4.0"
86 }
178 }
179+ ],
180+ "build_plan": [
181+ "aff",
182+ "aff-retry",
183+ "ansi",
184+ "argonaut-codecs",
185+ "argonaut-core",
186+ "arraybuffer-types",
187+ "arrays",
188+ "avar",
189+ "bifunctors",
190+ "catenable-lists",
191+ "console",
192+ "const",
193+ "contravariant",
194+ "control",
195+ "datetime",
196+ "distributive",
197+ "effect",
198+ "either",
199+ "enums",
200+ "exceptions",
201+ "exists",
202+ "exitcodes",
203+ "foldable-traversable",
204+ "foreign",
205+ "foreign-object",
206+ "fork",
207+ "free",
208+ "functions",
209+ "functors",
210+ "gen",
211+ "heterogeneous",
212+ "identity",
213+ "integers",
214+ "invariant",
215+ "js-bigints",
216+ "js-date",
217+ "js-promise",
218+ "js-promise-aff",
219+ "js-uri",
220+ "lazy",
221+ "lists",
222+ "literals",
223+ "maybe",
224+ "mmorph",
225+ "newtype",
226+ "node-buffer",
227+ "node-event-emitter",
228+ "node-fs",
229+ "node-path",
230+ "node-process",
231+ "node-streams",
232+ "nonempty",
233+ "now",
234+ "nullable",
235+ "numbers",
236+ "open-memoize",
237+ "optparse",
238+ "ordered-collections",
239+ "orders",
240+ "parallel",
241+ "partial",
242+ "pipes",
243+ "posix-types",
244+ "prelude",
245+ "profunctor",
246+ "random",
247+ "record",
248+ "record-studio",
249+ "ref-lifted",
250+ "refs",
251+ "routing-duplex",
252+ "safe-coerce",
253+ "spec",
254+ "spec-discovery",
255+ "spec-node",
256+ "st",
257+ "strings",
258+ "tailrec",
259+ "transformers",
260+ "tuples",
261+ "type-equality",
262+ "typelevel-prelude",
263+ "uncurried-transformers",
264+ "undefined-is-not-a-problem",
265+ "unfoldable",
266+ "unsafe-coerce",
267+ "untagged-union",
268+ "variant",
269+ "yoga-fastify",
270+ "yoga-fastify-om",
271+ "yoga-http-api",
272+ "yoga-json",
273+ "yoga-om",
274+ "yoga-om-layer",
275+ "yoga-om-strom",
276+ "yoga-options",
277+ "yoga-tree"
87 ]
278 ]
88 }
279 }
89 }
280 }
@@ -680,15 +871,28 @@
680 }
871 }
681 },
872 },
682 "extra_packages": {
873 "extra_packages": {
874+ "ref-lifted": {
875+ "path": "../purescript-ref-lifted"
876+ },
683− "yoga-fastify": "0.2.3",
877+ "yoga-fastify": {
878+ "path": "../purescript-yoga-fastify"
879+ },
684 "yoga-fastify-om": {
880 "yoga-fastify-om": {
685 "path": "../purescript-yoga-fastify-om"
881 "path": "../purescript-yoga-fastify-om"
686 },
882 },
687− "yoga-http-api": "0.1.6",
883+ "yoga-http-api": {
884+ "path": "../purescript-yoga-http-api"
885+ },
688 "yoga-json": "5.2.0",
886 "yoga-json": "5.2.0",
689− "yoga-om": "1.2.2",
887+ "yoga-om": {
888+ "path": "../purescript-yoga-om"
889+ },
690− "yoga-om-layer": "1.0.6",
890+ "yoga-om-layer": {
891+ "path": "../purescript-yoga-om-layer"
892+ },
691− "yoga-om-strom": "0.2.0",
893+ "yoga-om-strom": {
894+ "path": "../purescript-yoga-om-strom"
895+ },
692 "yoga-options": "0.1.0"
896 "yoga-options": "0.1.0"
693 }
897 }
694 },
898 },
@@ -714,6 +918,26 @@
714 "unsafe-coerce"
918 "unsafe-coerce"
715 ]
919 ]
716 },
920 },
921+ "aff-retry": {
922+ "type": "registry",
923+ "version": "2.0.0",
924+ "integrity": "sha256-rKZNcP4IKSyBFQBVzrCvE0ULoJt0N/uDLLJ0dPotr4k=",
925+ "dependencies": [
926+ "aff",
927+ "arrays",
928+ "datetime",
929+ "effect",
930+ "either",
931+ "exceptions",
932+ "integers",
933+ "maybe",
934+ "newtype",
935+ "numbers",
936+ "prelude",
937+ "random",
938+ "transformers"
939+ ]
940+ },
717 "ansi": {
941 "ansi": {
718 "type": "registry",
942 "type": "registry",
719 "version": "7.0.0",
943 "version": "7.0.0",
@@ -1637,6 +1861,16 @@
1637 "tuples"
1861 "tuples"
1638 ]
1862 ]
1639 },
1863 },
1864+ "random": {
1865+ "type": "registry",
1866+ "version": "6.0.0",
1867+ "integrity": "sha256-7sZRo6P9UIiXDZ9s2QL9T8h3wlIwLdQrR4+oZKOijQw=",
1868+ "dependencies": [
1869+ "effect",
1870+ "integers",
1871+ "prelude"
1872+ ]
1873+ },
1640 "record": {
1874 "record": {
1641 "type": "registry",
1875 "type": "registry",
1642 "version": "4.0.0",
1876 "version": "4.0.0",
@@ -1660,6 +1894,15 @@
1660 "unsafe-coerce"
1894 "unsafe-coerce"
1661 ]
1895 ]
1662 },
1896 },
1897+ "ref-lifted": {
1898+ "type": "local",
1899+ "path": "../purescript-ref-lifted",
1900+ "dependencies": [
1901+ "effect",
1902+ "prelude",
1903+ "refs"
1904+ ]
1905+ },
1663 "refs": {
1906 "refs": {
1664 "type": "registry",
1907 "type": "registry",
1665 "version": "6.0.0",
1908 "version": "6.0.0",
@@ -2012,9 +2255,8 @@
2012 ]
2255 ]
2013 },
2256 },
2014 "yoga-fastify": {
2257 "yoga-fastify": {
2015− "type": "registry",
2258+ "type": "local",
2016− "version": "0.2.3",
2259+ "path": "../purescript-yoga-fastify",
2017− "integrity": "sha256-150ocqfhYCc38HeChWbJXie0wn2bjKpORkZFQCRxICo=",
2018 "dependencies": [
2260 "dependencies": [
2019 "aff",
2261 "aff",
2020 "arrays",
2262 "arrays",
@@ -2054,6 +2296,7 @@
2054 "node-buffer",
2296 "node-buffer",
2055 "prelude",
2297 "prelude",
2056 "record",
2298 "record",
2299+ "ref-lifted",
2057 "refs",
2300 "refs",
2058 "routing-duplex",
2301 "routing-duplex",
2059 "transformers",
2302 "transformers",
@@ -2068,9 +2311,8 @@
2068 ]
2311 ]
2069 },
2312 },
2070 "yoga-http-api": {
2313 "yoga-http-api": {
2071− "type": "registry",
2314+ "type": "local",
2072− "version": "0.1.6",
2315+ "path": "../purescript-yoga-http-api",
2073− "integrity": "sha256-wI2aiM7S99gq7pPCBI8ijemMNV5vqjToLHVLr9nCtog=",
2074 "dependencies": [
2316 "dependencies": [
2075 "aff",
2317 "aff",
2076 "arrays",
2318 "arrays",
@@ -2133,11 +2375,11 @@
2133 ]
2375 ]
2134 },
2376 },
2135 "yoga-om": {
2377 "yoga-om": {
2136− "type": "registry",
2378+ "type": "local",
2137− "version": "1.2.2",
2379+ "path": "../purescript-yoga-om",
2138− "integrity": "sha256-uDV3/o8mUOTIgSEQFqIATPyladzL8BcjURvfS901hgI=",
2139 "dependencies": [
2380 "dependencies": [
2140 "aff",
2381 "aff",
2382+ "aff-retry",
2141 "avar",
2383 "avar",
2142 "console",
2384 "console",
2143 "control",
2385 "control",
@@ -2152,6 +2394,8 @@
2152 "prelude",
2394 "prelude",
2153 "record",
2395 "record",
2154 "record-studio",
2396 "record-studio",
2397+ "ref-lifted",
2398+ "refs",
2155 "tailrec",
2399 "tailrec",
2156 "transformers",
2400 "transformers",
2157 "tuples",
2401 "tuples",
@@ -2162,24 +2406,35 @@
2162 ]
2406 ]
2163 },
2407 },
2164 "yoga-om-layer": {
2408 "yoga-om-layer": {
2165− "type": "registry",
2409+ "type": "local",
2166− "version": "1.0.6",
2410+ "path": "../purescript-yoga-om-layer",
2167− "integrity": "sha256-RrCaTvkiMwCFarWKbu0Dkq+yFz4hDM7x18gZok2OxV4=",
2168 "dependencies": [
2411 "dependencies": [
2169 "aff",
2412 "aff",
2170− "newtype",
2413+ "aff-retry",
2414+ "arrays",
2415+ "effect",
2416+ "exceptions",
2417+ "foldable-traversable",
2418+ "maybe",
2419+ "nullable",
2420+ "ordered-collections",
2421+ "parallel",
2171 "prelude",
2422 "prelude",
2172 "record",
2423 "record",
2173 "record-studio",
2424 "record-studio",
2425+ "ref-lifted",
2426+ "refs",
2427+ "transformers",
2174 "type-equality",
2428 "type-equality",
2429+ "typelevel-prelude",
2175 "unsafe-coerce",
2430 "unsafe-coerce",
2431+ "variant",
2176 "yoga-om"
2432 "yoga-om"
2177 ]
2433 ]
2178 },
2434 },
2179 "yoga-om-strom": {
2435 "yoga-om-strom": {
2180− "type": "registry",
2436+ "type": "local",
2181− "version": "0.2.0",
2437+ "path": "../purescript-yoga-om-strom",
2182− "integrity": "sha256-WMrhLQ71RFObD4Vv/X5qef+uIkJ4zgCv/K8l986g1wE=",
2183 "dependencies": [
2438 "dependencies": [
2184 "aff",
2439 "aff",
2185 "arrays",
2440 "arrays",
@@ -2197,6 +2452,7 @@
2197 "maybe",
2452 "maybe",
2198 "now",
2453 "now",
2199 "prelude",
2454 "prelude",
2455+ "ref-lifted",
2200 "refs",
2456 "refs",
2201 "spec",
2457 "spec",
2202 "st",
2458 "st",
spago.yaml+17−8
@@ -4,18 +4,25 @@ workspace:
4 extraPackages:
4 extraPackages:
5 yoga-fastify-om:
5 yoga-fastify-om:
6 path: ../purescript-yoga-fastify-om
6 path: ../purescript-yoga-fastify-om
7− yoga-fastify: "0.2.3"
7+ yoga-fastify:
8+ path: ../purescript-yoga-fastify
8− yoga-http-api: "0.1.6"
9+ yoga-http-api:
10+ path: ../purescript-yoga-http-api
9− yoga-om: "1.2.2"
11+ yoga-om:
12+ path: ../purescript-yoga-om
10− yoga-om-layer: "1.0.6"
13+ yoga-om-layer:
14+ path: ../purescript-yoga-om-layer
11− yoga-om-strom: "0.2.0"
15+ yoga-om-strom:
16+ path: ../purescript-yoga-om-strom
12 yoga-options: "0.1.0"
17 yoga-options: "0.1.0"
13 yoga-json: "5.2.0"
18 yoga-json: "5.2.0"
19+ ref-lifted:
20+ path: ../purescript-ref-lifted
14
21
15package:
22package:
16 name: yoga-fetch-om
23 name: yoga-fetch-om
17 publish:
24 publish:
18− version: 0.5.2
25+ version: 0.6.0
19 license: MIT
26 license: MIT
20 location:
27 location:
21 githubOwner: rowtype-yoga
28 githubOwner: rowtype-yoga
@@ -44,14 +51,16 @@ package:
44 - typelevel-prelude: ">=7.0.0 <8.0.0"
51 - typelevel-prelude: ">=7.0.0 <8.0.0"
45 - unsafe-coerce: ">=6.0.0 <7.0.0"
52 - unsafe-coerce: ">=6.0.0 <7.0.0"
46 - variant: ">=8.0.0 <9.0.0"
53 - variant: ">=8.0.0 <9.0.0"
47− - yoga-http-api: ">=0.1.0 <0.2.0"
54+ - yoga-http-api: ">=0.1.0 <0.3.0"
48 - yoga-json: ">=5.2.0 <6.0.0"
55 - yoga-json: ">=5.2.0 <6.0.0"
56+ - arraybuffer-types: ">=3.0.2 <4.0.0"
49 - yoga-om: ">=1.2.2 <2.0.0"
57 - yoga-om: ">=1.2.2 <2.0.0"
58+ - yoga-om-strom: ">=0.2.0 <0.5.0"
50 test:
59 test:
51 main: Test.Main
60 main: Test.Main
52 dependencies:
61 dependencies:
53 - spec: ">=8.0.0 <9.0.0"
62 - spec: ">=8.0.0 <9.0.0"
54 - spec-discovery: ">=8.0.0 <9.0.0"
63 - spec-discovery: ">=8.0.0 <9.0.0"
55 - console: ">=6.0.0 <7.0.0"
64 - console: ">=6.0.0 <7.0.0"
56− - yoga-fastify-om: ">=0.1.0 <0.2.0"
65+ - yoga-fastify-om: ">=0.1.0 <0.4.0"
57 - refs: ">=6.0.0 <7.0.0"
66 - refs: ">=6.0.0 <7.0.0"
src/Yoga/Fetch/Om.purs+11−4
@@ -12,6 +12,8 @@ module Yoga.Fetch.Om
12 , toHeaders
12 , toHeaders
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
16+ , module Yoga.Fetch.Om.StreamDecode
15 , module Yoga.Fetch.Om.Simple
17 , module Yoga.Fetch.Om.Simple
16 ) where
18 ) where
17
19
@@ -30,16 +32,17 @@ import JS.Fetch.Headers as Headers
30import Type.Row.Homogeneous (class Homogeneous)
32import Type.Row.Homogeneous (class Homogeneous)
31import Yoga.Fetch.Om.BuildUrl (class BuildUrl, buildUrl)
33import Yoga.Fetch.Om.BuildUrl (class BuildUrl, buildUrl)
32import 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_)
35+import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
33import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
36import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
34import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractRequestHeaders)
37import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractRequestHeaders)
35−import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class SerializeBody, makeRequest, serializeBody)
38+import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class SerializeBody, class ContentType, makeRequest, serializeBody, contentType)
36import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
39import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
37import Yoga.Fetch.Om.SplitResponses (class SplitResponses)
40import Yoga.Fetch.Om.SplitResponses (class SplitResponses)
38import Yoga.Fetch.Om.Variant (class VariantOrValue, variantOrValue)
41import Yoga.Fetch.Om.Variant (class VariantOrValue, variantOrValue)
39import Yoga.HTTP.API.Path (Path, Root, Lit, Capture, PathCons, Param, QueryParams, Required, type (/), type (:), type (:?), class PathPattern)
42import Yoga.HTTP.API.Path (Path, Root, Lit, Capture, PathCons, Param, QueryParams, Required, type (/), type (:), type (:?), class PathPattern)
40−import Yoga.HTTP.API.Route (Route(..), GET, POST, PUT, DELETE, PATCH, Response(..), JSON, FormData, NoBody)
43+import Yoga.HTTP.API.Route (Route(..), GET, POST, PUT, DELETE, PATCH, Response(..), JSON, FormData, PlainText, Streaming, NoBody)
41import Yoga.HTTP.API.Route.Handler (class SegmentPathParams, class SegmentQueryParams)
44import Yoga.HTTP.API.Route.Handler (class SegmentPathParams, class SegmentQueryParams)
42−import Yoga.Om (Om, fromAff)
45+import Yoga.Om (class ToOm, Om, toOm)
43
46
44-- | Extract row type from Record type
47-- | Extract row type from Record type
45class RecordRow :: Type -> Row Type -> Constraint
48class RecordRow :: Type -> Row Type -> Constraint
@@ -72,6 +75,7 @@ instance
72 , BuildUrl segments pathParams queryParams
75 , BuildUrl segments pathParams queryParams
73 , MakeRequest method
76 , MakeRequest method
74 , SerializeBody body
77 , SerializeBody body
78+ , ContentType body
75 , SplitResponses response successRow routeErrors
79 , SplitResponses response successRow routeErrors
76 , ParseResponse routeErrors successRow
80 , ParseResponse routeErrors successRow
77 , RowToList successRow successRL
81 , RowToList successRow successRL
@@ -92,7 +96,7 @@ instance
92 impl pathQueryRec headersRec bodyVal = do
96 impl pathQueryRec headersRec bodyVal = do
93 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
97 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
94 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
98 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
95− fetchResp <- makeRequest (Proxy :: _ method) url hdrs (serializeBody bodyVal) # fromAff
99+ fetchResp <- makeRequest (Proxy :: _ method) url hdrs (contentType (Proxy :: _ body)) (serializeBody bodyVal) # toOm
96 variant <- parseResponse fetchResp :: Om (Record ()) routeErrors (Variant successRow)
100 variant <- parseResponse fetchResp :: Om (Record ()) routeErrors (Variant successRow)
97 variantOrValue (Proxy :: _ successRL) variant # pure
101 variantOrValue (Proxy :: _ successRL) variant # pure
98 where
102 where
@@ -120,6 +124,9 @@ client baseUrl = polymorphic (deriveClientImpl baseUrl (Proxy :: _ { | routesRow
120deriveClient :: forall @routesRow clientsRow polyClientsRow. DeriveClient routesRow clientsRow => String -> Record polyClientsRow
124deriveClient :: forall @routesRow clientsRow polyClientsRow. DeriveClient routesRow clientsRow => String -> Record polyClientsRow
121deriveClient baseUrl = polymorphic (deriveClientImpl baseUrl (Proxy :: _ { | routesRow }) :: Record clientsRow)
125deriveClient baseUrl = polymorphic (deriveClientImpl baseUrl (Proxy :: _ { | routesRow }) :: Record clientsRow)
122
126
127+plainText :: PlainText -> String
128+plainText = unsafeCoerce
129+
123polymorphic :: forall a b. a -> b
130polymorphic :: forall a b. a -> b
124polymorphic = unsafeCoerce
131polymorphic = unsafeCoerce
125
132
src/Yoga/Fetch/Om/BuildUrl.purs+4−1
@@ -46,7 +46,10 @@ instance
46 pattern = pathPattern segmentsProxy
46 pattern = pathPattern segmentsProxy
47 withBase = case baseUrl of
47 withBase = case baseUrl of
48 "" -> pattern
48 "" -> pattern
49− base -> base <> pattern
49+ base -> dropTrailingSlash base <> pattern
50+ dropTrailingSlash s = case String.stripSuffix (Pattern "/") s of
51+ Just s' -> dropTrailingSlash s'
52+ Nothing -> s
50 withPathParams = substitutePathParamsImpl (Proxy :: _ pathParams) pathParamsRec withBase
53 withPathParams = substitutePathParamsImpl (Proxy :: _ pathParams) pathParamsRec withBase
51 withQueryParams = appendQueryParamsImpl (Proxy :: _ queryParams) queryParamsRec withPathParams
54 withQueryParams = appendQueryParamsImpl (Proxy :: _ queryParams) queryParamsRec withPathParams
52
55
src/Yoga/Fetch/Om/ExtractParams.purs+2−1
@@ -9,7 +9,7 @@ module Yoga.Fetch.Om.ExtractParams
9import Data.Unit (Unit)
9import Data.Unit (Unit)
10import Prim.RowList (class RowToList, RowList)
10import Prim.RowList (class RowToList, RowList)
11import Prim.RowList as RL
11import Prim.RowList as RL
12−import Yoga.HTTP.API.Route.Encoding (JSON, FormData, NoBody)
12+import Yoga.HTTP.API.Route.Encoding (JSON, FormData, NoBody, PlainText)
13import Yoga.HTTP.API.Route.Handler (Request)
13import Yoga.HTTP.API.Route.Handler (Request)
14
14
15class ExtractRequestBody :: Type -> Type -> Constraint
15class ExtractRequestBody :: Type -> Type -> Constraint
@@ -50,6 +50,7 @@ class UnwrapEncoding encoding body | encoding -> body
50instance UnwrapEncoding (JSON ty) ty
50instance UnwrapEncoding (JSON ty) ty
51instance UnwrapEncoding (FormData ty) ty
51instance UnwrapEncoding (FormData ty) ty
52instance UnwrapEncoding NoBody Unit
52instance UnwrapEncoding NoBody Unit
53+instance UnwrapEncoding PlainText String
53
54
54class FindHeaders :: RowList Type -> Row Type -> Constraint
55class FindHeaders :: RowList Type -> Row Type -> Constraint
55class FindHeaders rl headers | rl -> headers
56class FindHeaders rl headers | rl -> headers
src/Yoga/Fetch/Om/MakeRequest.purs+19−3
@@ -4,6 +4,8 @@ module Yoga.Fetch.Om.MakeRequest
4 , makeRequest
4 , makeRequest
5 , class SerializeBody
5 , class SerializeBody
6 , serializeBody
6 , serializeBody
7+ , class ContentType
8+ , contentType
7 ) where
9 ) where
8
10
9import Prelude
11import Prelude
@@ -58,9 +60,10 @@ makeRequest
58 => Proxy method
60 => Proxy method
59 -> String
61 -> String
60 -> Headers
62 -> Headers
63+ -> String
61 -> Maybe String
64 -> Maybe String
62 -> Aff FetchResponse.Response
65 -> Aff FetchResponse.Response
63−makeRequest proxy url customHeaders maybeBody = do
66+makeRequest proxy url customHeaders defaultContentType maybeBody = do
64 request <- Request.new url options # liftEffect
67 request <- Request.new url options # liftEffect
65 Promise.toAffE $ Fetch.fetch request
68 Promise.toAffE $ Fetch.fetch request
66 where
69 where
@@ -70,7 +73,7 @@ makeRequest proxy url customHeaders maybeBody = do
70 contentTypeArr = case maybeBody of
73 contentTypeArr = case maybeBody of
71 Nothing -> []
74 Nothing -> []
72 Just _ | hasContentType -> []
75 Just _ | hasContentType -> []
73− Just _ -> [ "Content-Type" /\ "application/json" ]
76+ Just _ -> [ "Content-Type" /\ defaultContentType ]
74 allHeaders = Headers.fromFoldable (contentTypeArr <> customArr)
77 allHeaders = Headers.fromFoldable (contentTypeArr <> customArr)
75 body = case maybeBody of
78 body = case maybeBody of
76 Nothing -> Body.empty
79 Nothing -> Body.empty
@@ -88,11 +91,24 @@ makeRequest proxy url customHeaders maybeBody = do
88 , cache: Cache.Default
91 , cache: Cache.Default
89 }
92 }
90
93
94+class ContentType :: Type -> Constraint
95+class ContentType body where
96+ contentType :: Proxy body -> String
97+
98+instance ContentType String where
99+ contentType _ = "text/plain"
100+else instance ContentType Unit where
101+ contentType _ = "application/json"
102+else instance ContentType body where
103+ contentType _ = "application/json"
104+
91class SerializeBody :: Type -> Constraint
105class SerializeBody :: Type -> Constraint
92class SerializeBody body where
106class SerializeBody body where
93 serializeBody :: body -> Maybe String
107 serializeBody :: body -> Maybe String
94
108
95−instance SerializeBody Unit where
109+instance SerializeBody String where
110+ serializeBody s = Just s
111+else instance SerializeBody Unit where
96 serializeBody _ = Nothing
112 serializeBody _ = Nothing
97else instance WriteForeign body => SerializeBody body where
113else instance WriteForeign body => SerializeBody body where
98 serializeBody b = Just (writeJSON b)
114 serializeBody b = Just (writeJSON b)
src/Yoga/Fetch/Om/ParseResponse.purs+70−12
@@ -10,11 +10,13 @@ module Yoga.Fetch.Om.ParseResponse
10import Prelude
10import Prelude
11
11
12import Control.Monad.Error.Class (throwError)
12import Control.Monad.Error.Class (throwError)
13+import Data.Either (Either(..))
13import Data.Symbol (class IsSymbol)
14import Data.Symbol (class IsSymbol)
14import Data.Variant (Variant)
15import Data.Variant (Variant)
15import Data.Variant as Variant
16import Data.Variant as Variant
16import Effect.Aff (Aff)
17import Effect.Aff (Aff)
17import Effect.Aff as Aff
18import Effect.Aff as Aff
19+import Effect.Class (liftEffect)
18import Effect.Exception as Exception
20import Effect.Exception as Exception
19import JS.Fetch.Response (Response) as FetchResponse
21import JS.Fetch.Response (Response) as FetchResponse
20import JS.Fetch.Response as Fetch
22import JS.Fetch.Response as Fetch
@@ -23,12 +25,16 @@ import Prim.RowList (class RowToList, RowList)
23import Prim.RowList as RL
25import Prim.RowList as RL
24import Promise.Aff as Promise
26import Promise.Aff as Promise
25import Type.Proxy (Proxy(..))
27import Type.Proxy (Proxy(..))
28+import Unsafe.Coerce (unsafeCoerce)
29+import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
30+import Yoga.HTTP.API.Route.Encoding (PlainText, Streaming)
26import Yoga.HTTP.API.Route.StatusCode (class StatusCodeMap, statusCodeFor, StatusCode(..))
31import Yoga.HTTP.API.Route.StatusCode (class StatusCodeMap, statusCodeFor, StatusCode(..))
27−import Data.Either (Either(..))
28import Yoga.JSON (class ReadForeign, readJSON)
32import Yoga.JSON (class ReadForeign, readJSON)
29import Yoga.JSON.Error (withStringErrors)
33import Yoga.JSON.Error (withStringErrors)
30−import Yoga.Om (Om, fromAff)
34+import Yoga.Om (class ToOm, Om, toOm)
31import Yoga.Om.Error (Exception)
35import Yoga.Om.Error (Exception)
36+import Yoga.Om.Strom (Strom)
37+import Yoga.Om.Strom.WebStream as WebStream
32
38
33class ParseResponse (errorRow :: Row Type) (successRow :: Row Type) where
39class ParseResponse (errorRow :: Row Type) (successRow :: Row Type) where
34 parseResponse :: FetchResponse.Response -> Om {} errorRow (Variant successRow)
40 parseResponse :: FetchResponse.Response -> Om {} errorRow (Variant successRow)
@@ -42,21 +48,55 @@ instance
42 ParseResponse errorRow successRow where
48 ParseResponse errorRow successRow where
43 parseResponse fetchResp = do
49 parseResponse fetchResp = do
44 let status = Fetch.status fetchResp
50 let status = Fetch.status fetchResp
45− jsonText <- Promise.toAffE (Fetch.text fetchResp) # fromAff
46 if status >= 200 && status < 300 then
51 if status >= 200 && status < 300 then
47− parseSuccessRL (Proxy :: _ successRL) status jsonText # fromAff
52+ parseSuccessRL (Proxy :: _ successRL) status fetchResp # toOm
48− else if status >= 400 then
53+ else if status >= 400 then do
54+ errorText <- Promise.toAffE (Fetch.text fetchResp) # toOm
49− parseErrorRL (Proxy :: _ errorRL) (Proxy :: _ errorRow) status jsonText
55+ parseErrorRL (Proxy :: _ errorRL) (Proxy :: _ errorRow) status errorText
50 else
56 else
51 throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected HTTP status code: " <> show status)))
57 throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected HTTP status code: " <> show status)))
52
58
53class ParseSuccessRL (rl :: RowList Type) (successRow :: Row Type) | rl -> successRow where
59class ParseSuccessRL (rl :: RowList Type) (successRow :: Row Type) | rl -> successRow where
54− parseSuccessRL :: Proxy rl -> Int -> String -> Aff (Variant successRow)
60+ parseSuccessRL :: Proxy rl -> Int -> FetchResponse.Response -> Aff (Variant successRow)
55
61
56instance ParseSuccessRL RL.Nil successRow where
62instance ParseSuccessRL RL.Nil successRow where
57 parseSuccessRL _ status _ =
63 parseSuccessRL _ status _ =
58 Aff.throwError (Exception.error ("Unexpected success status code: " <> show status))
64 Aff.throwError (Exception.error ("Unexpected success status code: " <> show status))
59
65
66+else instance
67+ ( IsSymbol label
68+ , StatusCodeMap label
69+ , Row.Cons label PlainText tailRow successRow
70+ , Row.Lacks label tailRow
71+ , ParseSuccessRL tailRL successRow
72+ ) =>
73+ ParseSuccessRL (RL.Cons label PlainText tailRL) successRow where
74+ parseSuccessRL _ actualStatus fetchResp = do
75+ let StatusCode expected = statusCodeFor (Proxy :: _ label)
76+ if actualStatus == expected then do
77+ text <- liftEffect (Fetch.text fetchResp) >>= Promise.toAff
78+ pure (Variant.inj (Proxy :: _ label) (unsafeCoerce text))
79+ else
80+ parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
81+
82+else instance
83+ ( IsSymbol label
84+ , StatusCodeMap label
85+ , StreamDecode a
86+ , Row.Cons label (Streaming a) tailRow successRow
87+ , Row.Lacks label tailRow
88+ , ParseSuccessRL tailRL successRow
89+ ) =>
90+ ParseSuccessRL (RL.Cons label (Streaming a) tailRL) successRow where
91+ parseSuccessRL _ actualStatus fetchResp = do
92+ let StatusCode expected = statusCodeFor (Proxy :: _ label)
93+ if actualStatus == expected then do
94+ webStream <- liftEffect (Fetch.body fetchResp)
95+ let strom = decodeStream (WebStream.fromReadableStream (unsafeCoerce webStream)) :: Strom {} () a
96+ pure (Variant.inj (Proxy :: _ label) (unsafeCoerce strom))
97+ else
98+ parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
99+
60else instance
100else instance
61 ( IsSymbol label
101 ( IsSymbol label
62 , StatusCodeMap label
102 , StatusCodeMap label
@@ -66,13 +106,15 @@ else instance
66 , ParseSuccessRL tailRL successRow
106 , ParseSuccessRL tailRL successRow
67 ) =>
107 ) =>
68 ParseSuccessRL (RL.Cons label body tailRL) successRow where
108 ParseSuccessRL (RL.Cons label body tailRL) successRow where
69− parseSuccessRL _ actualStatus jsonText = do
109+ parseSuccessRL _ actualStatus fetchResp = do
70 let StatusCode expected = statusCodeFor (Proxy :: _ label)
110 let StatusCode expected = statusCodeFor (Proxy :: _ label)
111+ if actualStatus == expected then do
112+ jsonText <- liftEffect (Fetch.text fetchResp) >>= Promise.toAff
71− if actualStatus == expected then case withStringErrors (readJSON jsonText) of
113+ case withStringErrors (readJSON jsonText) of
72− Left errs -> Aff.throwError (Exception.error ("Failed to parse response (status " <> show actualStatus <> "):\n" <> errs))
114+ Left errs -> Aff.throwError (Exception.error ("Failed to parse response (status " <> show actualStatus <> "):\n" <> errs))
73− Right parsed -> pure (Variant.inj (Proxy :: _ label) parsed)
115+ Right parsed -> pure (Variant.inj (Proxy :: _ label) parsed)
74 else
116 else
75− parseSuccessRL (Proxy :: _ tailRL) actualStatus jsonText
117+ parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
76
118
77class ParseErrorRL (rl :: RowList Type) (errorRow :: Row Type) | rl -> errorRow where
119class ParseErrorRL (rl :: RowList Type) (errorRow :: Row Type) | rl -> errorRow where
78 parseErrorRL
120 parseErrorRL
@@ -87,6 +129,22 @@ instance ParseErrorRL RL.Nil errorRow where
87 parseErrorRL _ _ status _ =
129 parseErrorRL _ _ status _ =
88 throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected error status code: " <> show status)))
130 throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected error status code: " <> show status)))
89
131
132+else instance
133+ ( IsSymbol label
134+ , StatusCodeMap label
135+ , Row.Cons label PlainText tailRow errorRow
136+ , Row.Lacks label tailRow
137+ , Row.Cons label PlainText tailExc (Exception errorRow)
138+ , ParseErrorRL tailRL errorRow
139+ ) =>
140+ ParseErrorRL (RL.Cons label PlainText tailRL) errorRow where
141+ parseErrorRL _ errorProxy actualStatus text = do
142+ let StatusCode expected = statusCodeFor (Proxy :: _ label)
143+ if actualStatus == expected then
144+ throwError (Variant.inj (Proxy :: _ label) (unsafeCoerce text))
145+ else
146+ parseErrorRL (Proxy :: _ tailRL) errorProxy actualStatus text
147+
90else instance
148else instance
91 ( IsSymbol label
149 ( IsSymbol label
92 , StatusCodeMap label
150 , StatusCodeMap label
src/Yoga/Fetch/Om/Simple.purs+3−3
@@ -46,7 +46,7 @@ import Type.Row.Homogeneous (class Homogeneous)
46-- PlainText removed, using local phantom type
46-- PlainText removed, using local phantom type
47import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
47import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
48import Yoga.JSON.Error (withStringErrors)
48import Yoga.JSON.Error (withStringErrors)
49−import Yoga.Om (Om, fromAff, throw)
49+import Yoga.Om (class ToOm, Om, toOm, throw)
50
50
51type FetchError = { status :: Int, body :: String }
51type FetchError = { status :: Int, body :: String }
52
52
@@ -74,8 +74,8 @@ simpleFetch
74 -> Maybe String
74 -> Maybe String
75 -> Om ctx (fetchError :: FetchError | err) (FetchResponse result)
75 -> Om ctx (fetchError :: FetchError | err) (FetchResponse result)
76simpleFetch method headers url maybeBody = do
76simpleFetch method headers url maybeBody = do
77− resp <- doFetch # fromAff
77+ resp <- doFetch # toOm
78− respText <- Promise.toAffE (Resp.text resp) # fromAff
78+ respText <- Promise.toAffE (Resp.text resp) # toOm
79 let status = Resp.status resp
79 let status = Resp.status resp
80 let respHeaders = Resp.headers resp
80 let respHeaders = Resp.headers resp
81 if status >= 200 && status < 300 then
81 if status >= 200 && status < 300 then
src/Yoga/Fetch/Om/StreamDecode.jsnew file+1
@@ -0,0 +1 @@
1+export const decodeUtf8 = (uint8array) => new TextDecoder().decode(uint8array);
src/Yoga/Fetch/Om/StreamDecode.pursnew file+20
@@ -0,0 +1,20 @@
1+module Yoga.Fetch.Om.StreamDecode
2+ ( class StreamDecode
3+ , decodeStream
4+ ) where
5+
6+import Prelude
7+import Data.ArrayBuffer.Types (Uint8Array)
8+import Yoga.Om.Strom (Strom)
9+import Yoga.Om.Strom as Strom
10+
11+class StreamDecode a where
12+ decodeStream :: Strom {} () Uint8Array -> Strom {} () a
13+
14+instance StreamDecode Uint8Array where
15+ decodeStream = identity
16+
17+else instance StreamDecode String where
18+ decodeStream = Strom.mapStrom decodeUtf8
19+
20+foreign import decodeUtf8 :: Uint8Array -> String
test/Polymorphic.Spec.purs+1−1
@@ -62,7 +62,7 @@ withServer test = bracket acquire release (\_ -> test unit)
62
62
63 release = F.close
63 release = F.close
64
64
65−getUserHandler :: Handler GetUserRoute
65+getUserHandler :: Handler GetUserRoute ()
66getUserHandler = handle do
66getUserHandler = handle do
67 { path } <- ask
67 { path } <- ask
68 respond @"ok" { id: path.id, name: "User " <> show path.id }
68 respond @"ok" { id: path.id, name: "User " <> show path.id }