Skip to content

Commit 5913f21

Browse files
authored
Merge branch 'main' into suvij/eng-6332-store-persisted-operation-content-in-the-database-and-show
2 parents d9031e7 + 846d069 commit 5913f21

File tree

18 files changed

+645
-92
lines changed

18 files changed

+645
-92
lines changed

router-tests/introspection_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/sebdah/goldie/v2"
7+
78
"github.com/wundergraph/cosmo/router-tests/testenv"
89
)
910

router-tests/testdata/introspection/base-graph-schema.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

router-tests/testdata/introspection/feature-graph-schema.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

router/cmd/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import (
55
"errors"
66
"flag"
77
"fmt"
8+
"log"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
813
"github.com/wundergraph/cosmo/router/core"
914
"github.com/wundergraph/cosmo/router/internal/versioninfo"
1015
"github.com/wundergraph/cosmo/router/pkg/config"
1116
"github.com/wundergraph/cosmo/router/pkg/logging"
1217
"github.com/wundergraph/cosmo/router/pkg/profile"
13-
"log"
14-
"os"
15-
"os/signal"
16-
"syscall"
1718

1819
"go.uber.org/zap"
1920
)
@@ -29,7 +30,6 @@ var (
2930
)
3031

3132
func Main() {
32-
3333
// Parse flags before calling profile.Start(), since it may add flags
3434
flag.Parse()
3535

router/cmd/plan-generator/main.go

Lines changed: 2 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,11 @@
11
package main
22

33
import (
4-
"flag"
5-
"log"
64
"os"
7-
"path/filepath"
8-
"slices"
9-
"strings"
10-
"time"
115

12-
"github.com/wundergraph/cosmo/router/core"
13-
)
14-
15-
var (
16-
executionConfigFilePath = flag.String("execution-config", "config.json", "execution config file location")
17-
sourceOperationFoldersPath = flag.String("operations", "operations", "source operations folder location")
18-
plansOutPath = flag.String("plans", "plans", "output plans folder location")
19-
operationFilterFilePath = flag.String("filter", "", "operation filter file location which should contain file names of operations to include")
6+
"github.com/wundergraph/cosmo/router/cmd"
207
)
218

229
func main() {
23-
flag.Parse()
24-
25-
queriesPath, err := filepath.Abs(*sourceOperationFoldersPath)
26-
if err != nil {
27-
log.Fatalf("failed to get absolute path for queries: %v", err)
28-
}
29-
30-
outPath, err := filepath.Abs(*plansOutPath)
31-
if err != nil {
32-
log.Fatalf("failed to get absolute path for output: %v", err)
33-
}
34-
if err := os.MkdirAll(outPath, 0755); err != nil {
35-
log.Fatalf("failed to create output directory: %v", err)
36-
}
37-
38-
supergraphConfigPath, err := filepath.Abs(*executionConfigFilePath)
39-
log.Println("supergraphPath:", supergraphConfigPath)
40-
if err != nil {
41-
log.Fatalf("failed to get absolute path for supergraph: %v", err)
42-
}
43-
44-
queries, err := os.ReadDir(queriesPath)
45-
if err != nil {
46-
log.Fatalf("failed to read queries directory: %v", err)
47-
}
48-
49-
var filter []string
50-
if *operationFilterFilePath != "" {
51-
filterContent, err := os.ReadFile(*operationFilterFilePath)
52-
if err != nil {
53-
log.Fatalf("failed to read filter file: %v", err)
54-
}
55-
56-
filter = strings.Split(string(filterContent), "\n")
57-
}
58-
59-
pg, err := core.NewPlanGenerator(supergraphConfigPath)
60-
if err != nil {
61-
log.Fatalf("failed to create plan generator: %v", err)
62-
}
63-
64-
t := time.Now()
65-
66-
for i, queryFile := range queries {
67-
if filepath.Ext(queryFile.Name()) != ".graphql" {
68-
continue
69-
}
70-
71-
if len(filter) > 0 && !slices.Contains(filter, queryFile.Name()) {
72-
continue
73-
}
74-
75-
log.Println("Running query #", i, " name:", queryFile.Name())
76-
77-
queryFilePath := filepath.Join(queriesPath, queryFile.Name())
78-
79-
outContent, err := pg.PlanOperation(queryFilePath)
80-
if err != nil {
81-
log.Printf("failed operation #%d: %s err: %v\n", i, queryFile.Name(), err.Error())
82-
}
83-
84-
outFileName := filepath.Join(outPath, queryFile.Name())
85-
err = os.WriteFile(outFileName, []byte(outContent), 0644)
86-
if err != nil {
87-
log.Fatalf("failed to write file: %v", err)
88-
}
89-
}
90-
91-
log.Println("Total planning time:", time.Since(t))
10+
cmd.PlanGenerator(os.Args)
9211
}

router/cmd/plan_generator.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"flag"
6+
"log"
7+
"os"
8+
"os/signal"
9+
"syscall"
10+
11+
"github.com/wundergraph/cosmo/router/pkg/plan_generator"
12+
)
13+
14+
func PlanGenerator(args []string) {
15+
var planHelp bool
16+
17+
cfg := plan_generator.QueryPlanConfig{}
18+
f := flag.NewFlagSet("router "+args[0], flag.ExitOnError)
19+
f.BoolVar(&planHelp, "help", false, "Prints the help message")
20+
f.StringVar(&cfg.ExecutionConfig, "execution-config", "", "required, execution config file location")
21+
f.StringVar(&cfg.SourceDir, "operations", "", "required, source operations folder location")
22+
f.StringVar(&cfg.OutDir, "plans", "", "required, output plans folder location")
23+
f.StringVar(&cfg.Filter, "filter", "", "operation filter file location which should contain file names of operations to include")
24+
f.StringVar(&cfg.Timeout, "timeout", "30s", "timeout")
25+
f.IntVar(&cfg.Concurrency, "concurrency", 0, "how many query plan run concurrently")
26+
f.BoolVar(&cfg.OutputFiles, "print-per-file", true, "write a file for each query, with inside the plan or the query plan error")
27+
f.BoolVar(&cfg.OutputReport, "print-report", false, "write a report.json file, with all the query plans and errors sorted by file name")
28+
f.BoolVar(&cfg.FailOnPlanError, "fail-on-error", false, "if at least one plan fails, the command exit code will be 1")
29+
f.BoolVar(&cfg.FailFast, "fail-fast", false, "stop as soon as possible if a plan fails")
30+
31+
if err := f.Parse(args[1:]); err != nil {
32+
f.PrintDefaults()
33+
log.Fatalf("Failed to parse flags: %v", err)
34+
}
35+
36+
if planHelp {
37+
f.PrintDefaults()
38+
return
39+
}
40+
if cfg.ExecutionConfig == "" || cfg.SourceDir == "" || cfg.OutDir == "" {
41+
f.PrintDefaults()
42+
log.Fatalf("missing required flags")
43+
}
44+
45+
ctxNotify, stop := signal.NotifyContext(context.Background(), os.Interrupt,
46+
syscall.SIGHUP, // process is detached from terminal
47+
syscall.SIGTERM, // default for kill
48+
syscall.SIGKILL,
49+
syscall.SIGQUIT, // ctrl + \
50+
syscall.SIGINT, // ctrl+c
51+
)
52+
defer stop()
53+
54+
err := plan_generator.PlanGenerator(ctxNotify, cfg)
55+
if err != nil {
56+
log.Fatalf("Error during command plan-generator: %s", err)
57+
}
58+
}

