Files
schemas/graph/cosmo.go
T
argoyle 9368d77bc8 feat: add latestSchema query for retrieving schema updates
Implements the `latestSchema` query to fetch the latest schema 
updates for an organization. This change enhances the GraphQL API by
allowing clients to retrieve the most recent schema version and its 
associated subgraphs. The resolver performs necessary access checks, 
logs relevant information, and generates the Cosmo router configuration 
from fetched subgraph SDLs, returning structured schema update details.
2025-11-20 18:24:36 +01:00

107 lines
2.8 KiB
Go

package graph
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"gopkg.in/yaml.v3"
"gitlab.com/unboundsoftware/schemas/graph/model"
)
// GenerateCosmoRouterConfig generates a Cosmo Router execution config from subgraphs
// using the official wgc CLI tool via npx
func GenerateCosmoRouterConfig(subGraphs []*model.SubGraph) (string, error) {
if len(subGraphs) == 0 {
return "", fmt.Errorf("no subgraphs provided")
}
// Create a temporary directory for composition
tmpDir, err := os.MkdirTemp("", "cosmo-compose-*")
if err != nil {
return "", fmt.Errorf("create temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
// 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"`
}
type InputConfig struct {
Version int `yaml:"version"`
Subgraphs []SubgraphConfig `yaml:"subgraphs"`
}
inputConfig := InputConfig{
Version: 1,
Subgraphs: make([]SubgraphConfig, 0, len(subGraphs)),
}
for _, sg := range subGraphs {
// 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,
},
}
if sg.URL != nil {
subgraphCfg.RoutingURL = *sg.URL
}
if sg.WsURL != nil {
subgraphCfg.Subscription = map[string]interface{}{
"url": *sg.WsURL,
"protocol": "ws",
"websocket_subprotocol": "graphql-ws",
}
}
inputConfig.Subgraphs = append(inputConfig.Subgraphs, subgraphCfg)
}
// 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
}