Let a connection's form offer what its service has, several chosen with a mode each (#332) #1089

Merged
tiagoagueda merged 1 commit from 332-connection-discovery into main 2026-10-31 05:10:45 +00:00
Owner

A connection's form was drawn from FieldSpecs whose choices were fixed when
config_fields() ran, so a plugin like DAV could not offer the address books,
calendars and task lists only the server knows about (postulo/postulo-dav#13).

  • FieldSpec gains modes and a discovered type. DiscoveredOption (value,
    label, group, modes, note) is on postulo.plugins.api. A connected plugin may
    have an optional discover(config) that returns options keyed by field name.
  • A saved connection's form has a "Look on the server" button. Like #688's
    "Look up", it posts the whole form to connections:discover, which works
    without scripts. That view calls discover with the configuration as last
    saved and draws the form back as it was typed. It never runs on page load
    or on Save. It shares Test's allowance (POSTULO_CONNECTION_TEST_RATE), its
    consent check and its error handling, now one helper used by both views,
    and calls the server outside any transaction. A failure is shown in the
    plugin's words and keeps what was found before.
  • What was found is kept in the connection's config under
    "postulo:discovered", with when it was found. The field stores option value
    to mode for the chosen options only.
  • The field is drawn as a real table with a caption and header cells,
    grouped. Each option has one select labelled by its name, with "Not used"
    first and then the modes that option allows. A chosen option that is no
    longer found keeps its row and last label, marked "No longer on the
    server", until it is set to Not used. validate() sees the choice.
  • Connections only. A transport or a backup destination that declares the
    type gets a disabled field with a sentence and stores nothing.

Tests: tests/test_connection_discovery.py, page coverage, the isolation
sweep and an accessibility walk of the form showing found options.

Closes #332

Notes for review

  • New field type: discovered (with FieldSpec.modes), for connections only, plus DiscoveredOption on the plugin surface. A connected plugin may implement discover(config).
  • Checking what discover returns (plugins/discovery.py): bad items and undeclared modes are dropped, duplicates kept once, at most 200 options of up to 500 characters. The result is kept in the connection's config (postulo:discovered), so no migration.
  • The form: one select per found option, with Not used first, then the plugin's modes. Only chosen options are stored, as {value: mode}. An option that is no longer found stays visible, marked gone.
  • Look on the server: a POST of the whole form (connections:discover) that never saves. It shares the test button's guard: the same allowance (POSTULO_CONNECTION_TEST_RATE), the consent check, and errors in the plugin's words. It runs outside the request's transaction.
  • Transports and backup destinations: the type shows as a disabled field with a sentence, and nothing is stored.
  • Decisions the issue left open:
    • discovery uses the configuration as last saved;
    • required is ignored on a discovered field;
    • a plugin's validate problems on it are form-wide.
  • Tests: a new browser axe test for the option table, light and dark.

🤖 Generated with Claude Code

A connection's form was drawn from FieldSpecs whose choices were fixed when config_fields() ran, so a plugin like DAV could not offer the address books, calendars and task lists only the server knows about (postulo/postulo-dav#13). - FieldSpec gains `modes` and a `discovered` type. DiscoveredOption (value, label, group, modes, note) is on postulo.plugins.api. A connected plugin may have an optional `discover(config)` that returns options keyed by field name. - A saved connection's form has a "Look on the server" button. Like #688's "Look up", it posts the whole form to connections:discover, which works without scripts. That view calls discover with the configuration as last saved and draws the form back as it was typed. It never runs on page load or on Save. It shares Test's allowance (POSTULO_CONNECTION_TEST_RATE), its consent check and its error handling, now one helper used by both views, and calls the server outside any transaction. A failure is shown in the plugin's words and keeps what was found before. - What was found is kept in the connection's config under "postulo:discovered", with when it was found. The field stores option value to mode for the chosen options only. - The field is drawn as a real table with a caption and header cells, grouped. Each option has one select labelled by its name, with "Not used" first and then the modes that option allows. A chosen option that is no longer found keeps its row and last label, marked "No longer on the server", until it is set to Not used. validate() sees the choice. - Connections only. A transport or a backup destination that declares the type gets a disabled field with a sentence and stores nothing. Tests: tests/test_connection_discovery.py, page coverage, the isolation sweep and an accessibility walk of the form showing found options. Closes #332 ## Notes for review - **New field type:** `discovered` (with `FieldSpec.modes`), for connections only, plus `DiscoveredOption` on the plugin surface. A connected plugin may implement `discover(config)`. - **Checking what `discover` returns** (`plugins/discovery.py`): bad items and undeclared modes are dropped, duplicates kept once, at most 200 options of up to 500 characters. The result is kept in the connection's `config` (`postulo:discovered`), so no migration. - **The form:** one select per found option, with *Not used* first, then the plugin's modes. Only chosen options are stored, as `{value: mode}`. An option that is no longer found stays visible, marked gone. - ***Look on the server*:** a POST of the whole form (`connections:discover`) that never saves. It shares the test button's guard: the same allowance (`POSTULO_CONNECTION_TEST_RATE`), the consent check, and errors in the plugin's words. It runs outside the request's transaction. - **Transports and backup destinations:** the type shows as a disabled field with a sentence, and nothing is stored. - **Decisions the issue left open:** - discovery uses the configuration as last saved; - `required` is ignored on a discovered field; - a plugin's `validate` problems on it are form-wide. - **Tests:** a new browser axe test for the option table, light and dark. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Let a connection's form offer what its service has, several chosen with a mode each (#332)
Some checks failed
CI / Unit tests and coverage (Python 3.14) (pull_request) Failing after 15m7s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m9s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m18s
CI / Security audit (pull_request) Successful in 1m4s
CI / Browser tests (Chromium) (pull_request) Successful in 39m11s
d08598980e
A connection's form was drawn from FieldSpecs whose choices were fixed when
config_fields() ran, so a plugin like DAV could not offer the address books,
calendars and task lists only the server knows about (postulo/postulo-dav#13).

- FieldSpec gains `modes` and a `discovered` type. DiscoveredOption (value,
  label, group, modes, note) is on postulo.plugins.api. A connected plugin may
  have an optional `discover(config)` that returns options keyed by field name.
- A saved connection's form has a "Look on the server" button. Like #688's
  "Look up", it posts the whole form to connections:discover, which works
  without scripts. That view calls discover with the configuration as last
  saved and draws the form back as it was typed. It never runs on page load
  or on Save. It shares Test's allowance (POSTULO_CONNECTION_TEST_RATE), its
  consent check and its error handling, now one helper used by both views,
  and calls the server outside any transaction. A failure is shown in the
  plugin's words and keeps what was found before.
- What was found is kept in the connection's config under
  "postulo:discovered", with when it was found. The field stores option value
  to mode for the chosen options only.
- The field is drawn as a real table with a caption and header cells,
  grouped. Each option has one select labelled by its name, with "Not used"
  first and then the modes that option allows. A chosen option that is no
  longer found keeps its row and last label, marked "No longer on the
  server", until it is set to Not used. validate() sees the choice.
- Connections only. A transport or a backup destination that declares the
  type gets a disabled field with a sentence and stores nothing.

Tests: tests/test_connection_discovery.py, page coverage, the isolation
sweep and an accessibility walk of the form showing found options.

Closes #332

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed 332-connection-discovery from d08598980e
Some checks failed
CI / Unit tests and coverage (Python 3.14) (pull_request) Failing after 15m7s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m9s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m18s
CI / Security audit (pull_request) Successful in 1m4s
CI / Browser tests (Chromium) (pull_request) Successful in 39m11s
to 39c4e97560
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 14m19s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 1m42s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m24s
CI / Security audit (pull_request) Successful in 1m24s
CI / Browser tests (Chromium) (pull_request) Successful in 35m39s
2026-10-31 02:32:18 +00:00
Compare
tiagoagueda force-pushed 332-connection-discovery from 39c4e97560
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 14m19s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 1m42s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m24s
CI / Security audit (pull_request) Successful in 1m24s
CI / Browser tests (Chromium) (pull_request) Successful in 35m39s
to 46252480cf
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 15m17s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 1m45s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m37s
CI / Security audit (pull_request) Successful in 1m1s
CI / Browser tests (Chromium) (pull_request) Successful in 35m0s
2026-10-31 05:07:51 +00:00
Compare
tiagoagueda deleted branch 332-connection-discovery 2026-10-31 05:10:48 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo!1089
No description provided.