router/cmd/router/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package main
22

33
import (
4+
"os"
5+
46
routercmd "github.com/wundergraph/cosmo/router/cmd"
57
)
68

79
func main() {
8-
routercmd.Main()
10+
if len(os.Args) > 1 && os.Args[1] == "query-plan" {
11+
routercmd.PlanGenerator(os.Args[1:])
12+
} else {
13+
routercmd.Main()
14+
}
915
}

router/core/plan_generator.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
"os"
99

1010
log "github.com/jensneuse/abstractlogger"
11-
11+
nodev1 "github.com/wundergraph/cosmo/router/gen/proto/wg/cosmo/node/v1"
1212
"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
1313
"github.com/wundergraph/graphql-go-tools/v2/pkg/astnormalization"
1414
"github.com/wundergraph/graphql-go-tools/v2/pkg/astparser"
1515
"github.com/wundergraph/graphql-go-tools/v2/pkg/asttransform"
1616
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/graphql_datasource"
17+
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/introspection_datasource"
18+
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/pubsub_datasource"
1719
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/plan"
1820
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/postprocess"
1921
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
@@ -112,6 +114,27 @@ func (pg *PlanGenerator) loadConfiguration(configFilePath string) error {
112114
return err
113115
}
114116

117+
natSources := map[string]pubsub_datasource.NatsPubSub{}
118+
kafkaSources := map[string]pubsub_datasource.KafkaPubSub{}
119+
for _, ds := range routerConfig.GetEngineConfig().GetDatasourceConfigurations() {
120+
if ds.GetKind() != nodev1.DataSourceKind_PUBSUB || ds.GetCustomEvents() == nil {
121+
continue
122+
}
123+
for _, natConfig := range ds.GetCustomEvents().GetNats() {
124+
providerId := natConfig.GetEngineEventConfiguration().GetProviderId()
125+
if _, ok := natSources[providerId]; !ok {
126+
natSources[providerId] = nil
127+
}
128+
}
129+
for _, kafkaConfig := range ds.GetCustomEvents().GetKafka() {
130+
providerId := kafkaConfig.GetEngineEventConfiguration().GetProviderId()
131+
if _, ok := kafkaSources[providerId]; !ok {
132+
kafkaSources[providerId] = nil
133+
}
134+
}
135+
}
136+
pubSubFactory := pubsub_datasource.NewFactory(context.Background(), natSources, kafkaSources)
137+
115138
var netPollConfig graphql_datasource.NetPollConfiguration
116139
netPollConfig.ApplyDefaults()
117140

