pull fetch code out of homeserverapi
This commit is contained in:
parent
d635773ac0
commit
aa86748cdd
3 changed files with 88 additions and 55 deletions
|
@ -1,4 +1,5 @@
|
|||
import HomeServerApi from "./matrix/hs-api.js";
|
||||
import fetchRequest from "./matrix/net/fetch.js";
|
||||
import StorageFactory from "./matrix/storage/idb/create.js";
|
||||
import SessionsStore from "./matrix/sessions-store/localstorage/SessionsStore.js";
|
||||
import BrawlViewModel from "./domain/BrawlViewModel.js";
|
||||
|
@ -6,9 +7,10 @@ import BrawlView from "./ui/web/BrawlView.js";
|
|||
|
||||
export default async function main(container) {
|
||||
try {
|
||||
const request = fetchRequest;
|
||||
const vm = new BrawlViewModel({
|
||||
storageFactory: new StorageFactory(),
|
||||
createHsApi: (homeServer, accessToken = null) => new HomeServerApi(homeServer, accessToken),
|
||||
createHsApi: (homeServer, accessToken = null) => new HomeServerApi({homeServer, accessToken, request}),
|
||||
sessionStore: new SessionsStore("brawl_sessions_v1"),
|
||||
clock: Date //just for `now` fn
|
||||
});
|
||||
|
|
|
@ -1,30 +1,25 @@
|
|||
import {
|
||||
HomeServerError,
|
||||
RequestAbortError,
|
||||
NetworkError
|
||||
} from "./error.js";
|
||||
|
||||
class RequestWrapper {
|
||||
constructor(promise, controller) {
|
||||
if (!controller) {
|
||||
const abortPromise = new Promise((_, reject) => {
|
||||
this._controller = {
|
||||
abort() {
|
||||
const err = new Error("fetch request aborted");
|
||||
err.name = "AbortError";
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
this._promise = Promise.race([promise, abortPromise]);
|
||||
} else {
|
||||
this._promise = promise;
|
||||
this._controller = controller;
|
||||
}
|
||||
constructor(method, url, requestResult) {
|
||||
this._requestResult = requestResult;
|
||||
this._promise = this._requestResult.response().then(response => {
|
||||
// ok?
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response.body;
|
||||
} else {
|
||||
switch (response.status) {
|
||||
default:
|
||||
throw new HomeServerError(method, url, response.body);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
abort() {
|
||||
this._controller.abort();
|
||||
return this._requestResult.abort();
|
||||
}
|
||||
|
||||
response() {
|
||||
|
@ -32,13 +27,13 @@ class RequestWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
// todo: everywhere here, encode params in the url that could have slashes ... mainly event ids?
|
||||
export default class HomeServerApi {
|
||||
constructor(homeserver, accessToken) {
|
||||
constructor({homeServer, accessToken, request}) {
|
||||
// store these both in a closure somehow so it's harder to get at in case of XSS?
|
||||
// one could change the homeserver as well so the token gets sent there, so both must be protected from read/write
|
||||
this._homeserver = homeserver;
|
||||
this._homeserver = homeServer;
|
||||
this._accessToken = accessToken;
|
||||
this._requestFn = request;
|
||||
}
|
||||
|
||||
_url(csPath) {
|
||||
|
@ -66,42 +61,12 @@ export default class HomeServerApi {
|
|||
headers.append("Content-Type", "application/json");
|
||||
bodyString = JSON.stringify(body);
|
||||
}
|
||||
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
||||
// TODO: set authenticated headers with second arguments, cache them
|
||||
let promise = fetch(url, {
|
||||
const requestResult = this._requestFn(url, {
|
||||
method,
|
||||
headers,
|
||||
body: bodyString,
|
||||
signal: controller && controller.signal,
|
||||
mode: "cors",
|
||||
credentials: "omit",
|
||||
referrer: "no-referrer",
|
||||
cache: "no-cache",
|
||||
});
|
||||
promise = promise.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
} else {
|
||||
switch (response.status) {
|
||||
default:
|
||||
throw new HomeServerError(method, url, await response.json())
|
||||
}
|
||||
}
|
||||
}, err => {
|
||||
if (err.name === "AbortError") {
|
||||
throw new RequestAbortError();
|
||||
} else if (err instanceof TypeError) {
|
||||
// Network errors are reported as TypeErrors, see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
|
||||
// this can either mean user is offline, server is offline, or a CORS error (server misconfiguration).
|
||||
//
|
||||
// One could check navigator.onLine to rule out the first
|
||||
// but the 2 later ones are indistinguishable from javascript.
|
||||
throw new NetworkError(`${method} ${url}: ${err.message}`);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
return new RequestWrapper(promise, controller);
|
||||
return new RequestWrapper(method, url, requestResult);
|
||||
}
|
||||
|
||||
_post(csPath, queryParams, body) {
|
||||
|
|
66
src/matrix/net/fetch.js
Normal file
66
src/matrix/net/fetch.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
import {
|
||||
RequestAbortError,
|
||||
NetworkError
|
||||
} from "../error.js";
|
||||
|
||||
class RequestResult {
|
||||
constructor(promise, controller) {
|
||||
if (!controller) {
|
||||
const abortPromise = new Promise((_, reject) => {
|
||||
this._controller = {
|
||||
abort() {
|
||||
const err = new Error("fetch request aborted");
|
||||
err.name = "AbortError";
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
this._promise = Promise.race([promise, abortPromise]);
|
||||
} else {
|
||||
this._promise = promise;
|
||||
this._controller = controller;
|
||||
}
|
||||
}
|
||||
|
||||
abort() {
|
||||
this._controller.abort();
|
||||
}
|
||||
|
||||
response() {
|
||||
return this._promise;
|
||||
}
|
||||
}
|
||||
|
||||
export default function fetchRequest(url, options) {
|
||||
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
||||
if (controller) {
|
||||
options = Object.assign(options, {
|
||||
signal: controller.signal
|
||||
});
|
||||
}
|
||||
options = Object.assign(options, {
|
||||
mode: "cors",
|
||||
credentials: "omit",
|
||||
referrer: "no-referrer",
|
||||
cache: "no-cache",
|
||||
});
|
||||
const promise = fetch(url, options).then(async response => {
|
||||
const {status} = response;
|
||||
const body = await response.json();
|
||||
return {status, body};
|
||||
}, err => {
|
||||
if (err.name === "AbortError") {
|
||||
throw new RequestAbortError();
|
||||
} else if (err instanceof TypeError) {
|
||||
// Network errors are reported as TypeErrors, see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
|
||||
// this can either mean user is offline, server is offline, or a CORS error (server misconfiguration).
|
||||
//
|
||||
// One could check navigator.onLine to rule out the first
|
||||
// but the 2 later ones are indistinguishable from javascript.
|
||||
throw new NetworkError(`${options.method} ${url}: ${err.message}`);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
return new RequestResult(promise, controller);
|
||||
}
|
Reference in a new issue