Skip to content

Commit

Permalink
Merge pull request #415 from tanyabouman/backendtask-fatalerror-docs
Browse files Browse the repository at this point in the history
add error to BackendTask type in docs
  • Loading branch information
dillonkearns authored Sep 24, 2023
2 parents f472ae4 + 11e3ba9 commit aa02382
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 45 deletions.
3 changes: 2 additions & 1 deletion src/ApiRoute.elm
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ You define your ApiRoute's in `app/Api.elm`. Here's a simple example:
import ApiRoute
import BackendTask exposing (BackendTask)
import FatalError exposing (FatalError)
import Server.Request
routes :
BackendTask (List Route)
BackendTask FatalError (List Route)
-> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String)
-> List (ApiRoute.ApiRoute ApiRoute.Response)
routes getStaticRoutes htmlToString =
Expand Down
18 changes: 11 additions & 7 deletions src/BackendTask.elm
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,15 @@ resolve =
{-| Turn a list of `BackendTask`s into a single one.
import BackendTask
import FatalError exposing (FatalError)
import Json.Decode as Decode exposing (Decoder)
type alias Pokemon =
{ name : String
, sprite : String
}
pokemonDetailRequest : BackendTask (List Pokemon)
pokemonDetailRequest : BackendTask FatalError (List Pokemon)
pokemonDetailRequest =
BackendTask.Http.getJson
"https://pokeapi.co/api/v2/pokemon/?limit=3"
Expand All @@ -169,6 +170,7 @@ resolve =
)
)
|> BackendTask.andThen BackendTask.combine
|> BackendTask.allowFatal
-}
combine : List (BackendTask error value) -> BackendTask error (List value)
Expand All @@ -190,11 +192,11 @@ combine items =
}
)
(get
(Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
"https://api.github.com/repos/dillonkearns/elm-pages"
(Decode.field "stargazers_count" Decode.int)
)
(get
(Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-markdown")
"https://api.github.com/repos/dillonkearns/elm-markdown"
(Decode.field "stargazers_count" Decode.int)
)
Expand Down Expand Up @@ -239,17 +241,19 @@ map2 fn request1 request2 =
from the previous response to build up the URL, headers, etc. that you send to the subsequent request.
import BackendTask
import FatalError exposing (FatalError)
import Json.Decode as Decode exposing (Decoder)
licenseData : BackendTask String
licenseData : BackendTask FatalError String
licenseData =
BackendTask.Http.get
(Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
BackendTask.Http.getJson
"https://api.github.com/repos/dillonkearns/elm-pages"
(Decode.at [ "license", "url" ] Decode.string)
|> BackendTask.andThen
(\licenseUrl ->
BackendTask.Http.get (Secrets.succeed licenseUrl) (Decode.field "description" Decode.string)
BackendTask.Http.getJson licenseUrl (Decode.field "description" Decode.string)
)
|> BackendTask.allowFatal
-}
andThen : (a -> BackendTask error b) -> BackendTask error a -> BackendTask error b
Expand Down
65 changes: 34 additions & 31 deletions src/BackendTask/Glob.elm
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ With the `BackendTask.Glob` API, you could get all of those files like so:
import BackendTask exposing (BackendTask)
blogPostsGlob : BackendTask (List String)
blogPostsGlob : BackendTask error (List String)
blogPostsGlob =
Glob.succeed (\slug -> slug)
|> Glob.match (Glob.literal "content/blog/")
Expand Down Expand Up @@ -69,7 +69,7 @@ There will be one argument for every `capture` in your pipeline, whereas `match`
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
blogPostsGlob : BackendTask (List String)
blogPostsGlob : BackendTask error (List String)
blogPostsGlob =
Glob.succeed (\slug -> slug)
-- no argument from this, but we will only
Expand Down Expand Up @@ -97,7 +97,7 @@ Let's try our blogPostsGlob from before, but change every `match` to `capture`.
import BackendTask exposing (BackendTask)
blogPostsGlob :
BackendTask
BackendTask error
(List
{ filePath : String
, slug : String
Expand Down Expand Up @@ -155,20 +155,22 @@ This is my first post!
Then we could read that title for our blog post list page using our `blogPosts` `BackendTask` that we defined above.
import BackendTask.File
import FatalError exposing (FatalError)
import Json.Decode as Decode exposing (Decoder)
titles : BackendTask (List BlogPost)
titles : BackendTask FatalError (List BlogPost)
titles =
blogPosts
|> BackendTask.map
(List.map
(\blogPost ->
BackendTask.File.request
BackendTask.File.onlyFrontmatter
blogFrontmatterDecoder
blogPost.filePath
(BackendTask.File.frontmatter blogFrontmatterDecoder)
)
)
|> BackendTask.resolve
|> BackendTask.allowFatal
type alias BlogPost =
{ title : String }
Expand Down Expand Up @@ -249,7 +251,7 @@ could use
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
blogPostsGlob : BackendTask (List String)
blogPostsGlob : BackendTask error (List String)
blogPostsGlob =
Glob.succeed (\slug -> slug)
|> Glob.match (Glob.literal "content/blog/")
Expand All @@ -258,14 +260,14 @@ could use
|> Glob.toBackendTask
If you want to validate file formats, you can combine that with some `BackendTask` helpers to turn a `Glob (Result String value)` into
a `BackendTask (List value)`.
a `BackendTask FatalError (List value)`.
For example, you could take a date and parse it.
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
example : BackendTask (List ( String, String ))
example : BackendTask FatalError (List ( String, String ))
example =
Glob.succeed
(\dateResult slug ->
Expand All @@ -281,14 +283,14 @@ For example, you could take a date and parse it.
|> BackendTask.map (List.map BackendTask.fromResult)
|> BackendTask.resolve
expectDateFormat : List String -> Result String String
expectDateFormat : List String -> Result FatalError String
expectDateFormat dateParts =
case dateParts of
[ year, month, date ] ->
Ok (String.join "-" [ year, month, date ])
_ ->
Err "Unexpected date format, expected yyyy/mm/dd folder structure."
Err <| FatalError.fromString "Unexpected date format, expected yyyy/mm/dd folder structure."
-}
map : (a -> b) -> Glob a -> Glob b
Expand Down Expand Up @@ -322,7 +324,7 @@ fullFilePath =
import BackendTask.Glob as Glob
blogPosts :
BackendTask
BackendTask error
(List
{ filePath : String
, slug : String
Expand Down Expand Up @@ -368,11 +370,11 @@ match 0 or more path parts like, see `recursiveWildcard`.
, slug : String
}
example : BackendTask (List BlogPost)
example : BackendTask error (List BlogPost)
example =
Glob.succeed BlogPost
|> Glob.match (Glob.literal "blog/")
|> Glob.match Glob.wildcard
|> Glob.capture Glob.wildcard
|> Glob.match (Glob.literal "-")
|> Glob.capture Glob.wildcard
|> Glob.match (Glob.literal "-")
Expand All @@ -391,7 +393,7 @@ match 0 or more path parts like, see `recursiveWildcard`.
That will match to:
results : BackendTask (List BlogPost)
results : BackendTask error (List BlogPost)
results =
BackendTask.succeed
[ { year = "2021"
Expand Down Expand Up @@ -443,7 +445,7 @@ Leading 0's are ignored.
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
slides : BackendTask (List Int)
slides : BackendTask error (List Int)
slides =
Glob.succeed identity
|> Glob.match (Glob.literal "slide-")
Expand Down Expand Up @@ -472,7 +474,7 @@ With files
Yields
matches : BackendTask (List Int)
matches : BackendTask error (List Int)
matches =
BackendTask.succeed
[ 1
Expand Down Expand Up @@ -513,7 +515,7 @@ This is the elm-pages equivalent of `**/*.txt` in standard shell syntax:
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
example : BackendTask (List ( List String, String ))
example : BackendTask error (List ( List String, String ))
example =
Glob.succeed Tuple.pair
|> Glob.match (Glob.literal "articles/")
Expand All @@ -537,7 +539,7 @@ With these files:
We would get the following matches:
matches : BackendTask (List ( List String, String ))
matches : BackendTask error (List ( List String, String ))
matches =
BackendTask.succeed
[ ( [ "archive", "1977", "06", "10" ], "apple-2-announced" )
Expand All @@ -552,14 +554,14 @@ And also note that it matches 0 path parts into an empty list.
If we didn't include the `wildcard` after the `recursiveWildcard`, then we would only get
a single level of matches because it is followed by a file extension.
example : BackendTask (List String)
example : BackendTask error (List String)
example =
Glob.succeed identity
|> Glob.match (Glob.literal "articles/")
|> Glob.capture Glob.recursiveWildcard
|> Glob.match (Glob.literal ".txt")
matches : BackendTask (List String)
matches : BackendTask error (List String)
matches =
BackendTask.succeed
[ "google-io-2021-recap"
Expand Down Expand Up @@ -677,7 +679,7 @@ Exactly the same as `match` except it also captures the matched sub-pattern.
, slug : String
}
archives : BackendTask ArchivesArticle
archives : BackendTask error ArchivesArticle
archives =
Glob.succeed ArchivesArticle
|> Glob.match (Glob.literal "archive/")
Expand All @@ -693,7 +695,7 @@ Exactly the same as `match` except it also captures the matched sub-pattern.
The file `archive/1977/06/10/apple-2-released.md` will give us this match:
matches : List ArchivesArticle
matches : List error ArchivesArticle
matches =
BackendTask.succeed
[ { year = 1977
Expand Down Expand Up @@ -744,7 +746,7 @@ capture (Glob matcherPattern apply1) (Glob pattern apply2) =
, extension : String
}
dataFiles : BackendTask (List DataFile)
dataFiles : BackendTask error (List DataFile)
dataFiles =
Glob.succeed DataFile
|> Glob.match (Glob.literal "my-data/")
Expand All @@ -768,7 +770,7 @@ If we have the following files
That gives us
results : BackendTask (List DataFile)
results : BackendTask error (List DataFile)
results =
BackendTask.succeed
[ { name = "authors"
Expand All @@ -781,7 +783,7 @@ That gives us
You could also match an optional file path segment using `oneOf`.
rootFilesMd : BackendTask (List String)
rootFilesMd : BackendTask error (List String)
rootFilesMd =
Glob.succeed (\slug -> slug)
|> Glob.match (Glob.literal "blog/")
Expand All @@ -806,7 +808,7 @@ With these files:
This would give us:
results : BackendTask (List String)
results : BackendTask error (List String)
results =
BackendTask.succeed
[ "first-post"
Expand Down Expand Up @@ -965,7 +967,7 @@ encodeOptions options =
import BackendTask.Glob as Glob exposing (OnlyFolders, defaultOptions)
matchingFiles : Glob a -> BackendTask (List a)
matchingFiles : Glob a -> BackendTask error (List a)
matchingFiles glob =
glob
|> Glob.toBackendTaskWithOptions { defaultOptions | include = OnlyFolders }
Expand Down Expand Up @@ -1010,12 +1012,12 @@ For example, maybe you can have
import BackendTask exposing (BackendTask)
import BackendTask.Glob as Glob
findBlogBySlug : String -> BackendTask String
findBlogBySlug : String -> BackendTask FatalError String
findBlogBySlug slug =
Glob.succeed identity
|> Glob.captureFilePath
|> Glob.match (Glob.literal "blog/")
|> Glob.capture (Glob.literal slug)
|> Glob.match (Glob.literal slug)
|> Glob.match
(Glob.oneOf
( ( "", () )
Expand All @@ -1024,6 +1026,7 @@ For example, maybe you can have
)
|> Glob.match (Glob.literal ".md")
|> Glob.expectUniqueMatch
|> BackendTask.allowFatal
If we used `findBlogBySlug "first-post"` with these files:
Expand All @@ -1035,7 +1038,7 @@ If we used `findBlogBySlug "first-post"` with these files:
This would give us:
results : BackendTask String
results : BackendTask FatalError String
results =
BackendTask.succeed "blog/first-post/index.md"
Expand Down
15 changes: 9 additions & 6 deletions src/Server/Session.elm
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Using these functions, you can store and read session data in cookies to maintai
type alias Data =
{ darkMode : Bool }
data : RouteParams -> Request -> BackendTask (Response Data ErrorPage)
data : RouteParams -> Request -> BackendTask FatalError (Response Data ErrorPage)
data routeParams request =
request
|> Session.withSession
Expand All @@ -42,12 +42,15 @@ Using these functions, you can store and read session data in cookies to maintai
(session |> Session.get "mode" |> Maybe.withDefault "light")
== "dark"
in
( session
, { darkMode = darkMode }
)
BackendTask.succeed
( session
, Response.render
{ darkMode = darkMode
}
)
)
The elm-pages framework will manage signing these cookies using the `secrets : BackendTask (List String)` you pass in.
The elm-pages framework will manage signing these cookies using the `secrets : BackendTask FatalError (List String)` you pass in.
That means that the values you set in your session will be directly visible to anyone who has access to the cookie
(so don't directly store sensitive data in your session). Since the session cookie is signed using the secret you provide,
the cookie will be invalidated if it is tampered with because it won't match when elm-pages verifies that it has been
Expand All @@ -56,7 +59,7 @@ signed with your secrets. Of course you need to provide secure secrets and treat
### Rotating Secrets
The first String in `secrets : BackendTask (List String)` will be used to sign sessions, while the remaining String's will
The first String in `secrets : BackendTask FatalError (List String)` will be used to sign sessions, while the remaining String's will
still be used to attempt to "unsign" the cookies. So if you have a single secret:
Session.withSession
Expand Down

0 comments on commit aa02382

Please sign in to comment.