@@ -129,6 +152,7 @@ func (pg *PlanGenerator) loadConfiguration(configFilePath string) error {
129152
streamingClient: http.DefaultClient,
130153
subscriptionClient: subscriptionClient,
131154
transportOptions: &TransportOptions{SubgraphTransportOptions: NewSubgraphTransportOptions(config.TrafficShapingRules{})},
155+
pubsub: pubSubFactory,
132156
})
133157

134158
// this generates the plan configuration using the data source factories from the config package
@@ -162,6 +186,23 @@ func (pg *PlanGenerator) loadConfiguration(configFilePath string) error {
162186
return fmt.Errorf("failed to merge graphql schema with base schema: %w", err)
163187
}
164188

189+
// by default, the engine doesn't understand how to resolve the __schema and __type queries
190+
// we need to add a special datasource for that
191+
// it takes the definition as the input and generates introspection data
192+
// datasource is attached to Query.__schema, Query.__type, __Type.fields and __Type.enumValues fields
193+
introspectionFactory, err := introspection_datasource.NewIntrospectionConfigFactory(&definition)
194+
if err != nil {
195+
return fmt.Errorf("failed to create introspection config factory: %w", err)
196+
}
197+
dataSources := introspectionFactory.BuildDataSourceConfigurations()
198+
199+
fieldConfigs := introspectionFactory.BuildFieldConfigurations()
200+
// we need to add these fields to the config
201+
// otherwise the engine wouldn't know how to resolve them
202+
planConfig.Fields = append(planConfig.Fields, fieldConfigs...)
203+
// finally, we add our data source for introspection to the existing data sources
204+
planConfig.DataSources = append(planConfig.DataSources, dataSources...)
205+
165206
pg.planConfiguration = planConfig
166207
pg.definition = &definition
167208
return nil

0 commit comments

Comments
 (0)