feat: add paginated API endpoint for groups (#27603)

backend-only changes from #27271; see that PR for summary of changes +
implementation details
This commit is contained in:
Andrew Aquino
2026-08-10 13:23:14 -07:00
committed by GitHub
parent ddb2799009
commit 6e07e2610f
19 changed files with 1189 additions and 0 deletions
+95
View File
@@ -15125,6 +15125,101 @@ func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetG
return items, nil
}
const getGroupsByOrganizationIDPaginated = `-- name: GetGroupsByOrganizationIDPaginated :many
SELECT
groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source, groups.chat_spend_limit_micros,
organizations.name AS organization_name,
organizations.display_name AS organization_display_name,
COUNT(*) OVER() AS count
FROM
groups
INNER JOIN
organizations ON groups.organization_id = organizations.id
WHERE
true
AND groups.organization_id = $1
-- Keyset pagination cursor. When @after_id is set, return only groups
-- ordered after it, matching the ORDER BY (LOWER(name), id) below. This
-- lets callers page without duplicated or skipped rows even if groups are
-- inserted or deleted between page requests.
AND CASE
WHEN $2 :: uuid != '00000000-0000-0000-0000-000000000000' :: uuid THEN
(LOWER(groups.name), groups.id) > (
SELECT LOWER(name), id FROM groups WHERE id = $2
)
ELSE true
END
-- Filter by group name or display name (substring, case-insensitive).
AND CASE WHEN $3 :: text != '' THEN (
groups.name ILIKE concat('%', $3, '%')
OR groups.display_name ILIKE concat('%', $3, '%')
)
ELSE true
END
ORDER BY
-- Deterministic and consistent ordering of all groups. This is to ensure consistent pagination.
LOWER(groups.name) ASC, groups.id ASC OFFSET $4
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($5 :: int, 0)
`
type GetGroupsByOrganizationIDPaginatedParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
type GetGroupsByOrganizationIDPaginatedRow struct {
Group Group `db:"group" json:"group"`
OrganizationName string `db:"organization_name" json:"organization_name"`
OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"`
Count int64 `db:"count" json:"count"`
}
func (q *sqlQuerier) GetGroupsByOrganizationIDPaginated(ctx context.Context, arg GetGroupsByOrganizationIDPaginatedParams) ([]GetGroupsByOrganizationIDPaginatedRow, error) {
rows, err := q.db.QueryContext(ctx, getGroupsByOrganizationIDPaginated,
arg.OrganizationID,
arg.AfterID,
arg.Search,
arg.OffsetOpt,
arg.LimitOpt,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetGroupsByOrganizationIDPaginatedRow
for rows.Next() {
var i GetGroupsByOrganizationIDPaginatedRow
if err := rows.Scan(
&i.Group.ID,
&i.Group.Name,
&i.Group.OrganizationID,
&i.Group.AvatarURL,
&i.Group.QuotaAllowance,
&i.Group.DisplayName,
&i.Group.Source,
&i.Group.ChatSpendLimitMicros,
&i.OrganizationName,
&i.OrganizationDisplayName,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertAllUsersGroup = `-- name: InsertAllUsersGroup :one
INSERT INTO groups (
id,