Skip to content

feat: create consumer group bound to a topic#11

Merged
github-actions[bot] merged 10 commits into
mainfrom
feat/create-consumer-group
Jul 9, 2026
Merged

feat: create consumer group bound to a topic#11
github-actions[bot] merged 10 commits into
mainfrom
feat/create-consumer-group

Conversation

@FinkeFlo

@FinkeFlo FinkeFlo commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the ability to create a new consumer group bound to a single topic directly from kafkito. A group is created by committing initial offsets for all partitions of the chosen topic under a start strategy (default latest), so the group exists and is anchored at a known position before its consumer app is deployed.

In Kafka a group has no standalone "create" primitive — it exists once it has committed offsets — so this feature commits initial offsets for a not-yet-existing group name. Note that a group with no active consumer expires after offsets.retention.minutes; the UI surfaces this.

Changes

Backend (pkg/kafka, internal/server)

  • Extracted the offset resolve/clamp/commit core of ResetOffsets into a shared resolveAndCommit (behavior-preserving refactor).
  • New CreateGroup + ErrGroupExists: validates input, rejects shift-by (no prior offset to shift from on a new group), prechecks the group does not already exist, then delegates to resolveAndCommit.
  • New endpoint POST /clusters/{cluster}/groups → 200 / 409 if the group exists / 400 on validation / 404 unknown cluster.
  • RBAC: POST /groups is authorized against the group_id from the request body (name-scoped, parity with delete/reset-offsets), via an explicit bodyField on the permission mapping.
  • OpenAPI updated.

Frontend

  • createGroup API client.
  • CreateGroupModal with a dry-run preview of the resulting per-partition offsets, strategy selector (earliest/latest/timestamp/offset, default latest), and an expiry notice.
  • "Create consumer group" action on the topic Consumers tab.
  • Extracted shared msToLocalInput/localInputToMs datetime helpers into frontend/src/lib/datetime.ts (fixes a UTC-vs-local timestamp bug and is now shared with the reset-offsets modal).

Testing

  • make test (Go unit) — green across all packages.
  • Frontend vitest — 259 passing; build, typecheck, and check:strings/tokens/routes clean.
  • New integration test TestIntegration_CreateGroup (create at latest / listing / re-create → ErrGroupExists / shift-by rejected / dry-run does not create) is compile-verified only — it needs Docker/testcontainers, so please let CI run the -tags=integration suite.
  • Also fixes a pre-existing errlastErr bug in TestIntegration_ResetOffsets that broke compilation under -tags=integration.

FinkeFlo added 10 commits July 9, 2026 13:11
Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Adds ErrGroupExists, CreateGroupRequest, and Registry.CreateGroup in
admin_groups.go. CreateGroup prechecks via adm.ListGroups that the
target group does not already exist, rejects the shift-by strategy
(a new group has no prior commit to shift from), and otherwise
delegates offset resolution/clamping/commit to the shared
resolveAndCommit helper extracted from ResetOffsets.

Also fixes a pre-existing bug in TestIntegration_ResetOffsets
(integration_extended_test.go:161) that referenced the not-yet-declared
`err` instead of the retry loop's `lastErr`, which kept the file from
compiling under -tags=integration; fixing it was a prerequisite to
compile-verify the new TestIntegration_CreateGroup added here.

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Wire kafka.Registry.CreateGroup (Task 2) into the HTTP layer: register
POST /clusters/{cluster}/groups, add the createGroup handler mapping
ErrUnknownCluster->404, ErrGroupExists->409, validation errors->400,
else->502, and document the operation in openapi.yaml. Also map the
new route in resolvePermission so RBAC-enabled deployments enforce a
group:edit check on create instead of silently bypassing it (same gap
would otherwise exist as the sibling groups routes).

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Signed-off-by: FinkeFlo <florian.kube@gmail.com>
The datetime-local input in create-group-modal previously rendered
with toISOString() (UTC) but parsed with new Date() (local), causing
the submitted timestamp_ms to drift by the local UTC offset in any
non-UTC timezone. Extract the local-time round-trip helpers
(msToLocalInput/localInputToMs) already used by reset-offsets-modal
into a shared frontend/src/lib/datetime.ts and reuse them here.

Also fix a latent display bug where an empty timestampMs string was
treated as epoch 0 (Number("") === 0 is finite) instead of blank,
which surfaced while adding a timestamp round-trip test.

Strengthen the "confirming create" test to assert the
topic-consumers query invalidation and that onClose fires after
onCreated, and add a timestamp-strategy round-trip test guarding the
timezone fix.

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Signed-off-by: FinkeFlo <florian.kube@gmail.com>
…estamp error

- rbacMiddleware now derives the resource name from a resolvePermission-
  supplied body field (topics: "name", groups: "group_id"), so creating a
  consumer group authorizes against the specific group_id. A user with only
  group:billing-*:edit can no longer create group payments-prod; cluster-wide
  group:*:edit still authorizes any group_id.
- CreateGroup pre-validates timestamp_ms so the error reads "create group: ..."
  instead of the shared "reset offsets: ..." prefix.

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
…fetch

- create-group-modal offsetValid now uses Number.isInteger so "1.5" disables
  Preview/Create instead of sending a float that fails the Go int64 decode.
- consumers route drops onCreated={query.refetch()}; the modal's
  invalidateQueries already refetches the active topic-consumers query, so the
  list no longer double-fetches on create.

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
Creating a consumer group is non-destructive (the backend returns 409 if the group already exists) and the modal already offers a dry-run Preview, so the nested ConfirmDialog only produced a confusing stacked-modal look (two centered same-size panels overlapping). Create now commits directly; Preview remains the review step. Button feedback is scoped per action via mutation.variables so only the clicked action shows its pending state.

Signed-off-by: FinkeFlo <florian.kube@gmail.com>
@FinkeFlo
FinkeFlo force-pushed the feat/create-consumer-group branch from e52d4b6 to 68d1f5d Compare July 9, 2026 16:00
@github-actions
github-actions Bot enabled auto-merge (rebase) July 9, 2026 16:01
@github-actions
github-actions Bot merged commit 792b120 into main Jul 9, 2026
10 checks passed
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