Skip to content

feat(tui): CRUD foundation (form, state, keys, delete ops) - #20

Open
lvlcn-t wants to merge 2 commits into
mainfrom
feat/tui-crud-foundation
Open

feat(tui): CRUD foundation (form, state, keys, delete ops)#20
lvlcn-t wants to merge 2 commits into
mainfrom
feat/tui-crud-foundation

Conversation

@lvlcn-t

@lvlcn-t lvlcn-t commented Jul 21, 2026

Copy link
Copy Markdown
Owner

What

Foundation for adding create/edit/rename/delete to the TUI. This is PR
1 of a stacked series
(foundation → tenants → contexts → credentials).
No tab emits CRUD actions yet, so the TUI is behaviorally unchanged;
this PR only lands the plumbing that the per-tab PRs build on.

Contents

  • Domain (contexts.Manager): add DeleteTenant and
    DeleteCredential (mirroring DeleteContext via a small shared
    deleteEntry helper). config.Config gains DeleteTenant /
    DeleteCredential. Deleting a tenant/credential still referenced by a
    context is allowed and left for the caller to warn about (agreed
    behaviour; the TUI will surface a status-line warning in a later PR).
  • tui/form (new package): a reusable multi-field text-input form —
    focus navigation (tab/shift-tab/↑↓), required + custom validators,
    Submitted{Values} / Canceled{} messages. Fully unit-tested; knows
    nothing about the domain.
  • tui/state: add FormView and ConfirmView states, and a
    SetConfig setter to swap the store after a write-triggered reload.
  • tui/keys: add Create (n), Edit (e), Rename (r),
    Delete (ctrl+d). ctrl+d chosen k9s-style; verified no collision
    with bubbles/list (which uses plain d/f/pgdown).
  • tui/tabs: add CRUD TabAction kinds, tabKeys fields, and a
    Reload() method on every tab to rebuild list items from the current
    store after a write.

Deliberately deferred to PR 2

The contexts.Manager dependency injection into the TUI and the
handleAction CRUD dispatch (plus the ConfirmView model and the
reload() flow) land in PR 2 alongside the tenants tab, which is the
first to exercise them. Keeping them out here avoids shipping unused code.

Testing

  • go test -race -count=1 -short ./... — all pass (new tui/form and
    contexts delete tests included).
  • Full pre-commit (generate, mod tidy, gofumpt, golangci-lint, gitleaks,
    markdownlint) — green.
  • tui confirmed to still import neither cmd, login, nor contexts
    (DI deferred); contexts and login remain leaf packages.
  • Smoke-tested the built binary: non-TUI paths unchanged; TUI launch path
    unaffected.

Notes

  • Bubble Tea's value-receiver Update/View idiom trips gocritic's
    hugeParam; suppressed with //nolint:gocritic and a note that the
    existing models (details.Viewer, splash.Model) already use pointer
    receivers and a broader refactor is tracked separately.

Foundation for adding create/edit/rename/delete to the TUI, wired per
tab in follow-up PRs. No tab emits CRUD actions yet, so the TUI is
behaviorally unchanged.

- contexts.Manager: add DeleteTenant and DeleteCredential (deletion of an
  entry still referenced by a context is allowed and left for the caller
  to warn about); config.Config gains DeleteTenant/DeleteCredential.
- tui/form: new reusable multi-field text-input form (focus navigation,
  required/validator checks, Submitted/Canceled messages), fully unit
  tested.
- tui/state: add FormView and ConfirmView states plus a SetConfig setter
  to swap the store after a reload.
- tui/keys: add Create (n), Edit (e), Rename (r), Delete (ctrl+d) bindings.
- tui/tabs: add CRUD TabAction kinds, tabKeys fields, and a Reload() method
  on every tab to rebuild items from the current store after a write.

The contexts.Manager dependency injection and handleAction dispatch land
in PR2 alongside the first tab (tenants) that exercises them, keeping this
PR free of unused code.

Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

