web/elements: add support for non-field errors

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-03-31 19:10:10 +02:00
parent 55f68a9197
commit 3124b0f39c
2 changed files with 36 additions and 4 deletions

View File

@ -1,7 +1,12 @@
"""policy binding API Views""" """policy binding API Views"""
from typing import OrderedDict from typing import OrderedDict
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from rest_framework.serializers import ModelSerializer, PrimaryKeyRelatedField, ValidationError from rest_framework.serializers import (
ModelSerializer,
PrimaryKeyRelatedField,
ValidationError,
)
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
@ -77,8 +82,7 @@ class PolicyBindingSerializer(ModelSerializer):
def validate(self, data: OrderedDict) -> OrderedDict: def validate(self, data: OrderedDict) -> OrderedDict:
"""Check that either policy, group or user is set.""" """Check that either policy, group or user is set."""
count = sum([bool(data["policy"]), bool( count = sum([bool(data["policy"]), bool(data["group"]), bool(data["user"])])
data["group"]), bool(data["user"])])
invalid = count > 1 invalid = count > 1
empty = count < 1 empty = count < 1
if invalid: if invalid:
@ -87,6 +91,7 @@ class PolicyBindingSerializer(ModelSerializer):
raise ValidationError("One of 'policy', 'group' or 'user' must be set.") raise ValidationError("One of 'policy', 'group' or 'user' must be set.")
return data return data
class PolicyBindingViewSet(ModelViewSet): class PolicyBindingViewSet(ModelViewSet):
"""PolicyBinding Viewset""" """PolicyBinding Viewset"""

View File

@ -9,6 +9,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
import AKGlobal from "../../authentik.css"; import AKGlobal from "../../authentik.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import { MessageLevel } from "../messages/Message"; import { MessageLevel } from "../messages/Message";
import { IronFormElement } from "@polymer/iron-form/iron-form"; import { IronFormElement } from "@polymer/iron-form/iron-form";
import { camelToSnake } from "../../utils"; import { camelToSnake } from "../../utils";
@ -31,8 +32,11 @@ export class Form<T> extends LitElement {
@property() @property()
send!: (data: T) => Promise<unknown>; send!: (data: T) => Promise<unknown>;
@property({attribute: false})
nonFieldErrors?: string[];
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal, css` return [PFBase, PFCard, PFButton, PFForm, PFAlert, PFFormControl, AKGlobal, css`
select[multiple] { select[multiple] {
height: 15em; height: 15em;
} }
@ -116,6 +120,7 @@ export class Form<T> extends LitElement {
if (errorMessage instanceof Error) { if (errorMessage instanceof Error) {
throw errorMessage; throw errorMessage;
} }
// assign all input-related errors to their elements
const elements: PaperInputElement[] = ironForm._getSubmittableElements(); const elements: PaperInputElement[] = ironForm._getSubmittableElements();
elements.forEach((element) => { elements.forEach((element) => {
const elementName = element.name; const elementName = element.name;
@ -125,6 +130,9 @@ export class Form<T> extends LitElement {
element.invalid = true; element.invalid = true;
} }
}); });
if ("non_field_errors" in errorMessage) {
this.nonFieldErrors = errorMessage["non_field_errors"];
}
throw new APIError(errorMessage); throw new APIError(errorMessage);
}); });
} }
@ -136,6 +144,24 @@ export class Form<T> extends LitElement {
return html`<slot></slot>`; return html`<slot></slot>`;
} }
renderNonFieldErrors(): TemplateResult {
if (!this.nonFieldErrors) {
return html``;
}
return html`<div class="pf-c-form__alert">
${this.nonFieldErrors.map(err => {
return html`<div class="pf-c-alert pf-m-inline pf-m-danger">
<div class="pf-c-alert__icon">
<i class="fas fa-exclamation-circle"></i>
</div>
<h4 class="pf-c-alert__title">
${err}
</h4>
</div>`;
})}
</div>`;
}
render(): TemplateResult { render(): TemplateResult {
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();
if (rect.x + rect.y + rect.width + rect.height === 0) { if (rect.x + rect.y + rect.width + rect.height === 0) {
@ -143,6 +169,7 @@ export class Form<T> extends LitElement {
} }
return html`<iron-form return html`<iron-form
@iron-form-presubmit=${(ev: Event) => { this.submit(ev); }}> @iron-form-presubmit=${(ev: Event) => { this.submit(ev); }}>
${this.renderNonFieldErrors()}
${this.renderForm()} ${this.renderForm()}
</iron-form>`; </iron-form>`;
} }