Skip to content

CBG-5331: Wire up User Channel History compaction to REST API#8277

Merged
adamcfraser merged 10 commits into
mainfrom
CBG-5331
Jun 1, 2026
Merged

CBG-5331: Wire up User Channel History compaction to REST API#8277
adamcfraser merged 10 commits into
mainfrom
CBG-5331

Conversation

@RIT3shSapata

Copy link
Copy Markdown
Contributor

CBG-5331

Describe your PR here...

  • Wire up User Channel History compaction to REST API
  • Added test coverage for the API
  • Added test coverage for revocations

Pre-review checklist

  • Removed debug logging (fmt.Print, log.Print, ...)
  • Logging sensitive data? Make sure it's tagged (e.g. base.UD(docID), base.MD(dbName))
  • Updated relevant information in the API specifications (such as endpoint descriptions, schemas, ...) in docs/api

Dependencies (if applicable)

  • Link upstream PRs
  • Update Go module dependencies when merged

Integration Tests

@RIT3shSapata RIT3shSapata self-assigned this May 15, 2026
Copilot AI review requested due to automatic review settings May 15, 2026 16:46
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown

Redocly previews

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires user channel-history retrieval/compaction into the Admin REST API and adds tests/docs for the new behavior.

Changes:

  • Adds GET/POST /_user/{name}/_channel_history routes and handlers.
  • Adds API tests for retrieving and compacting user channel history.
  • Updates/removes OpenAPI path entries related to user history compaction.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
rest/admin_api.go Adds channel history response types and REST handlers.
rest/routing.go Registers channel history admin routes.
rest/user_api_test.go Adds coverage for GET/POST user channel history behavior.
rest/revocation_test.go Extends revocation coverage to include compaction.
auth/principal.go Adds channel history compaction to the user interface.
docs/api/admin.yaml Removes the old compact path reference.
docs/api/paths/admin/db-_user-name-_history.yaml Adds POST documentation for compaction.
docs/api/paths/admin/db-_user-name-_history-compact.yaml Deletes the standalone compact path spec.
Comments suppressed due to low confidence (1)

docs/api/paths/admin/db-_user-name-_history.yaml:83

  • The documented 400 response mentions invalid channel names, but the handler only returns 400 for JSON decoding failures and otherwise silently ignores unknown or malformed channel strings. Either validate the request and return this error, or narrow the documentation to malformed request bodies so clients do not rely on validation that is not implemented.
    '400':
      description: Bad request. Invalid channel names or malformed request body.
      content:
        application/json:
          schema:
            $ref: ../../components/schemas.yaml#/HTTP-Error
          example:
            error: "Bad Request"
            reason: "Invalid channel format: channels must be non-empty strings"

Comment thread rest/revocation_test.go Outdated
assert.Equal(t, "doc", changes.Results[0].ID)
assert.True(t, changes.Results[0].Revoked)

body := fmt.Sprintf(`{"channels": {%q:{%q:["A"]}}}'`, dataStore.ScopeName(), dataStore.CollectionName())
channels:
scope1:
collection1:
- scoped_channel2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this

Comment thread rest/admin_api.go
Comment on lines +2580 to +2585
err = authenticator.Save(user)
if err != nil {
return err
}

h.writeJSON(userCompactedChannelHistory)
Comment thread rest/admin_api.go Outdated
Comment on lines +2534 to +2540
for scope, _ := range colAccess {
colAccessHistoryMap[scope] = make(map[string][]string)
for col, _ := range colAccess[scope] {
colAccessHistoryMap[scope][col] = slices.Collect(maps.Keys(colAccess[scope][col].ChannelHistory_))
}
}

Comment thread rest/routing.go Outdated
Comment on lines +189 to +192
dbr.Handle("/_user/{name}/_channel_history",
makeHandler(sc, adminPrivs, []Permission{PermReadPrincipal}, nil, (*handler).getUserChannelHistory)).Methods("GET")
dbr.Handle("/_user/{name}/_channel_history",
makeHandler(sc, adminPrivs, []Permission{PermWritePrincipal}, nil, (*handler).compactUserChannelHistory)).Methods("POST")

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Comment thread rest/revocation_test.go Outdated
Comment thread rest/user_api_test.go Outdated
Comment thread docs/api/paths/admin/db-_user-name-_channel_history.yaml Outdated
Comment thread docs/api/paths/admin/db-_user-name-_channel_history.yaml Outdated
channels:
scope1:
collection1:
- scoped_channel2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this

Comment thread rest/user_api_test.go
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated

@adamcfraser adamcfraser left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few suggestions for refactoring to move logic to a more appropriate place, and some minor code style suggestions.

Comment thread auth/principal.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/admin_api.go Outdated
Comment thread rest/user_api_test.go

// TestCompactUserChannelHistory tests the POST /_user/{name}/_access_history/compact admin endpoint,
// which removes specified channels from a user's revocation history and returns those that were found and removed.
func TestCompactUserChannelHistory(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the custom handling for the default collection, this should include tests for a mix of default-only, named collections only, and the combination of both.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default collection and named collections are handled in the CI runs, when they are run using SG_TEST_USE_DEFAULT_COLLECTION=true for default collections. Did you want me to write subtests that use NewRestTesterDefaultCollection?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to test the combination of _default._default and _default.collection1 in the same database, which I don't think is covered by USE_DEFAULT_COLLECTION=true, which just uses _default._default?

In this scenario the channel history will end up both at the root level (for _default._default) and inside collection_access (for _default.collection1) and we want to make sure compaction is properly handling this case.

Comment thread rest/user_api_test.go

// TestGetUserChannelHistory tests the GET /_user/{name}/_access_history admin endpoint,
// which returns the revoked channel history for a user across all scopes and collections.
func TestGetUserChannelHistory(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the custom handling for the default collection, this should include tests for a mix of default-only, named collections only, and the combination of both.

Comment thread rest/revocation_test.go
- added docstrings
- updated api spec
- updated the rest api for get and post reqests
- fix the case for default collection
- update the open api spec
- moved few methods under principal
- abstract the channel history implementation
- rename few structs
Comment thread rest/user_api_test.go

// TestCompactUserChannelHistory tests the POST /_user/{name}/_access_history/compact admin endpoint,
// which removes specified channels from a user's revocation history and returns those that were found and removed.
func TestCompactUserChannelHistory(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to test the combination of _default._default and _default.collection1 in the same database, which I don't think is covered by USE_DEFAULT_COLLECTION=true, which just uses _default._default?

In this scenario the channel history will end up both at the root level (for _default._default) and inside collection_access (for _default.collection1) and we want to make sure compaction is properly handling this case.

@RIT3shSapata

Copy link
Copy Markdown
Contributor Author

As discussed offline, the test for the case that you pointed out is covered in the tests in rest/upgradetest/user_access_history_test.go

@RIT3shSapata
RIT3shSapata requested a review from adamcfraser June 1, 2026 08:53

@adamcfraser adamcfraser left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thanks.

@adamcfraser
adamcfraser merged commit 2cc594d into main Jun 1, 2026
51 checks passed
@adamcfraser
adamcfraser deleted the CBG-5331 branch June 1, 2026 14:54
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.

3 participants