
This commit adds HTMLTagNameElementMap entries to every web component in the front end. Activating and associating the HTMLTagNamElementMap with its class has enabled [LitAnalyzer](https://github.com/runem/lit-analyzer/tree/master/packages/lit-analyzer) to reveal a *lot* of basic problems within the UI, the most popular of which is "missing import." We usually get away with it because the object being imported was already registered with the browser elsewhere, but it still surprises me that we haven't gotten any complaints over things like: ``` ./src/flow/stages/base.ts Missing import for <ak-form-static> 96: <ak-form-static no-missing-import ``` Given how early and fundamental that seems to be in our code, I'd have expected to hear _something_ about it. I have not enabled most of the possible checks because, well, there are just a ton of warnings when I do. I'd like to get in and fix those. Aside from this, I have also _removed_ `customElement` declarations from anything declared as an `abstract class`. It makes no sense to try and instantiate something that cannot, by definition, be instantiated. If the class is capable of running on its own, it's not abstract, it just needs to be overridden in child classes. Before removing the declaration I did check to make sure no other piece of code was even *trying* to instantiate it, and so far I have detected no failures. Those elements were: - elements/forms/Form.ts - element-/wizard/WizardFormPage.ts The one that blows my mind, though, is this: ``` src/elements/forms/ProxyForm.ts 6-@customElement("ak-proxy-form") 7:export abstract class ProxyForm extends Form<unknown> { ``` Which, despite being `abstract`, is somehow instantiable? ``` src/admin/outposts/ServiceConnectionListPage.ts: <ak-proxy-form src/admin/providers/ProviderListPage.ts: <ak-proxy-form src/admin/sources/SourceWizard.ts: <ak-proxy-form src/admin/sources/SourceListPage.ts: <ak-proxy-form src/admin/providers/ProviderWizard.ts: <ak-proxy-form type=${type.component}></ak-proxy-form> src/admin/stages/StageListPage.ts: <ak-proxy-form ``` I've made a note to investigate. I've started a new folder where all of my one-off tools for *how* a certain PR was run. It has a README describing what it's for, and the first tool, `add-htmlelementtagnamemaps-to-everything`, is its first entry. That tool is also documented internally. ``` Gilbert & Sullivan I've got a little list, I've got a little list, Of all the code that would never be missed, The duplicate code of cute-and-paste, The weak abstractions that lead to waste, The embedded templates-- you get the gist, There ain't none of 'em that will ever be missed, And that's why I've got them on my list! ```
168 lines
6.6 KiB
TypeScript
168 lines
6.6 KiB
TypeScript
import "@goauthentik/admin/policies/PolicyTestForm";
|
|
import "@goauthentik/admin/policies/PolicyWizard";
|
|
import "@goauthentik/admin/policies/dummy/DummyPolicyForm";
|
|
import "@goauthentik/admin/policies/event_matcher/EventMatcherPolicyForm";
|
|
import "@goauthentik/admin/policies/expiry/ExpiryPolicyForm";
|
|
import "@goauthentik/admin/policies/expression/ExpressionPolicyForm";
|
|
import "@goauthentik/admin/policies/password/PasswordPolicyForm";
|
|
import "@goauthentik/admin/policies/reputation/ReputationPolicyForm";
|
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|
import { uiConfig } from "@goauthentik/common/ui/config";
|
|
import { PFColor } from "@goauthentik/elements/Label";
|
|
import "@goauthentik/elements/forms/ConfirmationForm";
|
|
import "@goauthentik/elements/forms/DeleteBulkForm";
|
|
import "@goauthentik/elements/forms/ModalForm";
|
|
import "@goauthentik/elements/forms/ProxyForm";
|
|
import "@goauthentik/elements/rbac/ObjectPermissionModal";
|
|
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
|
import { TableColumn } from "@goauthentik/elements/table/Table";
|
|
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
|
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
|
|
|
import { msg, str } from "@lit/localize";
|
|
import { TemplateResult, html } from "lit";
|
|
import { customElement, property } from "lit/decorators.js";
|
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
|
|
import { PoliciesApi, Policy } from "@goauthentik/api";
|
|
|
|
@customElement("ak-policy-list")
|
|
export class PolicyListPage extends TablePage<Policy> {
|
|
searchEnabled(): boolean {
|
|
return true;
|
|
}
|
|
pageTitle(): string {
|
|
return msg("Policies");
|
|
}
|
|
pageDescription(): string {
|
|
return msg(
|
|
"Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages.",
|
|
);
|
|
}
|
|
pageIcon(): string {
|
|
return "pf-icon pf-icon-infrastructure";
|
|
}
|
|
|
|
checkbox = true;
|
|
clearOnRefresh = true;
|
|
|
|
@property()
|
|
order = "name";
|
|
|
|
async apiEndpoint(page: number): Promise<PaginatedResponse<Policy>> {
|
|
return new PoliciesApi(DEFAULT_CONFIG).policiesAllList({
|
|
ordering: this.order,
|
|
page: page,
|
|
pageSize: (await uiConfig()).pagination.perPage,
|
|
search: this.search || "",
|
|
});
|
|
}
|
|
|
|
columns(): TableColumn[] {
|
|
return [
|
|
new TableColumn(msg("Name"), "name"),
|
|
new TableColumn(msg("Type")),
|
|
new TableColumn(msg("Actions")),
|
|
];
|
|
}
|
|
|
|
row(item: Policy): TemplateResult[] {
|
|
return [
|
|
html`<div>${item.name}</div>
|
|
${(item.boundTo || 0) > 0
|
|
? html`<ak-label color=${PFColor.Green} ?compact=${true}>
|
|
${msg(str`Assigned to ${item.boundTo} object(s).`)}
|
|
</ak-label>`
|
|
: html`<ak-label color=${PFColor.Orange} ?compact=${true}>
|
|
${msg("Warning: Policy is not assigned.")}
|
|
</ak-label>`}`,
|
|
html`${item.verboseName}`,
|
|
html` <ak-forms-modal>
|
|
<span slot="submit"> ${msg("Update")} </span>
|
|
<span slot="header"> ${msg(str`Update ${item.verboseName}`)} </span>
|
|
<ak-proxy-form
|
|
slot="form"
|
|
.args=${{
|
|
instancePk: item.pk,
|
|
}}
|
|
type=${ifDefined(item.component)}
|
|
>
|
|
</ak-proxy-form>
|
|
<button slot="trigger" class="pf-c-button pf-m-plain">
|
|
<pf-tooltip position="top" content=${msg("Edit")}>
|
|
<i class="fas fa-edit"></i>
|
|
</pf-tooltip>
|
|
</button>
|
|
</ak-forms-modal>
|
|
|
|
<ak-rbac-object-permission-modal model=${item.metaModelName} objectPk=${item.pk}>
|
|
</ak-rbac-object-permission-modal>
|
|
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
|
|
<span slot="submit"> ${msg("Test")} </span>
|
|
<span slot="header"> ${msg("Test Policy")} </span>
|
|
<ak-policy-test-form slot="form" .policy=${item}> </ak-policy-test-form>
|
|
<button slot="trigger" class="pf-c-button pf-m-plain">
|
|
<pf-tooltip position="top" content=${msg("Test")}>
|
|
<i class="fas fa-vial" aria-hidden="true"></i>
|
|
</pf-tooltip>
|
|
</button>
|
|
</ak-forms-modal>`,
|
|
];
|
|
}
|
|
|
|
renderToolbarSelected(): TemplateResult {
|
|
const disabled = this.selectedElements.length < 1;
|
|
return html`<ak-forms-delete-bulk
|
|
objectLabel=${msg("Policy / Policies")}
|
|
.objects=${this.selectedElements}
|
|
.usedBy=${(item: Policy) => {
|
|
return new PoliciesApi(DEFAULT_CONFIG).policiesAllUsedByList({
|
|
policyUuid: item.pk,
|
|
});
|
|
}}
|
|
.delete=${(item: Policy) => {
|
|
return new PoliciesApi(DEFAULT_CONFIG).policiesAllDestroy({
|
|
policyUuid: item.pk,
|
|
});
|
|
}}
|
|
>
|
|
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
|
${msg("Delete")}
|
|
</button>
|
|
</ak-forms-delete-bulk>`;
|
|
}
|
|
|
|
renderObjectCreate(): TemplateResult {
|
|
return html`<ak-policy-wizard> </ak-policy-wizard>`;
|
|
}
|
|
|
|
renderToolbar(): TemplateResult {
|
|
return html` ${super.renderToolbar()}
|
|
<ak-forms-confirm
|
|
successMessage=${msg("Successfully cleared policy cache")}
|
|
errorMessage=${msg("Failed to delete policy cache")}
|
|
action=${msg("Clear cache")}
|
|
.onConfirm=${() => {
|
|
return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate();
|
|
}}
|
|
>
|
|
<span slot="header"> ${msg("Clear Policy cache")} </span>
|
|
<p slot="body">
|
|
${msg(
|
|
"Are you sure you want to clear the policy cache? This will cause all policies to be re-evaluated on their next usage.",
|
|
)}
|
|
</p>
|
|
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
|
${msg("Clear cache")}
|
|
</button>
|
|
<div slot="modal"></div>
|
|
</ak-forms-confirm>`;
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-policy-list": PolicyListPage;
|
|
}
|
|
}
|