mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-24 06:54:03 +03:00
Merge pull request #7 from dillonkearns/json-data-optimization
Avoid creating unnecessary Elm Dicts by directly using JSON values fo…
This commit is contained in:
commit
33fc85b1b8
@ -1,5 +1,5 @@
|
||||
it(`glob tests`, () => {
|
||||
cy.visit("/tests");
|
||||
cy.contains("All tests passed");
|
||||
cy.document().should("not.include.text", "Expected");
|
||||
cy.get(".test-pass").should("exist");
|
||||
cy.get(".test-fail").should("not.exist");
|
||||
});
|
||||
|
5
cypress/e2e/http-backend-task.cy.ts
Normal file
5
cypress/e2e/http-backend-task.cy.ts
Normal file
@ -0,0 +1,5 @@
|
||||
it(`BackendTask tests`, () => {
|
||||
cy.visit("/http-tests");
|
||||
cy.get(".test-pass").should("exist");
|
||||
cy.get(".test-fail").should("not.exist");
|
||||
});
|
@ -16,6 +16,7 @@ import Route exposing (Route)
|
||||
import Server.Request as Request
|
||||
import Server.Response as Response exposing (Response)
|
||||
import Test.Glob
|
||||
import Test.HttpRequests
|
||||
import Test.Runner.Html
|
||||
import Time
|
||||
import Xml.Decode
|
||||
@ -43,6 +44,15 @@ routes getStaticRoutes htmlToString =
|
||||
)
|
||||
|> ApiRoute.literal "tests"
|
||||
|> ApiRoute.serverRender
|
||||
, ApiRoute.succeed
|
||||
(Request.succeed
|
||||
(Test.HttpRequests.all
|
||||
|> BackendTask.map viewHtmlResults
|
||||
|> BackendTask.map html
|
||||
)
|
||||
)
|
||||
|> ApiRoute.literal "http-tests"
|
||||
|> ApiRoute.serverRender
|
||||
, requestPrinter
|
||||
, xmlDecoder
|
||||
, multipleContentTypes
|
||||
|
128
examples/end-to-end/src/Test/HttpRequests.elm
Normal file
128
examples/end-to-end/src/Test/HttpRequests.elm
Normal file
@ -0,0 +1,128 @@
|
||||
module Test.HttpRequests exposing (all)
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import BackendTask.Http
|
||||
import Exception exposing (Catchable)
|
||||
import Expect
|
||||
import Json.Decode as Decode
|
||||
import Test exposing (Test)
|
||||
|
||||
|
||||
all : BackendTask error Test
|
||||
all =
|
||||
[ BackendTask.Http.get "http://httpstat.us/500" (Decode.succeed ())
|
||||
|> test "http 500 error"
|
||||
(\result ->
|
||||
case result of
|
||||
Err error ->
|
||||
case error of
|
||||
BackendTask.Http.BadStatus metadata string ->
|
||||
metadata.statusCode
|
||||
|> Expect.equal 500
|
||||
|
||||
_ ->
|
||||
Expect.fail ("Expected BadStatus, got :" ++ Debug.toString error)
|
||||
|
||||
Ok () ->
|
||||
Expect.fail "Expected HTTP error, got Ok"
|
||||
)
|
||||
, BackendTask.Http.get "http://httpstat.us/404" (Decode.succeed ())
|
||||
|> test "http 404 error"
|
||||
(\result ->
|
||||
case result of
|
||||
Err error ->
|
||||
case error of
|
||||
BackendTask.Http.BadStatus metadata string ->
|
||||
metadata.statusCode
|
||||
|> Expect.equal 404
|
||||
|
||||
_ ->
|
||||
Expect.fail ("Expected BadStatus, got: " ++ Debug.toString error)
|
||||
|
||||
Ok () ->
|
||||
Expect.fail "Expected HTTP error, got Ok"
|
||||
)
|
||||
, BackendTask.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (Decode.field "stargazers_count" Decode.int)
|
||||
|> test "200 JSON"
|
||||
(\result ->
|
||||
case result of
|
||||
Err error ->
|
||||
Expect.fail ("Expected BadStatus, got: " ++ Debug.toString error)
|
||||
|
||||
Ok count ->
|
||||
Expect.pass
|
||||
)
|
||||
, BackendTask.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (Decode.field "this-field-doesn't-exist" Decode.int)
|
||||
|> test "JSON decoding error"
|
||||
(\result ->
|
||||
case result of
|
||||
Err (BackendTask.Http.BadBody (Just (Decode.Failure failureString _)) _) ->
|
||||
failureString
|
||||
|> Expect.equal "Expecting an OBJECT with a field named `this-field-doesn't-exist`"
|
||||
|
||||
_ ->
|
||||
Expect.fail ("Expected BadStatus, got: " ++ Debug.toString result)
|
||||
)
|
||||
, BackendTask.Http.requestWithOptions
|
||||
{ url = "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
, body = BackendTask.Http.emptyBody
|
||||
}
|
||||
{ cacheStrategy = BackendTask.Http.IgnoreCache
|
||||
, retries = 0
|
||||
, timeoutInMs = Nothing
|
||||
}
|
||||
(BackendTask.Http.expectJson
|
||||
(Decode.field "this-field-doesn't-exist" Decode.int)
|
||||
)
|
||||
|> test "cache options"
|
||||
(\result ->
|
||||
case result of
|
||||
Err (BackendTask.Http.BadBody (Just (Decode.Failure failureString _)) _) ->
|
||||
failureString
|
||||
|> Expect.equal "Expecting an OBJECT with a field named `this-field-doesn't-exist`"
|
||||
|
||||
_ ->
|
||||
Expect.fail ("Expected BadStatus, got: " ++ Debug.toString result)
|
||||
)
|
||||
, BackendTask.Http.requestWithOptions
|
||||
{ url = "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
, body = BackendTask.Http.emptyBody
|
||||
}
|
||||
{ cacheStrategy = BackendTask.Http.ForceRevalidate
|
||||
, retries = 0
|
||||
, timeoutInMs = Nothing
|
||||
}
|
||||
(BackendTask.Http.withMetadata
|
||||
(BackendTask.Http.expectJson
|
||||
(Decode.field "stargazers_count" Decode.int)
|
||||
)
|
||||
)
|
||||
|> test "with metadata"
|
||||
(\result ->
|
||||
case result of
|
||||
Ok ( metadata, stars ) ->
|
||||
metadata.statusCode
|
||||
|> Expect.equal 200
|
||||
|
||||
_ ->
|
||||
Expect.fail ("Expected Ok, got: " ++ Debug.toString result)
|
||||
)
|
||||
]
|
||||
|> BackendTask.combine
|
||||
|> BackendTask.map (Test.describe "BackendTask tests")
|
||||
|
||||
|
||||
test : String -> (Result error data -> Expect.Expectation) -> BackendTask (Catchable error) data -> BackendTask noError Test
|
||||
test name assert task =
|
||||
task
|
||||
|> BackendTask.toResult
|
||||
|> BackendTask.map
|
||||
(\result ->
|
||||
Test.test name <|
|
||||
\() ->
|
||||
assert result
|
||||
)
|
@ -468,7 +468,7 @@ sendEmail :
|
||||
-> SendGrid.Email
|
||||
-> BackendTask (Result SendGrid.Error ())
|
||||
sendEmail apiKey_ email_ =
|
||||
BackendTask.Http.uncachedRequest
|
||||
BackendTask.Http.requestWithOptions
|
||||
{ method = "POST"
|
||||
, headers = [ ( "Authorization", "Bearer " ++ apiKey_ ) ]
|
||||
, url = SendGrid.sendGridApiUrl
|
||||
|
@ -15,7 +15,7 @@ backendTask selectionSet =
|
||||
BackendTask.Env.expect "SMOOTHIES_HASURA_SECRET"
|
||||
|> BackendTask.andThen
|
||||
(\hasuraSecret ->
|
||||
BackendTask.Http.uncachedRequest
|
||||
BackendTask.Http.requestWithOptions
|
||||
{ url = hasuraUrl
|
||||
, method = "POST"
|
||||
, headers = [ ( "x-hasura-admin-secret", hasuraSecret ) ]
|
||||
@ -42,7 +42,7 @@ mutationBackendTask selectionSet =
|
||||
BackendTask.Env.expect "SMOOTHIES_HASURA_SECRET"
|
||||
|> BackendTask.andThen
|
||||
(\hasuraSecret ->
|
||||
BackendTask.Http.uncachedRequest
|
||||
BackendTask.Http.requestWithOptions
|
||||
{ url = hasuraUrl
|
||||
, method = "POST"
|
||||
, headers = [ ( "x-hasura-admin-secret", hasuraSecret ) ]
|
||||
|
@ -16,7 +16,7 @@ process.on("unhandledRejection", (error) => {
|
||||
console.error(error);
|
||||
});
|
||||
let foundErrors;
|
||||
let pendingBackendTaskResponses;
|
||||
let pendingBackendTaskResponses = new Map();
|
||||
let pendingBackendTaskCount;
|
||||
|
||||
module.exports = { render, runGenerator };
|
||||
@ -44,7 +44,7 @@ async function render(
|
||||
const { fs, resetInMemoryFs } = require("./request-cache-fs.js")(hasFsAccess);
|
||||
resetInMemoryFs();
|
||||
foundErrors = false;
|
||||
pendingBackendTaskResponses = [];
|
||||
pendingBackendTaskResponses = new Map();
|
||||
pendingBackendTaskCount = 0;
|
||||
// since init/update are never called in pre-renders, and BackendTask.Http is called using pure NodeJS HTTP fetching
|
||||
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
||||
@ -74,7 +74,7 @@ async function runGenerator(cliOptions, portsFile, elmModule) {
|
||||
const { fs, resetInMemoryFs } = require("./request-cache-fs.js")(true);
|
||||
resetInMemoryFs();
|
||||
foundErrors = false;
|
||||
pendingBackendTaskResponses = [];
|
||||
pendingBackendTaskResponses = new Map();
|
||||
pendingBackendTaskCount = 0;
|
||||
// since init/update are never called in pre-renders, and BackendTask.Http is called using pure NodeJS HTTP fetching
|
||||
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
||||
@ -178,12 +178,14 @@ function runGeneratorAppHelp(
|
||||
);
|
||||
}
|
||||
} else if (fromElm.tag === "DoHttp") {
|
||||
const requestToPerform = fromElm.args[0];
|
||||
const requestHash = fromElm.args[0];
|
||||
const requestToPerform = fromElm.args[1];
|
||||
if (
|
||||
requestToPerform.url !== "elm-pages-internal://port" &&
|
||||
requestToPerform.url.startsWith("elm-pages-internal://")
|
||||
) {
|
||||
runInternalJob(
|
||||
requestHash,
|
||||
app,
|
||||
mode,
|
||||
requestToPerform,
|
||||
@ -193,6 +195,7 @@ function runGeneratorAppHelp(
|
||||
);
|
||||
} else {
|
||||
runHttpJob(
|
||||
requestHash,
|
||||
portsFile,
|
||||
app,
|
||||
mode,
|
||||
@ -318,12 +321,14 @@ function runElmApp(
|
||||
);
|
||||
}
|
||||
} else if (fromElm.tag === "DoHttp") {
|
||||
const requestToPerform = fromElm.args[0];
|
||||
const requestHash = fromElm.args[0];
|
||||
const requestToPerform = fromElm.args[1];
|
||||
if (
|
||||
requestToPerform.url !== "elm-pages-internal://port" &&
|
||||
requestToPerform.url.startsWith("elm-pages-internal://")
|
||||
) {
|
||||
runInternalJob(
|
||||
requestHash,
|
||||
app,
|
||||
mode,
|
||||
requestToPerform,
|
||||
@ -333,6 +338,7 @@ function runElmApp(
|
||||
);
|
||||
} else {
|
||||
runHttpJob(
|
||||
requestHash,
|
||||
portsFile,
|
||||
app,
|
||||
mode,
|
||||
@ -401,6 +407,7 @@ async function outputString(
|
||||
/** @typedef { { head: any[]; errors: any[]; contentJson: any[]; html: string; route: string; title: string; } } Arg */
|
||||
|
||||
async function runHttpJob(
|
||||
requestHash,
|
||||
portsFile,
|
||||
app,
|
||||
mode,
|
||||
@ -421,14 +428,14 @@ async function runHttpJob(
|
||||
|
||||
if (lookupResponse.kind === "cache-response-path") {
|
||||
const responseFilePath = lookupResponse.value;
|
||||
pendingBackendTaskResponses.push({
|
||||
pendingBackendTaskResponses.set(requestHash, {
|
||||
request: requestToPerform,
|
||||
response: JSON.parse(
|
||||
(await fs.promises.readFile(responseFilePath, "utf8")).toString()
|
||||
),
|
||||
});
|
||||
} else if (lookupResponse.kind === "response-json") {
|
||||
pendingBackendTaskResponses.push({
|
||||
pendingBackendTaskResponses.set(requestHash, {
|
||||
request: requestToPerform,
|
||||
response: lookupResponse.value,
|
||||
});
|
||||
@ -457,6 +464,7 @@ function jsonResponse(request, json) {
|
||||
}
|
||||
|
||||
async function runInternalJob(
|
||||
requestHash,
|
||||
app,
|
||||
mode,
|
||||
requestToPerform,
|
||||
@ -468,29 +476,40 @@ async function runInternalJob(
|
||||
pendingBackendTaskCount += 1;
|
||||
|
||||
if (requestToPerform.url === "elm-pages-internal://log") {
|
||||
pendingBackendTaskResponses.push(await runLogJob(requestToPerform));
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runLogJob(requestToPerform)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://read-file") {
|
||||
pendingBackendTaskResponses.push(
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await readFileJobNew(requestToPerform, patternsToWatch)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://glob") {
|
||||
pendingBackendTaskResponses.push(
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runGlobNew(requestToPerform, patternsToWatch)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://env") {
|
||||
pendingBackendTaskResponses.push(
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runEnvJob(requestToPerform, patternsToWatch)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://encrypt") {
|
||||
pendingBackendTaskResponses.push(
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runEncryptJob(requestToPerform, patternsToWatch)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://decrypt") {
|
||||
pendingBackendTaskResponses.push(
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runDecryptJob(requestToPerform, patternsToWatch)
|
||||
);
|
||||
} else if (requestToPerform.url === "elm-pages-internal://write-file") {
|
||||
pendingBackendTaskResponses.push(await runWriteFileJob(requestToPerform));
|
||||
pendingBackendTaskResponses.set(
|
||||
requestHash,
|
||||
await runWriteFileJob(requestToPerform)
|
||||
);
|
||||
} else {
|
||||
throw `Unexpected internal BackendTask request format: ${kleur.yellow(
|
||||
JSON.stringify(2, null, requestToPerform)
|
||||
@ -623,7 +642,7 @@ async function runDecryptJob(req, patternsToWatch) {
|
||||
|
||||
function flushIfDone(app) {
|
||||
if (foundErrors) {
|
||||
pendingBackendTaskResponses = [];
|
||||
pendingBackendTaskResponses = new Map();
|
||||
} else if (pendingBackendTaskCount === 0) {
|
||||
// console.log(
|
||||
// `Flushing ${pendingBackendTaskResponses.length} items in ${timeUntilThreshold}ms`
|
||||
@ -634,10 +653,9 @@ function flushIfDone(app) {
|
||||
}
|
||||
|
||||
function flushQueue(app) {
|
||||
const temp = pendingBackendTaskResponses;
|
||||
pendingBackendTaskResponses = [];
|
||||
app.ports.gotBatchSub.send(Object.fromEntries(pendingBackendTaskResponses));
|
||||
pendingBackendTaskResponses = new Map();
|
||||
// console.log("@@@ FLUSHING", temp.length);
|
||||
app.ports.gotBatchSub.send(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,4 @@
|
||||
const path = require("path");
|
||||
const fetch = require("node-fetch");
|
||||
const objectHash = require("object-hash");
|
||||
const kleur = require("kleur");
|
||||
|
||||
@ -44,6 +43,10 @@ function fullPath(portsHash, request, hasFsAccess) {
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
|
||||
const fetch = require("make-fetch-happen").defaults({
|
||||
cachePath: "./.elm-pages/http-cache",
|
||||
cache: mode === "build" ? "no-cache" : "default",
|
||||
});
|
||||
const { fs } = require("./request-cache-fs.js")(hasFsAccess);
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const request = toRequest(rawRequest);
|
||||
@ -146,62 +149,59 @@ function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
|
||||
"User-Agent": "request",
|
||||
...request.headers,
|
||||
},
|
||||
...rawRequest.useCache,
|
||||
});
|
||||
|
||||
console.timeEnd(`fetch ${request.url}`);
|
||||
const expectString = request.headers["elm-pages-internal"];
|
||||
|
||||
if (response.ok || expectString === "ExpectResponse") {
|
||||
let body;
|
||||
let bodyKind;
|
||||
if (expectString === "ExpectJson") {
|
||||
body = await response.json();
|
||||
let body;
|
||||
let bodyKind;
|
||||
if (expectString === "ExpectJson") {
|
||||
try {
|
||||
body = await response.buffer();
|
||||
body = JSON.parse(body.toString("utf-8"));
|
||||
bodyKind = "json";
|
||||
} else if (
|
||||
expectString === "ExpectBytes" ||
|
||||
expectString === "ExpectBytesResponse"
|
||||
) {
|
||||
bodyKind = "bytes";
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
body = Buffer.from(arrayBuffer).toString("base64");
|
||||
} else if (expectString === "ExpectWhatever") {
|
||||
bodyKind = "whatever";
|
||||
body = null;
|
||||
} else if (
|
||||
expectString === "ExpectResponse" ||
|
||||
expectString === "ExpectString"
|
||||
) {
|
||||
} catch (error) {
|
||||
body = body.toString("utf8");
|
||||
bodyKind = "string";
|
||||
body = await response.text();
|
||||
} else {
|
||||
throw `Unexpected expectString ${expectString}`;
|
||||
}
|
||||
|
||||
await fs.promises.writeFile(
|
||||
responsePath,
|
||||
JSON.stringify({
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
statusCode: response.status,
|
||||
body: body,
|
||||
bodyKind,
|
||||
url: response.url,
|
||||
statusText: response.statusText,
|
||||
})
|
||||
);
|
||||
|
||||
resolve({ kind: "cache-response-path", value: responsePath });
|
||||
} else if (
|
||||
expectString === "ExpectBytes" ||
|
||||
expectString === "ExpectBytesResponse"
|
||||
) {
|
||||
body = await response.buffer();
|
||||
try {
|
||||
body = body.toString("base64");
|
||||
bodyKind = "bytes";
|
||||
} catch (e) {
|
||||
body = body.toString("utf8");
|
||||
bodyKind = "string";
|
||||
}
|
||||
} else if (expectString === "ExpectWhatever") {
|
||||
bodyKind = "whatever";
|
||||
body = null;
|
||||
} else if (
|
||||
expectString === "ExpectResponse" ||
|
||||
expectString === "ExpectString"
|
||||
) {
|
||||
bodyKind = "string";
|
||||
body = await response.text();
|
||||
} else {
|
||||
console.log("@@@ request-cache1 bad HTTP response");
|
||||
reject({
|
||||
title: "BackendTask.Http Error",
|
||||
message: `${kleur
|
||||
.yellow()
|
||||
.underline(request.url)} Bad HTTP response ${response.status} ${
|
||||
response.statusText
|
||||
}
|
||||
`,
|
||||
});
|
||||
throw `Unexpected expectString ${expectString}`;
|
||||
}
|
||||
|
||||
resolve({
|
||||
kind: "response-json",
|
||||
value: {
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
statusCode: response.status,
|
||||
body,
|
||||
bodyKind,
|
||||
url: response.url,
|
||||
statusText: response.statusText,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.trace("@@@ request-cache2 HTTP error", error);
|
||||
reject({
|
||||
|
1351
package-lock.json
generated
1351
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,7 @@
|
||||
"gray-matter": "^4.0.3",
|
||||
"jsesc": "^3.0.2",
|
||||
"kleur": "^4.1.5",
|
||||
"make-fetch-happen": "^11.0.2",
|
||||
"memfs": "^3.4.7",
|
||||
"micromatch": "^4.0.5",
|
||||
"node-fetch": "^2.6.7",
|
||||
@ -51,6 +52,7 @@
|
||||
"devDependencies": {
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/make-fetch-happen": "^10.0.1",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/serve-static": "^1.15.0",
|
||||
@ -77,4 +79,4 @@
|
||||
"bin": {
|
||||
"elm-pages": "generator/src/cli.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ module BackendTask exposing
|
||||
, andThen, resolve, combine
|
||||
, andMap
|
||||
, map2, map3, map4, map5, map6, map7, map8, map9
|
||||
, catch, throw, mapError, onError
|
||||
, catch, throw, mapError, onError, toResult
|
||||
)
|
||||
|
||||
{-| In an `elm-pages` app, each Route Module can define a value `data` which is a `BackendTask` that will be resolved **before** `init` is called. That means it is also available
|
||||
@ -82,12 +82,12 @@ Any place in your `elm-pages` app where the framework lets you pass in a value o
|
||||
|
||||
## Exception Handling
|
||||
|
||||
@docs catch, throw, mapError, onError
|
||||
@docs catch, throw, mapError, onError, toResult
|
||||
|
||||
-}
|
||||
|
||||
import Dict
|
||||
import Exception exposing (Catchable(..), Throwable)
|
||||
import Json.Encode
|
||||
import Pages.StaticHttpRequest exposing (RawRequest(..))
|
||||
|
||||
|
||||
@ -276,7 +276,7 @@ andThen fn requestInfo =
|
||||
|
||||
Request urls lookupFn ->
|
||||
if List.isEmpty urls then
|
||||
andThen fn (lookupFn Nothing Dict.empty)
|
||||
andThen fn (lookupFn Nothing (Json.Encode.object []))
|
||||
|
||||
else
|
||||
Request urls
|
||||
@ -301,7 +301,7 @@ onError fromError backendTask =
|
||||
|
||||
Request urls lookupFn ->
|
||||
if List.isEmpty urls then
|
||||
onError fromError (lookupFn Nothing Dict.empty)
|
||||
onError fromError (lookupFn Nothing (Json.Encode.object []))
|
||||
|
||||
else
|
||||
Request urls
|
||||
@ -540,3 +540,12 @@ throw : BackendTask (Catchable error) data -> BackendTask Throwable data
|
||||
throw backendTask =
|
||||
backendTask
|
||||
|> onError (Exception.throw >> fail)
|
||||
|
||||
|
||||
{-| -}
|
||||
toResult : BackendTask (Catchable error) data -> BackendTask noError (Result error data)
|
||||
toResult backendTask =
|
||||
backendTask
|
||||
|> catch
|
||||
|> andThen (Ok >> succeed)
|
||||
|> onError (Err >> succeed)
|
||||
|
@ -2,10 +2,10 @@ module BackendTask.Http exposing
|
||||
( RequestDetails
|
||||
, get, request
|
||||
, Expect, expectString, expectJson, expectBytes, expectWhatever
|
||||
, Response(..), Metadata, Error(..)
|
||||
, expectStringResponse, expectBytesResponse
|
||||
, withMetadata, Metadata
|
||||
, Error(..)
|
||||
, Body, emptyBody, stringBody, jsonBody
|
||||
, uncachedRequest
|
||||
, CacheStrategy(..), requestWithOptions
|
||||
)
|
||||
|
||||
{-| `BackendTask.Http` requests are an alternative to doing Elm HTTP requests the traditional way using the `elm/http` package.
|
||||
@ -44,11 +44,14 @@ in [this article introducing BackendTask.Http requests and some concepts around
|
||||
@docs Expect, expectString, expectJson, expectBytes, expectWhatever
|
||||
|
||||
|
||||
## Expecting Responses
|
||||
## With Metadata
|
||||
|
||||
@docs Response, Metadata, Error
|
||||
@docs withMetadata, Metadata
|
||||
|
||||
@docs expectStringResponse, expectBytesResponse
|
||||
|
||||
## Errors
|
||||
|
||||
@docs Error
|
||||
|
||||
|
||||
## Building a BackendTask.Http Request Body
|
||||
@ -60,14 +63,14 @@ and describe your use case!
|
||||
@docs Body, emptyBody, stringBody, jsonBody
|
||||
|
||||
|
||||
## Uncached Requests
|
||||
## Caching Options
|
||||
|
||||
@docs uncachedRequest
|
||||
@docs CacheStrategy, requestWithOptions
|
||||
|
||||
-}
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import Bytes exposing (Bytes)
|
||||
import Base64
|
||||
import Bytes.Decode
|
||||
import Dict exposing (Dict)
|
||||
import Exception exposing (Catchable)
|
||||
@ -164,10 +167,9 @@ as XML, for example, or give an `elm-pages` build error if the response can't be
|
||||
type Expect value
|
||||
= ExpectJson (Json.Decode.Decoder value)
|
||||
| ExpectString (String -> value)
|
||||
| ExpectResponse (Response String -> value)
|
||||
| ExpectBytesResponse (Response Bytes -> value)
|
||||
| ExpectBytes (Bytes.Decode.Decoder value)
|
||||
| ExpectWhatever value
|
||||
| ExpectMetadata (Metadata -> Expect value)
|
||||
|
||||
|
||||
{-| Gives the HTTP response body as a raw String.
|
||||
@ -205,6 +207,29 @@ expectJson =
|
||||
ExpectJson
|
||||
|
||||
|
||||
{-| -}
|
||||
withMetadata : Expect value -> Expect ( Metadata, value )
|
||||
withMetadata originalExpect =
|
||||
case originalExpect of
|
||||
ExpectJson jsonDecoder ->
|
||||
ExpectMetadata (\metadata -> ExpectJson (jsonDecoder |> Json.Decode.map (Tuple.pair metadata)))
|
||||
|
||||
ExpectString stringToValue ->
|
||||
ExpectMetadata
|
||||
(\metadata ->
|
||||
ExpectString (\string -> string |> stringToValue |> Tuple.pair metadata)
|
||||
)
|
||||
|
||||
ExpectBytes bytesDecoder ->
|
||||
ExpectMetadata (\metadata -> ExpectBytes (bytesDecoder |> Bytes.Decode.map (Tuple.pair metadata)))
|
||||
|
||||
ExpectWhatever value ->
|
||||
ExpectMetadata (\metadata -> ExpectWhatever ( metadata, value ))
|
||||
|
||||
ExpectMetadata metadataToExpect ->
|
||||
Debug.todo ""
|
||||
|
||||
|
||||
{-| -}
|
||||
expectBytes : Bytes.Decode.Decoder value -> Expect value
|
||||
expectBytes =
|
||||
@ -217,18 +242,6 @@ expectWhatever =
|
||||
ExpectWhatever
|
||||
|
||||
|
||||
{-| -}
|
||||
expectStringResponse : Expect (Response String)
|
||||
expectStringResponse =
|
||||
ExpectResponse identity
|
||||
|
||||
|
||||
{-| -}
|
||||
expectBytesResponse : Expect (Response Bytes)
|
||||
expectBytesResponse =
|
||||
ExpectBytesResponse identity
|
||||
|
||||
|
||||
expectToString : Expect a -> String
|
||||
expectToString expect =
|
||||
case expect of
|
||||
@ -238,17 +251,24 @@ expectToString expect =
|
||||
ExpectString _ ->
|
||||
"ExpectString"
|
||||
|
||||
ExpectResponse _ ->
|
||||
"ExpectResponse"
|
||||
|
||||
ExpectBytes _ ->
|
||||
"ExpectBytes"
|
||||
|
||||
ExpectWhatever _ ->
|
||||
"ExpectWhatever"
|
||||
|
||||
ExpectBytesResponse _ ->
|
||||
"ExpectBytesResponse"
|
||||
ExpectMetadata toExpect ->
|
||||
-- It's safe to call this with fake metadata to get the kind of Expect because the exposed
|
||||
-- API, `withMetadata`, will never change the type of Expect it returns based on the metadata, it simply
|
||||
-- wraps the Expect with the additional Metadata.
|
||||
-- It's important not to expose the raw `ExpectMetadata` constructor however because that would break that guarantee.
|
||||
toExpect
|
||||
{ url = ""
|
||||
, statusCode = 123
|
||||
, statusText = ""
|
||||
, headers = Dict.empty
|
||||
}
|
||||
|> expectToString
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -264,18 +284,29 @@ request request__ expect =
|
||||
, headers = request__.headers
|
||||
, method = request__.method
|
||||
, body = request__.body
|
||||
, useCache = True
|
||||
, useCache = Nothing
|
||||
}
|
||||
in
|
||||
requestRaw request_ expect
|
||||
|
||||
|
||||
{-| -}
|
||||
uncachedRequest :
|
||||
type CacheStrategy
|
||||
= UseGlobalDefault
|
||||
| IgnoreCache -- 'no-store'
|
||||
| ForceRevalidate -- 'no-cache'
|
||||
| ForceReload -- 'reload'
|
||||
| ForceCache -- 'force-cache'
|
||||
| ErrorUnlessCached -- 'only-if-cached'
|
||||
|
||||
|
||||
{-| -}
|
||||
requestWithOptions :
|
||||
RequestDetails
|
||||
-> Options
|
||||
-> Expect a
|
||||
-> BackendTask (Catchable Error) a
|
||||
uncachedRequest request__ expect =
|
||||
requestWithOptions request__ options expect =
|
||||
let
|
||||
request_ : HashRequest.Request
|
||||
request_ =
|
||||
@ -283,12 +314,52 @@ uncachedRequest request__ expect =
|
||||
, headers = request__.headers
|
||||
, method = request__.method
|
||||
, body = request__.body
|
||||
, useCache = False
|
||||
, useCache = encodeOptions options |> Just
|
||||
}
|
||||
in
|
||||
requestRaw request_ expect
|
||||
|
||||
|
||||
encodeOptions : Options -> Encode.Value
|
||||
encodeOptions options =
|
||||
Encode.object
|
||||
([ ( "cache"
|
||||
, (case options.cacheStrategy of
|
||||
UseGlobalDefault ->
|
||||
Nothing
|
||||
|
||||
IgnoreCache ->
|
||||
Just "no-store"
|
||||
|
||||
ForceRevalidate ->
|
||||
Just "no-cache"
|
||||
|
||||
ForceReload ->
|
||||
Just "reload"
|
||||
|
||||
ForceCache ->
|
||||
Just "force-cache"
|
||||
|
||||
ErrorUnlessCached ->
|
||||
Just "only-if-cached"
|
||||
)
|
||||
|> Maybe.map Encode.string
|
||||
)
|
||||
, ( "retry", Encode.int options.retries |> Just )
|
||||
, ( "timeout", options.timeoutInMs |> Maybe.map Encode.int )
|
||||
]
|
||||
|> List.filterMap
|
||||
(\( a, b ) -> b |> Maybe.map (Tuple.pair a))
|
||||
)
|
||||
|
||||
|
||||
type alias Options =
|
||||
{ cacheStrategy : CacheStrategy
|
||||
, retries : Int
|
||||
, timeoutInMs : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
{-| Build a `BackendTask.Http` request (analogous to [Http.request](https://package.elm-lang.org/packages/elm/http/latest/Http#request)).
|
||||
This function takes in all the details to build a `BackendTask.Http` request, but you can build your own simplified helper functions
|
||||
with this as a low-level detail, or you can use functions like [BackendTask.Http.get](#get).
|
||||
@ -307,7 +378,7 @@ requestRaw request__ expect =
|
||||
:: request__.headers
|
||||
, method = request__.method
|
||||
, body = request__.body
|
||||
, useCache = False
|
||||
, useCache = request__.useCache
|
||||
}
|
||||
in
|
||||
Request
|
||||
@ -327,81 +398,69 @@ requestRaw request__ expect =
|
||||
|
||||
Nothing ->
|
||||
--Err (Pages.StaticHttpRequest.UserCalledStaticHttpFail ("INTERNAL ERROR - expected request" ++ request_.url))
|
||||
Err (BadBody ("INTERNAL ERROR - expected request" ++ request_.url))
|
||||
Err (BadBody Nothing ("INTERNAL ERROR - expected request" ++ request_.url))
|
||||
)
|
||||
|> Result.andThen
|
||||
(\(RequestsAndPending.Response maybeResponse body) ->
|
||||
case ( expect, body, maybeResponse ) of
|
||||
( ExpectJson decoder, RequestsAndPending.JsonBody json, _ ) ->
|
||||
json
|
||||
|> Json.Decode.decodeValue decoder
|
||||
|> Result.mapError
|
||||
(\error ->
|
||||
error
|
||||
|> Json.Decode.errorToString
|
||||
|> BadBody
|
||||
)
|
||||
let
|
||||
maybeBadResponse : Maybe Error
|
||||
maybeBadResponse =
|
||||
case maybeResponse of
|
||||
Just response ->
|
||||
if not (response.statusCode >= 200 && response.statusCode < 300) then
|
||||
case body of
|
||||
RequestsAndPending.StringBody s ->
|
||||
BadStatus
|
||||
{ url = response.url
|
||||
, statusCode = response.statusCode
|
||||
, statusText = response.statusText
|
||||
, headers = response.headers
|
||||
}
|
||||
s
|
||||
|> Just
|
||||
|
||||
( ExpectString mapStringFn, RequestsAndPending.StringBody string, _ ) ->
|
||||
string
|
||||
|> mapStringFn
|
||||
|> Ok
|
||||
RequestsAndPending.BytesBody bytes ->
|
||||
BadStatus
|
||||
{ url = response.url
|
||||
, statusCode = response.statusCode
|
||||
, statusText = response.statusText
|
||||
, headers = response.headers
|
||||
}
|
||||
(Base64.fromBytes bytes |> Maybe.withDefault "")
|
||||
|> Just
|
||||
|
||||
( ExpectResponse mapResponse, RequestsAndPending.StringBody asStringBody, Just rawResponse ) ->
|
||||
let
|
||||
asMetadata : Metadata
|
||||
asMetadata =
|
||||
{ url = rawResponse.url
|
||||
, statusCode = rawResponse.statusCode
|
||||
, statusText = rawResponse.statusText
|
||||
, headers = rawResponse.headers
|
||||
}
|
||||
RequestsAndPending.JsonBody value ->
|
||||
BadStatus
|
||||
{ url = response.url
|
||||
, statusCode = response.statusCode
|
||||
, statusText = response.statusText
|
||||
, headers = response.headers
|
||||
}
|
||||
(Encode.encode 0 value)
|
||||
|> Just
|
||||
|
||||
rawResponseToResponse : Response String
|
||||
rawResponseToResponse =
|
||||
if 200 <= rawResponse.statusCode && rawResponse.statusCode < 300 then
|
||||
GoodStatus_ asMetadata asStringBody
|
||||
RequestsAndPending.WhateverBody ->
|
||||
BadStatus
|
||||
{ url = response.url
|
||||
, statusCode = response.statusCode
|
||||
, statusText = response.statusText
|
||||
, headers = response.headers
|
||||
}
|
||||
""
|
||||
|> Just
|
||||
|
||||
else
|
||||
BadStatus_ asMetadata asStringBody
|
||||
in
|
||||
rawResponseToResponse
|
||||
|> mapResponse
|
||||
|> Ok
|
||||
Nothing
|
||||
|
||||
( ExpectBytesResponse mapResponse, RequestsAndPending.BytesBody rawBytesBody, Just rawResponse ) ->
|
||||
let
|
||||
asMetadata : Metadata
|
||||
asMetadata =
|
||||
{ url = rawResponse.url
|
||||
, statusCode = rawResponse.statusCode
|
||||
, statusText = rawResponse.statusText
|
||||
, headers = rawResponse.headers
|
||||
}
|
||||
Nothing ->
|
||||
Nothing
|
||||
in
|
||||
case maybeBadResponse of
|
||||
Just badResponse ->
|
||||
Err badResponse
|
||||
|
||||
rawResponseToResponse : Response Bytes
|
||||
rawResponseToResponse =
|
||||
if 200 <= rawResponse.statusCode && rawResponse.statusCode < 300 then
|
||||
GoodStatus_ asMetadata rawBytesBody
|
||||
|
||||
else
|
||||
BadStatus_ asMetadata rawBytesBody
|
||||
in
|
||||
rawResponseToResponse
|
||||
|> mapResponse
|
||||
|> Ok
|
||||
|
||||
( ExpectBytes bytesDecoder, RequestsAndPending.BytesBody rawBytes, _ ) ->
|
||||
rawBytes
|
||||
|> Bytes.Decode.decode bytesDecoder
|
||||
|> Result.fromMaybe
|
||||
(BadBody "Bytes decoding failed.")
|
||||
|
||||
( ExpectWhatever whateverValue, RequestsAndPending.WhateverBody, _ ) ->
|
||||
Ok whateverValue
|
||||
|
||||
_ ->
|
||||
Err (BadBody "Unexpected combination, internal error")
|
||||
Nothing ->
|
||||
toResultThing ( expect, body, maybeResponse )
|
||||
)
|
||||
|> BackendTask.fromResult
|
||||
|> BackendTask.mapError
|
||||
@ -411,6 +470,54 @@ requestRaw request__ expect =
|
||||
)
|
||||
|
||||
|
||||
toResultThing :
|
||||
( Expect value
|
||||
, RequestsAndPending.ResponseBody
|
||||
, Maybe RequestsAndPending.RawResponse
|
||||
)
|
||||
-> Result Error value
|
||||
toResultThing ( expect, body, maybeResponse ) =
|
||||
case ( expect, body, maybeResponse ) of
|
||||
( ExpectMetadata toExpect, _, Just rawResponse ) ->
|
||||
let
|
||||
asMetadata : Metadata
|
||||
asMetadata =
|
||||
{ url = rawResponse.url
|
||||
, statusCode = rawResponse.statusCode
|
||||
, statusText = rawResponse.statusText
|
||||
, headers = rawResponse.headers
|
||||
}
|
||||
in
|
||||
toResultThing ( toExpect asMetadata, body, maybeResponse )
|
||||
|
||||
( ExpectJson decoder, RequestsAndPending.JsonBody json, _ ) ->
|
||||
json
|
||||
|> Json.Decode.decodeValue decoder
|
||||
|> Result.mapError
|
||||
(\error ->
|
||||
error
|
||||
|> Json.Decode.errorToString
|
||||
|> BadBody (Just error)
|
||||
)
|
||||
|
||||
( ExpectString mapStringFn, RequestsAndPending.StringBody string, _ ) ->
|
||||
string
|
||||
|> mapStringFn
|
||||
|> Ok
|
||||
|
||||
( ExpectBytes bytesDecoder, RequestsAndPending.BytesBody rawBytes, _ ) ->
|
||||
rawBytes
|
||||
|> Bytes.Decode.decode bytesDecoder
|
||||
|> Result.fromMaybe
|
||||
(BadBody Nothing "Bytes decoding failed.")
|
||||
|
||||
( ExpectWhatever whateverValue, RequestsAndPending.WhateverBody, _ ) ->
|
||||
Ok whateverValue
|
||||
|
||||
_ ->
|
||||
Err (BadBody Nothing "Unexpected combination, internal error")
|
||||
|
||||
|
||||
errorToString : Error -> { title : String, body : String }
|
||||
errorToString error =
|
||||
{ title = "HTTP Error"
|
||||
@ -432,7 +539,7 @@ errorToString error =
|
||||
[ TerminalText.text ("BadStatus: " ++ string)
|
||||
]
|
||||
|
||||
BadBody string ->
|
||||
BadBody _ string ->
|
||||
[ TerminalText.text ("BadBody: " ++ string)
|
||||
]
|
||||
)
|
||||
@ -449,19 +556,10 @@ type alias Metadata =
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type Response body
|
||||
= BadUrl_ String
|
||||
| Timeout_
|
||||
| NetworkError_
|
||||
| BadStatus_ Metadata body
|
||||
| GoodStatus_ Metadata body
|
||||
|
||||
|
||||
{-| -}
|
||||
type Error
|
||||
= BadUrl String
|
||||
| Timeout
|
||||
| NetworkError
|
||||
| BadStatus Metadata String
|
||||
| BadBody String
|
||||
| BadBody (Maybe Json.Decode.Error) String
|
||||
|
@ -12,7 +12,7 @@ request :
|
||||
-> BackendTask error a
|
||||
request ({ name, body, expect } as params) =
|
||||
-- elm-review: known-unoptimized-recursion
|
||||
BackendTask.Http.uncachedRequest
|
||||
BackendTask.Http.request
|
||||
{ url = "elm-pages-internal://" ++ name
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
|
@ -61,12 +61,7 @@ type alias Model route =
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= GotDataBatch
|
||||
(List
|
||||
{ request : Pages.StaticHttp.Request.Request
|
||||
, response : RequestsAndPending.Response
|
||||
}
|
||||
)
|
||||
= GotDataBatch Decode.Value
|
||||
| GotBuildError BuildError
|
||||
|
||||
|
||||
@ -154,35 +149,11 @@ cliApplication config =
|
||||
)
|
||||
|> mergeResult
|
||||
)
|
||||
, config.gotBatchSub
|
||||
|> Sub.map
|
||||
(\newBatch ->
|
||||
Decode.decodeValue batchDecoder newBatch
|
||||
|> Result.map GotDataBatch
|
||||
|> Result.mapError
|
||||
(\error ->
|
||||
("From location 2: "
|
||||
++ (error
|
||||
|> Decode.errorToString
|
||||
)
|
||||
)
|
||||
|> BuildError.internal
|
||||
|> GotBuildError
|
||||
)
|
||||
|> mergeResult
|
||||
)
|
||||
, config.gotBatchSub |> Sub.map GotDataBatch
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
batchDecoder : Decode.Decoder (List { request : Pages.StaticHttp.Request.Request, response : RequestsAndPending.Response })
|
||||
batchDecoder =
|
||||
Decode.map2 (\request response -> { request = request, response = response })
|
||||
(Decode.field "request" requestDecoder)
|
||||
(Decode.field "response" RequestsAndPending.decoder)
|
||||
|> Decode.list
|
||||
|
||||
|
||||
mergeResult : Result a a -> a
|
||||
mergeResult r =
|
||||
case r of
|
||||
@ -240,7 +211,7 @@ perform site renderRequest config effect =
|
||||
flatten site renderRequest config list
|
||||
|
||||
Effect.FetchHttp unmasked ->
|
||||
ToJsPayload.DoHttp unmasked unmasked.useCache
|
||||
ToJsPayload.DoHttp (Pages.StaticHttp.Request.hash unmasked) unmasked
|
||||
|> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
|
||||
|> config.toJsPort
|
||||
|> Cmd.map never
|
||||
@ -295,15 +266,8 @@ flagsDecoder =
|
||||
, compatibilityKey = compatibilityKey
|
||||
}
|
||||
)
|
||||
--(Decode.field "staticHttpCache"
|
||||
-- (Decode.dict
|
||||
-- (Decode.string
|
||||
-- |> Decode.map Just
|
||||
-- )
|
||||
-- )
|
||||
--)
|
||||
-- TODO remove hardcoding and decode staticHttpCache here
|
||||
(Decode.succeed Dict.empty)
|
||||
(Decode.succeed (Json.Encode.object []))
|
||||
(Decode.field "mode" Decode.string |> Decode.map (\mode -> mode == "dev-server"))
|
||||
(Decode.field "compatibilityKey" Decode.int)
|
||||
|
||||
@ -346,7 +310,7 @@ init site renderRequest config flags =
|
||||
, path = ""
|
||||
}
|
||||
]
|
||||
, allRawResponses = Dict.empty
|
||||
, allRawResponses = Json.Encode.object []
|
||||
, maybeRequestJson = renderRequest
|
||||
, isDevServer = False
|
||||
}
|
||||
@ -361,7 +325,7 @@ init site renderRequest config flags =
|
||||
, path = ""
|
||||
}
|
||||
]
|
||||
, allRawResponses = Dict.empty
|
||||
, allRawResponses = Json.Encode.object []
|
||||
, maybeRequestJson = renderRequest
|
||||
, isDevServer = False
|
||||
}
|
||||
@ -799,7 +763,7 @@ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as rende
|
||||
initialModel =
|
||||
{ staticResponses = staticResponsesNew
|
||||
, errors = []
|
||||
, allRawResponses = Dict.empty
|
||||
, allRawResponses = Json.Encode.object []
|
||||
, maybeRequestJson = renderRequest
|
||||
, isDevServer = isDevServer
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import Dict
|
||||
import Exception exposing (Throwable)
|
||||
import HtmlPrinter
|
||||
import Json.Decode as Decode
|
||||
import Json.Encode
|
||||
import Json.Encode as Encode
|
||||
import Pages.GeneratorProgramConfig exposing (GeneratorProgramConfig)
|
||||
import Pages.Internal.Platform.CompatibilityKey
|
||||
import Pages.Internal.Platform.Effect as Effect exposing (Effect)
|
||||
@ -43,12 +43,7 @@ type alias Model =
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= GotDataBatch
|
||||
(List
|
||||
{ request : Pages.StaticHttp.Request.Request
|
||||
, response : RequestsAndPending.Response
|
||||
}
|
||||
)
|
||||
= GotDataBatch Decode.Value
|
||||
| GotBuildError BuildError
|
||||
|
||||
|
||||
@ -118,23 +113,7 @@ app config =
|
||||
)
|
||||
|> mergeResult
|
||||
)
|
||||
, config.gotBatchSub
|
||||
|> Sub.map
|
||||
(\newBatch ->
|
||||
Decode.decodeValue batchDecoder newBatch
|
||||
|> Result.map GotDataBatch
|
||||
|> Result.mapError
|
||||
(\error ->
|
||||
("From location 2: "
|
||||
++ (error
|
||||
|> Decode.errorToString
|
||||
)
|
||||
)
|
||||
|> BuildError.internal
|
||||
|> GotBuildError
|
||||
)
|
||||
|> mergeResult
|
||||
)
|
||||
, config.gotBatchSub |> Sub.map GotDataBatch
|
||||
]
|
||||
, config = cliConfig
|
||||
, printAndExitFailure =
|
||||
@ -151,7 +130,7 @@ app config =
|
||||
|> Codec.encodeToValue (ToJsPayload.successCodecNew2 "" "")
|
||||
|> config.toJsPort
|
||||
|> Cmd.map never
|
||||
, printAndExitSuccess = \string -> config.toJsPort (Json.Encode.string string) |> Cmd.map never
|
||||
, printAndExitSuccess = \string -> config.toJsPort (Encode.string string) |> Cmd.map never
|
||||
}
|
||||
|
||||
|
||||
@ -216,7 +195,7 @@ perform config effect =
|
||||
flatten config list
|
||||
|
||||
Effect.FetchHttp unmasked ->
|
||||
ToJsPayload.DoHttp unmasked unmasked.useCache
|
||||
ToJsPayload.DoHttp (Pages.StaticHttp.Request.hash unmasked) unmasked
|
||||
|> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
|
||||
|> config.toJsPort
|
||||
|> Cmd.map never
|
||||
@ -283,7 +262,7 @@ init :
|
||||
-> ( Model, Effect )
|
||||
init execute flags =
|
||||
if flags.compatibilityKey == Pages.Internal.Platform.CompatibilityKey.currentCompatibilityKey then
|
||||
initLegacy execute { staticHttpCache = Dict.empty }
|
||||
initLegacy execute
|
||||
|
||||
else
|
||||
let
|
||||
@ -310,16 +289,15 @@ init execute flags =
|
||||
, path = ""
|
||||
}
|
||||
]
|
||||
, allRawResponses = Dict.empty
|
||||
, allRawResponses = Encode.object []
|
||||
, done = False
|
||||
}
|
||||
|
||||
|
||||
initLegacy :
|
||||
BackendTask Throwable ()
|
||||
-> { staticHttpCache : RequestsAndPending }
|
||||
-> ( Model, Effect )
|
||||
initLegacy execute { staticHttpCache } =
|
||||
initLegacy execute =
|
||||
let
|
||||
staticResponses : BackendTask Throwable ()
|
||||
staticResponses =
|
||||
@ -329,7 +307,7 @@ initLegacy execute { staticHttpCache } =
|
||||
initialModel =
|
||||
{ staticResponses = staticResponses
|
||||
, errors = []
|
||||
, allRawResponses = staticHttpCache
|
||||
, allRawResponses = Encode.object []
|
||||
, done = False
|
||||
}
|
||||
in
|
||||
@ -390,7 +368,7 @@ nextStepToEffect model nextStep =
|
||||
updatedModel : Model
|
||||
updatedModel =
|
||||
{ model
|
||||
| allRawResponses = Dict.empty
|
||||
| allRawResponses = Encode.object []
|
||||
, staticResponses = updatedStaticResponsesModel
|
||||
}
|
||||
in
|
||||
@ -411,7 +389,7 @@ nextStepToEffect model nextStep =
|
||||
|
||||
StaticResponses.Finish () ->
|
||||
( model
|
||||
, { body = Json.Encode.null
|
||||
, { body = Encode.null
|
||||
, staticHttpCache = Dict.empty
|
||||
, statusCode = 200
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ module Pages.Internal.Platform.StaticResponses exposing (NextStep(..), batchUpda
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import BuildError exposing (BuildError)
|
||||
import Dict
|
||||
import Exception exposing (Catchable(..), Throwable)
|
||||
import Json.Decode as Decode
|
||||
import List.Extra
|
||||
import Pages.StaticHttp.Request as HashRequest
|
||||
import Pages.StaticHttpRequest as StaticHttpRequest
|
||||
@ -24,30 +24,17 @@ renderApiRequest request =
|
||||
|
||||
|
||||
batchUpdate :
|
||||
List
|
||||
{ request : HashRequest.Request
|
||||
, response : RequestsAndPending.Response
|
||||
Decode.Value
|
||||
->
|
||||
{ model
|
||||
| allRawResponses : Decode.Value
|
||||
}
|
||||
->
|
||||
{ model
|
||||
| allRawResponses : RequestsAndPending
|
||||
}
|
||||
->
|
||||
{ model
|
||||
| allRawResponses : RequestsAndPending
|
||||
| allRawResponses : Decode.Value
|
||||
}
|
||||
batchUpdate newEntries model =
|
||||
{ model
|
||||
| allRawResponses =
|
||||
newEntries
|
||||
|> List.map
|
||||
(\{ request, response } ->
|
||||
( HashRequest.hash request
|
||||
, response
|
||||
)
|
||||
)
|
||||
|> Dict.fromList
|
||||
}
|
||||
{ model | allRawResponses = newEntries }
|
||||
|
||||
|
||||
type NextStep route value
|
||||
|
@ -89,7 +89,7 @@ headCodec canonicalSiteUrl currentPagePath =
|
||||
type ToJsSuccessPayloadNewCombined
|
||||
= PageProgress ToJsSuccessPayloadNew
|
||||
| SendApiResponse { body : Json.Encode.Value, staticHttpCache : Dict String String, statusCode : Int }
|
||||
| DoHttp Pages.StaticHttp.Request.Request Bool
|
||||
| DoHttp String Pages.StaticHttp.Request.Request
|
||||
| Port String
|
||||
| Errors (List BuildError)
|
||||
| ApiResponse
|
||||
@ -109,8 +109,8 @@ successCodecNew2 canonicalSiteUrl currentPagePath =
|
||||
PageProgress payload ->
|
||||
success payload
|
||||
|
||||
DoHttp requestUrl _ ->
|
||||
vDoHttp requestUrl requestUrl.useCache
|
||||
DoHttp hash requestUrl ->
|
||||
vDoHttp hash requestUrl
|
||||
|
||||
SendApiResponse record ->
|
||||
vSendApiResponse record
|
||||
@ -123,8 +123,8 @@ successCodecNew2 canonicalSiteUrl currentPagePath =
|
||||
|> Codec.variant1 "PageProgress" PageProgress (successCodecNew canonicalSiteUrl currentPagePath)
|
||||
|> Codec.variant2 "DoHttp"
|
||||
DoHttp
|
||||
Codec.string
|
||||
Pages.StaticHttp.Request.codec
|
||||
Codec.bool
|
||||
|> Codec.variant1 "ApiResponse"
|
||||
SendApiResponse
|
||||
(Codec.object (\body staticHttpCache statusCode -> { body = body, staticHttpCache = staticHttpCache, statusCode = statusCode })
|
||||
|
@ -11,7 +11,7 @@ type alias Request =
|
||||
, method : String
|
||||
, headers : List ( String, String )
|
||||
, body : Body
|
||||
, useCache : Bool
|
||||
, useCache : Maybe Encode.Value
|
||||
}
|
||||
|
||||
|
||||
@ -40,5 +40,5 @@ codec =
|
||||
|> Codec.field "method" .method Codec.string
|
||||
|> Codec.field "headers" .headers (Codec.list (Codec.tuple Codec.string Codec.string))
|
||||
|> Codec.field "body" .body StaticHttpBody.codec
|
||||
|> Codec.field "useCache" .useCache Codec.bool
|
||||
|> Codec.nullableField "useCache" .useCache Codec.value
|
||||
|> Codec.buildObject
|
||||
|
@ -2,6 +2,7 @@ module Pages.StaticHttpRequest exposing (Error(..), MockResolver, RawRequest(..)
|
||||
|
||||
import BuildError exposing (BuildError)
|
||||
import Dict
|
||||
import Json.Encode
|
||||
import Pages.StaticHttp.Request
|
||||
import RequestsAndPending exposing (RequestsAndPending)
|
||||
import TerminalText as Terminal
|
||||
@ -48,7 +49,7 @@ mockResolve : RawRequest error value -> MockResolver -> Result error value
|
||||
mockResolve request mockResolver =
|
||||
case request of
|
||||
Request _ lookupFn ->
|
||||
case lookupFn (Just mockResolver) Dict.empty of
|
||||
case lookupFn (Just mockResolver) (Json.Encode.object []) of
|
||||
nextRequest ->
|
||||
mockResolve nextRequest mockResolver
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
module RequestsAndPending exposing (RequestsAndPending, Response(..), ResponseBody(..), batchDecoder, bodyEncoder, decoder, get)
|
||||
module RequestsAndPending exposing (RawResponse, RequestsAndPending, Response(..), ResponseBody(..), batchDecoder, bodyEncoder, decoder, get)
|
||||
|
||||
import Base64
|
||||
import Bytes exposing (Bytes)
|
||||
@ -10,7 +10,7 @@ import Pages.StaticHttp.Request
|
||||
|
||||
|
||||
type alias RequestsAndPending =
|
||||
Dict String Response
|
||||
Decode.Value
|
||||
|
||||
|
||||
type ResponseBody
|
||||
@ -117,5 +117,9 @@ responseDecoder =
|
||||
|
||||
get : String -> RequestsAndPending -> Maybe Response
|
||||
get key requestsAndPending =
|
||||
requestsAndPending
|
||||
|> Dict.get key
|
||||
Decode.decodeValue
|
||||
(Decode.field key
|
||||
(Decode.field "response" decoder)
|
||||
)
|
||||
requestsAndPending
|
||||
|> Result.toMaybe
|
||||
|
Loading…
Reference in New Issue
Block a user