133 lines
3.7 KiB
Go
133 lines
3.7 KiB
Go
|
|
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
|
|||
|
|
}
|