2025-11-19 11:29:30 +01:00
|
|
|
package graph
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2025-11-20 17:02:19 +01:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
|
|
"gopkg.in/yaml.v3"
|
2025-11-19 11:29:30 +01:00
|
|
|
|
|
|
|
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// GenerateCosmoRouterConfig generates a Cosmo Router execution config from subgraphs
|
2025-11-20 17:02:19 +01:00
|
|
|
// using the official wgc CLI tool via npx
|
2025-11-19 11:29:30 +01:00
|
|
|
func GenerateCosmoRouterConfig(subGraphs []*model.SubGraph) (string, error) {
|
2025-11-20 17:02:19 +01:00
|
|
|
if len(subGraphs) == 0 {
|
|
|
|
|
return "", fmt.Errorf("no subgraphs provided")
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 17:02:19 +01:00
|
|
|
// Create a temporary directory for composition
|
|
|
|
|
tmpDir, err := os.MkdirTemp("", "cosmo-compose-*")
|
2025-11-19 11:29:30 +01:00
|
|
|
if err != nil {
|
2025-11-20 17:02:19 +01:00
|
|
|
return "", fmt.Errorf("create temp dir: %w", err)
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|
2025-11-20 17:02:19 +01:00
|
|
|
defer os.RemoveAll(tmpDir)
|
2025-11-19 11:29:30 +01:00
|
|
|
|
2025-11-20 17:02:19 +01:00
|
|
|
// Write each subgraph SDL to a file
|
|
|
|
|
type SubgraphConfig struct {
|
|
|
|
|
Name string `yaml:"name"`
|
|
|
|
|
RoutingURL string `yaml:"routing_url,omitempty"`
|
|
|
|
|
Schema map[string]string `yaml:"schema"`
|
|
|
|
|
Subscription map[string]interface{} `yaml:"subscription,omitempty"`
|
|
|
|
|
}
|
2025-11-19 11:29:30 +01:00
|
|
|
|
2025-11-20 17:02:19 +01:00
|
|
|
type InputConfig struct {
|
|
|
|
|
Version int `yaml:"version"`
|
|
|
|
|
Subgraphs []SubgraphConfig `yaml:"subgraphs"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inputConfig := InputConfig{
|
|
|
|
|
Version: 1,
|
|
|
|
|
Subgraphs: make([]SubgraphConfig, 0, len(subGraphs)),
|
|
|
|
|
}
|
2025-11-19 11:29:30 +01:00
|
|
|
|
|
|
|
|
for _, sg := range subGraphs {
|
2025-11-20 17:02:19 +01:00
|
|
|
// Write SDL to a temp file
|
|
|
|
|
schemaFile := filepath.Join(tmpDir, fmt.Sprintf("%s.graphql", sg.Service))
|
|
|
|
|
if err := os.WriteFile(schemaFile, []byte(sg.Sdl), 0o644); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write schema file for %s: %w", sg.Service, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subgraphCfg := SubgraphConfig{
|
|
|
|
|
Name: sg.Service,
|
|
|
|
|
Schema: map[string]string{
|
|
|
|
|
"file": schemaFile,
|
|
|
|
|
},
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sg.URL != nil {
|
2025-11-20 17:02:19 +01:00
|
|
|
subgraphCfg.RoutingURL = *sg.URL
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sg.WsURL != nil {
|
2025-11-20 17:02:19 +01:00
|
|
|
subgraphCfg.Subscription = map[string]interface{}{
|
2025-11-19 11:29:30 +01:00
|
|
|
"url": *sg.WsURL,
|
|
|
|
|
"protocol": "ws",
|
|
|
|
|
"websocket_subprotocol": "graphql-ws",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 17:02:19 +01:00
|
|
|
inputConfig.Subgraphs = append(inputConfig.Subgraphs, subgraphCfg)
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 17:02:19 +01:00
|
|
|
// Write input config YAML
|
|
|
|
|
inputFile := filepath.Join(tmpDir, "input.yaml")
|
|
|
|
|
inputYAML, err := yaml.Marshal(inputConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("marshal input config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := os.WriteFile(inputFile, inputYAML, 0o644); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write input config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute wgc router compose
|
|
|
|
|
// wgc is installed globally in the Docker image
|
|
|
|
|
outputFile := filepath.Join(tmpDir, "config.json")
|
|
|
|
|
cmd := exec.Command("wgc", "router", "compose",
|
|
|
|
|
"--input", inputFile,
|
|
|
|
|
"--out", outputFile,
|
|
|
|
|
"--suppress-warnings",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("wgc router compose failed: %w\nOutput: %s", err, string(output))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read the generated config
|
|
|
|
|
configJSON, err := os.ReadFile(outputFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("read output config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(configJSON), nil
|
2025-11-19 11:29:30 +01:00
|
|
|
}
|