* 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: streamline CaptchaStage
# What
This commit:
1. Replaces the mass of `if () { if() { if() } }` with two state tables:
- One for `render()`
- One for `renderBody()`
2. Breaks each Captcha out into "interactive" and "executive" versions
3. Creates a handler table for each Captcha type
4. Replaces the `.forEach` expression with a `for` loop.
5. Move `updated` to the end of the class.
6. Make captchDocument and captchaFrame constructed-on-demand with a cache.
7. Remove a lot of `@state` handlers
8. Give IframeMessageEvent its own type.
9. Removed `this.scriptElement`
10. Replaced `window.removeEventListener` with an `AbortController()`
# Why
1. **Replacing `if` trees with a state table.** The logic of the original was really hard to follow.
With the state table, we can clearly see now that for the `render()` function, we care about the
Boolean flags `[embedded, challenged, interactive]` and have appropriate effects for each. With
`renderBody()`, we can see that we care about the Boolean flags `[hasError, challenged]`, and can
see the appropriate effects for each one.
2. (and 3.) **Breaking each Captcha clause into separate handlers.** Again, the logic was convoluted,
when what we really care about is "Does this captcha have a corresponding handler attached to
`window`, and, if so, should we run the interactive or executive version of it?" By putting all
of that into a table of `[name, challenge, execute]`, we can clearly see what's being handled
when.
4. **Replacing `foreach()` with `for()`**: [You cannot use a `forEach()` with async
expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#:~:text=does%20not%20wait%20for%20promises).
If you need asynchronous behavior in an ordered loop, `for()` is the safest way to handle it; if
you need asynchronous behavior from multiple promises, `Promise.allSettled(handlers.map())` is
the way to go.
I tried to tell if this function *meant* to run every handler it found simultaneously, or if it
meant to test them in order; I went with the second option, breaking and exiting the loop once a
handler had run successfully.
5. **Reordered the code a bit**. We're trying to evolve a pattern in our source code: styles,
properties, states, internal variables, constructor, getters & setters that are not `@property()`
or `@state()`, DOM-related lifecycle handlers, event handlers, pre-render lifecycle handlers,
renderers, and post-render lifecycle handlers. Helper methods (including subrenderers) go above
the method(s) they help.
6. **Constructing Elements on demand with a cache**. It is not guaranteed that we will actually need
either of those. Constructing them on demand with a cache is both performant and cleaner.
Likewise, I removed these from the Lit object's `state()` table, since they're constructed once
and never change over the lifetime of an instance of `ak-stage-captcha`.
9. **Remove `this.scriptElement`**: It was never referenced outside the one function where it was used.
10. **Remove `removeEventListener()`**: `AbortController()` is a bit more verbose for small event
handler collections, but it's considered much more reliable and much cleaner.
* Didn't save the extracted ListenerController.
* 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: simplify `?inline` handler for Storybook
# What
- Revise the `?inline` handler for Storybook
- Enable headless test runs of E2E
- Reduce headless testing to single instances
# Why
## `?inline` handling
Vite-for-Storybook-for-Web-Components has a requirement that all component CSS imports be
suffixed with an `?inline` argument so Vite knows to put the CSS into the component and
not inject it into the document head.
This `?inline` argument is an implementation detail of Storybook. It would be irrelevant clutter
added to our codebase. We were using `rollup-plugin-modify` to find every instance of an
import-to-component, but the implementation was clunky and involved scanning the source code
manually.
`rollup-plugin-modify` version 3 has regular expressions and takes a function as an argument. This
allows us to generate the CSS import maps on-the-fly when Storybook is run, eliminating a fragile
build step. We can also remove the source code scanner for those imports.
## Changes to testing
It's just nice to be able to run the E2E tests headlessly, without them eating up your screen real
estate, flashing, or grabbing your mouse.
WebdriverIO's testing of Web Components is new and, as we've seen, a bit cranky. The WebdriverIO
team currently recommends not running the tests in parallel. We only have about 70 tests so far, and
they're fairly speedy, especially when you don't have to invoke a browser session for every test.
* 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>
* 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: fix selector warnings in WebdriverIO
Despite the [promises made](https://webdriver.io/docs/selectors#deep-selectors) by the WebdriverIO
team, we are still getting a lot of warnings and "falling back to pre-BIDI behavior" messages
when we attempt to access ShadowDOM contexts without the "pierce" (`>>>`) syntax. So I've put
it back wherever it occurred and the system now uses the BIDI controllers correctly.
* web: update to Chromedriver 131 breaks a lot of stuff
This annoying bit of janitorial work cleans up the failure messages and resolution bugs
that arose when updating to the latest version of Chrome. Keeping track of all the
weakness and breakage while the in-browser testing teams figure out how to live with
the ShadowDOM is just really time-consuming.
* 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/bugfix/fix-reporting-in-wizard-submit
# What
- Preserves the errors locally for the Wizard, providing explanation and links to fix the issues
# Why
Just a silly mistake on my part. There shouldn't be two copies of errors (and there isn't in the BIG
PRs), but this is how it's designed right now and making the errors show up is an easy fix. In doing
so, the "hack" to move the "bad provider name" to the provider page is included.
* Updated package.json to use Chromedriver 130
* 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.
* First things first: save the blueprint that initializes the test runner.
* Committing to having the PKs be a string, and streamlining an event handler. Type solidity needed for the footer control.
* web/admin/better-footer-links
# What
- A data control that takes two string fields and returns the JSON object for a FooterLink
- A data control that takes a control like the one above and assists the user in entering a
collection of such objects.
# Why
We're trying to move away from CodeMirror for the simple things, like tables of what is essentially
data entry. Jens proposed this ArrayInput thing, and I've simplified it so you define what "a row"
is as a small, lightweight custom Component that returns and validates the datatype for that row,
and ArrayInput creates a table of rows, and that's that.
We're still working out the details, but the demo is to replace the "Name & URL" table in
AdminSettingsForm with this, since it was silly to ask the customer to hand-write JSON or YAML,
getting the keys right every time, for an `Array<Record<{ name: string, href: string }>>`. And some
client-side validation can't hurt.
Storybook included. Tests to come.
* Not ready for prime time.
* One lint. Other lints are still in progress.
* web: lots of 'as unknown as Foo'
I know this is considered bad practice, but we use Lit and Lit.spread
to send initialization arguments to functions that create DOM
objects, and Lit's prefix convention of '.' for object, '?' for
boolean, and '@' for event handler doesn't map at all to the Interface
declarations of Typescript. So we have to cast these types when
sending them via functions to constructors.
* web/admin/better-footer-links
# What
- Remove the "JSON or YAML" language from the AdminSettings page for describing FooterLinks inputs.
- Add unit tests for ArrayInput and AdminSettingsFooterLinks.
- Provide a property for accessing a component's value
# Why
Providing a property by which the JSONified version of the value can be accessed enhances the
ability of tests to independently check that the value is in a state we desire, since properties can
easily be accessed across the wire protocol used by browser-based testing environments.
* Ensure the UI is built from _current_ before running tests.