Update part8
This commit is contained in:
1
part8/elm-stuff/packages/elm-lang/http/1.0.0/.gitignore
vendored
Normal file
1
part8/elm-stuff/packages/elm-lang/http/1.0.0/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
elm-stuff
|
||||
30
part8/elm-stuff/packages/elm-lang/http/1.0.0/LICENSE
vendored
Normal file
30
part8/elm-stuff/packages/elm-lang/http/1.0.0/LICENSE
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2016-present, Evan Czaplicki
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Evan Czaplicki nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
57
part8/elm-stuff/packages/elm-lang/http/1.0.0/README.md
vendored
Normal file
57
part8/elm-stuff/packages/elm-lang/http/1.0.0/README.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
# HTTP in Elm
|
||||
|
||||
Make HTTP requests in Elm.
|
||||
|
||||
```elm
|
||||
import Http
|
||||
import Json.Decode as Decode
|
||||
|
||||
|
||||
-- GET A STRING
|
||||
|
||||
getWarAndPeace : Http.Request String
|
||||
getWarAndPeace =
|
||||
Http.getString "https://example.com/books/war-and-peace"
|
||||
|
||||
|
||||
-- GET JSON
|
||||
|
||||
getMetadata : Http.Request Metadata
|
||||
getMetadata =
|
||||
Http.get "https://example.com/books/war-and-peace/metadata" decodeMetadata
|
||||
|
||||
type alias Metadata =
|
||||
{ author : String
|
||||
, pages : Int
|
||||
}
|
||||
|
||||
decodeMetadata : Decode.Decoder Metadata
|
||||
decodeMetadata =
|
||||
Decode.map2 Metadata
|
||||
(Decode.field "author" Decode.string)
|
||||
(Decode.field "pages" Decode.int)
|
||||
|
||||
|
||||
-- SEND REQUESTS
|
||||
|
||||
type Msg
|
||||
= LoadMetadata (Result Http.Error Metadata)
|
||||
|
||||
send : Cmd Msg
|
||||
send =
|
||||
Http.send LoadMetadata getMetadata
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
- GET requests - [demo and code](http://elm-lang.org/examples/http)
|
||||
- Download progress - [demo](https://hirafuji.com.br/elm/http-progress-example/) and [code](https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4)
|
||||
|
||||
|
||||
## Learn More
|
||||
|
||||
To understand how HTTP works in Elm, check out:
|
||||
|
||||
- [The HTTP example in the guide](https://guide.elm-lang.org/architecture/effects/http.html) to see a simple usage with some explanation.
|
||||
- [The Elm Architecture](https://guide.elm-lang.org/architecture/) to understand how HTTP fits into Elm in a more complete way. This will explain concepts like `Cmd` and `Sub` that appear in this package.
|
||||
18
part8/elm-stuff/packages/elm-lang/http/1.0.0/elm-package.json
vendored
Normal file
18
part8/elm-stuff/packages/elm-lang/http/1.0.0/elm-package.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"summary": "Make HTTP requests (download progress, rate-limit, debounce, throttle)",
|
||||
"repository": "https://github.com/elm-lang/http.git",
|
||||
"license": "BSD3",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"exposed-modules": [
|
||||
"Http",
|
||||
"Http.Progress"
|
||||
],
|
||||
"native-modules": true,
|
||||
"dependencies": {
|
||||
"elm-lang/core": "5.0.0 <= v < 6.0.0"
|
||||
},
|
||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
||||
}
|
||||
40
part8/elm-stuff/packages/elm-lang/http/1.0.0/rate-limit.md
vendored
Normal file
40
part8/elm-stuff/packages/elm-lang/http/1.0.0/rate-limit.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
## Custom Rate-Limiting Strategies
|
||||
|
||||
This package has `Http.RateLimit` which helps you rate-limit the HTTP requests you make. Instead of sending one request per keystroke, you filter it down because not all requests are important.
|
||||
|
||||
The `Http.RateLimit` module comes with a `debounce` strategy that covers the common case, but you may want to define a custom strategy with other characteristics. Maybe you want to send the first request. Maybe you want to send when the previous request is done instead of using timers. Etc.
|
||||
|
||||
If so, you can define a custom strategy with `Http.RateLimit.customStrategy`. For example, you would define `throttle` like this:
|
||||
|
||||
```elm
|
||||
import Http.RateLimit as Limit
|
||||
|
||||
throttle : Time -> Limit.Strategy
|
||||
throttle ms =
|
||||
Limit.customStrategy <| \timeNow event state ->
|
||||
case event of
|
||||
Limit.New _ ->
|
||||
-- wait after a new request
|
||||
[ Limit.WakeUpIn ms ]
|
||||
|
||||
Limit.Done _ ->
|
||||
-- we do not care when requests finish
|
||||
[]
|
||||
|
||||
Limit.WakeUp ->
|
||||
case state.next of
|
||||
Nothing ->
|
||||
-- do nothing if there is no pending request
|
||||
[]
|
||||
|
||||
Just req ->
|
||||
-- send if enough time has passed since the previous request
|
||||
case state.prev of
|
||||
Nothing ->
|
||||
[ Limit.Send req.id ]
|
||||
|
||||
Just prev ->
|
||||
if timeNow - prev.time >= ms then [ Limit.Send req.id ] else []
|
||||
```
|
||||
|
||||
It would be nice to have some useful strategies defined in a separate package so folks can experiment and find names and implementations that work well for specific scenarios.
|
||||
411
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http.elm
vendored
Normal file
411
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http.elm
vendored
Normal file
@@ -0,0 +1,411 @@
|
||||
module Http exposing
|
||||
( Request, send, Error(..)
|
||||
, getString, get
|
||||
, post
|
||||
, request
|
||||
, Header, header
|
||||
, Body, emptyBody, jsonBody, stringBody, multipartBody, Part, stringPart
|
||||
, Expect, expectString, expectJson, expectStringResponse, Response
|
||||
, encodeUri, decodeUri, toTask
|
||||
)
|
||||
|
||||
{-| Create and send HTTP requests.
|
||||
|
||||
# Send Requests
|
||||
@docs Request, send, Error
|
||||
|
||||
# GET
|
||||
@docs getString, get
|
||||
|
||||
# POST
|
||||
@docs post
|
||||
|
||||
# Custom Requests
|
||||
@docs request
|
||||
|
||||
## Headers
|
||||
@docs Header, header
|
||||
|
||||
## Request Bodies
|
||||
@docs Body, emptyBody, jsonBody, stringBody, multipartBody, Part, stringPart
|
||||
|
||||
## Responses
|
||||
@docs Expect, expectString, expectJson, expectStringResponse, Response
|
||||
|
||||
# Low-Level
|
||||
@docs encodeUri, decodeUri, toTask
|
||||
|
||||
-}
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import Http.Internal
|
||||
import Json.Decode as Decode
|
||||
import Json.Encode as Encode
|
||||
import Maybe exposing (Maybe(..))
|
||||
import Native.Http
|
||||
import Platform.Cmd as Cmd exposing (Cmd)
|
||||
import Result exposing (Result(..))
|
||||
import Task exposing (Task)
|
||||
import Time exposing (Time)
|
||||
|
||||
|
||||
|
||||
-- REQUESTS
|
||||
|
||||
|
||||
{-| Describes an HTTP request.
|
||||
-}
|
||||
type alias Request a =
|
||||
Http.Internal.Request a
|
||||
|
||||
|
||||
{-| Send a `Request`. We could get the text of “War and Peace” like this:
|
||||
|
||||
import Http
|
||||
|
||||
type Msg = Click | NewBook (Result Http.Error String)
|
||||
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
Click ->
|
||||
( model, getWarAndPeace )
|
||||
|
||||
NewBook (Ok book) ->
|
||||
...
|
||||
|
||||
NewBook (Err _) ->
|
||||
...
|
||||
|
||||
getWarAndPeace : Cmd Msg
|
||||
getWarAndPeace =
|
||||
Http.send NewBook <|
|
||||
Http.getString "https://example.com/books/war-and-peace.md"
|
||||
-}
|
||||
send : (Result Error a -> msg) -> Request a -> Cmd msg
|
||||
send resultToMessage request =
|
||||
Task.attempt resultToMessage (toTask request)
|
||||
|
||||
|
||||
{-| Convert a `Request` into a `Task`. This is only really useful if you want
|
||||
to chain together a bunch of requests (or any other tasks) in a single command.
|
||||
-}
|
||||
toTask : Request a -> Task Error a
|
||||
toTask (Http.Internal.Request request) =
|
||||
Native.Http.toTask request Nothing
|
||||
|
||||
|
||||
{-| A `Request` can fail in a couple ways:
|
||||
|
||||
- `BadUrl` means you did not provide a valid URL.
|
||||
- `Timeout` means it took too long to get a response.
|
||||
- `NetworkError` means the user turned off their wifi, went in a cave, etc.
|
||||
- `BadStatus` means you got a response back, but the [status code][sc]
|
||||
indicates failure.
|
||||
- `BadPayload` means you got a response back with a nice status code, but
|
||||
the body of the response was something unexpected. The `String` in this
|
||||
case is a debugging message that explains what went wrong with your JSON
|
||||
decoder or whatever.
|
||||
|
||||
[sc]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
-}
|
||||
type Error
|
||||
= BadUrl String
|
||||
| Timeout
|
||||
| NetworkError
|
||||
| BadStatus (Response String)
|
||||
| BadPayload String (Response String)
|
||||
|
||||
|
||||
|
||||
-- GET
|
||||
|
||||
|
||||
{-| Create a `GET` request and interpret the response body as a `String`.
|
||||
|
||||
import Http
|
||||
|
||||
getWarAndPeace : Http.Request String
|
||||
getWarAndPeace =
|
||||
Http.getString "https://example.com/books/war-and-peace"
|
||||
-}
|
||||
getString : String -> Request String
|
||||
getString url =
|
||||
request
|
||||
{ method = "GET"
|
||||
, headers = []
|
||||
, url = url
|
||||
, body = emptyBody
|
||||
, expect = expectString
|
||||
, timeout = Nothing
|
||||
, withCredentials = False
|
||||
}
|
||||
|
||||
|
||||
{-| Create a `GET` request and try to decode the response body from JSON to
|
||||
some Elm value.
|
||||
|
||||
import Http
|
||||
import Json.Decode exposing (list, string)
|
||||
|
||||
getBooks : Http.Request (List String)
|
||||
getBooks =
|
||||
Http.get "https://example.com/books" (list string)
|
||||
|
||||
You can learn more about how JSON decoders work [here][] in the guide.
|
||||
|
||||
[here]: https://guide.elm-lang.org/interop/json.html
|
||||
-}
|
||||
get : String -> Decode.Decoder a -> Request a
|
||||
get url decoder =
|
||||
request
|
||||
{ method = "GET"
|
||||
, headers = []
|
||||
, url = url
|
||||
, body = emptyBody
|
||||
, expect = expectJson decoder
|
||||
, timeout = Nothing
|
||||
, withCredentials = False
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- POST
|
||||
|
||||
|
||||
{-| Create a `POST` request and try to decode the response body from JSON to
|
||||
an Elm value. For example, if we want to send a POST without any data in the
|
||||
request body, it would be like this:
|
||||
|
||||
import Http
|
||||
import Json.Decode exposing (list, string)
|
||||
|
||||
postBooks : Http.Request (List String)
|
||||
postBooks =
|
||||
Http.post "https://example.com/books" Http.emptyBody (list string)
|
||||
|
||||
See [`jsonBody`](#jsonBody) to learn how to have a more interesting request
|
||||
body. And check out [this section][here] of the guide to learn more about
|
||||
JSON decoders.
|
||||
|
||||
[here]: https://guide.elm-lang.org/interop/json.html
|
||||
|
||||
-}
|
||||
post : String -> Body -> Decode.Decoder a -> Request a
|
||||
post url body decoder =
|
||||
request
|
||||
{ method = "POST"
|
||||
, headers = []
|
||||
, url = url
|
||||
, body = body
|
||||
, expect = expectJson decoder
|
||||
, timeout = Nothing
|
||||
, withCredentials = False
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- CUSTOM REQUESTS
|
||||
|
||||
|
||||
{-| Create a custom request. For example, a custom PUT request would look like
|
||||
this:
|
||||
|
||||
put : String -> Body -> Request ()
|
||||
put url body =
|
||||
request
|
||||
{ method = "PUT"
|
||||
, headers = []
|
||||
, url = url
|
||||
, body = body
|
||||
, expect = expectStringResponse (\_ -> Ok ())
|
||||
, timeout = Nothing
|
||||
, withCredentials = False
|
||||
}
|
||||
-}
|
||||
request
|
||||
: { method : String
|
||||
, headers : List Header
|
||||
, url : String
|
||||
, body : Body
|
||||
, expect : Expect a
|
||||
, timeout : Maybe Time
|
||||
, withCredentials : Bool
|
||||
}
|
||||
-> Request a
|
||||
request =
|
||||
Http.Internal.Request
|
||||
|
||||
|
||||
|
||||
-- HEADERS
|
||||
|
||||
|
||||
{-| An HTTP header for configuring requests. See a bunch of common headers
|
||||
[here][].
|
||||
|
||||
[here]: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
|
||||
-}
|
||||
type alias Header = Http.Internal.Header
|
||||
|
||||
|
||||
{-| Create a `Header`.
|
||||
|
||||
header "If-Modified-Since" "Sat 29 Oct 1994 19:43:31 GMT"
|
||||
header "Max-Forwards" "10"
|
||||
header "X-Requested-With" "XMLHttpRequest"
|
||||
|
||||
**Note:** In the future, we may split this out into an `Http.Headers` module
|
||||
and provide helpers for cases that are common on the client-side. If this
|
||||
sounds nice to you, open an issue [here][] describing the helper you want and
|
||||
why you need it.
|
||||
|
||||
[here]: https://github.com/elm-lang/http/issues
|
||||
-}
|
||||
header : String -> String -> Header
|
||||
header =
|
||||
Http.Internal.Header
|
||||
|
||||
|
||||
|
||||
-- BODY
|
||||
|
||||
|
||||
{-| Represents the body of a `Request`.
|
||||
-}
|
||||
type alias Body = Http.Internal.Body
|
||||
|
||||
|
||||
{-| Create an empty body for your `Request`. This is useful for GET requests
|
||||
and POST requests where you are not sending any data.
|
||||
-}
|
||||
emptyBody : Body
|
||||
emptyBody =
|
||||
Http.Internal.EmptyBody
|
||||
|
||||
|
||||
{-| Put some JSON value in the body of your `Request`. This will automatically
|
||||
add the `Content-Type: application/json` header.
|
||||
-}
|
||||
jsonBody : Encode.Value -> Body
|
||||
jsonBody value =
|
||||
Http.Internal.StringBody "application/json" (Encode.encode 0 value)
|
||||
|
||||
|
||||
{-| Put some string in the body of your `Request`. Defining `jsonBody` looks
|
||||
like this:
|
||||
|
||||
import Json.Encode as Encode
|
||||
|
||||
jsonBody : Encode.Value -> Body
|
||||
jsonBody value =
|
||||
stringBody "application/json" (Encode.encode 0 value)
|
||||
|
||||
Notice that the first argument is a [MIME type][mime] so we know to add
|
||||
`Content-Type: application/json` to our request headers. Make sure your
|
||||
MIME type matches your data. Some servers are strict about this!
|
||||
|
||||
[mime]: https://en.wikipedia.org/wiki/Media_type
|
||||
-}
|
||||
stringBody : String -> String -> Body
|
||||
stringBody =
|
||||
Http.Internal.StringBody
|
||||
|
||||
|
||||
{-| Create multi-part bodies for your `Request`, automatically adding the
|
||||
`Content-Type: multipart/form-data` header.
|
||||
-}
|
||||
multipartBody : List Part -> Body
|
||||
multipartBody =
|
||||
Native.Http.multipart
|
||||
|
||||
|
||||
{-| Contents of a multi-part body. Right now it only supports strings, but we
|
||||
will support blobs and files when we get an API for them in Elm.
|
||||
-}
|
||||
type Part
|
||||
= StringPart String String
|
||||
|
||||
|
||||
{-| A named chunk of string data.
|
||||
|
||||
body =
|
||||
multipartBody
|
||||
[ stringPart "user" "tom"
|
||||
, stringPart "payload" "42"
|
||||
]
|
||||
-}
|
||||
stringPart : String -> String -> Part
|
||||
stringPart =
|
||||
StringPart
|
||||
|
||||
|
||||
|
||||
-- RESPONSES
|
||||
|
||||
|
||||
{-| Logic for interpreting a response body.
|
||||
-}
|
||||
type alias Expect a =
|
||||
Http.Internal.Expect a
|
||||
|
||||
|
||||
{-| Expect the response body to be a `String`.
|
||||
-}
|
||||
expectString : Expect String
|
||||
expectString =
|
||||
expectStringResponse (\response -> Ok response.body)
|
||||
|
||||
|
||||
{-| Expect the response body to be JSON. You provide a `Decoder` to turn that
|
||||
JSON into an Elm value. If the body cannot be parsed as JSON or if the JSON
|
||||
does not match the decoder, the request will resolve to a `BadPayload` error.
|
||||
-}
|
||||
expectJson : Decode.Decoder a -> Expect a
|
||||
expectJson decoder =
|
||||
expectStringResponse (\response -> Decode.decodeString decoder response.body)
|
||||
|
||||
|
||||
{-| Maybe you want the whole `Response`: status code, headers, body, etc. This
|
||||
lets you get all of that information. From there you can use functions like
|
||||
`Json.Decode.decodeString` to interpret it as JSON or whatever else you want.
|
||||
-}
|
||||
expectStringResponse : (Response String -> Result String a) -> Expect a
|
||||
expectStringResponse =
|
||||
Native.Http.expectStringResponse
|
||||
|
||||
|
||||
{-| The response from a `Request`.
|
||||
-}
|
||||
type alias Response body =
|
||||
{ url : String
|
||||
, status : { code : Int, message : String }
|
||||
, headers : Dict String String
|
||||
, body : body
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- LOW-LEVEL
|
||||
|
||||
|
||||
{-| Use this to escape query parameters. Converts characters like `/` to `%2F`
|
||||
so that it does not clash with normal URL
|
||||
|
||||
It work just like `encodeURIComponent` in JavaScript.
|
||||
-}
|
||||
encodeUri : String -> String
|
||||
encodeUri =
|
||||
Native.Http.encodeUri
|
||||
|
||||
|
||||
{-| Use this to unescape query parameters. It converts things like `%2F` to
|
||||
`/`. It can fail in some cases. For example, there is no way to unescape `%`
|
||||
because it could never appear alone in a properly escaped string.
|
||||
|
||||
It works just like `decodeURIComponent` in JavaScript.
|
||||
-}
|
||||
decodeUri : String -> Maybe String
|
||||
decodeUri =
|
||||
Native.Http.decodeUri
|
||||
|
||||
45
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http/Internal.elm
vendored
Normal file
45
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http/Internal.elm
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
module Http.Internal exposing
|
||||
( Request(..)
|
||||
, RawRequest
|
||||
, Expect
|
||||
, Body(..)
|
||||
, Header(..)
|
||||
, map
|
||||
)
|
||||
|
||||
|
||||
import Native.Http
|
||||
import Time exposing (Time)
|
||||
|
||||
|
||||
|
||||
type Request a = Request (RawRequest a)
|
||||
|
||||
|
||||
type alias RawRequest a =
|
||||
{ method : String
|
||||
, headers : List Header
|
||||
, url : String
|
||||
, body : Body
|
||||
, expect : Expect a
|
||||
, timeout : Maybe Time
|
||||
, withCredentials : Bool
|
||||
}
|
||||
|
||||
|
||||
type Expect a = Expect
|
||||
|
||||
|
||||
type Body
|
||||
= EmptyBody
|
||||
| StringBody String String
|
||||
| FormDataBody
|
||||
|
||||
|
||||
|
||||
type Header = Header String String
|
||||
|
||||
|
||||
map : (a -> b) -> RawRequest a -> RawRequest b
|
||||
map func request =
|
||||
{ request | expect = Native.Http.mapExpect func request.expect }
|
||||
200
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http/Progress.elm
vendored
Normal file
200
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Http/Progress.elm
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
effect module Http.Progress where { subscription = MySub } exposing
|
||||
( Progress(..)
|
||||
, track
|
||||
)
|
||||
|
||||
{-| Track the progress of an HTTP request. This can be useful if you are
|
||||
requesting a large amount of data and want to show the user a progress bar
|
||||
or something.
|
||||
|
||||
Here is an example usage: [demo][] and [code][].
|
||||
|
||||
[demo]: https://hirafuji.com.br/elm/http-progress-example/
|
||||
[code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4
|
||||
|
||||
**Note:** If you stop tracking progress, you cancel the request.
|
||||
|
||||
# Progress
|
||||
@docs Progress, track
|
||||
|
||||
-}
|
||||
|
||||
|
||||
import Dict
|
||||
import Http
|
||||
import Http.Internal exposing ( Request(Request) )
|
||||
import Task exposing (Task)
|
||||
import Platform exposing (Router)
|
||||
import Process
|
||||
|
||||
|
||||
|
||||
-- PROGRESS
|
||||
|
||||
|
||||
{-| The progress of an HTTP request.
|
||||
|
||||
You start with `None`. As data starts to come in, you will see `Some`. The
|
||||
`bytesExpected` field will match the `Content-Length` header, indicating how
|
||||
long the response body is in bytes (8-bits). The `bytes` field indicates how
|
||||
many bytes have been loaded so far, so if you want progress as a percentage,
|
||||
you would say:
|
||||
|
||||
Some { bytes, bytesExpected } ->
|
||||
toFloat bytes / toFloat bytesExpected
|
||||
|
||||
You will end up with `Fail` or `Done` depending on the success of the request.
|
||||
-}
|
||||
type Progress data
|
||||
= None
|
||||
| Some { bytes : Int, bytesExpected : Int}
|
||||
| Fail Http.Error
|
||||
| Done data
|
||||
|
||||
|
||||
|
||||
-- TRACK
|
||||
|
||||
|
||||
{-| Create a subscription that tracks the progress of an HTTP request.
|
||||
|
||||
See it in action in this example: [demo][] and [code][].
|
||||
|
||||
[demo]: https://hirafuji.com.br/elm/http-progress-example/
|
||||
[code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4
|
||||
-}
|
||||
track : String -> (Progress data -> msg) -> Http.Request data -> Sub msg
|
||||
track id toMessage (Request request) =
|
||||
subscription <| Track id <|
|
||||
{ request = Http.Internal.map (Done >> toMessage) request
|
||||
, toProgress = Some >> toMessage
|
||||
, toError = Fail >> toMessage
|
||||
}
|
||||
|
||||
|
||||
type alias TrackedRequest msg =
|
||||
{ request : Http.Internal.RawRequest msg
|
||||
, toProgress : { bytes : Int, bytesExpected : Int } -> msg
|
||||
, toError : Http.Error -> msg
|
||||
}
|
||||
|
||||
|
||||
map : (a -> b) -> TrackedRequest a -> TrackedRequest b
|
||||
map func { request, toProgress, toError } =
|
||||
{ request = Http.Internal.map func request
|
||||
, toProgress = toProgress >> func
|
||||
, toError = toError >> func
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
type MySub msg =
|
||||
Track String (TrackedRequest msg)
|
||||
|
||||
|
||||
subMap : (a -> b) -> MySub a -> MySub b
|
||||
subMap func (Track id trackedRequest) =
|
||||
Track id (map func trackedRequest)
|
||||
|
||||
|
||||
|
||||
-- EFFECT MANAGER
|
||||
|
||||
|
||||
type alias State =
|
||||
Dict.Dict String Process.Id
|
||||
|
||||
|
||||
init : Task Never State
|
||||
init =
|
||||
Task.succeed Dict.empty
|
||||
|
||||
|
||||
|
||||
-- APP MESSAGES
|
||||
|
||||
|
||||
onEffects : Platform.Router msg Never -> List (MySub msg) -> State -> Task Never State
|
||||
onEffects router subs state =
|
||||
let
|
||||
subDict =
|
||||
collectSubs subs
|
||||
|
||||
leftStep id process (dead, ongoing, new) =
|
||||
( Process.kill process :: dead
|
||||
, ongoing
|
||||
, new
|
||||
)
|
||||
|
||||
bothStep id process _ (dead, ongoing, new) =
|
||||
( dead
|
||||
, Dict.insert id process ongoing
|
||||
, new
|
||||
)
|
||||
|
||||
rightStep id trackedRequest (dead, ongoing, new) =
|
||||
( dead
|
||||
, ongoing
|
||||
, (id, trackedRequest) :: new
|
||||
)
|
||||
|
||||
(dead, ongoing, new) =
|
||||
Dict.merge leftStep bothStep rightStep state subDict ([], Dict.empty, [])
|
||||
in
|
||||
Task.sequence dead
|
||||
|> Task.andThen (\_ -> spawnRequests router new ongoing)
|
||||
|
||||
|
||||
spawnRequests : Router msg Never -> List (String, TrackedRequest msg) -> State -> Task Never State
|
||||
spawnRequests router trackedRequests state =
|
||||
case trackedRequests of
|
||||
[] ->
|
||||
Task.succeed state
|
||||
|
||||
(id, trackedRequest) :: others ->
|
||||
Process.spawn (toTask router trackedRequest)
|
||||
|> Task.andThen (\process -> spawnRequests router others (Dict.insert id process state))
|
||||
|
||||
|
||||
toTask : Router msg Never -> TrackedRequest msg -> Task Never ()
|
||||
toTask router { request, toProgress, toError } =
|
||||
Native.Http.toTask request (Just (Platform.sendToApp router << toProgress))
|
||||
|> Task.andThen (Platform.sendToApp router)
|
||||
|> Task.onError (Platform.sendToApp router << toError)
|
||||
|
||||
|
||||
|
||||
-- COLLECT SUBS AS DICT
|
||||
|
||||
|
||||
type alias SubDict msg =
|
||||
Dict.Dict String (TrackedRequest msg)
|
||||
|
||||
|
||||
collectSubs : List (MySub msg) -> SubDict msg
|
||||
collectSubs subs =
|
||||
List.foldl addSub Dict.empty subs
|
||||
|
||||
|
||||
addSub : MySub msg -> SubDict msg -> SubDict msg
|
||||
addSub (Track id trackedRequest) subDict =
|
||||
let
|
||||
request =
|
||||
trackedRequest.request
|
||||
|
||||
uid =
|
||||
id ++ request.method ++ request.url
|
||||
in
|
||||
Dict.insert uid trackedRequest subDict
|
||||
|
||||
|
||||
|
||||
-- SELF MESSAGES
|
||||
|
||||
|
||||
onSelfMsg : Platform.Router msg Never -> Never -> State -> Task Never State
|
||||
onSelfMsg router _ state =
|
||||
Task.succeed state
|
||||
238
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Native/Http.js
vendored
Normal file
238
part8/elm-stuff/packages/elm-lang/http/1.0.0/src/Native/Http.js
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
var _elm_lang$http$Native_Http = function() {
|
||||
|
||||
|
||||
// ENCODING AND DECODING
|
||||
|
||||
function encodeUri(string)
|
||||
{
|
||||
return encodeURIComponent(string);
|
||||
}
|
||||
|
||||
function decodeUri(string)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _elm_lang$core$Maybe$Just(decodeURIComponent(string));
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
return _elm_lang$core$Maybe$Nothing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SEND REQUEST
|
||||
|
||||
function toTask(request, maybeProgress)
|
||||
{
|
||||
return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
configureProgress(xhr, maybeProgress);
|
||||
|
||||
xhr.addEventListener('error', function() {
|
||||
callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'NetworkError' }));
|
||||
});
|
||||
xhr.addEventListener('timeout', function() {
|
||||
callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'Timeout' }));
|
||||
});
|
||||
xhr.addEventListener('load', function() {
|
||||
callback(handleResponse(xhr, request.expect.responseToResult));
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
xhr.open(request.method, request.url, true);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'BadUrl', _0: request.url }));
|
||||
}
|
||||
|
||||
configureRequest(xhr, request);
|
||||
send(xhr, request.body);
|
||||
|
||||
return function() { xhr.abort(); };
|
||||
});
|
||||
}
|
||||
|
||||
function configureProgress(xhr, maybeProgress)
|
||||
{
|
||||
if (maybeProgress.ctor === 'Nothing')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
xhr.addEventListener('progress', function(event) {
|
||||
if (!event.lengthComputable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_elm_lang$core$Native_Scheduler.rawSpawn(maybeProgress._0({
|
||||
bytes: event.loaded,
|
||||
bytesExpected: event.total
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function configureRequest(xhr, request)
|
||||
{
|
||||
function setHeader(pair)
|
||||
{
|
||||
xhr.setRequestHeader(pair._0, pair._1);
|
||||
}
|
||||
|
||||
A2(_elm_lang$core$List$map, setHeader, request.headers);
|
||||
xhr.responseType = request.expect.responseType;
|
||||
xhr.withCredentials = request.withCredentials;
|
||||
|
||||
if (request.timeout.ctor === 'Just')
|
||||
{
|
||||
xhr.timeout = request.timeout._0;
|
||||
}
|
||||
}
|
||||
|
||||
function send(xhr, body)
|
||||
{
|
||||
switch (body.ctor)
|
||||
{
|
||||
case 'EmptyBody':
|
||||
xhr.send();
|
||||
return;
|
||||
|
||||
case 'StringBody':
|
||||
xhr.setRequestHeader('Content-Type', body._0);
|
||||
xhr.send(body._1);
|
||||
return;
|
||||
|
||||
case 'FormDataBody':
|
||||
xhr.send(body._0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// RESPONSES
|
||||
|
||||
function handleResponse(xhr, responseToResult)
|
||||
{
|
||||
var response = toResponse(xhr);
|
||||
|
||||
if (xhr.status < 200 || 300 <= xhr.status)
|
||||
{
|
||||
response.body = xhr.responseText;
|
||||
return _elm_lang$core$Native_Scheduler.fail({
|
||||
ctor: 'BadStatus',
|
||||
_0: response
|
||||
});
|
||||
}
|
||||
|
||||
var result = responseToResult(response);
|
||||
|
||||
if (result.ctor === 'Ok')
|
||||
{
|
||||
return _elm_lang$core$Native_Scheduler.succeed(result._0);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.body = xhr.responseText;
|
||||
return _elm_lang$core$Native_Scheduler.fail({
|
||||
ctor: 'BadPayload',
|
||||
_0: result._0,
|
||||
_1: response
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toResponse(xhr)
|
||||
{
|
||||
return {
|
||||
status: { code: xhr.status, message: xhr.statusText },
|
||||
headers: parseHeaders(xhr.getAllResponseHeaders()),
|
||||
url: xhr.responseURL,
|
||||
body: xhr.response
|
||||
};
|
||||
}
|
||||
|
||||
function parseHeaders(rawHeaders)
|
||||
{
|
||||
var headers = _elm_lang$core$Dict$empty;
|
||||
|
||||
if (!rawHeaders)
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
var headerPairs = rawHeaders.split('\u000d\u000a');
|
||||
for (var i = headerPairs.length; i--; )
|
||||
{
|
||||
var headerPair = headerPairs[i];
|
||||
var index = headerPair.indexOf('\u003a\u0020');
|
||||
if (index > 0)
|
||||
{
|
||||
var key = headerPair.substring(0, index);
|
||||
var value = headerPair.substring(index + 2);
|
||||
|
||||
headers = A3(_elm_lang$core$Dict$update, key, function(oldValue) {
|
||||
if (oldValue.ctor === 'Just')
|
||||
{
|
||||
return _elm_lang$core$Maybe$Just(value + ', ' + oldValue._0);
|
||||
}
|
||||
return _elm_lang$core$Maybe$Just(value);
|
||||
}, headers);
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
// EXPECTORS
|
||||
|
||||
function expectStringResponse(responseToResult)
|
||||
{
|
||||
return {
|
||||
responseType: 'text',
|
||||
responseToResult: responseToResult
|
||||
};
|
||||
}
|
||||
|
||||
function mapExpect(func, expect)
|
||||
{
|
||||
return {
|
||||
responseType: expect.responseType,
|
||||
responseToResult: function(response) {
|
||||
var convertedResponse = expect.responseToResult(response);
|
||||
return A2(_elm_lang$core$Result$map, func, convertedResponse);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// BODY
|
||||
|
||||
function multipart(parts)
|
||||
{
|
||||
var formData = new FormData();
|
||||
|
||||
while (parts.ctor !== '[]')
|
||||
{
|
||||
var part = parts._0;
|
||||
formData.append(part._0, part._1);
|
||||
parts = parts._1;
|
||||
}
|
||||
|
||||
return { ctor: 'FormDataBody', _0: formData };
|
||||
}
|
||||
|
||||
return {
|
||||
toTask: F2(toTask),
|
||||
expectStringResponse: expectStringResponse,
|
||||
mapExpect: F2(mapExpect),
|
||||
multipart: multipart,
|
||||
encodeUri: encodeUri,
|
||||
decodeUri: decodeUri
|
||||
};
|
||||
|
||||
}();
|
||||
Reference in New Issue
Block a user