-
-
Notifications
You must be signed in to change notification settings - Fork 986
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
web/admin: bugfix: dual select initialization revision (#12051)
* web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * Start of dual select revision process. * Progress. * Made the RuleFormHelper's dualselect conform. * Providers and Selectors harmonized for sources. * web/bugfix/dual-select-full-options # What - Replaces the dual-select "selected" list mechanism with a more comprehensive (if computationally expensive) version that is correct. # How In the previous iteration, each dual select controller gets a *provider* and a *selector*; the latter keeps the keys of all the objects a specific instance may have, and marks those objects as "selected" when they appear in the dual-selects "selected" panel. In order to distinguish between "selected on the existing instance" and "selected by the user," the *selector* only runs at construction time, creating a unified "selected" list; this is standard and allows for a uniform experience of adding and deleting items. Unfortunately, this means that the "selected" items, because their displays are crafted bespoke, are only chosen from those available at construction. If there are selected items later in the paginated collection, they will not be marked as selected. This defeats the purpose of having a paginated multi-select! The correct way to do this is to retrieve every item pased to the *selector* and use the same algorithm to craft the views in both windows. For every instance of Dual Select with dynamic selection, the *provider* and *selector* have been put in a separate file (usually suffixed as a `*FormHelper.ts` file); the algorithm by which an item is crafted for use by DualSelect has been broken out into a small function (usually named `*toSelect()`). The *provider* works as before. The *selector* takes every instance key passed to it and runs a `Promise.allSettled(...*Retrieve({ uuid: instanceId }))` on them, mapping them onto the `selected` collection using the same `*toSelect()`, so they resemble the possibilities in every way. # Lessons This exercise emphasizes just how much sheer *repetition* the Django REST API creates on the client side. Every Helper file is a copy-pasta of a sibling, with only a few minor changes: - How the objects are turned into displays for DualSelect - The type and calls being used; - The field on which retrival is defined - The defaulting rule. There are 19 `*FormHelper` files, and each one is 50 lines long. That's 950 lines of code. Of those 950 lines of code, 874 of those lines are *complete duplicates* of those in the other FormHelper files. Only 76 lines are unique. This language really needs macros. That, or I need to seriously level up my Typescript and figure out how to make this whole thing a lot smarter. * order fields by field_key and order Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io>
- Loading branch information
1 parent
248fcdd
commit e077a5c
Showing
47 changed files
with
1,025 additions
and
603 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; | ||
|
||
import { EventsApi, NotificationTransport } from "@goauthentik/api"; | ||
|
||
const transportToSelect = (transport: NotificationTransport) => [transport.pk, transport.name]; | ||
|
||
export async function eventTransportsProvider(page = 1, search = "") { | ||
const eventTransports = await new EventsApi(DEFAULT_CONFIG).eventsTransportsList({ | ||
ordering: "name", | ||
pageSize: 20, | ||
search: search.trim(), | ||
page, | ||
}); | ||
|
||
return { | ||
pagination: eventTransports.pagination, | ||
options: eventTransports.results.map(transportToSelect), | ||
}; | ||
} | ||
|
||
export function eventTransportsSelector(instanceTransports: string[] | undefined) { | ||
if (!instanceTransports) { | ||
return async (transports: DualSelectPair<NotificationTransport>[]) => | ||
transports.filter( | ||
([_0, _1, _2, stage]: DualSelectPair<NotificationTransport>) => stage !== undefined, | ||
); | ||
} | ||
|
||
return async () => { | ||
const transportsApi = new EventsApi(DEFAULT_CONFIG); | ||
const transports = await Promise.allSettled( | ||
instanceTransports.map((instanceId) => | ||
transportsApi.eventsTransportsRetrieve({ uuid: instanceId }), | ||
), | ||
); | ||
return transports | ||
.filter((s) => s.status === "fulfilled") | ||
.map((s) => s.value) | ||
.map(transportToSelect); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
web/src/admin/providers/google_workspace/GoogleWorkspaceProviderFormHelpers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; | ||
|
||
import { GoogleWorkspaceProviderMapping, PropertymappingsApi } from "@goauthentik/api"; | ||
|
||
const mappingToSelect = (m: GoogleWorkspaceProviderMapping) => [m.pk, m.name, m.name, m]; | ||
|
||
export async function propertyMappingsProvider(page = 1, search = "") { | ||
const propertyMappings = await new PropertymappingsApi( | ||
DEFAULT_CONFIG, | ||
).propertymappingsProviderGoogleWorkspaceList({ | ||
ordering: "managed", | ||
pageSize: 20, | ||
search: search.trim(), | ||
page, | ||
}); | ||
return { | ||
pagination: propertyMappings.pagination, | ||
options: propertyMappings.results.map(mappingToSelect), | ||
}; | ||
} | ||
|
||
export function propertyMappingsSelector( | ||
instanceMappings: string[] | undefined, | ||
defaultSelection: string, | ||
) { | ||
if (!instanceMappings) { | ||
return async (mappings: DualSelectPair<GoogleWorkspaceProviderMapping>[]) => | ||
mappings.filter( | ||
([_0, _1, _2, mapping]: DualSelectPair<GoogleWorkspaceProviderMapping>) => | ||
mapping?.managed === defaultSelection, | ||
); | ||
} | ||
|
||
return async () => { | ||
const pm = new PropertymappingsApi(DEFAULT_CONFIG); | ||
const mappings = await Promise.allSettled( | ||
instanceMappings.map((instanceId) => | ||
pm.propertymappingsProviderGoogleWorkspaceRetrieve({ pmUuid: instanceId }), | ||
), | ||
); | ||
|
||
return mappings | ||
.filter((s) => s.status === "fulfilled") | ||
.map((s) => s.value) | ||
.map(mappingToSelect); | ||
}; | ||
} |
30 changes: 0 additions & 30 deletions
30
web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; | ||
|
||
import { LDAPSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api"; | ||
|
||
const mappingToSelect = (m: LDAPSourcePropertyMapping) => [m.pk, m.name, m.name, m]; | ||
|
||
export async function propertyMappingsProvider(page = 1, search = "") { | ||
const propertyMappings = await new PropertymappingsApi( | ||
DEFAULT_CONFIG, | ||
).propertymappingsSourceLdapList({ | ||
ordering: "managed", | ||
pageSize: 20, | ||
search: search.trim(), | ||
page, | ||
}); | ||
return { | ||
pagination: propertyMappings.pagination, | ||
options: propertyMappings.results.map(mappingToSelect), | ||
}; | ||
} | ||
|
||
export function propertyMappingsSelector(instanceMappings?: string[]) { | ||
if (!instanceMappings) { | ||
return async (transports: DualSelectPair<LDAPSourcePropertyMapping>[]) => | ||
transports.filter( | ||
([_0, _1, _2, mapping]: DualSelectPair<LDAPSourcePropertyMapping>) => | ||
mapping?.managed?.startsWith("goauthentik.io/sources/ldap/default") || | ||
mapping?.managed?.startsWith("goauthentik.io/sources/ldap/ms"), | ||
); | ||
} | ||
|
||
return async () => { | ||
const pm = new PropertymappingsApi(DEFAULT_CONFIG); | ||
const mappings = await Promise.allSettled( | ||
instanceMappings.map((instanceId) => | ||
pm.propertymappingsSourceLdapRetrieve({ pmUuid: instanceId }), | ||
), | ||
); | ||
|
||
return mappings | ||
.filter((s) => s.status === "fulfilled") | ||
.map((s) => s.value) | ||
.map(mappingToSelect); | ||
}; | ||
} |
Oops, something went wrong.