Files
query-orchestration/internal/bot/scope_util.go
T

133 lines
3.7 KiB
Go
Raw Normal View History

package bot
import (
"time"
"queryorchestration/internal/database/repository"
"github.com/google/uuid"
)
// sessionListRow is a small in-package shape that lets ListSessionsForOwner
// and ListSessionsForSuperAdmin share decorateSessionList without a generic
// or interface assertion at every site. The two underlying sqlc rows have
// the same columns but distinct Go types (per sqlc's row-per-query rule),
// so we copy the field values once at the boundary.
type sessionListRow struct {
id uuid.UUID
clientID string
createdBy string
title string
state []byte
createdAt time.Time
updatedAt time.Time
lastTerminal int32
isDeleted bool
}
// rowsOwner adapts ListBotSessionsForOwnerRow rows to the shared shape.
func rowsOwner(rows []*repository.ListBotSessionsForOwnerRow) []sessionListRow {
out := make([]sessionListRow, 0, len(rows))
for _, r := range rows {
out = append(out, sessionListRow{
id: r.ID,
clientID: r.ClientID,
createdBy: r.CreatedBy,
title: r.Title,
state: r.State,
createdAt: r.CreatedAt.Time,
updatedAt: r.UpdatedAt.Time,
lastTerminal: r.LastTerminalOrdinal,
isDeleted: r.IsDeleted,
})
}
return out
}
// rowsSuperAdmin adapts ListBotSessionsForSuperAdminRow rows to the shared
// shape. Identical to rowsOwner but on a different generated type.
func rowsSuperAdmin(rows []*repository.ListBotSessionsForSuperAdminRow) []sessionListRow {
out := make([]sessionListRow, 0, len(rows))
for _, r := range rows {
out = append(out, sessionListRow{
id: r.ID,
clientID: r.ClientID,
createdBy: r.CreatedBy,
title: r.Title,
state: r.State,
createdAt: r.CreatedAt.Time,
updatedAt: r.UpdatedAt.Time,
lastTerminal: r.LastTerminalOrdinal,
isDeleted: r.IsDeleted,
})
}
return out
}
// dedupUUIDs returns the input slice with duplicates removed, preserving
// first-seen order. Returns a non-nil empty slice for nil input so the
// caller can pass the result directly to set-comparison helpers.
func dedupUUIDs(in []uuid.UUID) []uuid.UUID {
if len(in) == 0 {
return []uuid.UUID{}
}
seen := make(map[uuid.UUID]struct{}, len(in))
out := make([]uuid.UUID, 0, len(in))
for _, id := range in {
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
return out
}
// hasIntersection returns true when sets a and b share at least one
// element. Both inputs are expected to be deduplicated already.
func hasIntersection(a, b []uuid.UUID) bool {
if len(a) == 0 || len(b) == 0 {
return false
}
set := make(map[uuid.UUID]struct{}, len(a))
for _, id := range a {
set[id] = struct{}{}
}
for _, id := range b {
if _, ok := set[id]; ok {
return true
}
}
return false
}
// computeUnion returns (existing - remove) add as a deduplicated
// slice in deterministic order: first the surviving existing ids in
// their original order, then any new add ids that are not already in
// the surviving set. The bot_session_documents / bot_session_folders
// PRIMARY KEY guarantees `existing` is already deduplicated when read
// from SQL, so the loop can rely on that invariant.
func computeUnion(existing, add, remove []uuid.UUID) []uuid.UUID {
removeSet := make(map[uuid.UUID]struct{}, len(remove))
for _, id := range remove {
removeSet[id] = struct{}{}
}
out := make([]uuid.UUID, 0, len(existing)+len(add))
seen := make(map[uuid.UUID]struct{}, len(existing)+len(add))
for _, id := range existing {
if _, dropped := removeSet[id]; dropped {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
for _, id := range add {
if _, dup := seen[id]; dup {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
return out
}