Add API for form posts, clean up Server.Request API and docs.

This commit is contained in:
Dillon Kearns 2021-12-29 07:58:40 -08:00
parent bf98a76aed
commit 6534b10534
2 changed files with 142 additions and 10 deletions

View File

@ -27,6 +27,7 @@ routes getStaticRoutes htmlToString =
, repoStars2 , repoStars2
, logout , logout
, greet , greet
, fileLength
] ]
@ -40,6 +41,11 @@ greet =
(\field optionalField -> (\field optionalField ->
field "first" field "first"
) )
, Server.Request.expectQueryParam "first"
, Server.Request.expectMultiPartFormPost
(\{ field, optionalField } ->
field "first"
)
] ]
|> Server.Request.thenRespond |> Server.Request.thenRespond
(\firstName -> (\firstName ->
@ -54,6 +60,39 @@ greet =
|> ApiRoute.serverRender |> ApiRoute.serverRender
fileLength : ApiRoute ApiRoute.Response
fileLength =
ApiRoute.succeed
(Server.Request.expectMultiPartFormPost
(\{ field, optionalField, fileField } ->
fileField "file"
)
|> Server.Request.thenRespond
(\file ->
ServerResponse.json
(Json.Encode.object
[ ( "File name: ", Json.Encode.string file.name )
, ( "Length", Json.Encode.int (String.length file.body) )
, ( "mime-type", Json.Encode.string file.mimeType )
, ( "First line"
, Json.Encode.string
(file.body
|> String.split "\n"
|> List.head
|> Maybe.withDefault ""
)
)
]
)
|> DataSource.succeed
)
)
|> ApiRoute.literal "api"
|> ApiRoute.slash
|> ApiRoute.literal "file"
|> ApiRoute.serverRender
redirectRoute : ApiRoute ApiRoute.Response redirectRoute : ApiRoute ApiRoute.Response
redirectRoute = redirectRoute =
ApiRoute.succeed ApiRoute.succeed

View File

@ -1,8 +1,16 @@
module Server.Request exposing module Server.Request exposing
( ServerRequest ( ServerRequest
, Method(..) , Method(..)
, init , succeed
, Handler, Handlers, andMap, cookie, errorToString, expectCookie, expectFormField, expectQueryParam, getDecoder, map, map2, oneOf, oneOfHandler, requestTime, succeed, thenRespond, expectHeader, optionalHeader, expectContentType, expectFormPost, expectJsonBody , Handler, Handlers
, oneOfHandler, requestTime, thenRespond, optionalHeader, expectContentType, expectJsonBody, acceptMethod
, map, map2, oneOf, andMap
, expectQueryParam
, cookie, expectCookie
, expectHeader
, expectFormField, expectFormPost
, File, expectMultiPartFormPost
, errorToString, getDecoder
) )
{-| {-|
@ -11,9 +19,46 @@ module Server.Request exposing
@docs Method @docs Method
@docs init @docs succeed
@docs Handler, Handlers, andMap, cookie, errorToString, expectCookie, expectFormField, expectQueryParam, getDecoder, map, map2, oneOf, oneOfHandler, requestTime, succeed, thenRespond, expectHeader, optionalHeader, expectContentType, expectFormPost, expectJsonBody @docs Handler, Handlers
@docs oneOfHandler, requestTime, thenRespond, optionalHeader, expectContentType, expectJsonBody, acceptMethod
## Transforming
@docs map, map2, oneOf, andMap
## Query Parameters
@docs expectQueryParam
## Cookies
@docs cookie, expectCookie
## Headers
@docs expectHeader
## Form Posts
@docs expectFormField, expectFormPost
## Multi-part forms and file uploads
@docs File, expectMultiPartFormPost
## Internals
@docs errorToString, getDecoder
-} -}
@ -106,12 +151,6 @@ errorToString validationError =
"Unable to parse JSON body\n" ++ Json.Decode.errorToString error "Unable to parse JSON body\n" ++ Json.Decode.errorToString error
{-| -}
init : constructor -> ServerRequest constructor
init constructor =
ServerRequest (OptimizedDecoder.succeed (Ok constructor))
{-| -} {-| -}
map : (a -> b) -> ServerRequest a -> ServerRequest b map : (a -> b) -> ServerRequest a -> ServerRequest b
map mapFn (ServerRequest decoder) = map mapFn (ServerRequest decoder) =
@ -353,6 +392,36 @@ optionalFormField_ name =
|> ServerRequest |> ServerRequest
{-| -}
type alias File =
{ name : String
, mimeType : String
, body : String
}
fileField_ : String -> ServerRequest File
fileField_ name =
OptimizedDecoder.optionalField name
(OptimizedDecoder.oneOf
[ OptimizedDecoder.map3 File
(OptimizedDecoder.field "filename" OptimizedDecoder.string)
(OptimizedDecoder.field "mimeType" OptimizedDecoder.string)
(OptimizedDecoder.field "body" OptimizedDecoder.string)
]
)
|> OptimizedDecoder.map
(\value ->
case value of
Just justValue ->
Ok justValue
Nothing ->
Err (ValidationError ("Missing form field " ++ name))
)
|> ServerRequest
{-| -} {-| -}
expectFormPost : ((String -> ServerRequest String) -> (String -> ServerRequest (Maybe String)) -> ServerRequest decodedForm) -> ServerRequest decodedForm expectFormPost : ((String -> ServerRequest String) -> (String -> ServerRequest (Maybe String)) -> ServerRequest decodedForm) -> ServerRequest decodedForm
expectFormPost toForm = expectFormPost toForm =
@ -366,6 +435,30 @@ expectFormPost toForm =
) )
{-| -}
expectMultiPartFormPost :
({ field : String -> ServerRequest String
, optionalField : String -> ServerRequest (Maybe String)
, fileField : String -> ServerRequest File
}
-> ServerRequest decodedForm
)
-> ServerRequest decodedForm
expectMultiPartFormPost toForm =
map2 (\_ value -> value)
(expectContentType "multipart/form-data")
(toForm
{ field = formField_
, optionalField = optionalFormField_
, fileField = fileField_
}
|> (\(ServerRequest decoder) -> decoder)
|> OptimizedDecoder.field "multiPartFormData"
|> ServerRequest
|> acceptMethod ( Post, [] )
)
{-| -} {-| -}
body : ServerRequest (Maybe String) body : ServerRequest (Maybe String)
body = body =