elm-pages 3.0.0-beta.12 → 3.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/codegen/elm-pages-codegen.js +1496 -1126
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +151 -39
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/dead-code-review/elm.json +3 -2
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +58 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +45 -29
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +45 -4
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/review/elm.json +3 -3
- package/generator/src/RouteBuilder.elm +66 -52
- package/generator/src/SharedTemplate.elm +3 -2
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/build.js +6 -6
- package/generator/src/cli.js +12 -7
- package/generator/src/compatibility-key.js +1 -1
- package/generator/src/dev-server.js +7 -7
- package/generator/src/render-test.js +109 -0
- package/generator/src/render.js +77 -51
- package/generator/src/request-cache.js +149 -158
- package/generator/template/app/Api.elm +2 -2
- package/generator/template/app/Route/Index.elm +3 -3
- package/generator/template/app/Shared.elm +3 -3
- package/generator/template/app/Site.elm +3 -3
- package/package.json +11 -11
- package/src/ApiRoute.elm +63 -57
- package/src/BackendTask/Env.elm +87 -0
- package/src/{DataSource → BackendTask}/File.elm +89 -43
- package/src/{DataSource → BackendTask}/Glob.elm +134 -125
- package/src/BackendTask/Http.elm +637 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +28 -0
- package/src/BackendTask/Port.elm +202 -0
- package/src/{DataSource.elm → BackendTask.elm} +223 -207
- package/src/Exception.elm +37 -0
- package/src/Form.elm +20 -20
- package/src/Head.elm +7 -7
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/Generate.elm +35 -63
- package/src/Pages/Internal/Platform/Cli.elm +422 -731
- package/src/Pages/Internal/Platform/Cli.elm.bak +22 -22
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/Effect.elm +0 -1
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +53 -113
- package/src/Pages/Internal/Platform/StaticResponses.elm +72 -256
- package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -4
- package/src/Pages/Internal/Platform.elm +25 -31
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +5 -4
- package/src/Pages/ProgramConfig.elm +8 -7
- package/src/Pages/Script.elm +34 -25
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +37 -90
- package/src/RequestsAndPending.elm +8 -19
- package/src/Server/Request.elm +14 -14
- package/src/Server/Session.elm +34 -34
- package/src/Server/SetCookie.elm +1 -1
- package/src/DataSource/Env.elm +0 -62
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
module BackendTask.Http exposing
|
|
2
|
+
( get, getJson
|
|
3
|
+
, post
|
|
4
|
+
, Expect, expectString, expectJson, expectBytes, expectWhatever
|
|
5
|
+
, Error(..)
|
|
6
|
+
, request
|
|
7
|
+
, Body, emptyBody, stringBody, jsonBody, bytesBody
|
|
8
|
+
, getWithOptions
|
|
9
|
+
, CacheStrategy(..)
|
|
10
|
+
, withMetadata, Metadata
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
{-| `BackendTask.Http` requests are an alternative to doing Elm HTTP requests the traditional way using the `elm/http` package.
|
|
14
|
+
|
|
15
|
+
The key differences are:
|
|
16
|
+
|
|
17
|
+
- `BackendTask.Http.Request`s are performed once at build time (`Http.Request`s are performed at runtime, at whenever point you perform them)
|
|
18
|
+
- `BackendTask.Http.Request`s have a built-in `BackendTask.andThen` that allows you to perform follow-up requests without using tasks
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Scenarios where BackendTask.Http is a good fit
|
|
22
|
+
|
|
23
|
+
If you need data that is refreshed often you may want to do a traditional HTTP request with the `elm/http` package.
|
|
24
|
+
The kinds of situations that are served well by static HTTP are with data that updates moderately frequently or infrequently (or never).
|
|
25
|
+
A common pattern is to trigger a new build when data changes. Many JAMstack services
|
|
26
|
+
allow you to send a WebHook to your host (for example, Netlify is a good static file host that supports triggering builds with webhooks). So
|
|
27
|
+
you may want to have your site rebuild everytime your calendar feed has an event added, or whenever a page or article is added
|
|
28
|
+
or updated on a CMS service like Contentful.
|
|
29
|
+
|
|
30
|
+
In scenarios like this, you can serve data that is just as up-to-date as it would be using `elm/http`, but you get the performance
|
|
31
|
+
gains of using `BackendTask.Http.Request`s as well as the simplicity and robustness that comes with it. Read more about these benefits
|
|
32
|
+
in [this article introducing BackendTask.Http requests and some concepts around it](https://elm-pages.com/blog/static-http).
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Scenarios where BackendTask.Http is not a good fit
|
|
36
|
+
|
|
37
|
+
- Data that is specific to the logged-in user
|
|
38
|
+
- Data that needs to be the very latest and changes often (for example, sports scores)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Making a Request
|
|
42
|
+
|
|
43
|
+
@docs get, getJson
|
|
44
|
+
|
|
45
|
+
@docs post
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Decoding Request Body
|
|
49
|
+
|
|
50
|
+
@docs Expect, expectString, expectJson, expectBytes, expectWhatever
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Error Handling
|
|
54
|
+
|
|
55
|
+
@docs Error
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## General Requests
|
|
59
|
+
|
|
60
|
+
@docs request
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Building a BackendTask.Http Request Body
|
|
64
|
+
|
|
65
|
+
The way you build a body is analogous to the `elm/http` package. Currently, only `emptyBody` and
|
|
66
|
+
`stringBody` are supported. If you have a use case that calls for a different body type, please open a Github issue
|
|
67
|
+
and describe your use case!
|
|
68
|
+
|
|
69
|
+
@docs Body, emptyBody, stringBody, jsonBody, bytesBody
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Caching Options
|
|
73
|
+
|
|
74
|
+
`elm-pages` performs GET requests using a local HTTP cache by default.
|
|
75
|
+
|
|
76
|
+
@docs getWithOptions
|
|
77
|
+
|
|
78
|
+
@docs CacheStrategy
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
## Including HTTP Metadata
|
|
82
|
+
|
|
83
|
+
@docs withMetadata, Metadata
|
|
84
|
+
|
|
85
|
+
-}
|
|
86
|
+
|
|
87
|
+
import BackendTask exposing (BackendTask)
|
|
88
|
+
import Base64
|
|
89
|
+
import Bytes exposing (Bytes)
|
|
90
|
+
import Bytes.Decode
|
|
91
|
+
import Dict exposing (Dict)
|
|
92
|
+
import Exception exposing (Catchable)
|
|
93
|
+
import Json.Decode
|
|
94
|
+
import Json.Encode as Encode
|
|
95
|
+
import Pages.Internal.StaticHttpBody as Body
|
|
96
|
+
import Pages.StaticHttp.Request as HashRequest
|
|
97
|
+
import Pages.StaticHttpRequest exposing (RawRequest(..))
|
|
98
|
+
import RequestsAndPending
|
|
99
|
+
import TerminalText
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
{-| Build an empty body for a BackendTask.Http request. See [elm/http's `Http.emptyBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#emptyBody).
|
|
103
|
+
-}
|
|
104
|
+
emptyBody : Body
|
|
105
|
+
emptyBody =
|
|
106
|
+
Body.EmptyBody
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
{-| Build a body from `Bytes` for a BackendTask.Http request. See [elm/http's `Http.bytesBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#bytesBody).
|
|
110
|
+
-}
|
|
111
|
+
bytesBody : String -> Bytes -> Body
|
|
112
|
+
bytesBody =
|
|
113
|
+
Body.BytesBody
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
{-| Builds a string body for a BackendTask.Http request. See [elm/http's `Http.stringBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#stringBody).
|
|
117
|
+
|
|
118
|
+
Note from the `elm/http` docs:
|
|
119
|
+
|
|
120
|
+
> The first argument is a [MIME type](https://en.wikipedia.org/wiki/Media_type) of the body. Some servers are strict about this!
|
|
121
|
+
|
|
122
|
+
-}
|
|
123
|
+
stringBody : String -> String -> Body
|
|
124
|
+
stringBody contentType content =
|
|
125
|
+
Body.StringBody contentType content
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
{-| Builds a JSON body for a BackendTask.Http request. See [elm/http's `Http.jsonBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#jsonBody).
|
|
129
|
+
-}
|
|
130
|
+
jsonBody : Encode.Value -> Body
|
|
131
|
+
jsonBody content =
|
|
132
|
+
Body.JsonBody content
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
{-| A body for a BackendTask.Http request.
|
|
136
|
+
-}
|
|
137
|
+
type alias Body =
|
|
138
|
+
Body.Body
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
{-| A simplified helper around [`BackendTask.Http.request`](#request), which builds up a BackendTask.Http GET request.
|
|
142
|
+
|
|
143
|
+
import BackendTask
|
|
144
|
+
import BackendTask.Http
|
|
145
|
+
import Json.Decode as Decode exposing (Decoder)
|
|
146
|
+
|
|
147
|
+
getRequest : BackendTask Int
|
|
148
|
+
getRequest =
|
|
149
|
+
BackendTask.Http.get
|
|
150
|
+
"https://api.github.com/repos/dillonkearns/elm-pages"
|
|
151
|
+
(Decode.field "stargazers_count" Decode.int)
|
|
152
|
+
|
|
153
|
+
-}
|
|
154
|
+
getJson :
|
|
155
|
+
String
|
|
156
|
+
-> Json.Decode.Decoder a
|
|
157
|
+
-> BackendTask (Catchable Error) a
|
|
158
|
+
getJson url decoder =
|
|
159
|
+
getWithOptions
|
|
160
|
+
{ url = url
|
|
161
|
+
, expect = expectJson decoder
|
|
162
|
+
, headers = []
|
|
163
|
+
, timeoutInMs = Nothing
|
|
164
|
+
, retries = Nothing
|
|
165
|
+
, cacheStrategy = Nothing
|
|
166
|
+
, cachePath = Nothing
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
{-| -}
|
|
171
|
+
get :
|
|
172
|
+
String
|
|
173
|
+
-> Expect a
|
|
174
|
+
-> BackendTask (Catchable Error) a
|
|
175
|
+
get url expect =
|
|
176
|
+
getWithOptions
|
|
177
|
+
{ url = url
|
|
178
|
+
, expect = expect
|
|
179
|
+
, headers = []
|
|
180
|
+
, timeoutInMs = Nothing
|
|
181
|
+
, retries = Nothing
|
|
182
|
+
, cacheStrategy = Nothing
|
|
183
|
+
, cachePath = Nothing
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
{-| Perform a GET request, with some additional options for the HTTP request, including options for caching behavior.
|
|
188
|
+
-}
|
|
189
|
+
getWithOptions :
|
|
190
|
+
{ url : String
|
|
191
|
+
, expect : Expect a
|
|
192
|
+
, headers : List ( String, String )
|
|
193
|
+
, cacheStrategy : Maybe CacheStrategy
|
|
194
|
+
, retries : Maybe Int
|
|
195
|
+
, timeoutInMs : Maybe Int
|
|
196
|
+
, cachePath : Maybe String
|
|
197
|
+
}
|
|
198
|
+
-> BackendTask (Catchable Error) a
|
|
199
|
+
getWithOptions request__ =
|
|
200
|
+
let
|
|
201
|
+
request_ : HashRequest.Request
|
|
202
|
+
request_ =
|
|
203
|
+
{ url = request__.url
|
|
204
|
+
, headers = request__.headers
|
|
205
|
+
, body = emptyBody
|
|
206
|
+
, method = "GET"
|
|
207
|
+
, cacheOptions =
|
|
208
|
+
{ cacheStrategy = request__.cacheStrategy
|
|
209
|
+
, retries = request__.retries
|
|
210
|
+
, timeoutInMs = request__.timeoutInMs
|
|
211
|
+
, cachePath = request__.cachePath
|
|
212
|
+
}
|
|
213
|
+
|> encodeOptions
|
|
214
|
+
|> Just
|
|
215
|
+
}
|
|
216
|
+
in
|
|
217
|
+
requestRaw request_ request__.expect
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
{-| -}
|
|
221
|
+
post :
|
|
222
|
+
String
|
|
223
|
+
-> Body
|
|
224
|
+
-> Expect a
|
|
225
|
+
-> BackendTask (Catchable Error) a
|
|
226
|
+
post url body expect =
|
|
227
|
+
request
|
|
228
|
+
{ url = url
|
|
229
|
+
, method = "POST"
|
|
230
|
+
, headers = []
|
|
231
|
+
, body = body
|
|
232
|
+
, retries = Nothing
|
|
233
|
+
, timeoutInMs = Nothing
|
|
234
|
+
}
|
|
235
|
+
expect
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
{-| Analogous to the `Expect` type in the `elm/http` package. This represents how you will process the data that comes
|
|
239
|
+
back in your BackendTask.Http request.
|
|
240
|
+
|
|
241
|
+
You can derive `ExpectJson` from `ExpectString`. Or you could build your own helper to process the String
|
|
242
|
+
as XML, for example, or give an `elm-pages` build error if the response can't be parsed as XML.
|
|
243
|
+
|
|
244
|
+
-}
|
|
245
|
+
type Expect value
|
|
246
|
+
= ExpectJson (Json.Decode.Decoder value)
|
|
247
|
+
| ExpectString (String -> value)
|
|
248
|
+
| ExpectBytes (Bytes.Decode.Decoder value)
|
|
249
|
+
| ExpectWhatever value
|
|
250
|
+
| ExpectMetadata (Metadata -> Expect value)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
{-| Gives the HTTP response body as a raw String.
|
|
254
|
+
|
|
255
|
+
import BackendTask exposing (BackendTask)
|
|
256
|
+
import BackendTask.Http
|
|
257
|
+
|
|
258
|
+
request : BackendTask String
|
|
259
|
+
request =
|
|
260
|
+
BackendTask.Http.request
|
|
261
|
+
{ url = "https://example.com/file.txt"
|
|
262
|
+
, method = "GET"
|
|
263
|
+
, headers = []
|
|
264
|
+
, body = BackendTask.Http.emptyBody
|
|
265
|
+
}
|
|
266
|
+
BackendTask.Http.expectString
|
|
267
|
+
|
|
268
|
+
-}
|
|
269
|
+
expectString : Expect String
|
|
270
|
+
expectString =
|
|
271
|
+
ExpectString identity
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
{-| Handle the incoming response as JSON and don't optimize the asset and strip out unused values.
|
|
275
|
+
Be sure to use the `BackendTask.Http.request` function if you want an optimized request that
|
|
276
|
+
strips out unused JSON to optimize your asset size. This function makes sense to use for things like a GraphQL request
|
|
277
|
+
where the JSON payload is already trimmed down to the data you explicitly requested.
|
|
278
|
+
|
|
279
|
+
If the function you pass to `expectString` yields an `Err`, then you will get a build error that will
|
|
280
|
+
fail your `elm-pages` build and print out the String from the `Err`.
|
|
281
|
+
|
|
282
|
+
-}
|
|
283
|
+
expectJson : Json.Decode.Decoder value -> Expect value
|
|
284
|
+
expectJson =
|
|
285
|
+
ExpectJson
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
{-| -}
|
|
289
|
+
withMetadata : (Metadata -> value -> combined) -> Expect value -> Expect combined
|
|
290
|
+
withMetadata combineFn originalExpect =
|
|
291
|
+
-- known-unoptimized-recursion
|
|
292
|
+
case originalExpect of
|
|
293
|
+
ExpectJson jsonDecoder ->
|
|
294
|
+
ExpectMetadata (\metadata -> ExpectJson (jsonDecoder |> Json.Decode.map (combineFn metadata)))
|
|
295
|
+
|
|
296
|
+
ExpectString stringToValue ->
|
|
297
|
+
ExpectMetadata
|
|
298
|
+
(\metadata ->
|
|
299
|
+
ExpectString (\string -> string |> stringToValue |> combineFn metadata)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
ExpectBytes bytesDecoder ->
|
|
303
|
+
ExpectMetadata (\metadata -> ExpectBytes (bytesDecoder |> Bytes.Decode.map (combineFn metadata)))
|
|
304
|
+
|
|
305
|
+
ExpectWhatever value ->
|
|
306
|
+
ExpectMetadata (\metadata -> ExpectWhatever (combineFn metadata value))
|
|
307
|
+
|
|
308
|
+
ExpectMetadata metadataToExpect ->
|
|
309
|
+
ExpectMetadata (\metadata -> withMetadata combineFn (metadataToExpect metadata))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
{-| -}
|
|
313
|
+
expectBytes : Bytes.Decode.Decoder value -> Expect value
|
|
314
|
+
expectBytes =
|
|
315
|
+
ExpectBytes
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
{-| -}
|
|
319
|
+
expectWhatever : value -> Expect value
|
|
320
|
+
expectWhatever =
|
|
321
|
+
ExpectWhatever
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
expectToString : Expect a -> String
|
|
325
|
+
expectToString expect =
|
|
326
|
+
-- known-unoptimized-recursion
|
|
327
|
+
case expect of
|
|
328
|
+
ExpectJson _ ->
|
|
329
|
+
"ExpectJson"
|
|
330
|
+
|
|
331
|
+
ExpectString _ ->
|
|
332
|
+
"ExpectString"
|
|
333
|
+
|
|
334
|
+
ExpectBytes _ ->
|
|
335
|
+
"ExpectBytes"
|
|
336
|
+
|
|
337
|
+
ExpectWhatever _ ->
|
|
338
|
+
"ExpectWhatever"
|
|
339
|
+
|
|
340
|
+
ExpectMetadata toExpect ->
|
|
341
|
+
-- It's safe to call this with fake metadata to get the kind of Expect because the exposed
|
|
342
|
+
-- API, `withMetadata`, will never change the type of Expect it returns based on the metadata, it simply
|
|
343
|
+
-- wraps the Expect with the additional Metadata.
|
|
344
|
+
-- It's important not to expose the raw `ExpectMetadata` constructor however because that would break that guarantee.
|
|
345
|
+
toExpect
|
|
346
|
+
{ url = ""
|
|
347
|
+
, statusCode = 123
|
|
348
|
+
, statusText = ""
|
|
349
|
+
, headers = Dict.empty
|
|
350
|
+
}
|
|
351
|
+
|> expectToString
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
{-| -}
|
|
355
|
+
request :
|
|
356
|
+
{ url : String
|
|
357
|
+
, method : String
|
|
358
|
+
, headers : List ( String, String )
|
|
359
|
+
, body : Body
|
|
360
|
+
, retries : Maybe Int
|
|
361
|
+
, timeoutInMs : Maybe Int
|
|
362
|
+
}
|
|
363
|
+
-> Expect a
|
|
364
|
+
-> BackendTask (Catchable Error) a
|
|
365
|
+
request request__ expect =
|
|
366
|
+
let
|
|
367
|
+
request_ : HashRequest.Request
|
|
368
|
+
request_ =
|
|
369
|
+
{ url = request__.url
|
|
370
|
+
, headers = request__.headers
|
|
371
|
+
, method = request__.method
|
|
372
|
+
, body = request__.body
|
|
373
|
+
, cacheOptions =
|
|
374
|
+
{ cacheStrategy = Nothing -- cache strategy only applies to GET and HEAD, need to use getWithOptions to customize
|
|
375
|
+
, cachePath = Nothing
|
|
376
|
+
, retries = request__.retries
|
|
377
|
+
, timeoutInMs = request__.timeoutInMs
|
|
378
|
+
}
|
|
379
|
+
|> encodeOptions
|
|
380
|
+
|> Just
|
|
381
|
+
}
|
|
382
|
+
in
|
|
383
|
+
requestRaw request_ expect
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
{-| -}
|
|
387
|
+
type CacheStrategy
|
|
388
|
+
= IgnoreCache -- 'no-store'
|
|
389
|
+
| ForceRevalidate -- 'no-cache'
|
|
390
|
+
| ForceReload -- 'reload'
|
|
391
|
+
| ForceCache -- 'force-cache'
|
|
392
|
+
| ErrorUnlessCached -- 'only-if-cached'
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
encodeOptions :
|
|
396
|
+
{ cacheStrategy : Maybe CacheStrategy
|
|
397
|
+
, cachePath : Maybe String
|
|
398
|
+
, retries : Maybe Int
|
|
399
|
+
, timeoutInMs : Maybe Int
|
|
400
|
+
}
|
|
401
|
+
-> Encode.Value
|
|
402
|
+
encodeOptions options =
|
|
403
|
+
Encode.object
|
|
404
|
+
([ ( "cache"
|
|
405
|
+
, options.cacheStrategy
|
|
406
|
+
|> Maybe.map
|
|
407
|
+
(\cacheStrategy ->
|
|
408
|
+
case cacheStrategy of
|
|
409
|
+
IgnoreCache ->
|
|
410
|
+
"no-store"
|
|
411
|
+
|
|
412
|
+
ForceRevalidate ->
|
|
413
|
+
"no-cache"
|
|
414
|
+
|
|
415
|
+
ForceReload ->
|
|
416
|
+
"reload"
|
|
417
|
+
|
|
418
|
+
ForceCache ->
|
|
419
|
+
"force-cache"
|
|
420
|
+
|
|
421
|
+
ErrorUnlessCached ->
|
|
422
|
+
"only-if-cached"
|
|
423
|
+
)
|
|
424
|
+
|> Maybe.map Encode.string
|
|
425
|
+
)
|
|
426
|
+
, ( "retry", options.retries |> Maybe.map Encode.int )
|
|
427
|
+
, ( "timeout", options.timeoutInMs |> Maybe.map Encode.int )
|
|
428
|
+
, ( "cachePath", options.cachePath |> Maybe.map Encode.string )
|
|
429
|
+
]
|
|
430
|
+
|> List.filterMap
|
|
431
|
+
(\( a, b ) -> b |> Maybe.map (Tuple.pair a))
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
{-| Build a `BackendTask.Http` request (analogous to [Http.request](https://package.elm-lang.org/packages/elm/http/latest/Http#request)).
|
|
436
|
+
This function takes in all the details to build a `BackendTask.Http` request, but you can build your own simplified helper functions
|
|
437
|
+
with this as a low-level detail, or you can use functions like [BackendTask.Http.get](#get).
|
|
438
|
+
-}
|
|
439
|
+
requestRaw :
|
|
440
|
+
HashRequest.Request
|
|
441
|
+
-> Expect a
|
|
442
|
+
-> BackendTask (Catchable Error) a
|
|
443
|
+
requestRaw request__ expect =
|
|
444
|
+
let
|
|
445
|
+
request_ : HashRequest.Request
|
|
446
|
+
request_ =
|
|
447
|
+
{ url = request__.url
|
|
448
|
+
, headers =
|
|
449
|
+
( "elm-pages-internal", expectToString expect )
|
|
450
|
+
:: request__.headers
|
|
451
|
+
, method = request__.method
|
|
452
|
+
, body = request__.body
|
|
453
|
+
, cacheOptions = request__.cacheOptions
|
|
454
|
+
}
|
|
455
|
+
in
|
|
456
|
+
Request
|
|
457
|
+
[ request_ ]
|
|
458
|
+
(\maybeMockResolver rawResponseDict ->
|
|
459
|
+
(case maybeMockResolver of
|
|
460
|
+
Just mockResolver ->
|
|
461
|
+
mockResolver request_
|
|
462
|
+
|
|
463
|
+
Nothing ->
|
|
464
|
+
rawResponseDict |> RequestsAndPending.get (request_ |> HashRequest.hash)
|
|
465
|
+
)
|
|
466
|
+
|> (\maybeResponse ->
|
|
467
|
+
case maybeResponse of
|
|
468
|
+
Just rawResponse ->
|
|
469
|
+
Ok rawResponse
|
|
470
|
+
|
|
471
|
+
Nothing ->
|
|
472
|
+
--Err (Pages.StaticHttpRequest.UserCalledStaticHttpFail ("INTERNAL ERROR - expected request" ++ request_.url))
|
|
473
|
+
Err (BadBody Nothing ("INTERNAL ERROR - expected request" ++ request_.url))
|
|
474
|
+
)
|
|
475
|
+
|> Result.andThen
|
|
476
|
+
(\(RequestsAndPending.Response maybeResponse body) ->
|
|
477
|
+
let
|
|
478
|
+
maybeBadResponse : Maybe Error
|
|
479
|
+
maybeBadResponse =
|
|
480
|
+
case maybeResponse of
|
|
481
|
+
Just response ->
|
|
482
|
+
if not (response.statusCode >= 200 && response.statusCode < 300) then
|
|
483
|
+
case body of
|
|
484
|
+
RequestsAndPending.StringBody s ->
|
|
485
|
+
BadStatus
|
|
486
|
+
{ url = response.url
|
|
487
|
+
, statusCode = response.statusCode
|
|
488
|
+
, statusText = response.statusText
|
|
489
|
+
, headers = response.headers
|
|
490
|
+
}
|
|
491
|
+
s
|
|
492
|
+
|> Just
|
|
493
|
+
|
|
494
|
+
RequestsAndPending.BytesBody bytes ->
|
|
495
|
+
BadStatus
|
|
496
|
+
{ url = response.url
|
|
497
|
+
, statusCode = response.statusCode
|
|
498
|
+
, statusText = response.statusText
|
|
499
|
+
, headers = response.headers
|
|
500
|
+
}
|
|
501
|
+
(Base64.fromBytes bytes |> Maybe.withDefault "")
|
|
502
|
+
|> Just
|
|
503
|
+
|
|
504
|
+
RequestsAndPending.JsonBody value ->
|
|
505
|
+
BadStatus
|
|
506
|
+
{ url = response.url
|
|
507
|
+
, statusCode = response.statusCode
|
|
508
|
+
, statusText = response.statusText
|
|
509
|
+
, headers = response.headers
|
|
510
|
+
}
|
|
511
|
+
(Encode.encode 0 value)
|
|
512
|
+
|> Just
|
|
513
|
+
|
|
514
|
+
RequestsAndPending.WhateverBody ->
|
|
515
|
+
BadStatus
|
|
516
|
+
{ url = response.url
|
|
517
|
+
, statusCode = response.statusCode
|
|
518
|
+
, statusText = response.statusText
|
|
519
|
+
, headers = response.headers
|
|
520
|
+
}
|
|
521
|
+
""
|
|
522
|
+
|> Just
|
|
523
|
+
|
|
524
|
+
else
|
|
525
|
+
Nothing
|
|
526
|
+
|
|
527
|
+
Nothing ->
|
|
528
|
+
Nothing
|
|
529
|
+
in
|
|
530
|
+
case maybeBadResponse of
|
|
531
|
+
Just badResponse ->
|
|
532
|
+
Err badResponse
|
|
533
|
+
|
|
534
|
+
Nothing ->
|
|
535
|
+
toResultThing ( expect, body, maybeResponse )
|
|
536
|
+
)
|
|
537
|
+
|> BackendTask.fromResult
|
|
538
|
+
|> BackendTask.mapError
|
|
539
|
+
(\error ->
|
|
540
|
+
Exception.Catchable error (errorToString error)
|
|
541
|
+
)
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
toResultThing :
|
|
546
|
+
( Expect value
|
|
547
|
+
, RequestsAndPending.ResponseBody
|
|
548
|
+
, Maybe RequestsAndPending.RawResponse
|
|
549
|
+
)
|
|
550
|
+
-> Result Error value
|
|
551
|
+
toResultThing ( expect, body, maybeResponse ) =
|
|
552
|
+
case ( expect, body, maybeResponse ) of
|
|
553
|
+
( ExpectMetadata toExpect, _, Just rawResponse ) ->
|
|
554
|
+
let
|
|
555
|
+
asMetadata : Metadata
|
|
556
|
+
asMetadata =
|
|
557
|
+
{ url = rawResponse.url
|
|
558
|
+
, statusCode = rawResponse.statusCode
|
|
559
|
+
, statusText = rawResponse.statusText
|
|
560
|
+
, headers = rawResponse.headers
|
|
561
|
+
}
|
|
562
|
+
in
|
|
563
|
+
toResultThing ( toExpect asMetadata, body, maybeResponse )
|
|
564
|
+
|
|
565
|
+
( ExpectJson decoder, RequestsAndPending.JsonBody json, _ ) ->
|
|
566
|
+
json
|
|
567
|
+
|> Json.Decode.decodeValue decoder
|
|
568
|
+
|> Result.mapError
|
|
569
|
+
(\error ->
|
|
570
|
+
error
|
|
571
|
+
|> Json.Decode.errorToString
|
|
572
|
+
|> BadBody (Just error)
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
( ExpectString mapStringFn, RequestsAndPending.StringBody string, _ ) ->
|
|
576
|
+
string
|
|
577
|
+
|> mapStringFn
|
|
578
|
+
|> Ok
|
|
579
|
+
|
|
580
|
+
( ExpectBytes bytesDecoder, RequestsAndPending.BytesBody rawBytes, _ ) ->
|
|
581
|
+
rawBytes
|
|
582
|
+
|> Bytes.Decode.decode bytesDecoder
|
|
583
|
+
|> Result.fromMaybe
|
|
584
|
+
(BadBody Nothing "Bytes decoding failed.")
|
|
585
|
+
|
|
586
|
+
( ExpectWhatever whateverValue, RequestsAndPending.WhateverBody, _ ) ->
|
|
587
|
+
Ok whateverValue
|
|
588
|
+
|
|
589
|
+
_ ->
|
|
590
|
+
Err (BadBody Nothing "Unexpected combination, internal error")
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
errorToString : Error -> { title : String, body : String }
|
|
594
|
+
errorToString error =
|
|
595
|
+
{ title = "HTTP Error"
|
|
596
|
+
, body =
|
|
597
|
+
(case error of
|
|
598
|
+
BadUrl string ->
|
|
599
|
+
[ TerminalText.text ("BadUrl " ++ string)
|
|
600
|
+
]
|
|
601
|
+
|
|
602
|
+
Timeout ->
|
|
603
|
+
[ TerminalText.text "Timeout"
|
|
604
|
+
]
|
|
605
|
+
|
|
606
|
+
NetworkError ->
|
|
607
|
+
[ TerminalText.text "NetworkError"
|
|
608
|
+
]
|
|
609
|
+
|
|
610
|
+
BadStatus _ string ->
|
|
611
|
+
[ TerminalText.text ("BadStatus: " ++ string)
|
|
612
|
+
]
|
|
613
|
+
|
|
614
|
+
BadBody _ string ->
|
|
615
|
+
[ TerminalText.text ("BadBody: " ++ string)
|
|
616
|
+
]
|
|
617
|
+
)
|
|
618
|
+
|> TerminalText.toString
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
{-| -}
|
|
623
|
+
type alias Metadata =
|
|
624
|
+
{ url : String
|
|
625
|
+
, statusCode : Int
|
|
626
|
+
, statusText : String
|
|
627
|
+
, headers : Dict String String
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
{-| -}
|
|
632
|
+
type Error
|
|
633
|
+
= BadUrl String
|
|
634
|
+
| Timeout
|
|
635
|
+
| NetworkError
|
|
636
|
+
| BadStatus Metadata String
|
|
637
|
+
| BadBody (Maybe Json.Decode.Error) String
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module BackendTask.Internal.Request exposing (request)
|
|
2
|
+
|
|
3
|
+
import BackendTask exposing (BackendTask)
|
|
4
|
+
import BackendTask.Http exposing (Body, Expect)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
request :
|
|
8
|
+
{ name : String
|
|
9
|
+
, body : Body
|
|
10
|
+
, expect : Expect a
|
|
11
|
+
}
|
|
12
|
+
-> BackendTask error a
|
|
13
|
+
request ({ name, body, expect } as params) =
|
|
14
|
+
-- elm-review: known-unoptimized-recursion
|
|
15
|
+
BackendTask.Http.request
|
|
16
|
+
{ url = "elm-pages-internal://" ++ name
|
|
17
|
+
, method = "GET"
|
|
18
|
+
, headers = []
|
|
19
|
+
, body = body
|
|
20
|
+
, timeoutInMs = Nothing
|
|
21
|
+
, retries = Nothing
|
|
22
|
+
}
|
|
23
|
+
expect
|
|
24
|
+
|> BackendTask.onError
|
|
25
|
+
(\_ ->
|
|
26
|
+
-- TODO avoid crash here, this should be handled as an internal error
|
|
27
|
+
request params
|
|
28
|
+
)
|