Streamline TypeCreate lists. This commit removes the highly repetitive definitions for each
of the TypeCreate objects and replaces them with a single generic ReactiveController, which it then instantiates six times, but at least they're shorter!
This commit is contained in:
		@ -1,5 +1,6 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
 | 
			
		||||
import { eventActionLabels } from "@goauthentik/common/labels";
 | 
			
		||||
import { me } from "@goauthentik/common/users";
 | 
			
		||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
@ -16,35 +17,41 @@ import { consume } from "@lit-labs/context";
 | 
			
		||||
import { msg, str } from "@lit/localize";
 | 
			
		||||
import { html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
import { eventActionLabels } from "@goauthentik/common/labels";
 | 
			
		||||
 | 
			
		||||
import { ProvidersApi, TypeCreate } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    AdminApi,
 | 
			
		||||
    CapabilitiesEnum,
 | 
			
		||||
    CoreApi,
 | 
			
		||||
    OutpostsApi,
 | 
			
		||||
    PoliciesApi,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
    StagesApi,
 | 
			
		||||
    Version,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
import { AdminApi, CapabilitiesEnum, CoreApi, Version } from "@goauthentik/api";
 | 
			
		||||
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { flowDesignationTable } from "../flows/utils";
 | 
			
		||||
import ConnectionTypesController from "./SidebarEntries/ConnectionTypesController";
 | 
			
		||||
import PolicyTypesController from "./SidebarEntries/PolicyTypesController";
 | 
			
		||||
import PropertyMappingsController from "./SidebarEntries/PropertyMappingsController";
 | 
			
		||||
import ProviderTypesController from "./SidebarEntries/ProviderTypesController";
 | 
			
		||||
import SourceTypesController from "./SidebarEntries/SourceTypesController";
 | 
			
		||||
import StageTypesController from "./SidebarEntries/StageTypesController";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * AdminSidebar
 | 
			
		||||
 *
 | 
			
		||||
 * Encapsulates the logic for the administration sidebar: what to show and, initially, when to show
 | 
			
		||||
 * it.  Rendering decisions are left to the sidebar itself.
 | 
			
		||||
 */
 | 
			
		||||
 * The AdminSidebar has two responsibilities:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Control the styling of the sidebar host, specifically when to show it and whether to show
 | 
			
		||||
 *    it as an overlay or as a push.
 | 
			
		||||
 * 2. Control what content the sidebar will receive.  The sidebar takes a tree, maximally three deep,
 | 
			
		||||
 *    of type SidebarEventHandler.
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
type SidebarUrl = string;
 | 
			
		||||
 | 
			
		||||
export type LocalSidebarEntry = [
 | 
			
		||||
    string | SidebarEventHandler | null,
 | 
			
		||||
    // - null: This entry is not a link.
 | 
			
		||||
    // - string: the url for the entry
 | 
			
		||||
    // - SidebarEventHandler: a function to run if the entry is clicked.
 | 
			
		||||
    SidebarUrl | SidebarEventHandler | null,
 | 
			
		||||
    // The visible text of the entry.
 | 
			
		||||
    string,
 | 
			
		||||
    // Attributes to which the sidebar responds. See the sidebar for details.
 | 
			
		||||
    (SidebarAttributes | string[] | null)?, // eslint-disable-line
 | 
			
		||||
    // Children of the entry
 | 
			
		||||
    LocalSidebarEntry[]?,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -55,12 +62,6 @@ const localToSidebarEntry = (l: LocalSidebarEntry): SidebarEntry => ({
 | 
			
		||||
    ...(l[3] ? { children: l[3].map(localToSidebarEntry) } : {}),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const typeCreateToSidebar = (baseUrl: string, tcreate: TypeCreate[]): LocalSidebarEntry[] =>
 | 
			
		||||
    tcreate.map((t) => [
 | 
			
		||||
        `${baseUrl};${encodeURIComponent(JSON.stringify({ search: t.name }))}`,
 | 
			
		||||
        t.name,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
@customElement("ak-admin-sidebar")
 | 
			
		||||
export class AkAdminSidebar extends AKElement {
 | 
			
		||||
    @consume({ context: authentikConfigContext })
 | 
			
		||||
@ -75,23 +76,12 @@ export class AkAdminSidebar extends AKElement {
 | 
			
		||||
    @state()
 | 
			
		||||
    impersonation: UserSelf["username"] | null = null;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    providerTypes: TypeCreate[] = [];
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    stageTypes: TypeCreate[] = [];
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    mappingTypes: TypeCreate[] = [];
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    sourceTypes: TypeCreate[] = [];
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    policyTypes: TypeCreate[] = [];
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    connectionTypes: TypeCreate[] = [];
 | 
			
		||||
    private connectionTypes = new ConnectionTypesController(this);
 | 
			
		||||
    private policyTypes = new PolicyTypesController(this);
 | 
			
		||||
    private propertyMapper = new PropertyMappingsController(this);
 | 
			
		||||
    private providerTypes = new ProviderTypesController(this);
 | 
			
		||||
    private sourceTypes = new SourceTypesController(this);
 | 
			
		||||
    private stageTypes = new StageTypesController(this);
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
@ -101,25 +91,6 @@ export class AkAdminSidebar extends AKElement {
 | 
			
		||||
        me().then((user: SessionUser) => {
 | 
			
		||||
            this.impersonation = user.original ? user.user.username : null;
 | 
			
		||||
        });
 | 
			
		||||
        new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
 | 
			
		||||
            this.providerTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
        new StagesApi(DEFAULT_CONFIG).stagesAllTypesList().then((types) => {
 | 
			
		||||
            this.stageTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
        new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypesList().then((types) => {
 | 
			
		||||
            this.mappingTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
        new SourcesApi(DEFAULT_CONFIG).sourcesAllTypesList().then((types) => {
 | 
			
		||||
            this.sourceTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
        new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList().then((types) => {
 | 
			
		||||
            this.policyTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
        new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllTypesList().then((types) => {
 | 
			
		||||
            this.connectionTypes = types;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.toggleOpen = this.toggleOpen.bind(this);
 | 
			
		||||
        this.checkWidth = this.checkWidth.bind(this);
 | 
			
		||||
    }
 | 
			
		||||
@ -183,27 +154,21 @@ export class AkAdminSidebar extends AKElement {
 | 
			
		||||
            ? [[reload, msg(str`You're currently impersonating ${this.impersonation}. Click to stop.`)]]
 | 
			
		||||
            : [];
 | 
			
		||||
 | 
			
		||||
        // prettier-ignore
 | 
			
		||||
        const enterpriseMenu: LocalSidebarEntry[] = this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
 | 
			
		||||
        const enterpriseMenu: LocalSidebarEntry[] = this.config?.capabilities.includes(
 | 
			
		||||
            CapabilitiesEnum.IsEnterprise
 | 
			
		||||
        )
 | 
			
		||||
            ? [[null, msg("Enterprise"), null, [["/enterprise/licenses", msg("Licenses")]]]]
 | 
			
		||||
            : [];
 | 
			
		||||
 | 
			
		||||
        // prettier-ignore
 | 
			
		||||
        const flowTypes: LocalSidebarEntry[] = flowDesignationTable.map(([_designation, label]) =>  
 | 
			
		||||
            ([`/flow/flows;${encodeURIComponent(JSON.stringify({ search: label }))}`, label]));
 | 
			
		||||
        const flowTypes: LocalSidebarEntry[] = flowDesignationTable.map(([_designation, label]) => [
 | 
			
		||||
            `/flow/flows;${encodeURIComponent(JSON.stringify({ search: label }))}`,
 | 
			
		||||
            label,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        const eventTypes: LocalSidebarEntry[] = eventActionLabels.map(([_action, label]) =>  
 | 
			
		||||
            ([`/events/log;${encodeURIComponent(JSON.stringify({ search: label }))}`, label]));
 | 
			
		||||
        
 | 
			
		||||
        const [mappingTypes,  providerTypes, sourceTypes, stageTypes, connectionTypes, policyTypes] = [
 | 
			
		||||
            typeCreateToSidebar("/core/property-mappings", this.mappingTypes),
 | 
			
		||||
            typeCreateToSidebar("/core/providers", this.providerTypes),
 | 
			
		||||
            typeCreateToSidebar("/core/sources", this.sourceTypes),
 | 
			
		||||
            typeCreateToSidebar("/flow/stages", this.stageTypes),
 | 
			
		||||
            typeCreateToSidebar("/outpost/integrations", this.connectionTypes),
 | 
			
		||||
            typeCreateToSidebar("/policy/policies", this.policyTypes),
 | 
			
		||||
        ];
 | 
			
		||||
        const eventTypes: LocalSidebarEntry[] = eventActionLabels.map(([_action, label]) => [
 | 
			
		||||
            `/events/log;${encodeURIComponent(JSON.stringify({ search: label }))}`,
 | 
			
		||||
            label,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // prettier-ignore
 | 
			
		||||
        const localSidebar: LocalSidebarEntry[] = [
 | 
			
		||||
@ -216,32 +181,32 @@ export class AkAdminSidebar extends AKElement {
 | 
			
		||||
                ["/administration/system-tasks", msg("System Tasks")]]],
 | 
			
		||||
            [null, msg("Applications"), null, [
 | 
			
		||||
                ["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
 | 
			
		||||
                ["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`], providerTypes],
 | 
			
		||||
                ["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`], this.providerTypes.entries()],
 | 
			
		||||
                ["/outpost/outposts", msg("Outposts")]]],
 | 
			
		||||
            [null, msg("Events"), null, [
 | 
			
		||||
                ["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`], eventTypes],
 | 
			
		||||
                ["/events/rules", msg("Notification Rules")],
 | 
			
		||||
                ["/events/transports", msg("Notification Transports")]]],
 | 
			
		||||
            [null, msg("Customisation"), null, [
 | 
			
		||||
                ["/policy/policies", msg("Policies"), null, policyTypes],
 | 
			
		||||
                ["/core/property-mappings", msg("Property Mappings"), null, mappingTypes],
 | 
			
		||||
                ["/policy/policies", msg("Policies"), null, this.policyTypes.entries()],
 | 
			
		||||
                ["/core/property-mappings", msg("Property Mappings"), null, this.propertyMapper.entries()],
 | 
			
		||||
                ["/blueprints/instances", msg("Blueprints")],
 | 
			
		||||
                ["/policy/reputation", msg("Reputation scores")]]],
 | 
			
		||||
            [null, msg("Flows and Stages"), null, [
 | 
			
		||||
                ["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`], flowTypes],
 | 
			
		||||
                ["/flow/stages", msg("Stages"), null, stageTypes],
 | 
			
		||||
                ["/flow/stages", msg("Stages"), null, this.stageTypes.entries()],
 | 
			
		||||
                ["/flow/stages/prompts", msg("Prompts")]]],
 | 
			
		||||
            [null, msg("Directory"), null, [
 | 
			
		||||
                ["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
 | 
			
		||||
                ["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
 | 
			
		||||
                ["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
 | 
			
		||||
                ["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`], sourceTypes],
 | 
			
		||||
                ["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`], this.sourceTypes.entries()],
 | 
			
		||||
                ["/core/tokens", msg("Tokens and App passwords")],
 | 
			
		||||
                ["/flow/stages/invitations", msg("Invitations")]]],
 | 
			
		||||
            [null, msg("System"), null, [
 | 
			
		||||
                ["/core/tenants", msg("Tenants")],
 | 
			
		||||
                ["/crypto/certificates", msg("Certificates")],
 | 
			
		||||
                ["/outpost/integrations", msg("Outpost Integrations"), null, connectionTypes]]],
 | 
			
		||||
                ["/outpost/integrations", msg("Outpost Integrations"), null, this.connectionTypes.entries()]]],
 | 
			
		||||
            ...(enterpriseMenu)
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { OutpostsApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const ConnectionTypesController = createTypesController(
 | 
			
		||||
    () => new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllTypesList(),
 | 
			
		||||
    "/outpost/integrations"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default ConnectionTypesController;
 | 
			
		||||
@ -0,0 +1,49 @@
 | 
			
		||||
import { ReactiveControllerHost } from "lit";
 | 
			
		||||
import { TypeCreate } from "@goauthentik/api";
 | 
			
		||||
import { LocalSidebarEntry } from "../AdminSidebar";
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
type Fetcher = () => Promise<TypeCreate[]>;
 | 
			
		||||
 | 
			
		||||
const typeCreateToSidebar = (baseUrl: string, tcreate: TypeCreate[]): LocalSidebarEntry[] =>
 | 
			
		||||
    tcreate.map((t) => [
 | 
			
		||||
        `${baseUrl};${encodeURIComponent(JSON.stringify({ search: t.name }))}`,
 | 
			
		||||
        t.name,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * createTypesController
 | 
			
		||||
 *
 | 
			
		||||
 * The Sidebar accesses a number objects of `TypeCreate`, which all have the exact same type, just
 | 
			
		||||
 * different accessors for generating the lists and different paths to which they respond. This
 | 
			
		||||
 * function is a template for a (simple) reactive controller that fetches the data for that type on
 | 
			
		||||
 * construction, then informs the host that the data is available.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODO (2023-11-17): This function is unlikely to survive in this form. It would be nice if it were more
 | 
			
		||||
 * generic, able to take a converter that can handle more that TypeCreate[] as its inbound argument,
 | 
			
		||||
 * since we need to refine what's displayed and on what the search is conducted.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export function createTypesController(fetch: Fetcher, path: string, converter = typeCreateToSidebar) {
 | 
			
		||||
    return class GenericTypesController {
 | 
			
		||||
        createTypes: TypeCreate[] = [];
 | 
			
		||||
        host: ReactiveControllerHost;
 | 
			
		||||
 | 
			
		||||
        constructor(host: ReactiveControllerHost) {
 | 
			
		||||
            this.host = host;
 | 
			
		||||
            fetch().then((types) => {
 | 
			
		||||
                this.createTypes = types;
 | 
			
		||||
                host.requestUpdate();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        entries(): LocalSidebarEntry[] {
 | 
			
		||||
            return converter(path, this.createTypes);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default createTypesController;
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { PoliciesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const PolicyTypesController = createTypesController(
 | 
			
		||||
    () => new PoliciesApi(DEFAULT_CONFIG).policiesAllTypesList(),
 | 
			
		||||
    "/policy/policies"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default PolicyTypesController;
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { PropertymappingsApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const PropertyMappingsController = createTypesController(
 | 
			
		||||
    () => new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypesList(),
 | 
			
		||||
    "/core/property-mappings"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default PropertyMappingsController;
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { ProvidersApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const ProviderTypesController = createTypesController(
 | 
			
		||||
    () => new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList(),
 | 
			
		||||
    "/core/providers"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default ProviderTypesController;
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { SourcesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const SourceTypesController = createTypesController(
 | 
			
		||||
    () => new SourcesApi(DEFAULT_CONFIG).sourcesAllTypesList(),
 | 
			
		||||
    "/core/sources"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default SourceTypesController;
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
 | 
			
		||||
import { StagesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { createTypesController } from "./GenericTypesController";
 | 
			
		||||
 | 
			
		||||
export const StageTypesController = createTypesController(
 | 
			
		||||
    () => new StagesApi(DEFAULT_CONFIG).stagesAllTypesList(),
 | 
			
		||||
    "/flow/stages"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default StageTypesController;
 | 
			
		||||
		Reference in New Issue
	
	Block a user