File Coverage
All files 50%
az/azconfig.go 66%
az/client.go 0%
az/client.go 0%
az/commands.go 0%
az/secrets.go 0%
az/secrets.go 15%
cmd/context_view.go 85%
cmd/current.go 80%
cmd/delete_context.go 71%
cmd/get.go 56%
cmd/list.go 46%
cmd/output_flags.go 85%
cmd/rename_context.go 69%
cmd/set_context.go 69%
cmd/set_credential.go 97%
cmd/set_credential.go 80%
cmd/set_tenant.go 95%
cmd/set_tenant.go 57%
cmd/use.go 47%
cmd/view.go 95%
cmd/view.go 48%
config/config.go 91%
config/credential.go 0%
config/credential.go 0%
config/credential.go 64%
config/credential.go 48%
config/keyvault.go 14%
config/loader.go 82%
config/loader.go 79%
config/store.go 69%
config/writer.go 69%
contexts/contexts.go 92%
contexts/contexts.go 82%
internal/gendemo/main.go 0%
internal/gendoc/main.go 0%
keyvault/azure.go 0%
keyvault/azure.go 0%
keyvault/keyvault.go 75%
login/login.go 76%
main.go 0%
output/printer.go 79%
semver/version.go 94%
tui/app.go 0%
tui/app.go 0%
tui/tui.go 0%
tui/details/viewer.go 0%
tui/details/viewer.go 0%
tui/form/form.go 60%
tui/keys/builder.go 80%
tui/keys/keys.go 66%
tui/splash/splash.go 0%
tui/splash/splash.go 0%
tui/state/state.go 0%
tui/state/state.go 0%
tui/styles/azure_renderer.go 0%
tui/styles/azure_renderer.go 0%
tui/styles/styles.go 50%
tui/tabs/browse.go 0%
tui/tabs/browse.go 0%
tui/tabs/context_item.go 0%
tui/tabs/context_item.go 0%
tui/tabs/contexts.go 0%
tui/tabs/contexts.go 0%
tui/tabs/credential_items.go 0%
tui/tabs/credential_items.go 0%
tui/tabs/credentials.go 0%
tui/tabs/list_builder.go 0%
tui/tabs/list_builder.go 0%
tui/tabs/tab.go 0%
tui/tabs/tab.go 0%
tui/tabs/tabs.go 0%
tui/tabs/tabs.go 0%
tui/tabs/tenant_items.go 0%
tui/tabs/tenant_items.go 0%
tui/tabs/tenants.go 0%
wif/factory/factory.go 0%
wif/oauth2/browser.go 0%
wif/oauth2/browser_linux.go 0%
wif/oauth2/cache.go 76%
wif/oauth2/cache.go 79%
wif/oauth2/oauth2.go 25%
wif/oauth2/oauth2.go 59%
wif/options.go 20%

Minimum allowed coverage is 70%

Generated by 🐒 cobertura-action against b59dbe7

Replace the storage-shaped Set* verbs with intent-shaped operations so
both UIs (cmd, tui) map user intent to one shared service instead of
re-deriving CRUD semantics from upsert-by-name. This is the fix for the
TUI edit-rename bug: renaming is a distinct operation, not an upsert with
a different name.

Per entity (tenant, credential, context):
- CreateX  -> fails with ErrXExists if present (POST).
- UpdateX  -> fails with ErrXNotFound if absent; never changes the name (PATCH).
- RenameX  -> the only identity change; cascades the new name to every
  referencing context across all affected config files (PATCH of identity).

Reference integrity lives in the service: RenameTenant/RenameCredential
rewrite context tenant/credential references (contexts only ever point at
tenants/credentials, never the reverse), grouped by owning file so each
file is written once. DeleteTenant/DeleteCredential now report
OrphanedContexts so callers can warn (allow-and-warn, as agreed).

Set*/SetContext are retained as thin create-or-update shims delegating to
Create*/Update*, so the CLI and all existing tests keep working unchanged;
the CLI migration onto the intent API is a separate later PR.

config gains RenameTenant/RenameCredential, RetargetTenant/RetargetCredential,
and ContextsReferencingTenant/Credential, each unit tested. New sentinels
(ErrTenantExists/NotFound, etc.) enable errors.Is in callers and tests.

Adds intent tests including same-file and cross-file rename cascade and
delete orphan reporting; keeps all prior Set*/Delete*/RenameContext tests.

Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant