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.
This commit is contained in:
+73
-21
@@ -1,54 +1,106 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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) {
|
||||
// Build the Cosmo router config structure
|
||||
// This is a simplified version - you may need to adjust based on actual Cosmo requirements
|
||||
config := map[string]interface{}{
|
||||
"version": "1",
|
||||
"subgraphs": convertSubGraphsToCosmo(subGraphs),
|
||||
// Add other Cosmo-specific configuration as needed
|
||||
if len(subGraphs) == 0 {
|
||||
return "", fmt.Errorf("no subgraphs provided")
|
||||
}
|
||||
|
||||
// Marshal to JSON
|
||||
configJSON, err := json.MarshalIndent(config, "", " ")
|
||||
// Create a temporary directory for composition
|
||||
tmpDir, err := os.MkdirTemp("", "cosmo-compose-*")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal cosmo config: %w", err)
|
||||
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"`
|
||||
}
|
||||
|
||||
return string(configJSON), nil
|
||||
}
|
||||
type InputConfig struct {
|
||||
Version int `yaml:"version"`
|
||||
Subgraphs []SubgraphConfig `yaml:"subgraphs"`
|
||||
}
|
||||
|
||||
func convertSubGraphsToCosmo(subGraphs []*model.SubGraph) []map[string]interface{} {
|
||||
cosmoSubgraphs := make([]map[string]interface{}, 0, len(subGraphs))
|
||||
inputConfig := InputConfig{
|
||||
Version: 1,
|
||||
Subgraphs: make([]SubgraphConfig, 0, len(subGraphs)),
|
||||
}
|
||||
|
||||
for _, sg := range subGraphs {
|
||||
cosmoSg := map[string]interface{}{
|
||||
"name": sg.Service,
|
||||
"sdl": sg.Sdl,
|
||||
// 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 {
|
||||
cosmoSg["routing_url"] = *sg.URL
|
||||
subgraphCfg.RoutingURL = *sg.URL
|
||||
}
|
||||
|
||||
if sg.WsURL != nil {
|
||||
cosmoSg["subscription"] = map[string]interface{}{
|
||||
subgraphCfg.Subscription = map[string]interface{}{
|
||||
"url": *sg.WsURL,
|
||||
"protocol": "ws",
|
||||
"websocket_subprotocol": "graphql-ws",
|
||||
}
|
||||
}
|
||||
|
||||
cosmoSubgraphs = append(cosmoSubgraphs, cosmoSg)
|
||||
inputConfig.Subgraphs = append(inputConfig.Subgraphs, subgraphCfg)
|
||||
}
|
||||
|
||||
return cosmoSubgraphs
|
||||
// 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user