Files
query-orchestration/internal/bot/scope_util.go
T
Jay Brown 72de690894 Merged in feature/chatbot1 (pull request #223)
Chatbot functionality

* baseline working

* missing test file

* more tests
2026-05-07 20:56:18 +00:00

133 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}