72de690894
Chatbot functionality * baseline working * missing test file * more tests
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
|
||
}
|