static: rewrite API Fetching to make API Version upgrades easier

This commit is contained in:
Jens Langhammer 2020-11-26 22:37:41 +01:00
parent f83087d04d
commit c7b6eac33d
13 changed files with 79 additions and 106 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,5 @@
import { DefaultClient } from "./client";
export class Application {
pk?: string;
name?: string;
@ -11,8 +13,6 @@ export class Application {
policies?: string[];
static get(slug: string): Promise<Application> {
return fetch(`/api/v2beta/core/applications/${slug}/`)
.then((r) => r.json())
.then((r) => <Application>r);
return DefaultClient.fetch<Application>("core", "applications", slug);
}
}

View file

@ -0,0 +1,30 @@
import { NotFoundError, RequestError } from "./errors";
export const VERSION = "v2beta";
export class Client {
private makeUrl(...url: string[]): string {
return `/api/${VERSION}/${url.join("/")}/`;
}
fetch<T>(...url: string[]): Promise<T> {
return fetch(this.makeUrl(...url))
.then((r) => {
if (r.status > 300) {
switch (r.status) {
case 404:
throw new NotFoundError(
`URL ${this.makeUrl(...url)} not found`
);
default:
throw new RequestError(r.statusText);
}
}
return r;
})
.then((r) => r.json())
.then((r) => <T>r);
}
}
export const DefaultClient = new Client();

View file

@ -1,10 +1,10 @@
export interface Config {
branding_logo: string;
branding_title: string;
}
import { DefaultClient } from "./client";
export function getConfig(): Promise<Config> {
return fetch("/api/v2beta/root/config/")
.then((r) => r.json())
.then((r) => <Config>r);
export class Config {
branding_logo?: string;
branding_title?: string;
static get(): Promise<Config> {
return DefaultClient.fetch<Config>("root", "config");
}
}

View file

@ -1 +1,2 @@
export class NotFoundError extends Error {}
export class RequestError extends Error {}

View file

@ -1,5 +1,14 @@
export function tokenByIdentifier(identifier: string): Promise<string> {
return fetch(`/api/v2beta/core/tokens/${identifier}/view_key/`)
.then((r) => r.json())
.then((r) => r["key"]);
import { DefaultClient } from "./client";
interface TokenResponse {
key: string;
}
export function tokenByIdentifier(identifier: string): Promise<string> {
return DefaultClient.fetch<TokenResponse>(
"core",
"tokens",
identifier,
"view_key"
).then((r) => r.key);
}

View file

@ -1,14 +1,15 @@
export interface User {
pk: number;
username: string;
name: string;
is_superuser: boolean;
email: boolean;
avatar: string;
}
import { Primitive } from "lit-html/lib/parts";
import { DefaultClient } from "./client";
export function me(): Promise<User> {
return fetch("/api/v2beta/core/users/me/")
.then((r) => r.json())
.then((r) => <User>r);
export class User {
pk?: number;
username?: string;
name?: string;
is_superuser?: boolean;
email?: boolean;
avatar?: string;
static me(): Promise<User> {
return DefaultClient.fetch<User>("core", "users", "me");
}
}

View file

@ -54,7 +54,7 @@ export class Messages extends LitElement {
console.debug(`passbook/messages: closed ws connection: ${e}`);
setTimeout(() => {
console.debug(
`passbook/messages: reconnecting ws in ${this.retryDelay}`
`passbook/messages: reconnecting ws in ${this.retryDelay}ms`
);
this.connect();
}, this.retryDelay);
@ -67,7 +67,7 @@ export class Messages extends LitElement {
this.messageSocket.addEventListener("error", (e) => {
console.warn(`passbook/messages: error ${e}`);
this.retryDelay = this.retryDelay * 2;
})
});
}
/* Fetch messages which were stored in the session.
@ -90,9 +90,7 @@ export class Messages extends LitElement {
}
renderMessage(message: Message) {
const container = <HTMLElement>(
this.querySelector(".pf-c-alert-group")!
);
const container = <HTMLElement>this.querySelector(".pf-c-alert-group")!;
const id = ID("pb-message");
const el = document.createElement("template");
el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">

View file

@ -13,7 +13,7 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
// @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
import { me, User } from "../api/user";
import { User } from "../api/user";
export interface SidebarItem {
name: string;
@ -166,7 +166,7 @@ export class Sidebar extends LitElement {
constructor() {
super();
me().then((u) => (this.user = u));
User.me().then((u) => (this.user = u));
this.activePath = window.location.hash.slice(1, Infinity);
window.addEventListener("hashchange", (e) => {
this.activePath = window.location.hash.slice(1, Infinity);

View file

@ -3,7 +3,7 @@ import { css, customElement, html, LitElement, property } from "lit-element";
import PageStyle from "@patternfly/patternfly/components/Page/page.css";
// @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
import { Config, getConfig } from "../api/config";
import { Config } from "../api/config";
@customElement("pb-sidebar-brand")
export class SidebarBrand extends LitElement {
@ -37,7 +37,7 @@ export class SidebarBrand extends LitElement {
constructor() {
super();
getConfig().then((c) => (this.config = c));
Config.get().then((c) => (this.config = c));
}
render() {

View file

@ -1,64 +0,0 @@
// Fetch from data-attributes
document.querySelectorAll("[data-pb-fetch-fill]").forEach((el) => {
const url = el.dataset.pbFetchFill;
const key = el.dataset.pbFetchKey;
fetch(url)
.then((r) => r.json())
.then((r) => {
el.textContent = r[key];
el.value = r[key];
});
});
// Modal
document.querySelectorAll("[data-target='modal']").forEach((m) => {
m.addEventListener("click", (e) => {
const parentContainer = e.target.closest('[data-target="modal"]');
const modalId = parentContainer.attributes["data-modal"].value;
document.querySelector(`#${modalId}`).removeAttribute("hidden");
});
});
document.querySelectorAll(".pf-c-modal-box [data-modal-close]").forEach((b) => {
b.addEventListener("click", (e) => {
const parentContainer = e.target.closest(".pf-c-backdrop");
parentContainer.setAttribute("hidden", true);
});
});
// Make Checkbox label click trigger checkbox toggle
document.querySelectorAll(".pf-c-check__label").forEach((checkLabel) => {
checkLabel.addEventListener("click", (e) => {
const checkbox = e.target.parentElement.querySelector(
"input[type=checkbox]"
);
checkbox.checked = !checkbox.checked;
});
});
// Hamburger Menu
document
.querySelectorAll(".pf-c-page__header-brand-toggle>button")
.forEach((toggle) => {
toggle.addEventListener("click", (e) => {
const sidebar = document.querySelector(".pf-c-page__sidebar");
if (sidebar.classList.contains("pf-m-expanded")) {
// Sidebar already expanded
sidebar.classList.remove("pf-m-expanded");
sidebar.style.zIndex = 0;
} else {
// Sidebar not expanded yet
sidebar.classList.add("pf-m-expanded");
sidebar.style.zIndex = 200;
}
});
});
// Collapsable Menus in Sidebar
document
.querySelectorAll(".pf-m-expandable>.pf-c-nav__link")
.forEach((menu) => {
menu.addEventListener("click", (e) => {
e.preventDefault();
menu.parentElement.classList.toggle("pf-m-expanded");
});
});

View file

@ -1,7 +1,5 @@
import "construct-style-sheets-polyfill";
import "./legacy.js";
import "./elements/ActionButton";
import "./elements/AdminLoginsChart";
import "./elements/CodeMirror";