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:
parent
d5875a597b
commit
5386f0f4c3
|
@ -1,5 +1,6 @@
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
|
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
|
||||||
|
import { eventActionLabels } from "@goauthentik/common/labels";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
@ -16,35 +17,41 @@ import { consume } from "@lit-labs/context";
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { eventActionLabels } from "@goauthentik/common/labels";
|
|
||||||
|
|
||||||
import { ProvidersApi, TypeCreate } from "@goauthentik/api";
|
import { AdminApi, CapabilitiesEnum, CoreApi, Version } from "@goauthentik/api";
|
||||||
import {
|
|
||||||
AdminApi,
|
|
||||||
CapabilitiesEnum,
|
|
||||||
CoreApi,
|
|
||||||
OutpostsApi,
|
|
||||||
PoliciesApi,
|
|
||||||
PropertymappingsApi,
|
|
||||||
SourcesApi,
|
|
||||||
StagesApi,
|
|
||||||
Version,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
|
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
|
||||||
|
|
||||||
import { flowDesignationTable } from "../flows/utils";
|
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
|
* AdminSidebar
|
||||||
*
|
*
|
||||||
* Encapsulates the logic for the administration sidebar: what to show and, initially, when to show
|
* The AdminSidebar has two responsibilities:
|
||||||
* it. Rendering decisions are left to the sidebar itself.
|
*
|
||||||
|
* 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 = [
|
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,
|
string,
|
||||||
|
// Attributes to which the sidebar responds. See the sidebar for details.
|
||||||
(SidebarAttributes | string[] | null)?, // eslint-disable-line
|
(SidebarAttributes | string[] | null)?, // eslint-disable-line
|
||||||
|
// Children of the entry
|
||||||
LocalSidebarEntry[]?,
|
LocalSidebarEntry[]?,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -55,12 +62,6 @@ const localToSidebarEntry = (l: LocalSidebarEntry): SidebarEntry => ({
|
||||||
...(l[3] ? { children: l[3].map(localToSidebarEntry) } : {}),
|
...(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")
|
@customElement("ak-admin-sidebar")
|
||||||
export class AkAdminSidebar extends AKElement {
|
export class AkAdminSidebar extends AKElement {
|
||||||
@consume({ context: authentikConfigContext })
|
@consume({ context: authentikConfigContext })
|
||||||
|
@ -75,23 +76,12 @@ export class AkAdminSidebar extends AKElement {
|
||||||
@state()
|
@state()
|
||||||
impersonation: UserSelf["username"] | null = null;
|
impersonation: UserSelf["username"] | null = null;
|
||||||
|
|
||||||
@state()
|
private connectionTypes = new ConnectionTypesController(this);
|
||||||
providerTypes: TypeCreate[] = [];
|
private policyTypes = new PolicyTypesController(this);
|
||||||
|
private propertyMapper = new PropertyMappingsController(this);
|
||||||
@state()
|
private providerTypes = new ProviderTypesController(this);
|
||||||
stageTypes: TypeCreate[] = [];
|
private sourceTypes = new SourceTypesController(this);
|
||||||
|
private stageTypes = new StageTypesController(this);
|
||||||
@state()
|
|
||||||
mappingTypes: TypeCreate[] = [];
|
|
||||||
|
|
||||||
@state()
|
|
||||||
sourceTypes: TypeCreate[] = [];
|
|
||||||
|
|
||||||
@state()
|
|
||||||
policyTypes: TypeCreate[] = [];
|
|
||||||
|
|
||||||
@state()
|
|
||||||
connectionTypes: TypeCreate[] = [];
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -101,25 +91,6 @@ export class AkAdminSidebar extends AKElement {
|
||||||
me().then((user: SessionUser) => {
|
me().then((user: SessionUser) => {
|
||||||
this.impersonation = user.original ? user.user.username : null;
|
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.toggleOpen = this.toggleOpen.bind(this);
|
||||||
this.checkWidth = this.checkWidth.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.`)]]
|
? [[reload, msg(str`You're currently impersonating ${this.impersonation}. Click to stop.`)]]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// prettier-ignore
|
const enterpriseMenu: LocalSidebarEntry[] = this.config?.capabilities.includes(
|
||||||
const enterpriseMenu: LocalSidebarEntry[] = this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
|
CapabilitiesEnum.IsEnterprise
|
||||||
|
)
|
||||||
? [[null, msg("Enterprise"), null, [["/enterprise/licenses", msg("Licenses")]]]]
|
? [[null, msg("Enterprise"), null, [["/enterprise/licenses", msg("Licenses")]]]]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// prettier-ignore
|
const flowTypes: LocalSidebarEntry[] = flowDesignationTable.map(([_designation, label]) => [
|
||||||
const flowTypes: LocalSidebarEntry[] = flowDesignationTable.map(([_designation, label]) =>
|
`/flow/flows;${encodeURIComponent(JSON.stringify({ search: label }))}`,
|
||||||
([`/flow/flows;${encodeURIComponent(JSON.stringify({ search: label }))}`, label]));
|
label,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const eventTypes: LocalSidebarEntry[] = eventActionLabels.map(([_action, label]) => [
|
||||||
const eventTypes: LocalSidebarEntry[] = eventActionLabels.map(([_action, label]) =>
|
`/events/log;${encodeURIComponent(JSON.stringify({ search: label }))}`,
|
||||||
([`/events/log;${encodeURIComponent(JSON.stringify({ search: label }))}`, 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),
|
|
||||||
];
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const localSidebar: LocalSidebarEntry[] = [
|
const localSidebar: LocalSidebarEntry[] = [
|
||||||
|
@ -216,32 +181,32 @@ export class AkAdminSidebar extends AKElement {
|
||||||
["/administration/system-tasks", msg("System Tasks")]]],
|
["/administration/system-tasks", msg("System Tasks")]]],
|
||||||
[null, msg("Applications"), null, [
|
[null, msg("Applications"), null, [
|
||||||
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
|
["/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")]]],
|
["/outpost/outposts", msg("Outposts")]]],
|
||||||
[null, msg("Events"), null, [
|
[null, msg("Events"), null, [
|
||||||
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`], eventTypes],
|
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`], eventTypes],
|
||||||
["/events/rules", msg("Notification Rules")],
|
["/events/rules", msg("Notification Rules")],
|
||||||
["/events/transports", msg("Notification Transports")]]],
|
["/events/transports", msg("Notification Transports")]]],
|
||||||
[null, msg("Customisation"), null, [
|
[null, msg("Customisation"), null, [
|
||||||
["/policy/policies", msg("Policies"), null, policyTypes],
|
["/policy/policies", msg("Policies"), null, this.policyTypes.entries()],
|
||||||
["/core/property-mappings", msg("Property Mappings"), null, mappingTypes],
|
["/core/property-mappings", msg("Property Mappings"), null, this.propertyMapper.entries()],
|
||||||
["/blueprints/instances", msg("Blueprints")],
|
["/blueprints/instances", msg("Blueprints")],
|
||||||
["/policy/reputation", msg("Reputation scores")]]],
|
["/policy/reputation", msg("Reputation scores")]]],
|
||||||
[null, msg("Flows and Stages"), null, [
|
[null, msg("Flows and Stages"), null, [
|
||||||
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`], flowTypes],
|
["/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")]]],
|
["/flow/stages/prompts", msg("Prompts")]]],
|
||||||
[null, msg("Directory"), null, [
|
[null, msg("Directory"), null, [
|
||||||
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
|
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
|
||||||
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
|
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
|
||||||
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<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")],
|
["/core/tokens", msg("Tokens and App passwords")],
|
||||||
["/flow/stages/invitations", msg("Invitations")]]],
|
["/flow/stages/invitations", msg("Invitations")]]],
|
||||||
[null, msg("System"), null, [
|
[null, msg("System"), null, [
|
||||||
["/core/tenants", msg("Tenants")],
|
["/core/tenants", msg("Tenants")],
|
||||||
["/crypto/certificates", msg("Certificates")],
|
["/crypto/certificates", msg("Certificates")],
|
||||||
["/outpost/integrations", msg("Outpost Integrations"), null, connectionTypes]]],
|
["/outpost/integrations", msg("Outpost Integrations"), null, this.connectionTypes.entries()]]],
|
||||||
...(enterpriseMenu)
|
...(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