From 6f82aa59f8900c0646963f5799c341a130f09f33 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Thu, 23 Dec 2021 13:34:54 -0800 Subject: [PATCH] Add ServerRequest.withRequestTime, and make body nullable. --- elm.json | 1 + examples/pokedex/adapter.js | 7 +++-- examples/pokedex/src/Page/Greet.elm | 26 ++++++---------- generator/src/dev-server.js | 21 ++++++++----- src/DataSource/ServerRequest.elm | 48 +++++++++++++++++++---------- 5 files changed, 62 insertions(+), 41 deletions(-) diff --git a/elm.json b/elm.json index 3ef2d643..bf3e6a05 100644 --- a/elm.json +++ b/elm.json @@ -40,6 +40,7 @@ "elm/json": "1.1.3 <= v < 2.0.0", "elm/parser": "1.1.0 <= v < 2.0.0", "elm/regex": "1.0.0 <= v < 2.0.0", + "elm/time": "1.0.0 <= v < 2.0.0", "elm/url": "1.0.0 <= v < 2.0.0", "elm/virtual-dom": "1.0.2 <= v < 2.0.0", "elm-community/dict-extra": "2.4.0 <= v < 3.0.0", diff --git a/examples/pokedex/adapter.js b/examples/pokedex/adapter.js index d81a296a..0baf553c 100644 --- a/examples/pokedex/adapter.js +++ b/examples/pokedex/adapter.js @@ -119,6 +119,7 @@ exports.handler = render;` * @param {any} context */ async function render(event, context) { + const requestTime = new Date(); console.log(JSON.stringify(event)); global.staticHttpCache = {}; @@ -134,7 +135,7 @@ async function render(event, context) { require(compiledElmPath), mode, event.path, - reqToJson(event), + reqToJson(event, renderTime), addWatcher ); console.log('@@@renderResult', renderResult); @@ -185,9 +186,10 @@ async function render(event, context) { /** * @param {import('aws-lambda').APIGatewayProxyEvent} req + * @param {Date} requestTime * @returns {{ method: string; hostname: string; query: string; headers: Object; host: string; pathname: string; port: number | null; protocol: string; rawUrl: string; }} */ -function reqToJson(req) { +function reqToJson(req, requestTime) { const queryString = req.multiValueQueryStringParameters ? Object.entries(req.multiValueQueryStringParameters).reduce( (acc, [key, values]) => { return acc + values.map(value => \`\${encodeURIComponent(key)}=\${encodeURIComponent(value)}\`).join('&') @@ -206,6 +208,7 @@ function reqToJson(req) { protocol: "https", // TODO rawUrl: "", // TODO body: req.body, + requestTime: Math.round(requestTime.getTime()), }; } `; diff --git a/examples/pokedex/src/Page/Greet.elm b/examples/pokedex/src/Page/Greet.elm index b534e258..3c44ee51 100644 --- a/examples/pokedex/src/Page/Greet.elm +++ b/examples/pokedex/src/Page/Greet.elm @@ -14,6 +14,7 @@ import Pages.PageUrl exposing (PageUrl) import Pages.Url import ServerResponse import Shared +import Time import View exposing (View) @@ -41,17 +42,16 @@ page = data : ServerRequest.IsAvailable -> RouteParams -> DataSource (PageServerResponse Data) data serverRequestKey routeParams = let - serverReq : ServerRequest (Maybe String) + serverReq : ServerRequest ( Maybe String, Time.Posix ) serverReq = - ServerRequest.init identity + ServerRequest.init Tuple.pair |> ServerRequest.optionalHeader "cookie" + |> ServerRequest.withRequestTime in serverReq |> ServerRequest.toDataSource serverRequestKey |> DataSource.andThen - (\cookies -> - --DataSource.succeed (PageServerResponse.ServerResponse (ServerResponse.temporaryRedirect "/")) - --DataSource.succeed (PageServerResponse.ServerResponse (ServerResponse.stringBody (foo |> Maybe.withDefault "NOT FOUND"))) + (\( cookies, requestTime ) -> case cookies |> Maybe.withDefault "" @@ -60,23 +60,14 @@ data serverRequestKey routeParams = of Just username -> DataSource.succeed - (PageServerResponse.RenderPage { username = username }) + (PageServerResponse.RenderPage { username = username, requestTime = requestTime }) - --(PageServerResponse.ServerResponse - -- (ServerResponse.stringBody - -- "Alright, here's the secret! This is all running with elm-pages serverless :D" - -- ) - --) Nothing -> DataSource.succeed (PageServerResponse.ServerResponse (ServerResponse.temporaryRedirect "/login")) ) - ---DataSource.succeed (PageServerResponse.RenderPage {}) - - head : StaticPayload Data RouteParams -> List Head.Tag @@ -98,7 +89,9 @@ head static = type alias Data = - { username : String } + { username : String + , requestTime : Time.Posix + } view : @@ -110,6 +103,7 @@ view maybeUrl sharedModel static = { title = "Hello!" , body = [ Html.text <| "Hello " ++ static.data.username ++ "!" + , Html.text <| "Requested page at " ++ String.fromInt (Time.posixToMillis static.data.requestTime) , Html.div [] [ Html.form [ Attr.method "post" diff --git a/generator/src/dev-server.js b/generator/src/dev-server.js index 11b345eb..68347e20 100644 --- a/generator/src/dev-server.js +++ b/generator/src/dev-server.js @@ -346,19 +346,20 @@ async function start(options) { return; } - let body = ""; + const requestTime = new Date(); + /** @type {string | null} */ + let body = null; req.on("data", function (data) { + if (!body) { + body = ""; + } body += data; - - // Too much POST data, kill the connection! - // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB - if (body.length > 1e6) req.connection.destroy(); }); req.on("end", async function () { await runRenderThread( - reqToJson(req, body), + reqToJson(req, body, requestTime), pathname, function (renderResult) { const is404 = renderResult.is404; @@ -594,7 +595,12 @@ async function ensureRequiredExecutables() { } } -function reqToJson(req, body) { +/** + * @param {http.IncomingMessage} req + * @param {string | null} body + * @param {Date} requestTime + */ +function reqToJson(req, body, requestTime) { const url = new URL(req.url, "http://localhost:1234"); return { method: req.method, @@ -607,6 +613,7 @@ function reqToJson(req, body) { protocol: url.protocol, rawUrl: req.url, body: body, + requestTime: Math.round(requestTime.getTime()), }; } diff --git a/src/DataSource/ServerRequest.elm b/src/DataSource/ServerRequest.elm index af07a1ee..db99d69f 100644 --- a/src/DataSource/ServerRequest.elm +++ b/src/DataSource/ServerRequest.elm @@ -1,13 +1,22 @@ module DataSource.ServerRequest exposing - ( IsAvailable - , ServerRequest, expectHeader, init, optionalHeader, staticData, toDataSource, withFormData, withCookies, withBody, withHost, withAllHeaders, withMethod, withProtocol, Method(..), withQueryParams + ( ServerRequest, IsAvailable + , Method(..) + , init + , expectHeader, optionalHeader, withFormData, withCookies, withBody, withHost, withAllHeaders, withMethod, withProtocol, withQueryParams, withRequestTime + , toDataSource ) {-| -@docs IsAvailable +@docs ServerRequest, IsAvailable -@docs ServerRequest, expectHeader, init, optionalHeader, staticData, toDataSource, withFormData, withCookies, withBody, withHost, withAllHeaders, withMethod, withProtocol, Method, withQueryParams +@docs Method + +@docs init + +@docs expectHeader, optionalHeader, withFormData, withCookies, withBody, withHost, withAllHeaders, withMethod, withProtocol, withQueryParams, withRequestTime + +@docs toDataSource -} @@ -20,6 +29,7 @@ import Internal.ServerRequest import OptimizedDecoder import QueryParams exposing (QueryParams) import Secrets +import Time import Url @@ -34,15 +44,6 @@ init constructor = ServerRequest (OptimizedDecoder.succeed constructor) -{-| -} -staticData : DataSource.DataSource String -staticData = - DataSource.Http.get (Secrets.succeed "$$elm-pages$$headers") - (OptimizedDecoder.field "headers" - (OptimizedDecoder.field "accept-language" OptimizedDecoder.string) - ) - - {-| In order to access the ServerRequest data, you first need to turn it into a DataSource. Note that you can only access it in the context of a serverless request because there is no request @@ -70,6 +71,17 @@ expectHeader headerName (ServerRequest decoder) = |> ServerRequest +{-| -} +withRequestTime : ServerRequest (Time.Posix -> value) -> ServerRequest value +withRequestTime (ServerRequest decoder) = + decoder + |> OptimizedDecoder.andMap + (OptimizedDecoder.field "requestTime" + (OptimizedDecoder.int |> OptimizedDecoder.map Time.millisToPosix) + ) + |> ServerRequest + + {-| -} withAllHeaders : ServerRequest (Dict String String -> value) -> ServerRequest value withAllHeaders (ServerRequest decoder) = @@ -165,11 +177,15 @@ withCookies (ServerRequest decoder) = withBody : ServerRequest (Maybe String -> value) -> ServerRequest value withBody (ServerRequest decoder) = decoder - |> OptimizedDecoder.andMap - (OptimizedDecoder.optionalField "body" OptimizedDecoder.string) + |> OptimizedDecoder.andMap bodyDecoder |> ServerRequest +bodyDecoder : OptimizedDecoder.Decoder (Maybe String) +bodyDecoder = + OptimizedDecoder.field "body" (OptimizedDecoder.nullable OptimizedDecoder.string) + + {-| -} withFormData : ServerRequest @@ -197,7 +213,7 @@ withFormData (ServerRequest decoder) = OptimizedDecoder.string |> OptimizedDecoder.field "headers" ) - (OptimizedDecoder.optionalField "body" OptimizedDecoder.string) + bodyDecoder ) |> ServerRequest