Feat v0.6.15 user display audit
Some checks failed
CI/CD / detect-changes (pull_request) Successful in 3s
CI/CD / test-frontend (pull_request) Successful in 6s
CI/CD / build-and-deploy (pull_request) Has been cancelled
CI/CD / test-go-pg (pull_request) Has been cancelled
CI/CD / test-sqlite (pull_request) Has been cancelled
Some checks failed
CI/CD / detect-changes (pull_request) Successful in 3s
CI/CD / test-frontend (pull_request) Successful in 6s
CI/CD / build-and-deploy (pull_request) Has been cancelled
CI/CD / test-go-pg (pull_request) Has been cancelled
CI/CD / test-sqlite (pull_request) Has been cancelled
Add batch user resolve endpoint, sw.users SDK module, and migrate all surfaces to show display_name instead of UUIDs. Canonical fallback chain: display_name → username → "Unknown". - GET /api/v1/users/resolve?ids=... (max 100, map response) - sw.users.resolve(), resolveMany(), displayName() with 60s cache - Chat participants resolved from users table, not snapshot - Admin users/teams/groups/team-admin show display_name - Chat-core participants.display_name column deprecated - 5 new handler tests, OpenAPI spec updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,10 @@ type UserStore interface {
|
||||
// Excludes the calling user. Max 20 results.
|
||||
SearchActive(ctx context.Context, excludeUserID, query string) ([]UserSearchResult, error)
|
||||
|
||||
// ResolveByIDs returns lightweight identity records for the given user IDs.
|
||||
// Missing IDs are silently omitted. Max 100 IDs.
|
||||
ResolveByIDs(ctx context.Context, ids []string) ([]UserSearchResult, error)
|
||||
|
||||
// ── CS2 additions ──
|
||||
|
||||
// CountAll returns the total number of users.
|
||||
@@ -132,6 +136,7 @@ type UserSearchResult struct {
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Handle string `json:"handle"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
|
||||
@@ -250,6 +250,47 @@ func (s *UserStore) SearchActive(ctx context.Context, excludeUserID, query strin
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
// ResolveByIDs returns lightweight identity records for the given user IDs.
|
||||
func (s *UserStore) ResolveByIDs(ctx context.Context, ids []string) ([]store.UserSearchResult, error) {
|
||||
if len(ids) == 0 {
|
||||
return []store.UserSearchResult{}, nil
|
||||
}
|
||||
if len(ids) > 100 {
|
||||
ids = ids[:100]
|
||||
}
|
||||
|
||||
// Build $1,$2,... placeholders
|
||||
placeholders := make([]string, len(ids))
|
||||
args := make([]interface{}, len(ids))
|
||||
for i, id := range ids {
|
||||
placeholders[i] = fmt.Sprintf("$%d", i+1)
|
||||
args[i] = id
|
||||
}
|
||||
|
||||
q := `SELECT id, username, COALESCE(display_name, '') AS display_name,
|
||||
COALESCE(handle, '') AS handle, COALESCE(avatar_url, '') AS avatar_url
|
||||
FROM users WHERE id IN (` + strings.Join(placeholders, ",") + `)`
|
||||
|
||||
rows, err := DB.QueryContext(ctx, q, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []store.UserSearchResult
|
||||
for rows.Next() {
|
||||
var u store.UserSearchResult
|
||||
if err := rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Handle, &u.AvatarURL); err != nil {
|
||||
continue
|
||||
}
|
||||
results = append(results, u)
|
||||
}
|
||||
if results == nil {
|
||||
results = []store.UserSearchResult{}
|
||||
}
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
// ── CS2 additions ─────────────────────────────────────────────
|
||||
|
||||
func (s *UserStore) MergeSettings(ctx context.Context, userID string, patch []byte) error {
|
||||
|
||||
@@ -262,6 +262,47 @@ func (s *UserStore) SearchActive(ctx context.Context, excludeUserID, query strin
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
// ResolveByIDs returns lightweight identity records for the given user IDs.
|
||||
func (s *UserStore) ResolveByIDs(ctx context.Context, ids []string) ([]store.UserSearchResult, error) {
|
||||
if len(ids) == 0 {
|
||||
return []store.UserSearchResult{}, nil
|
||||
}
|
||||
if len(ids) > 100 {
|
||||
ids = ids[:100]
|
||||
}
|
||||
|
||||
// Build ?,?,... placeholders
|
||||
placeholders := make([]string, len(ids))
|
||||
args := make([]interface{}, len(ids))
|
||||
for i, id := range ids {
|
||||
placeholders[i] = "?"
|
||||
args[i] = id
|
||||
}
|
||||
|
||||
q := `SELECT id, username, COALESCE(display_name, '') AS display_name,
|
||||
COALESCE(handle, '') AS handle, COALESCE(avatar_url, '') AS avatar_url
|
||||
FROM users WHERE id IN (` + strings.Join(placeholders, ",") + `)`
|
||||
|
||||
rows, err := DB.QueryContext(ctx, q, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []store.UserSearchResult
|
||||
for rows.Next() {
|
||||
var u store.UserSearchResult
|
||||
if err := rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Handle, &u.AvatarURL); err != nil {
|
||||
continue
|
||||
}
|
||||
results = append(results, u)
|
||||
}
|
||||
if results == nil {
|
||||
results = []store.UserSearchResult{}
|
||||
}
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
// ── CS2 additions ─────────────────────────────────────────────
|
||||
|
||||
func (s *UserStore) MergeSettings(ctx context.Context, userID string, patch []byte) error {
|
||||
|
||||
Reference in New Issue
Block a user