web: add sentry, add spinner button as base for action button

This commit is contained in:
Jens Langhammer 2020-11-29 18:10:12 +01:00
parent 2417d5a59e
commit a8dad2e393
13 changed files with 266 additions and 104 deletions

View file

@ -30,3 +30,5 @@ values =
[bumpversion:file:passbook/__init__.py] [bumpversion:file:passbook/__init__.py]
[bumpversion:file:proxy/pkg/version.go] [bumpversion:file:proxy/pkg/version.go]
[bumpversion:file:web/src/constants.ts]

View file

@ -15,6 +15,10 @@ class ConfigSerializer(Serializer):
branding_logo = ReadOnlyField() branding_logo = ReadOnlyField()
branding_title = ReadOnlyField() branding_title = ReadOnlyField()
error_reporting_enabled = ReadOnlyField()
error_reporting_environment = ReadOnlyField()
error_reporting_send_pii = ReadOnlyField()
def create(self, request: Request) -> Response: def create(self, request: Request) -> Response:
raise NotImplementedError raise NotImplementedError
@ -34,6 +38,9 @@ class ConfigsViewSet(ViewSet):
{ {
"branding_logo": CONFIG.y("passbook.branding.logo"), "branding_logo": CONFIG.y("passbook.branding.logo"),
"branding_title": CONFIG.y("passbook.branding.title"), "branding_title": CONFIG.y("passbook.branding.title"),
"error_reporting_enabled": CONFIG.y("error_reporting.enabled"),
"error_reporting_environment": CONFIG.y("error_reporting.environment"),
"error_reporting_send_pii": CONFIG.y("error_reporting.send_pii"),
} }
) )
return Response(config.data) return Response(config.data)

35
web/dist/main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

111
web/package-lock.json generated
View file

