Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions api/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,22 @@ func GetChatPublicKeys(client Client, userID string, opts RequestOptions) ([]Cha
}

// GetChatUsersPublicKeys fetches registered public keys for the given users;
// each returned row carries its owner's user_id. The endpoint accepts at
// most 100 ids per request, so larger inputs are fetched in batches.
// each returned row carries its owner's user_id. Public keys are fetched via
// the per-user route because the batch route is not enabled for every X app.
func GetChatUsersPublicKeys(client Client, userIDs []string, opts RequestOptions) ([]ChatPublicKey, error) {
if len(userIDs) == 0 {
return nil, nil
}
var keys []ChatPublicKey
for start := 0; start < len(userIDs); start += 100 {
batch := userIDs[start:min(start+100, len(userIDs))]
opts.Method = "GET"
opts.Endpoint = "/2/users/public_keys?ids=" + url.QueryEscape(strings.Join(batch, ","))
opts.Data = ""

resp, err := client.SendRequest(opts)
for _, userID := range userIDs {
userKeys, err := GetChatPublicKeys(client, userID, opts)
if err != nil {
return nil, err
}
var out struct {
Data []ChatPublicKey `json:"data"`
return nil, fmt.Errorf("failed to fetch public keys for user %s: %w", userID, err)
}
if err := json.Unmarshal(resp, &out); err != nil {
return nil, fmt.Errorf("failed to parse public keys response: %w", err)
for i := range userKeys {
userKeys[i].UserID = userID
}
keys = append(keys, out.Data...)
keys = append(keys, userKeys...)
}
return keys, nil
}
Expand Down
46 changes: 46 additions & 0 deletions api/chat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,52 @@ func TestGetChatPublicKeys(t *testing.T) {
assert.Empty(t, requests[0].URL.RawQuery)
}

func TestGetChatUsersPublicKeysUsesPerUserRoutes(t *testing.T) {
var paths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
paths = append(paths, r.URL.Path)
w.Header().Set("Content-Type", "application/json")
switch r.URL.Path {
case "/2/users/7/public_keys":
_, _ = w.Write([]byte(`{"data":[{"public_key_version":"1700","public_key":"idpk7","signing_public_key":"sigpk7","identity_public_key_signature":"binding7"}]}`))
case "/2/users/8/public_keys":
_, _ = w.Write([]byte(`{"data":[{"public_key_version":"1800","public_key":"idpk8","signing_public_key":"sigpk8","identity_public_key_signature":"binding8"}]}`))
default:
http.Error(w, `{"error":"unexpected route"}`, http.StatusNotFound)
}
}))
defer server.Close()
client := chatTestClient(t, server)

keys, err := GetChatUsersPublicKeys(client, []string{"7", "8"}, RequestOptions{})
require.NoError(t, err)
require.Len(t, keys, 2)
assert.Equal(t, []string{"/2/users/7/public_keys", "/2/users/8/public_keys"}, paths)
assert.Equal(t, "7", keys[0].UserID)
assert.Equal(t, "1700", keys[0].Version)
assert.Equal(t, "8", keys[1].UserID)
assert.Equal(t, "1800", keys[1].Version)
}

func TestGetChatUsersPublicKeysIdentifiesFailedUserAndReturnsNoPartialKeys(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.URL.Path == "/2/users/7/public_keys" {
_, _ = w.Write([]byte(`{"data":[{"public_key_version":"1700","public_key":"idpk7","signing_public_key":"sigpk7","identity_public_key_signature":"binding7"}]}`))
return
}
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte(`{"error":"denied"}`))
}))
defer server.Close()
client := chatTestClient(t, server)

keys, err := GetChatUsersPublicKeys(client, []string{"7", "8"}, RequestOptions{})
require.Error(t, err)
assert.Nil(t, keys)
assert.Contains(t, err.Error(), "user 8")
}

func TestGetChatEvents(t *testing.T) {
var requests []*http.Request
var bodies []string
Expand Down