Skip to content

Commit 077765b

Browse files
committed
add documentation
- added docstrings - updated api spec
1 parent 0629bc7 commit 077765b

4 files changed

Lines changed: 63 additions & 61 deletions

File tree

docs/api/admin.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ paths:
5353
$ref: './paths/admin/db-_user-name-_session-sessionid.yaml'
5454
'/{db}/_user/{name}/_history':
5555
$ref: './paths/admin/db-_user-name-_history.yaml'
56-
'/{db}/_user/{name}/_history/compact':
57-
$ref: './paths/admin/db-_user-name-_history-compact.yaml'
5856
'/{db}/_role/':
5957
$ref: './paths/admin/db-_role-.yaml'
6058
'/{db}/_role/{name}':

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

Lines changed: 0 additions & 58 deletions
This file was deleted.

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,51 @@ get:
4040
tags:
4141
- Database Security
4242
operationId: get_db-_user-name-_history
43+
post:
44+
summary: Compact user's channel history
45+
description: |-
46+
Remove specified channels from a user's channel history.
47+
48+
Required Sync Gateway RBAC roles:
49+
50+
* Sync Gateway Architect
51+
* Sync Gateway Application
52+
requestBody:
53+
description: Channels to be compacted from the user
54+
content:
55+
application/json:
56+
schema:
57+
$ref: ../../components/schemas.yaml#/UserHistory
58+
example:
59+
channels:
60+
scope1:
61+
collection1:
62+
- scoped_channel1
63+
responses:
64+
'200':
65+
description: Channels compacted successfully from user
66+
content:
67+
application/json:
68+
schema:
69+
$ref: ../../components/schemas.yaml#/UserHistory
70+
example:
71+
channels:
72+
scope1:
73+
collection1:
74+
- scoped_channel2
75+
'400':
76+
description: Bad request. Invalid channel names or malformed request body.
77+
content:
78+
application/json:
79+
schema:
80+
$ref: ../../components/schemas.yaml#/HTTP-Error
81+
example:
82+
error: "Bad Request"
83+
reason: "Invalid channel format: channels must be non-empty strings"
84+
'403':
85+
$ref: ../../components/responses.yaml#/Unauthorized-database
86+
'404':
87+
$ref: ../../components/responses.yaml#/Not-found
88+
tags:
89+
- Database Security
90+
operationId: post_db-_user-name-_history-compact

rest/user_api_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1622,16 +1622,19 @@ func TestDeletedRoleChanHistory(t *testing.T) {
16221622

16231623
}
16241624