@ -96,6 +96,117 @@
} }
} }
}, },
"@sentry/browser": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.27.6.tgz",
"integrity": "sha512-pqrojE2ZmLUVz7l/ogtogK0+M2pK3bigYm0fja7vG7F7kXnCAwqAHDYfkFXEvFI8WvNwH+niy28lSoV95lnm0Q==",
"requires": {
"@sentry/core": "5.27.6",
"@sentry/types": "5.27.6",
"@sentry/utils": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/core": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.27.6.tgz",
"integrity": "sha512-izCS5iyc6HAfpW1AsGXLAKetx82C1Sq1siAh97tOlSK58PVJAEH/WMiej9WuZJxCDTOtj94QtoLflssrZyAtFg==",
"requires": {
"@sentry/hub": "5.27.6",
"@sentry/minimal": "5.27.6",
"@sentry/types": "5.27.6",
"@sentry/utils": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/hub": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.27.6.tgz",
"integrity": "sha512-bOMky3iu7zEghSaWmTayfme5tCpUok841qDCGxGKuyAtOhBDsgGNS/ApNEEDF2fyX0oo4G1cHYPWhX90ZFf/xA==",
"requires": {
"@sentry/types": "5.27.6",
"@sentry/utils": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/minimal": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.27.6.tgz",
"integrity": "sha512-pKhzVQX9nL4m1dcnb2i2Y47IWVNs+K3wiYLgCB9hl9+ApxppfOc+fquiFoCloST3IuaD4yly2TtbOJgAMWcMxQ==",
"requires": {
"@sentry/hub": "5.27.6",
"@sentry/types": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/tracing": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.27.6.tgz",
"integrity": "sha512-ms3vprEId+hi8hcqtf8weqsNGASaDXAZzIOT4g2gASGpwLb5hLuScpM8z6Yhu5FGjb8DektlW5OrXJSsStIozw==",
"requires": {
"@sentry/hub": "5.27.6",
"@sentry/minimal": "5.27.6",
"@sentry/types": "5.27.6",
"@sentry/utils": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/types": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.27.6.tgz",
"integrity": "sha512-XOW9W8DrMk++4Hk7gWi9o5VR0o/GrqGfTKyFsHSIjqt2hL6kiMPvKeb2Hhmp7Iq37N2bDmRdWpM5m+68S2Jk6w=="
},
"@sentry/utils": {
"version": "5.27.6",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.27.6.tgz",
"integrity": "sha512-/QMVLv+zrTfiIj2PU+SodSbSzD5MmamMOaljkDsRIVsj6gpkm1/VG1g2+40TZ0FbQ4hCW2F+iR7cnqzZBNmchA==",
"requires": {
"@sentry/types": "5.27.6",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@types/chart.js": { "@types/chart.js": {
"version": "2.9.28", "version": "2.9.28",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.28.tgz", "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.28.tgz",

View file

@ -8,6 +8,8 @@
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1", "@fortawesome/fontawesome-free": "^5.15.1",
"@patternfly/patternfly": "^4.65.6", "@patternfly/patternfly": "^4.65.6",
"@sentry/browser": "^5.27.6",
"@sentry/tracing": "^5.27.6",
"@types/chart.js": "^2.9.28", "@types/chart.js": "^2.9.28",
"@types/codemirror": "0.0.100", "@types/codemirror": "0.0.100",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",

View file

@ -1,10 +1,31 @@
import { DefaultClient } from "./client"; import { DefaultClient } from "./client";
import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants";
export class Config { export class Config {
branding_logo?: string; branding_logo?: string;
branding_title?: string; branding_title?: string;
error_reporting_enabled?: boolean;
error_reporting_environment?: string;
error_reporting_send_pii?: boolean;
static get(): Promise<Config> { static get(): Promise<Config> {
return DefaultClient.fetch<Config>(["root", "config"]); return DefaultClient.fetch<Config>(["root", "config"])
.then((config) => {
if (config.error_reporting_enabled) {
Sentry.init({
dsn: "https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3",
release: `passbook@${VERSION}`,
integrations: [new Integrations.BrowserTracing()],
tracesSampleRate: 1.0,
environment: config.error_reporting_environment,
sendDefaultPii: config.error_reporting_send_pii,
});
console.debug(`passbook/config: Sentry enabled.`);
}
return config;
});
} }
} }

View file

@ -28,3 +28,4 @@ export const ColorStyles = css`
background-color: var(--pf-global--danger-color--100); background-color: var(--pf-global--danger-color--100);
} }
`; `;
export const VERSION = "0.12.11-stable";

View file

@ -1,64 +1,13 @@
import { getCookie } from "../utils"; import { getCookie } from "../utils";
import { css, customElement, html, LitElement, property } from "lit-element"; import { customElement, html, property } from "lit-element";
// @ts-ignore import { ERROR_CLASS, SUCCESS_CLASS } from "../constants";
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; import { SpinnerButton } from "./SpinnerButton";
// @ts-ignore
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
// @ts-ignore
import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css";
import {
ColorStyles,
ERROR_CLASS,
PRIMARY_CLASS,
PROGRESS_CLASS,
SUCCESS_CLASS,
} from "../constants";
@customElement("pb-action-button") @customElement("pb-action-button")
export class ActionButton extends LitElement { export class ActionButton extends SpinnerButton {
@property() @property()
url: string = ""; url: string = "";
@property()
isRunning = false;
static get styles() {
return [
GlobalsStyle,
ButtonStyle,
SpinnerStyle,
ColorStyles,
css`
button {
/* Have to use !important here, as buttons with pf-m-progress have transition already */
transition: all var(--pf-c-button--m-progress--TransitionDuration) ease 0s !important;
}
`,
];
}
constructor() {
super();
this.classList.add(PRIMARY_CLASS);
}
setLoading() {
this.isRunning = true;
this.classList.add(PROGRESS_CLASS);
this.requestUpdate();
}
setDone(statusClass: string) {
this.isRunning = false;
this.classList.remove(PROGRESS_CLASS);
this.classList.replace(PRIMARY_CLASS, statusClass);
this.requestUpdate();
setTimeout(() => {
this.classList.replace(statusClass, PRIMARY_CLASS);
this.requestUpdate();
}, 1000);
}
callAction() { callAction() {
if (this.isRunning === true) { if (this.isRunning === true) {
return; return;
@ -80,26 +29,4 @@ export class ActionButton extends LitElement {
this.setDone(ERROR_CLASS); this.setDone(ERROR_CLASS);
}); });
} }
render() {
return html`<button
class="pf-c-button pf-m-progress ${this.classList.toString()}"
@click=${() => this.callAction()}
>
${this.isRunning
? html` <span class="pf-c-button__progress">
<span
class="pf-c-spinner pf-m-md"
role="progressbar"
aria-valuetext="Loading..."
>
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</span>`
: ""}
<slot></slot>
</button>`;
}
} }

View file

@ -11,6 +11,8 @@ import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
import fa from "@fortawesome/fontawesome-free/css/solid.css"; import fa from "@fortawesome/fontawesome-free/css/solid.css";
import { convertToSlug } from "../utils"; import { convertToSlug } from "../utils";
import { SpinnerButton } from "./SpinnerButton";
import { PRIMARY_CLASS } from "../constants";
@customElement("pb-modal-button") @customElement("pb-modal-button")
export class ModalButton extends LitElement { export class ModalButton extends LitElement {
@ -119,6 +121,9 @@ export class ModalButton extends LitElement {
this.querySelector("[slot=modal]")!.innerHTML = t; this.querySelector("[slot=modal]")!.innerHTML = t;
this.updateHandlers(); this.updateHandlers();
this.open = true; this.open = true;
this.querySelectorAll<SpinnerButton>("pb-spinner-button").forEach(sb => {
sb.setDone(PRIMARY_CLASS);
});
}) })
.catch((e) => { .catch((e) => {
console.error(e); console.error(e);

View file

@ -0,0 +1,84 @@
import { css, customElement, html, LitElement, property } from "lit-element";
// @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
// @ts-ignore
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
// @ts-ignore
import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css";
import {
ColorStyles,
PRIMARY_CLASS,
PROGRESS_CLASS,
} from "../constants";
@customElement("pb-spinner-button")
export class SpinnerButton extends LitElement {
@property()
isRunning = false;
static get styles() {
return [
GlobalsStyle,
ButtonStyle,
SpinnerStyle,
ColorStyles,
css`
button {
/* Have to use !important here, as buttons with pf-m-progress have transition already */
transition: all var(--pf-c-button--m-progress--TransitionDuration) ease 0s !important;
}
`,
];
}
constructor() {
super();
this.classList.add(PRIMARY_CLASS);
}
setLoading() {
this.isRunning = true;
this.classList.add(PROGRESS_CLASS);
this.requestUpdate();
}
setDone(statusClass: string) {
this.isRunning = false;
this.classList.remove(PROGRESS_CLASS);
this.classList.replace(PRIMARY_CLASS, statusClass);
this.requestUpdate();
setTimeout(() => {
this.classList.replace(statusClass, PRIMARY_CLASS);
this.requestUpdate();
}, 1000);
}
callAction() {
if (this.isRunning === true) {
return;
}
this.setLoading();
}
render() {
return html`<button
class="pf-c-button pf-m-progress ${this.classList.toString()}"
@click=${() => this.callAction()}
>
${this.isRunning
? html` <span class="pf-c-button__progress">
<span
class="pf-c-spinner pf-m-md"
role="progressbar"
aria-valuetext="Loading..."
>
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</span>`
: ""}
<slot></slot>
</button>`;
}
}

View file

@ -1,6 +1,7 @@
import "construct-style-sheets-polyfill"; import "construct-style-sheets-polyfill";
import "./elements/ActionButton"; import "./elements/ActionButton";
import "./elements/SpinnerButton";
import "./elements/AdminLoginsChart"; import "./elements/AdminLoginsChart";
import "./elements/CodeMirror"; import "./elements/CodeMirror";
import "./elements/Dropdown"; import "./elements/Dropdown";

View file

@ -22,24 +22,24 @@ export class BoundPoliciesList extends Table {
row(item: any): string[] { row(item: any): string[] {
return [ return [
item.policy.name, item.policy_obj.name,
item.enabled, item.enabled,
item.order, item.order,
item.timeout, item.timeout,
` `
<pb-modal-button href="{% url 'passbook_admin:policy-binding-update' pk=binding.pk %}"> <pb-modal-button href="administration/policies/bindings/${item.pk}/update/">
<button slot="trigger" class="pf-c-button pf-m-secondary"> <pb-spinner-button slot="trigger" class="pf-m-secondary">
Edit Edit
</button> </pb-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</pb-modal-button> </pb-modal-button>
<pb-modal-button href="{% url 'passbook_admin:policy-binding-delete' pk=binding.pk %}"> <pb-modal-button href="administration/policies/bindings/${item.pk}/delete/">
<button slot="trigger" class="pf-c-button pf-m-danger"> <pb-spinner-button slot="trigger" class="pf-m-danger">
Delete Delete
</button> </pb-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</pb-modal-button> </pb-modal-button>
` `,
]; ];
} }
} }