e0d2beb225b990e376de01b79b799d6ff44d94f6
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| c528a6c336 |
web/admin: add application bindings to the application wizard (#11462)
* web: fix Flash of Unstructured Content while SearchSelect is loading from the backend Provide an alternative, readonly, disabled, unindexed input object with the text "Loading...", to be replaced with the _real_ input element after the content is loaded. This provides the correct appearance and spacing so the content doesn't jiggle about between the start of loading and the SearchSelect element being finalized. It was visually distracting and unappealing. * web: comment on state management in API layer, move file to point to correct component under test. * web: test for flash of unstructured content - Add a unit test to ensure the "Loading..." element is displayed correctly before data arrives - Demo how to mock a `fetchObjects()` call in testing. Very cool. - Make distinguishing rule sets for code, tests, and scripts in nightmare mode - In SearchSelect, Move the `styles()` declaration to the top of the class for consistency. - To test for the FLOUC issue in SearchSelect. This is both an exercise in mocking @beryju's `fetchObjects()` protocol, and shows how we can unit test generic components that render API objects. * web: interim commit of the basic sortable & selectable table. * web: added basic unit testing to API-free tables Mostly these tests assert that the table renders and that the content we give it is where we expect it to be after sorting. For select tables, it also asserts that the overall value of the table is what we expect it to be when we click on a single row, or on the "select all" button. * web: finalize testing for tables Includes documentation updates and better tests for select-table. * Provide unit test accessibility to Firefox and Safari; wrap calls to manipulate test DOMs directly in a browser.exec call so they run in the proper context and be await()ed properly * web: repeat is needed to make sure sub-elements move around correctly. Map does not do full tracking. * web: Update HorizontalLightComponent to accurately convey its value "upwards." * interim commit, gods, the CSS is finally working. * web: update Got the binding editor in. The tests complete. Removed sonarjs. * web: fixed tests to complete. * web: fixed round-trip between binding list and binding editor. Fixed 'delete'. TODO: Fix error reporting on home page, the edit button is ugly, and the height is off somehow, but I'm not yet sure how. I just know it bugs my eyes. * core: add support to set policy bindings in transactional endpoint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve permission checks especially since we'll be using the wizard as default in the future, it shouldn't be superuser only Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web: update api-less tables - Replace `th` with `td` in `thead` components. Because Patternfly. - Add @beryju's styling to the tables, which make it much better looking * web: wizard for applications, now with bindings! - Add policy bindings to the application wizard - Restructures the Wizard base code. - ak-wizard-steps holds the steps and listens for NavigationRequest events to move from one step to the next. - WizardStep is a base class (no component registration provided) that provides the *whole frame*, not just the form. It receives the navigation content for the sidebar from ak-wizard-steps, and provides the styling for the header, footer, sidebar, and main form. It has abstractions for `buttons`, `renderMain()`, `handleButton()`, `handleEnable()`, in a section well-marked as "Public API". Steps inherit from this class. Conceptually: - A wizard is a series of pages ("steps") with a distinct beginning and end, linked in a series, to complete a task. - Later steps in the series are inaccessible until an earlier steps has granted access to it. - Access is predicated on the earlier step being complete and valid. The developer is responsible for determining what "complete and valid" means. - The series is visible, giving the customer a sense of how much effort is needed to complete the task. - A parent object maintains (and can modify as needed) the list of steps. It *can* maintain the information being collected from the user. Alternatively, that information can be kept in each step. Details: - Keeping with the Lit paradigm, "requests to change the system flow up, information changed by valid requests flows down." - The information flows up using events: WizardNavigation, WizardUpdate, WizardClose. - The information flows down using properties. - ak-application-wizard-main holds the list of steps, providing a unique slot name for each. - It maintains the ApplicationWizardState object. - ApplicationWizardStep inherits from WizardStep and provides: - A means of extraction information from forms - A convenience method for updating the ApplicationWizardState object, enabling future steps, and navigating to a future step, in the correct order. - A method for cleaning error from the error reporting mechanism as the user navigates from an error-handling state. - The title, description, and cancelability of the wizard. - Steps: - step: Handles the application. A good starting point for understanding the point of the Wizard. Check the `handleButton()` method to understand how we enable or disable access to future steps. - provider-choice: Just a list. Shows validation without the form. - provider: Uses a *very* esoteric Lit feature, `unsafeStaticTag`, which enables the display to show anything that conforms to the expectations of ApplicationWizardProviderForm. - ApplicationWizardProviderForm repeats some of the base of ApplicationWizardStep, but allows us to provide multiple variants on a single form without having to create separate steps for each form. - The forms (`provider-for-ldap`, `provider-for-radius`) are therefore *just* the form and any fetchers needed to populate it. - bindings: Shows the table of bindings. Has a custom display for "This table is empty." - edit-binding: Showcase for the `SearchSelectEZ` configuration format. Has an override on the `handleButton` feature to figure out which binding is about to be overridden. Is also a `.hidden` page; it doesn't show up on the navigation sidebar, as is only navigable-to by buttons not associated with the button bar at the bottom. - submit: Has a lot of machinery of state: Reviewing with errors, reviewing without errors, running submission, and success. Uses `ts-pattern` a lot to make sure the state/request pairs make sense. The key insight is that, even though a wizard is a series in order, that order can't be simply maintained in a list. The parent needs various strategies for swapping pages in and out of the sequence, while still maintaining a coherent idea of "flow" and providing the visual cues the user needs to feel confident that the work can be completed and completed quickly. The entire mechanism for using an array and index to navigate, with index numbering, blocked the implementation of the bindings pages. One thing led to another. *Sigh* Really wish this hadn't been as much of a mess as it turned out. The end result is pretty good, though. Definitely re-usable. One important feature to note is that the wizard is *not* tied to the ModalButton object; it's simply embedded in a modal as-needed. This allows us to use wizards in other places, such as just being in a DIV, or just a page on its own. * web: rollback dependabot "upgrade" that broke testing Dependabot rolled us into WebdriverIO 9. While that's probably the right thing to do, right now it breaks out end-to-end tests badly. Dependabot's mucking with infrastructure should not be taken lightly, especially in cases when the infrastructure is for DX, not UX, and doesn't create a bigger attack surface on the running product. * web: small fixes for wdio and lint - Roll back another dependabot breaking change, this time to WebdriverIO - Remove the redundant scripts wrapping ESLint for Precommit mode. Access to those modes is available through the flags to the `./web/scripts/eslint.mjs` script. - Remove SonarJS checks until SonarJS is ESLint 9 compatible. - Minor nitpicking. * web: not sure where all these getElement() additions come from; did I add them? Anyway, they were breaking the tests, they're a Wdio9-ism. * package-lock.json update * web: small fixes for wdio and lint **PLEASE** Stop trying to upgrade WebdriverIO following Dependabot's instructions. The changes between wdio8 and wdio9 are extensive enough to require a lot more manual intervention. The unit tests fail in wdio 9, with the testbed driver Wdio uses to compile content to push to the browser ([vite](https://vitejs.dev) complaining: ``` 2024-09-27T15:30:03.672Z WARN @wdio/browser-runner:vite: warning: Unrecognized default export in file /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css Plugin: postcss-lit File: /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css [0-6] 2024-09-27T15:30:04.083Z INFO webdriver: BIDI COMMAND script.callFunction {"functionDeclaration":"<Function[976 bytes]>","awaitPromise":true,"arguments":[],"target":{"context":"8E608E6D13E355DFFC28112C236B73AF"}} [0-6] Error: Test failed due to following error(s): - ak-search-select.test.ts: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default': SyntaxError: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default' ``` So until we can figure out why the Vite installation isn't liking our CSS import scheme, we'll have to soldier on with what we have. At least with Wdio 8, we get: ``` Spec Files: 7 passed, 7 total (100% completed) in 00:00:19 ``` * Forgot to run prettier. * web: small fixes for elements and forms - provides a new utility, `_isSlug_`, used to verify a user input - extends the ak-horizontal-component wrapper to have a stronger identity and available value - updates the types that use the wrapper to be typed more strongly - (Why) The above are used in the wizard to get and store values - fixes a bug in SearchSelectEZ that broke the display if the user didn't supply a `groupBy` field. - Adds `@wdio/types` to the package file so eslint is satisfied wdio builds correctly - updates the end-to-end test to understand the revised button identities on the login page - Running the end-to-end tests verifies that changes to the components listed above did not break the semantics of those components. * Prettier had opinions * Fix the oauth2 provider test. * web: fix oauth2 provider. Fix resolutions in package-lock.json * Provide an error field for the form errors on the OAuth2 form. Unfortunately, this does not solve the general problem that we have a UX issue with which stage bindings to show where now that we've introduced the Invalidation Stage. * 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. * web/admin: provide default invalidation flows for LDAP provider. * admin/web: the default invalidation flows for LDAP and Radius are different from the others. * Updating the SAML Wizard page to correspond to the provider page. *This is an intermediate fix to get the tests passing. It will probably be mooted with the next revision.* * Making progress... * web/admin: provider formectomy complete * fix minor issues Signed-off-by: Jens Langhammer <jens@goauthentik.io> * custom ordering for provider types Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix css Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing PFBase causing wrong font Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing card for type select Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix padding on last page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add card to bindings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web/element/wizard: fix the CSS cascade so the modifications to the title display don't affect the wiard header. * web/elements/wizard: fix logic on unavailable / available / current indicators in nav bar. * Debugging code is not needed. * web: small visual fixes As requested by reviewers: - Fixed the height to 75% of the viewport - Put 1rem of whitespace between the hint label and the Wizard startup button. * web: disable lint check for cAsEfUnNy AtTrIbUtE nAmEs. * Apply suggestions from code review Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> Signed-off-by: Jens L. <jens@beryju.org> * rework title Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Signed-off-by: Jens L. <jens@beryju.org> Co-authored-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> |
|||
| 3c0a8f4641 |
web/admin: improve invalidation flow default & field grouping (#11769)
* web/admin: auto-select provider invalidation flow Signed-off-by: Jens Langhammer <jens@goauthentik.io> * new structuring Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing ldap unbind flow Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated: add enter for redirect Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> |
|||
| 752735d480 |
web: search select with focus, autocomplete, and progressive search (#10728)
* web: much better focus discipline Fix the way focus is handled in SearchSelect so that the drop-down isn't grabbing the focus away from the Input when the user wants to type in their selection. Because it was broken otherwise! There's still a bug where it's possible to type in a complete value *Label*, then leave the component's focus (input and menu) completely, in which case the Label remains, looking innocent and correct, but it is *not* reflective of the value as understood by the SearchSelect API controller. Gonna try to fix that next. But I'm saving this as a useful checkpoint. * . * root: insert daphne app in correct order Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web: implement ak-list-select Creates a new element, ak-list-select, which is a scrollable list that reports when an element is clicked or selected by the keyboard. I was hideously over-engineering ak-search-select-menu, and I decided to try something simpler. This is that something. The events we care about are just "change" and "lost focus", and both of those can be attached by the parent regardless of portaling. * web: ak-list-select is complete An extraction of the "menu" and "list" features from SearchSelect and DualSelect, this is a very simplified version of a visible list that emulates the Radio/Select behavior (i.e only one from the collection may be "valued" at the time). It has no visible indicators of selection (aside from some highlighting), as it's meant to be used to present the list rather than be indicative of any state of the list. I was seriously over-engineering the menu. It turns out, it's just not that difficult after all. The only things we care about, really, are "did the user change the selection," "did the user click out of the list," and "did the user press the escape key." Those are pre-existing events (click w/value, blur, and keydown w/keycode, respectively), so there was no need for me to introduce new custom events to handler them. * web: downgrade sonarjs again, because dependabot Dammit, really need to tell that machine to leave our versions alone. * web: search select After a lot of testing and experimenting, it's finally starting to look stable. What a pain in the neck this has all been. * web: hold * web: search select with focus and progressive search - New component: ak-list-select, which allows you to select from a list of elements, with keyboard control. - New component: ak-portal, which manages elements by moving "slotted" content into a distant component, usually one attached to the body, and positions it relative to an existing element. - ak-search-select-view has been revamped to handle focus, change, input, and blur using the browser native event handlers, rather than inventing my own. - ak-search-select has been turned into a simple driver that manages the view. - ak-search-select has a new declarative syntax for the most common use case. I seriously over-engineered this thing, leaning too heavily on outdated knowledge or assumptions about how the browser works. The native event handlers attached at the component's borders works more than fine, and by attaching the event handlers to the portaled component before sending it off to the slots, the correct handlers get the message. This revision leverages the browser a *lot* more, and gets much more effective interaction with much less code. `<ak-list-select>` is a new component that replaces the ad-hoc menu object of the old SearchSelect. It is a standalone component that just shows a list, allows someone to navigate that list with the keyboard or the mouse. By default, it is limited to half the height of the viewport. The list does not have an indicator of "selected" at this time. That's just a side effect of it being developed as an adjunct to search-select. Its design does not preclude extension. It has a *lot* of CSS components that can be customized. The properties and events are documented, but there is only one event: `change`. Consistent with HTML, the value is not sent with the `change` event; clients are expected to extract it with `change:event.target.value`. Like all HTML components, it is completely stringly defined; the value is either a string or undefined. `<ak-portal>` is a somewhat specialized "portal" component that places an `ak-list-select` in an object on top of the existing DOM content. It can generalized to do this with any component, though, and can be extended. It has no events or CSS, since it's "just" managing the portaling relationship. `<ak-search-select-view>` is the heart of the system. It takes a collection options and behaves like an autocomplete component for them. The only unique event it sends out is `change`, and like `ak-list-select`, it expects the client to retrieve the value. Like all HTML components, it is completely stringly defined; the value is either a string or undefined. This is the SearchSelect component we've all known to come and love, but with a better pop-up and cleaner keyboard interaction. It emits only one event, `ak-change`, which *does* carry the value with it. The Storybooks have been updated to show the current version of Search Select, with a (simulated) API layer as well as more blunt stringly-typed tests for the View layer. A handful of tests have been provided to cover a number of edge cases that I discovered during testing. These run fine with the `npx` command, and I would love to see them integrated into CI/CD. The search select fields `renderElement`, `renderDescription`, and `value` properties of `ak-search-select` have been modified to take a string. For example, the search for the list of user looks like this: ``` <ak-search-select .fetchObjects=${async (query?: string): Promise<User[]> => { const args: CoreUsersListRequest = { ordering: "username" }; if (query !== undefined) { args.search = query; } const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); return users.results; }} .renderElement=${(user: User): string => { return user.username; }} .renderDescription=${(user: User): TemplateResult => { return html`${user.name}`; }} .value=${(user: User | undefined): string | undefined => { return user?.username; }} ></ak-search-select> ``` The most common syntax for the these three fields is "just return the string contents of a field by name," in the case of the description wrapped in a TemplateResult with no DOM components. By automating that initialization in the `connectedCallback` of the `ak-search-select` component, this object would look like: <ak-search-select .fetchObjects=${async (query?: string): Promise<User[]> => { const args: CoreUsersListRequest = { ordering: "username" }; if (query !== undefined) { args.search = query; } const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); return users.results; }} .renderElement=${"username"} .renderDescription=${"name"} .value=${"username"} ></ak-search-select> ``` Due to a limitation in the way properties (such as functions) are interpreted, the syntax `renderElement="username"` is invalid; it has to be a property expression. Sorry; best I could do. The old syntax works just fine. This is a "detect and extend at runtime" enhancement. * Added comments to the Component Driver Harness. * Added more safety and comments. * web: remove string-based access to API; replace with a consolidated "adapter" layer. Clean out the string-based API layer in SearchSelect. Break SearchSelect into a "Base" that does all the work, and then wrap it in two different front-ends: one that conforms to the old WCAPI, and one with a slightly new WCAPI: ``` <ak-search-select-ez .config=${{ fetchObjects: async (query?: string): Promise<Group[]> => { const args: CoreGroupsListRequest = { ordering: "name", includeUsers: false, }; if (query !== undefined) { args.search = query; } const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList( args, ); return groups.results; }, renderElement: (group: Group): string => group.name, value: (group: Group | undefined): string | undefined => group?.pk, selected: (group: Group): boolean => group.pk === this.instance?.group }} blankable > </ak-search-select-ez> ``` * Prettier had opinions. In one case, an important opinion. * Rename test and fix lint error. * fix lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| 19c3f7dd80 |
sources/saml: Basic support for EncryptedAssertion element. (#10099)
* source/saml: Updated backend for encrypted assertion support * source/saml: all lint-fix checks passed * source/saml: Used Optional type instead of union, on enc_key_descriptor type hint * source/saml: request_encrypted_assertion model field migration * source/saml: Added 'noqa' comment to type hint on encryption key descriptor * small fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add to UI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add some error handling Signed-off-by: Jens Langhammer <jens@goauthentik.io> * sources/saml: Pivot to encryption_kp model field, instead of request_encryption bool * sources/saml: Typo fix * re-create migrations Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update web Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add to release notes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add improve error handling, add tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * test metadata with encryption and remove WantAssertionsEncrypted since it's not in the schema Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated fix to radius path Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix unrelated fix...sigh Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-migrate Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| ee58cf0c1c |
web: add HTMLTagNameElementMaps to everything to activate lit analyzer (#10217)
* web: fix esbuild issue with style sheets
Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).
Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.
In standard mode, the following warning appears on the console when running a Flow:
```
Autofocus processing was blocked because a document already has a focused element.
```
In compatibility mode, the following **error** appears on the console when running a Flow:
```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
at initDomMutationObservers (crawler-inject.js:1106:18)
at crawler-inject.js:1114:24
at Array.forEach (<anonymous>)
at initDomMutationObservers (crawler-inject.js:1114:10)
at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```
Despite this error, nothing seems to be broken and flows work as anticipated.
* web: add more linting
* A reliable test for the extra code needed in analyzer, passing shellcheck
* web: re-enable custom-element-manifest and enable component checking in Typescript
This commit includes a monkeypatch to allow custom-element-manifest (CEM) to work correctly again
despite our rich collection of mixins, reactive controllers, symbol-oriented event handlers, and the
like. With that monkeypatch in place, we can now create the CEM manifest file and then exploit it so
that IDEs and the Typescript compilation pass can tell when a component is being used incorrectly;
when the wrong types are being passed to it, or when a required attribute is not initialized.
* Added building the manifest to the build process, rather than storing it. It is not appreciably slow.
* web: the most boring PR in the universe: Add HTMLTagNameElementMap to everyhing
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!
```
|
|||
| 6c4c535d57 |
web/admin: rework initial wizard pages and add grid layout (#9668)
* remove @goauthentik/authentik as TS path Signed-off-by: Jens Langhammer <jens@goauthentik.io> * initial implementation Signed-off-by: Jens Langhammer <jens@goauthentik.io> * oh yeah Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format earlier changes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * support plain alert Signed-off-by: Jens Langhammer <jens@goauthentik.io> * initial attempt at dedupe Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make it a base class Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate all wizards Signed-off-by: Jens Langhammer <jens@goauthentik.io> * create type create mixin to dedupe more, add icon to source create Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add ldap icon Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Optimised images with calibre/image-actions * match inverting we should probably replace all icons with coloured ones so we don't need to invert them...I guess Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make everything more explicit Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add icons to provider Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add remaining provider icons Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework to not use inheritance Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix unrelated typo Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make app wizard use grid layout Signed-off-by: Jens Langhammer <jens@goauthentik.io> * keep wizard height consistent Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com> |
|||
| 2e91b9d035 |
web: bump API Client version (#9785)
* web: bump API Client version Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * don't include users in group calls Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| dcbfe73891 |
web: provide a context for checking the status of the enterprise license (#8153)
* web: provide a context for enterprise license status There are a few places (currently 5) in our code where we have checks for the current enterprise licensing status of our product. While not particularly heavy or onerous, there's no reason to repeat those same lines, and since our UI is always running in the context of authentik, may as well make that status a client-side context in its own right. The status will update with an EVENT_REFRESH request. A context-aware custom alert has also been provided; it draws itself (or `nothing`) depending on the state of the license, and the default message, "This feature requires an enterprise license," can be overriden with the `notice` property. These two changes reduce the amount of code needed to manage our license alerting from 67 to 38 lines code, and while removing 29 lines from a product with 54,145 lines of code (a savings of 0.05%, oh boy!) isn't a miracle, it does mean there's a single source of truth for "Is this instance enterprise-licensed?" that's easy to access and use. * web: [x] The translation files have been updated |
|||
| 830689f1cb |
web: bad default in select (#8258)
* web: fix event propogation in search-select wrappers Two different patches, an older one that extracted long search blocks that were cut-and-pasted into a standalone component, and a newer one that fixed displaying placeholder values properly, conflicted and broke a relationship that allowed for the values to be propagated through those standalone components correctly. This restores the event handling and updates the listener set-ups with more idiomatic hooks into Lit's event system. * Updated search-select to properly render with Storybook, and provided a foundation for testing the Search-Select component with Storybook. * Accidentally deleted this line while making Sonar accept my test data. * Fixing a small issue that's bugged me for awhile: there's no reason to manually duplicate what code can duplicate. * Provided a storybook for testing out the flow search. Discovered along the way that I'd mis-used a prop-drilling technique which caused the currentFlow to be "undefined" when pass forward, giving rise to Marc's bug. I *think* this shakes out the last of the bugs. Events are passed up correctly and the initial value is recorded correctly. * Added comments and prettier had opinions. * Restoring old variable names; they didn't have to change after all. * fix lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| abc0c2d2a2 |
root: Multi-tenancy (#7590)
* tenants -> brands, init new tenant model, migrate some config to tenants Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * setup logging for tenants Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * configure celery and cache Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * small fixes, runs Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * task fixes, creation of tenant now works by cloning a template schema, some other small stuff Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix-tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * upstream fixes Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix-pylint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix avatar tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * migrate config reputation_expiry as well Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix web rebase Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix migrations for template schema Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix migrations for template schema Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix migrations for template schema 3 Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * revert reputation expiry migration Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix type Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix some more tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * website: tenants -> brands Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * try fixing e2e tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * start frontend :help: Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * add ability to disable tenants api Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * delete embedded outpost if it is disabled Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * make sure embedded outpost is disabled when tenants are enabled Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * management commands: add --schema option where relevant Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * store files per-tenant Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix embedded outpost deletion Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix files migration Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * add tenant api tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * add domain tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * add settings tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * make --schema-name default to public in mgmt commands Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * sources/ldap: make sure lock is per-tenant Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix stuff I broke Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix remaining failing tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * try fixing e2e tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * much better frontend, but save does not refresh form properly Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * update django-tenants with latest fixes Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * i18n-extract Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * review comments Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * move event_retention from brands to tenants Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * wip Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * root: add support for storing media files in S3 Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * use permissions for settings api Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * blueprints: disable tenants management Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix embedded outpost create/delete logic Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * make gen Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * make sure prometheus metrics are correctly served Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * makefile: don't delete the go api client when not regenerating it Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * tenants api: add recovery group and token creation endpoints Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix startup Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix prometheus metrics Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix tests Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * lint Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix web stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix migrations from stable Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> * fix oauth source type import Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Revert "fix oauth source type import" This reverts commit |
|||
| abf1f0e348 |
web: fix event propagation in search-select wrappers (#8224)
web: fix event propogation in search-select wrappers Two different patches, an older one that extracted long search blocks that were cut-and-pasted into a standalone component, and a newer one that fixed displaying placeholder values properly, conflicted and broke a relationship that allowed for the values to be propagated through those standalone components correctly. This restores the event handling and updates the listener set-ups with more idiomatic hooks into Lit's event system. |
|||
| 6792bf8876 |
web: package up horizontal elements into their own components (#7053)
* web: laying the groundwork for future expansion This commit is a hodge-podge of updates and changes to the web. Functional changes: - Makefile: Fixed a bug in the `help` section that prevented the WIDTH from being accurately calculated if `help` was included rather than in-lined. - ESLint: Modified the "unused vars" rule so that variables starting with an underline are not considered by the rule. This allows for elided variables in event handlers. It's not a perfect solution-- a better one would be to use Typescript's function-specialization typing, but there are too many places where we elide or ignore some variables in a function's usage that switching over to specialization would be a huge lift. - locale: It turns out, lit-locale does its own context management. We don't need to have a context at all in this space, and that's one less listener we need to attach t othe DOM. - ModalButton: A small thing, but using `nothing` instead of "html``" allows lit better control over rendering and reduces the number of actual renders of the page. - FormGroup: Provided a means to modify the aria-label, rather than stick with the just the word "Details." Specializing this field will both help users of screen readers in the future, and will allow test suites to find specific form groups now. - RadioButton: provide a more consistent interface to the RadioButton. First, we dispatch the events to the outside world, and we set the value locally so that the current `Form.ts` continues to behave as expected. We also prevent the "button lost value" event from propagating; this presents a unified select-like interface to users of the RadioButtonGroup. The current value semantics are preserved; other clients of the RadioButton do not see a change in behavior. - EventEmitter: If the custom event detail is *not* an object, do not use the object-like semantics for forwarding it; just send it as-is. - Comments: In the course of laying the groundwork for the application wizard, I throw a LOT of comments into the code, describing APIs, interfaces, class and function signatures, to better document the behavior inside and as signposts for future work. * web: permit arrays to be sent in custom events without interpolation. * actually use assignValue or rather serializeFieldRecursive Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web: package up horizontal elements into their own components. This commit introduces a number of "components." Jens has this idiom: ``` <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required /> </ak-form-element-horizontal> ``` It's a very web-oriented idiom in that it's built out of two building blocks, the "element-horizontal" descriptor, and the input object itself. This idiom is repeated a lot throughout the code. As an alternative, let's wrap everything into an inheritable interface: ``` <ak-text-input name="name" label=${msg("Name")} value="${ifDefined(this.instance?.name)} required > </ak-text-input> ``` This preserves all the information of the above, makes it much clearer what kind of interaction we're having (sometimes the `type=` information in an input is lost or easily missed), and while it does require you know that there are provided components rather than the pair of layout-behavior as in the original it also gives the developer more precision over the look and feel of the components. *Right now* these components are placed into the LightDOM, as they are in the existing source code, because the Form handler has a need to be able to "peer into" the "element-horizontal" component to find the values of the input objects. In a future revision I hope to place the burden of type/value processing onto the input objects themselves such that the form handler will need only look for the `.value` of the associated input control. Other fixes: - update the FlowSearch() such that it actually emits an input event when its value changes. - Disable the storybook shortcuts; on Chrome, at least, they get confused with simple inputs - Fix an issue with precommit to not scan any Python with ESLint! :-) * web: provide storybook stories for the components This commit provides storybook stories for the ak-horizontal-element wrappers. A few bugs were found along the way, including one rather nasty one from Radio where we were still getting the "set/unset" pair in the wrong order, so I had to knuckle down and fix the event handler properly. * web: test oauth2 provider "guinea pig" for new components I used the Oauth2 provider page as my experiment in seeing if the horizontal-element wrappers could be used instead of the raw wrappers themselves, and I wanted to make sure a test existed that asserts that filling out THAT form in the ProvidersList and ProvidersForm didn't break anything. This commit updates the WDIO tests to do just that; the test is simple, but it does exercise the `name` field of the Provider, something not needed in the Wizard because it's set automatically based on the Application name, and it even asserts that the new Provider exists in the list of available Providers when it's done. * web: making sure ESlint and Prettier are happy * "fix" lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| 671b7156ed |
web: improve testability (#6952)
web/improve testability This is a trio of small hacks that allow the E2E tests to find several components on the page while the test is running: - Add a `data-managed-for` field to SearchSelect's positioned elements. If a search has a `name` field, it will be reflected here, allowing tests to find specific instances of the dropdown elements. - Add a forwarder to the search select wrappers we use for our SearchSelect. - Added aria details to the UserLibrary header to make it easy to identify. |
|||
| 5ac30c4901 |
web/admin: fix flow-search not being able to unset (#6838)
similar to https://github.com/goauthentik/authentik/pull/6767 Signed-off-by: Jens Langhammer <jens@goauthentik.io> |
|||
| 0a9880547c |
web/admin: fix not being able to unset certificates (#6767)
* web: fix 6742: empty web certificate request needs to return null, not undefined This replaces the `undefined` setting of the certificate search wrapper to `null` when the admin requests no certificate. * only set singleton if we don't have an instance Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| f5394da9f7 |
web: Replace ad-hoc toggle control with ak-toggle-group (#6470)
* web: Replace ad-hoc toggle control with ak-toggle-group
This commit replaces various ad-hoc implementations of the Patternfly Toggle Group HTML with a web
component that encapsulates all of the needed behavior and exposes a single API with a single event
handler, return the value of the option clicked.
The results are: Lots of visual clutter is eliminated. A single link of:
```
<div class="pf-c-toggle-group__item">
<button
class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy
? "pf-m-selected"
: ""}"
type="button"
@click=${() => {
this.mode = ProxyMode.Proxy;
}}>
<span class="pf-c-toggle-group__text">${msg("Proxy")}</span>
</button>
</div>
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
```
Now looks like:
```
<option value=${ProxyMode.Proxy}>${msg("Proxy")}</option>
```
This also means that the three pages that used the Patternfly Toggle Group could eliminate all of
their Patternfly PFToggleGroup needs, as well as the `justify-content: center` extension, which also
eliminated the `css` import.
The savings aren't as spectacular as I'd hoped: removed 178 lines, but added 123; total savings 55
lines of code. I still count this a win: we need never write another toggle component again, and
any bugs, extensions or features we may want to add can be centralized or forked without risking the
whole edifice.
* web: minor code formatting issue.
* web: adding a storybook for the ak-toggle-group component
* Bugs found by CI/CD.
* web: Replace ad-hoc search for CryptoCertificateKeyPairs with crypto-certificate-search (#6475)
* web: Replace ad-hoc search for CryptoCertificateKeyPairs with ak-crypto-certeficate-search
This commit replaces various ad-hoc implementations of `search-select` for CryptoCertificateKeyPairs
with a web component that encapsulates all of the needed behavior and exposes a single API.
The results are: Lots of visual clutter is eliminated. A single search of:
```HTML
<ak-search-select
.fetchObjects=${async (query?: string): Promise<CertificateKeyPair[]> => {
const args: CryptoCertificatekeypairsListRequest = {
ordering: "name",
hasKey: true,
includeDetails: false,
};
if (query !== undefined) {
args.search = query;
}
const certificates = await new CryptoApi(
DEFAULT_CONFIG,
).cryptoCertificatekeypairsList(args);
return certificates.results;
}}
.renderElement=${(item: CertificateKeyPair): string => {
return item.name;
}}
.value=${(item: CertificateKeyPair | undefined): string | undefined => {
return item?.pk;
}}
.selected=${(item: CertificateKeyPair): boolean => {
return this.instance?.tlsVerification === item.pk;
}}
?blankable=${true}
>
</ak-search-select>
```
Now looks like:
```HTML
<ak-crypto-certificate-search certificate=${this.instance?.tlsVerification}>
</ak-crypto-certificate-search>
```
There are three searches that do not require there to be a valid key with the certificate; these are
supported with the boolean property `nokey`; likewise, there is one search (in SAMLProviderForm)
that states that if there is no current certificate in the SAMLProvider and only one certificate can
be found in the Authentik database, use that one; this is supported with the boolean property
`singleton`.
These changes replace 382 lines of object-oriented invocations with 36 lines of declarative
configuration, and 98 lines for the class. Overall, the code for "find a crypto certificate" has
been reduced by 46%.
Suggestions for a better word than `singleton` are welcome!
* web: display tests for CryptoCertificateKeypair search
This adds a Storybook for the CryptoCertificateKeypair search, including
a mock fetch of the data. In the course of running the tests, we discovered
that including the SearchSelect _class_ won't include the customElement declaration
unless you include the whole file! Other bugs found: including the CSS from
Storybook is different from that of LitElement native, so much so that the
adapter needed to be included. FlowSearch had a similar bug. The problem
only manifests when building via Webpack (which Storybook uses) and not
Rollup, but we should support both in distribution.
|
|||
| 3f02534eb1 |
web: weightloss program, part 1: FlowSearch (#6332)
* web: weightloss program, part 1: FlowSearch
This commit extracts the multiple uses of SearchSelect for Flow lookups in the `providers`
collection and replaces them with a slightly more legible format, from:
```HTML
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Flow[]> => {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation: FlowsInstancesListDesignationEnum.Authentication,
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(args);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.authenticationFlow;
}}
>
</ak-search-select>
```
... to:
```HTML
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authentication}
.currentFlow=${this.instance?.authenticationFlow}
required
></ak-flow-search>
```
All of those middle methods, like `renderElement`, `renderDescription`, etc, are *completely the
same* for *all* of the searches, and there are something like 25 of them; this commit only covers
the 8 in `providers`, but the next commit should carry all of them.
The topmost example has been extracted into its own Web Component, `ak-flow-search`, that takes only
two arguments: the type of `FlowInstanceListDesignation` and the current instance of the flow.
The static methods for `renderElement`, `renderDescription` and `value` (which are all the same in
all 25 instances of `FlowInstancesListRequest`) have been made into standalone functions.
`fetchObjects` has been made into a method that takes the parameter from the `designation` property,
and `selected` has been turned into a method that takes the comparator instance from the
`currentFlow` property. That's it. That's the whole of it.
`SearchSelect` now emits an event whenever the user changes the field, and `ak-flow-search`
intercepts that event to mirror the value locally.
`Form` has been adapted to recognize the `ak-flow-search` element and extract the current value.
There are a number of legibility issues remaining, even with this fix. The Authentik Form manager
is dependent upon a component named `ak-form-element-horizontal`, which is a container for a single
displayed element in a form:
```HTML
<ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
```
Imagine, instead, if we could write:
```HTML
<ak-form-element-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
name="authorizationFlow">
<label slot="label">${msg("Authorization flow")}</label>
<span slot="help">${msg("Flow used when authorizing this provider.")}</span>
<ak-form-element-flow-search>
```
Starting with a superclass that understands the need for `label` and `help` slots, it would
automatically configure the input object that would be used. We've already specified multiple
identical copies of this thing in multiple different places; centralizing their definition and then
re-using them would be classic code re-use.
Even better, since the Authorization flow is used 10 times in the whole of our code base, and the
Authentication flow 8 times, and they are *all identical*, it would be fitting if we just created
wrappers:
```HTML
<ak-form-element-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}>
<ak-form-element-flow-search>
```
That's really all that's needed. There are *hundreds* (about 470 total) cases where nine or more
lines of repetitious HTML could be replaced with a one-liner like the above.
A "narrow waist" design is one that allows for a system to communicate between two different
components through a small but consistent collection of calls. The Form manager needs to be narrowed
hard. The `ak-form-element-horizontal` is a wrapper around an input object, and it has this at its
core for extracting that information. This forwards the name component to the containing input
object so that when the input object generates an event, we can identify the field it's associated
with.
```Javascript
this.querySelectorAll("*").forEach((input) => {
switch (input.tagName.toLowerCase()) {
case "input":
case "textarea":
case "select":
case "ak-codemirror":
case "ak-chip-group":
case "ak-search-select":
case "ak-radio":
input.setAttribute("name", this.name);
break;
default:
return;
}
```
A *temporary* variant of this is in the `ak-flow-search` component, to support this API without
having to modify `ak-form-element-horizontal`.
And then `ak-form` itself has this:
```Javascript
if (
inputElement.tagName.toLowerCase() === "select" &&
"multiple" in inputElement.attributes
) {
const selectElement = inputElement as unknown as HTMLSelectElement;
json[element.name] = Array.from(selectElement.selectedOptions).map((v) => v.value);
} else if (
inputElement.tagName.toLowerCase() === "input" &&
inputElement.type === "date"
) {
json[element.name] = inputElement.valueAsDate;
} else if (
inputElement.tagName.toLowerCase() === "input" &&
inputElement.type === "datetime-local"
) {
json[element.name] = new Date(inputElement.valueAsNumber);
}
// ... another 20 lines removed
```
This ought to read:
```Javascript
const json = elements.filter((element => element instanceof AkFormComponent)
.reduce((acc, element) => ({ ...acc, [element.name]: element.value] });
```
Where, instead of hand-writing all the different input objects for date and datetime and checkbox
into our forms, and then having to craft custom value extractors for each and every one of them,
just write *one* version of each with all the wrappers and bells and whistles already attached, and
have each one of them have a `value` getter descriptor that returns the value expected by our form
handler.
A back-of-the-envelope estimation is that there's about four *thousand* lines that could disappear
if we did this right.
More importantly, it would be possible to create new `AkFormComponent`s without having to register
them or define them for `ak-form`; as long as they conformed to the AkFormComponent's expectations
for "what is a source of values for a Form", `ak-form` would understand how to handle it.
Ultimately, what I want is to be able to do this:
``` HTML
<ak-input-form
itemtype="ak-search"
itemid="ak-authentication"
itemprop=${this.instance}></ak-inputform>
```
And it will (1) go out and find the right kind of search to put there, (2) conduct the right kind of
fetch to fill that search, (3) pre-configure it with the user's current choice in that locale.
I don't think this is possible-- for one thing, it would be very expensive in terms of development,
and it may break the "narrow waist" ideal by require that the `ak-input-form` object know all the
different kinds of searches that are available. The old Midgardian dream was that the object would
have *just* the identity triple (A table, a row of that table, a field of that row), and the
Javascript would go out and, using the identity, *find* the right object for CRUD (Creating,
Retrieving, Updating, and Deleting) it.
But that inspiration, as unreachable as it is, is where I'm headed. Where our objects are both
*smart* and *standalone*. Where they're polite citizens in an ordered universe, capable of
independence sufficient to be tested and validated and trusted, but working in concert to achieve
our aims.
* web: unravel the search-select for flows completely.
This commit removes *all* instances of the search-select
for flows, classifying them into four different categories:
- a search with no default
- a search with a default
- a search with a default and a fallback to a static default if non specified
- a search with a default and a fallback to the tenant's preferred default if this is a new instance
and no flow specified.
It's not humanly possible to test all the instances where this has been committed, but the linters
are very happy with the results, and I'm going to eyeball every one of them in the github
presentation before I move this out of draft.
* web: several were declared 'required' that were not.
* web: I can't believe this was rejected because of a misspelling in a code comment. Well done\!
* web: another codespell fix for a comment.
* web: adding 'codespell' to the pre-commit command. Fixed spelling error in eventEmitter.
|