1625-
// TestDisabledUser ensures that a disabled (non-guest) user cannot authenticate to make requests.
1625+
// TestGetUserChannelHistory tests the GET /_user/{name}/_channel_history admin endpoint,
1626+
// which returns the revoked channel history for a user across all scopes and collections.
16261627
func TestGetUserChannelHistory(t *testing.T) {
16271628
rt := NewRestTester(t, nil)
16281629
defer rt.Close()
16291630

1631+
// Returns 404 when the requested user does not exist.
16301632
t.Run("UserNotFound", func(t *testing.T) {
16311633
response := rt.SendAdminRequest(http.MethodGet, "/db/_user/ghost/_channel_history", "")
16321634
RequireStatus(t, response, http.StatusNotFound)
16331635
})
16341636

1637+
// Returns empty history when the user has active channels but none have ever been revoked.
16351638
t.Run("UserWithNoChannelHistory", func(t *testing.T) {
16361639
ds := rt.GetSingleDataStore()
16371640
response := rt.SendAdminRequest(http.MethodPut, "/db/_user/user1",
@@ -1650,6 +1653,7 @@ func TestGetUserChannelHistory(t *testing.T) {
16501653
assert.Empty(t, channelHistory[scope][collection])
16511654
})
16521655

1656+
// Returns all channels that have been revoked from the user.
16531657
t.Run("UserWithChannelHistory", func(t *testing.T) {
16541658
ds := rt.GetSingleDataStore()
16551659
response := rt.SendAdminRequest(http.MethodPut, "/db/_user/user2",
@@ -1673,6 +1677,7 @@ func TestGetUserChannelHistory(t *testing.T) {
16731677
assert.ElementsMatch(t, []string{"chan1", "chan2"}, channelHistory[scope][collection])
16741678
})
16751679

1680+
// Returns only revoked channels; active channels do not appear in the history.
16761681
t.Run("UserWithPartialChannelHistory", func(t *testing.T) {
16771682
ds := rt.GetSingleDataStore()
16781683
response := rt.SendAdminRequest(http.MethodPut, "/db/_user/user3",
@@ -1696,6 +1701,7 @@ func TestGetUserChannelHistory(t *testing.T) {
16961701
assert.ElementsMatch(t, []string{"chan1", "chan2"}, channelHistory[scope][collection])
16971702
})
16981703

1704+
// Revocation history is preserved even after a previously revoked channel is re-granted.
16991705
t.Run("ChannelHistoryAfterReGrant", func(t *testing.T) {
17001706
ds := rt.GetSingleDataStore()
17011707
response := rt.SendAdminRequest(http.MethodPut, "/db/_user/user4",
@@ -1725,10 +1731,13 @@ func TestGetUserChannelHistory(t *testing.T) {
17251731
})
17261732
}
17271733

1734+
// TestCompactUserChannelHistory tests the POST /_user/{name}/_channel_history admin endpoint,
1735+
// which removes specified channels from a user's revocation history and returns those that were found and removed.
17281736
func TestCompactUserChannelHistory(t *testing.T) {
17291737
rt := NewRestTester(t, nil)
17301738
defer rt.Close()
17311739

1740+
// Returns 404 when the requested user does not exist.
17321741
t.Run("UserNotFound", func(t *testing.T) {
17331742
ds := rt.GetSingleDataStore()
17341743
scope := ds.ScopeName()
@@ -1738,6 +1747,7 @@ func TestCompactUserChannelHistory(t *testing.T) {
17381747
RequireStatus(t, response, http.StatusNotFound)
17391748
})
17401749

1750+
// Returns 400 when the request body is not valid JSON.
17411751
t.Run("InvalidBody", func(t *testing.T) {
17421752
ds := rt.GetSingleDataStore()
17431753
response := rt.SendAdminRequest(http.MethodPut, "/db/_user/user1",
@@ -1748,6 +1758,7 @@ func TestCompactUserChannelHistory(t *testing.T) {
17481758
RequireStatus(t, response, http.StatusBadRequest)
17491759
})
17501760

1761+
// Removes specified channels from history and returns them; subsequent GET shows empty history.
17511762
t.Run("CompactsExistingChannels", func(t *testing.T) {
17521763
ds := rt.GetSingleDataStore()
17531764
scope := ds.ScopeName()
@@ -1786,6 +1797,7 @@ func TestCompactUserChannelHistory(t *testing.T) {
17861797
assert.Empty(t, afterResult.Channels[scope][collection])
17871798
})
17881799

1800+
// Returns empty for a collection when none of the requested channels exist in history; existing history is untouched.
17891801
t.Run("NonExistentChannelReturnsEmpty", func(t *testing.T) {
17901802
ds := rt.GetSingleDataStore()
17911803
scope := ds.ScopeName()
@@ -1824,6 +1836,7 @@ func TestCompactUserChannelHistory(t *testing.T) {
18241836
assert.ElementsMatch(t, []string{"chan1", "chan2"}, afterResult.Channels[scope][collection])
18251837
})
18261838

1839+
// Only channels present in history are removed and returned; unknown channels are silently ignored.
18271840
t.Run("MixOfExistingAndNonExistingChannels", func(t *testing.T) {
18281841
ds := rt.GetSingleDataStore()
18291842
scope := ds.ScopeName()
@@ -1863,6 +1876,7 @@ func TestCompactUserChannelHistory(t *testing.T) {
18631876
})
18641877
}
18651878

1879+
// TestDisabledUser ensures that a disabled (non-guest) user cannot authenticate to make requests.
18661880
func TestDisabledUser(t *testing.T) {
18671881
rt := NewRestTester(t, nil)
18681882
defer rt.Close()

0 commit comments

Comments
 (0)