83 lines
3.9 KiB
Go
83 lines
3.9 KiB
Go
// saas-mock serves approved, static SaaS test snapshots through the formal
|
|
// Dispatcher configuration API. One-shot publishing is an explicit separate mode.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
dataDir := flag.String("data", "", "private directory with sip.json, providers.json, quota.json and tasks/*.json")
|
|
dispatcherID := flag.String("dispatcher-id", "", "approved test Dispatcher UUID")
|
|
listen := flag.String("listen", "", "explicit HTTPS listen address")
|
|
cert := flag.String("tls-cert", "", "HTTPS certificate file")
|
|
key := flag.String("tls-key", "", "HTTPS private key file")
|
|
validateOnly := flag.Bool("validate-only", false, "validate private snapshots offline without MQ or HTTPS")
|
|
publishID := flag.String("publish-event-id", "", "one-shot MQ event ID matching capture --call-id")
|
|
publishTask := flag.String("publish-task-id", "", "one approved single-trunk task")
|
|
publishCallee := flag.String("publish-callee", "", "one original allowlisted callee")
|
|
awaitResultFile := flag.String("await-result-file", "", "absolute private path for durable final result; blocks until the exact call ends")
|
|
flag.Parse()
|
|
secret := os.Getenv("SAAS_MOCK_DISPATCHER_SECRET")
|
|
brokerURL := os.Getenv("SAAS_MOCK_RABBITMQ_URL")
|
|
if *validateOnly {
|
|
if *dataDir == "" || *dispatcherID == "" || *publishID != "" || *publishTask != "" || *publishCallee != "" || *awaitResultFile != "" || flag.NArg() != 0 {
|
|
log.Fatal("offline validation requires only a private data directory and bound Dispatcher ID")
|
|
}
|
|
data, err := loadDataset(*dataDir, *dispatcherID)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Printf("private SaaS snapshots validated: task_count=%d", len(data.tasks))
|
|
return
|
|
}
|
|
if *publishID != "" || *publishTask != "" || *publishCallee != "" || *awaitResultFile != "" {
|
|
if *dataDir == "" || *dispatcherID == "" || brokerURL == "" || *publishID == "" || *publishTask == "" || *publishCallee == "" || flag.NArg() != 0 {
|
|
log.Fatal("one-shot publish requires private data, dispatcher ID, MQ environment and explicit event/task/callee")
|
|
}
|
|
data, err := loadDataset(*dataDir, *dispatcherID)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if *awaitResultFile != "" {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
|
defer cancel()
|
|
result, err := publishAndAwait(ctx, brokerURL, data, *publishID, *publishTask, *publishCallee, *awaitResultFile, time.Now())
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Printf("one-shot SaaS result saved and acknowledged: event_id=%q outcome=%q recording_status=%q final_user_turns=%d result_sha256=%s", *publishID, result.Outcome, result.RecordingStatus, result.Transcripts, result.BodySHA256)
|
|
if result.Outcome != "answered" || result.RecordingStatus != "uploaded" {
|
|
log.Fatal("real call did not produce the required answered, uploaded-recording result; stop further attempts")
|
|
}
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if err := publishExecute(ctx, brokerURL, data, *publishID, *publishTask, *publishCallee); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Printf("one-shot command broker-confirmed: event_id=%q task_id=%q (not SaaS result receipt)", *publishID, *publishTask)
|
|
return
|
|
}
|
|
if *dataDir == "" || *dispatcherID == "" || *listen == "" || *cert == "" || *key == "" || secret == "" || brokerURL == "" || flag.NArg() != 0 {
|
|
log.Fatal(errors.New("data, dispatcher-id, listen, tls-cert, tls-key, SAAS_MOCK_DISPATCHER_SECRET and SAAS_MOCK_RABBITMQ_URL are required"))
|
|
}
|
|
data, err := loadDataset(*dataDir, *dispatcherID)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := provisionMQ(brokerURL, data); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Printf("SaaS test configuration and MQ topology ready: tasks=%d dispatcher=%s", len(data.tasks), *dispatcherID)
|
|
server := http.Server{Addr: *listen, Handler: data.handler(secret), ReadHeaderTimeout: 5 * time.Second}
|
|
log.Fatal(server.ListenAndServeTLS(*cert, *key))
|
|
}
|