diff --git a/codegen/example/example_client_test.go b/codegen/example/example_client_test.go index f831ee3d12..e48d5d592a 100644 --- a/codegen/example/example_client_test.go +++ b/codegen/example/example_client_test.go @@ -2,9 +2,9 @@ package example import ( "bytes" + "path/filepath" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "goa.design/goa/v3/codegen" @@ -16,13 +16,12 @@ func TestExampleCLIFiles(t *testing.T) { cases := []struct { Name string DSL func() - Code string }{ - {"no-server", testdata.NoServerDSL, testdata.NoServerCLIMainCode}, - {"single-server-single-host", testdata.SingleServerSingleHostDSL, testdata.SingleServerSingleHostCLIMainCode}, - {"single-server-single-host-with-variables", testdata.SingleServerSingleHostWithVariablesDSL, testdata.SingleServerSingleHostWithVariablesCLIMainCode}, - {"single-server-multiple-hosts", testdata.SingleServerMultipleHostsDSL, testdata.SingleServerMultipleHostsCLIMainCode}, - {"single-server-multiple-hosts-with-variables", testdata.SingleServerMultipleHostsWithVariablesDSL, testdata.SingleServerMultipleHostsWithVariablesCLIMainCode}, + {"no-server", testdata.NoServerDSL}, + {"single-server-single-host", testdata.SingleServerSingleHostDSL}, + {"single-server-single-host-with-variables", testdata.SingleServerSingleHostWithVariablesDSL}, + {"single-server-multiple-hosts", testdata.SingleServerMultipleHostsDSL}, + {"single-server-multiple-hosts-with-variables", testdata.SingleServerMultipleHostsWithVariablesDSL}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { @@ -37,7 +36,8 @@ func TestExampleCLIFiles(t *testing.T) { require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, "package foo\n"+buf.String()) - assert.Equal(t, c.Code, code) + golden := filepath.Join("testdata", "client-"+c.Name+".golden") + compareOrUpdateGolden(t, code, golden) }) } } diff --git a/codegen/example/example_server.go b/codegen/example/example_server.go index fa861973be..f896106eb4 100644 --- a/codegen/example/example_server.go +++ b/codegen/example/example_server.go @@ -35,7 +35,6 @@ func exampleSvrMain(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *c {Path: "context"}, {Path: "flag"}, {Path: "fmt"}, - {Path: "log"}, {Path: "net"}, {Path: "net/url"}, {Path: "os"}, @@ -44,7 +43,8 @@ func exampleSvrMain(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *c {Path: "sync"}, {Path: "syscall"}, {Path: "time"}, - codegen.GoaImport("middleware"), + {Path: "goa.design/clue/debug"}, + {Path: "goa.design/clue/log"}, } // Iterate through services listed in the server expression. diff --git a/codegen/example/example_server_test.go b/codegen/example/example_server_test.go index b3cba34a6e..a318b743a5 100644 --- a/codegen/example/example_server_test.go +++ b/codegen/example/example_server_test.go @@ -2,6 +2,10 @@ package example import ( "bytes" + "flag" + "os" + "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -13,29 +17,49 @@ import ( "goa.design/goa/v3/expr" ) +// updateGolden is true when -w is passed to `go test`, e.g. `go test ./... -w` +var updateGolden = false + +func init() { + flag.BoolVar(&updateGolden, "w", false, "update golden files") +} + +func compareOrUpdateGolden(t *testing.T, code, golden string) { + t.Helper() + if updateGolden { + require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0750)) + require.NoError(t, os.WriteFile(golden, []byte(code), 0640)) + return + } + data, err := os.ReadFile(golden) + require.NoError(t, err) + if runtime.GOOS == "windows" { + data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n")) + } + assert.Equal(t, string(data), code) +} + func TestExampleServerFiles(t *testing.T) { cases := []struct { Name string DSL func() - Code string }{ - {"no-server", testdata.NoServerDSL, testdata.NoServerServerMainCode}, - {"same-api-service-name", testdata.SameAPIServiceNameDSL, testdata.SameAPIServiceNameServerMainCode}, - {"single-server-single-host", testdata.SingleServerSingleHostDSL, testdata.SingleServerSingleHostServerMainCode}, - {"single-server-single-host-with-variables", testdata.SingleServerSingleHostWithVariablesDSL, testdata.SingleServerSingleHostWithVariablesServerMainCode}, - {"server-hosting-service-with-file-server", testdata.ServerHostingServiceWithFileServerDSL, testdata.ServerHostingServiceWithFileServerServerMainCode}, - {"server-hosting-service-subset", testdata.ServerHostingServiceSubsetDSL, testdata.ServerHostingServiceSubsetServerMainCode}, - {"server-hosting-multiple-services", testdata.ServerHostingMultipleServicesDSL, testdata.ServerHostingMultipleServicesServerMainCode}, - {"single-server-multiple-hosts", testdata.SingleServerMultipleHostsDSL, testdata.SingleServerMultipleHostsServerMainCode}, - {"single-server-multiple-hosts-with-variables", testdata.SingleServerMultipleHostsWithVariablesDSL, testdata.SingleServerMultipleHostsWithVariablesServerMainCode}, - {"service-name-with-spaces", testdata.NamesWithSpacesDSL, testdata.NamesWithSpacesServerMainCode}, - {"service-for-only-http", testdata.ServiceForOnlyHTTPDSL, testdata.ServiceForOnlyHTTPServerMainCode}, - {"sercice-for-only-grpc", testdata.ServiceForOnlyGRPCDSL, testdata.ServiceForOnlyGRPCServerMainCode}, - {"service-for-http-and-part-of-grpc", testdata.ServiceForHTTPAndPartOfGRPCDSL, testdata.ServiceForHTTPAndPartOfGRPCServerMainCode}, + {"no-server", testdata.NoServerDSL}, + {"same-api-service-name", testdata.SameAPIServiceNameDSL}, + {"single-server-single-host", testdata.SingleServerSingleHostDSL}, + {"single-server-single-host-with-variables", testdata.SingleServerSingleHostWithVariablesDSL}, + {"server-hosting-service-with-file-server", testdata.ServerHostingServiceWithFileServerDSL}, + {"server-hosting-service-subset", testdata.ServerHostingServiceSubsetDSL}, + {"server-hosting-multiple-services", testdata.ServerHostingMultipleServicesDSL}, + {"single-server-multiple-hosts", testdata.SingleServerMultipleHostsDSL}, + {"single-server-multiple-hosts-with-variables", testdata.SingleServerMultipleHostsWithVariablesDSL}, + {"service-name-with-spaces", testdata.NamesWithSpacesDSL}, + {"service-for-only-http", testdata.ServiceForOnlyHTTPDSL}, + {"sercice-for-only-grpc", testdata.ServiceForOnlyGRPCDSL}, + {"service-for-http-and-part-of-grpc", testdata.ServiceForHTTPAndPartOfGRPCDSL}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { - // reset global variable service.Services = make(service.ServicesData) Servers = make(ServersData) codegen.RunDSL(t, c.DSL) @@ -47,7 +71,8 @@ func TestExampleServerFiles(t *testing.T) { require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, "package foo\n"+buf.String()) - assert.Equal(t, c.Code, code) + golden := filepath.Join("testdata", "server-"+c.Name+".golden") + compareOrUpdateGolden(t, code, golden) }) } } diff --git a/codegen/example/templates/server_end.go.tpl b/codegen/example/templates/server_end.go.tpl index 6ea9b7b4fd..8581650d8c 100644 --- a/codegen/example/templates/server_end.go.tpl +++ b/codegen/example/templates/server_end.go.tpl @@ -1,11 +1,11 @@ {{ comment "Wait for signal." }} - logger.Printf("exiting (%v)", <-errc) + log.Printf(ctx, "exiting (%v)", <-errc) {{ comment "Send cancellation signal to the goroutines." }} cancel() wg.Wait() - logger.Println("exited") + log.Printf(ctx, "exited") } \ No newline at end of file diff --git a/codegen/example/templates/server_endpoints.go.tpl b/codegen/example/templates/server_endpoints.go.tpl index ca547da2ea..3916ab3bd4 100644 --- a/codegen/example/templates/server_endpoints.go.tpl +++ b/codegen/example/templates/server_endpoints.go.tpl @@ -12,6 +12,8 @@ {{- range .Services }} {{- if .Methods }} {{ .VarName }}Endpoints = {{ .PkgName }}.NewEndpoints({{ .VarName }}Svc) + {{ .VarName }}Endpoints.Use(debug.LogPayloads()) + {{ .VarName }}Endpoints.Use(log.Endpoint) {{- end }} {{- end }} } diff --git a/codegen/example/templates/server_handler.go.tpl b/codegen/example/templates/server_handler.go.tpl index fe8528d16c..6870eedb11 100644 --- a/codegen/example/templates/server_handler.go.tpl +++ b/codegen/example/templates/server_handler.go.tpl @@ -20,14 +20,14 @@ } } if !{{ .VarName }}Seen { - logger.Fatalf("invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})\n", *{{ .VarName }}F) + log.Fatalf(ctx, "invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})\n", *{{ .VarName }}F) } {{- end }} addr = strings.Replace(addr, "{{ printf "{%s}" .Name }}", *{{ .VarName }}F, -1) {{- end }} u, err := url.Parse(addr) if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) } if *secureF { u.Scheme = "{{ $u.Transport.Type }}s" @@ -38,17 +38,17 @@ if *{{ $u.Transport.Type }}PortF != "" { h, _, err := net.SplitHostPort(u.Host) if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) } u.Host = net.JoinHostPort(h, *{{ $u.Transport.Type }}PortF) } else if u.Port() == "" { u.Host = net.JoinHostPort(u.Host, "{{ $u.Port }}") } - handle{{ toUpper $u.Transport.Name }}Server(ctx, u, {{ range $t := $.Server.Transports }}{{ if eq $t.Type $u.Transport.Type }}{{ range $s := $t.Services }}{{ range $.Services }}{{ if eq $s .Name }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}{{ end }}{{ end }}{{ end }}{{ end }}&wg, errc, logger, *dbgF) + handle{{ toUpper $u.Transport.Name }}Server(ctx, u, {{ range $t := $.Server.Transports }}{{ if eq $t.Type $u.Transport.Type }}{{ range $s := $t.Services }}{{ range $.Services }}{{ if eq $s .Name }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}{{ end }}{{ end }}{{ end }}{{ end }}&wg, errc, *dbgF) } {{- end }} {{ end }} {{- end }} default: - logger.Fatalf("invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})\n", *hostF) + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})\n", *hostF) } \ No newline at end of file diff --git a/codegen/example/templates/server_interrupts.go.tpl b/codegen/example/templates/server_interrupts.go.tpl index 20e5f26750..38afb74558 100644 --- a/codegen/example/templates/server_interrupts.go.tpl +++ b/codegen/example/templates/server_interrupts.go.tpl @@ -13,4 +13,4 @@ }() var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) \ No newline at end of file + ctx, cancel := context.WithCancel(ctx) \ No newline at end of file diff --git a/codegen/example/templates/server_logger.go.tpl b/codegen/example/templates/server_logger.go.tpl index a819270a52..1afad381f9 100644 --- a/codegen/example/templates/server_logger.go.tpl +++ b/codegen/example/templates/server_logger.go.tpl @@ -1,9 +1,13 @@ {{ comment "Setup logger. Replace logger with your own log package of choice." }} - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[{{ .APIPkg }}] ", log.Ltime) - } \ No newline at end of file + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) \ No newline at end of file diff --git a/codegen/example/templates/server_services.go.tpl b/codegen/example/templates/server_services.go.tpl index 4535434c5d..3385b77a85 100644 --- a/codegen/example/templates/server_services.go.tpl +++ b/codegen/example/templates/server_services.go.tpl @@ -11,7 +11,7 @@ { {{- range .Services }} {{- if .Methods }} - {{ .VarName }}Svc = {{ $.APIPkg }}.New{{ .StructName }}(logger) + {{ .VarName }}Svc = {{ $.APIPkg }}.New{{ .StructName }}() {{- end }} {{- end }} } diff --git a/codegen/example/testdata/client-no-server.golden b/codegen/example/testdata/client-no-server.golden new file mode 100644 index 0000000000..3f88912cbc --- /dev/null +++ b/codegen/example/testdata/client-no-server.golden @@ -0,0 +1,108 @@ +func main() { + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + addrF = flag.String("url", "", "URL to service host") + + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "localhost": + addr = "http://localhost:80" + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload any + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + case "grpc", "grpcs": + endpoint, payload, err = doGRPC(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http)\n", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the test api API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] + + -host HOST: server host (localhost). valid values: localhost + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/codegen/example/testdata/client-single-server-multiple-hosts-with-variables.golden b/codegen/example/testdata/client-single-server-multiple-hosts-with-variables.golden new file mode 100644 index 0000000000..d8209d75dd --- /dev/null +++ b/codegen/example/testdata/client-single-server-multiple-hosts-with-variables.golden @@ -0,0 +1,130 @@ +func main() { + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") + addrF = flag.String("url", "", "URL to service host") + + versionF = flag.String("version", "v1", "Version") + domainF = flag.String("domain", "test", "Domain") + portF = flag.String("port", "8080", "Port") + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "dev": + addr = "http://example-{version}:8090" + var versionSeen bool + { + for _, v := range []string{"v1", "v2"} { + if v == *versionF { + versionSeen = true + break + } + } + } + if !versionSeen { + fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF) + os.Exit(1) + } + addr = strings.Replace(addr, "{version}", *versionF, -1) + case "stage": + addr = "https://example-{domain}:{port}" + addr = strings.Replace(addr, "{domain}", *domainF, -1) + addr = strings.Replace(addr, "{port}", *portF, -1) + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload any + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the SingleServerMultipleHostsWithVariables API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-version VERSION][-domain DOMAIN][-port PORT] SERVICE ENDPOINT [flags] + + -host HOST: server host (dev). valid values: dev, stage + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + -version: Version (v1) + -domain: Domain (test) + -port: Port (8080) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/codegen/example/testdata/client-single-server-multiple-hosts.golden b/codegen/example/testdata/client-single-server-multiple-hosts.golden new file mode 100644 index 0000000000..c9905c385d --- /dev/null +++ b/codegen/example/testdata/client-single-server-multiple-hosts.golden @@ -0,0 +1,108 @@ +func main() { + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") + addrF = flag.String("url", "", "URL to service host") + + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "dev": + addr = "http://example:8090" + case "stage": + addr = "https://example" + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload any + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the SingleServerMultipleHosts API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] + + -host HOST: server host (dev). valid values: dev, stage + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/codegen/example/testdata/client-single-server-single-host-with-variables.golden b/codegen/example/testdata/client-single-server-single-host-with-variables.golden new file mode 100644 index 0000000000..fcfcda20af --- /dev/null +++ b/codegen/example/testdata/client-single-server-single-host-with-variables.golden @@ -0,0 +1,133 @@ +func main() { + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + addrF = flag.String("url", "", "URL to service host") + + int_F = flag.String("int", "1", "") + uint_F = flag.String("uint", "1", "") + float32_F = flag.String("float32", "1.1", "") + int32_F = flag.String("int32", "1", "") + int64_F = flag.String("int64", "1", "") + uint32_F = flag.String("uint32", "1", "") + uint64_F = flag.String("uint64", "1", "") + float64_F = flag.String("float64", "1", "") + bool_F = flag.String("bool", "true", "") + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "dev": + addr = "http://example-{int}-{uint}-{float32}:8090" + addr = strings.Replace(addr, "{int}", *int_F, -1) + addr = strings.Replace(addr, "{uint}", *uint_F, -1) + addr = strings.Replace(addr, "{float32}", *float32_F, -1) + addr = strings.Replace(addr, "{int32}", *int32_F, -1) + addr = strings.Replace(addr, "{int64}", *int64_F, -1) + addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) + addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) + addr = strings.Replace(addr, "{float64}", *float64_F, -1) + addr = strings.Replace(addr, "{bool}", *bool_F, -1) + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload any + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the SingleServerSingleHostWithVariables API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-int INT][-uint UINT][-float32 FLOAT32][-int32 INT32][-int64 INT64][-uint32 UINT32][-uint64 UINT64][-float64 FLOAT64][-bool BOOL] SERVICE ENDPOINT [flags] + + -host HOST: server host (dev). valid values: dev + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + -int: (1) + -uint: (1) + -float32: (1.1) + -int32: (1) + -int64: (1) + -uint32: (1) + -uint64: (1) + -float64: (1) + -bool: (true) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/codegen/example/testdata/client-single-server-single-host.golden b/codegen/example/testdata/client-single-server-single-host.golden new file mode 100644 index 0000000000..bb2055b014 --- /dev/null +++ b/codegen/example/testdata/client-single-server-single-host.golden @@ -0,0 +1,108 @@ +func main() { + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + addrF = flag.String("url", "", "URL to service host") + + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "dev": + addr = "http://example:8090" + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload any + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + case "grpc", "grpcs": + endpoint, payload, err = doGRPC(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http|https)\n", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the SingleServerSingleHost API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] + + -host HOST: server host (dev). valid values: dev + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/codegen/example/testdata/example_cli_code.go b/codegen/example/testdata/example_cli_code.go deleted file mode 100644 index ea40d08bf6..0000000000 --- a/codegen/example/testdata/example_cli_code.go +++ /dev/null @@ -1,600 +0,0 @@ -package testdata - -const ( - NoServerCLIMainCode = `func main() { - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - addrF = flag.String("url", "", "URL to service host") - - verboseF = flag.Bool("verbose", false, "Print request and response details") - vF = flag.Bool("v", false, "Print request and response details") - timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") - ) - flag.Usage = usage - flag.Parse() - var ( - addr string - timeout int - debug bool - ) - { - addr = *addrF - if addr == "" { - switch *hostF { - case "localhost": - addr = "http://localhost:80" - default: - fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) - os.Exit(1) - } - } - timeout = *timeoutF - debug = *verboseF || *vF - } - - var ( - scheme string - host string - ) - { - u, err := url.Parse(addr) - if err != nil { - fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) - os.Exit(1) - } - scheme = u.Scheme - host = u.Host - } - var ( - endpoint goa.Endpoint - payload any - err error - ) - { - switch scheme { - case "http", "https": - endpoint, payload, err = doHTTP(scheme, host, timeout, debug) - case "grpc", "grpcs": - endpoint, payload, err = doGRPC(scheme, host, timeout, debug) - default: - fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http)\n", scheme) - os.Exit(1) - } - } - if err != nil { - if err == flag.ErrHelp { - os.Exit(0) - } - fmt.Fprintln(os.Stderr, err.Error()) - fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") - os.Exit(1) - } - - data, err := endpoint(context.Background(), payload) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - if data != nil { - m, _ := json.MarshalIndent(data, "", " ") - fmt.Println(string(m)) - } -} - -func usage() { - fmt.Fprintf(os.Stderr, ` + "`" + `%s is a command line client for the test api API. - -Usage: - %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] - - -host HOST: server host (localhost). valid values: localhost - -url URL: specify service URL overriding host URL (http://localhost:8080) - -timeout: maximum number of seconds to wait for response (30) - -verbose|-v: print request and response details (false) - -Commands: -%s -Additional help: - %s SERVICE [ENDPOINT] --help - -Example: -%s -` + "`" + `, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) -} - -func indent(s string) string { - if s == "" { - return "" - } - return " " + strings.Replace(s, "\n", "\n ", -1) -} -` - - SingleServerSingleHostCLIMainCode = `func main() { - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - addrF = flag.String("url", "", "URL to service host") - - verboseF = flag.Bool("verbose", false, "Print request and response details") - vF = flag.Bool("v", false, "Print request and response details") - timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") - ) - flag.Usage = usage - flag.Parse() - var ( - addr string - timeout int - debug bool - ) - { - addr = *addrF - if addr == "" { - switch *hostF { - case "dev": - addr = "http://example:8090" - default: - fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF) - os.Exit(1) - } - } - timeout = *timeoutF - debug = *verboseF || *vF - } - - var ( - scheme string - host string - ) - { - u, err := url.Parse(addr) - if err != nil { - fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) - os.Exit(1) - } - scheme = u.Scheme - host = u.Host - } - var ( - endpoint goa.Endpoint - payload any - err error - ) - { - switch scheme { - case "http", "https": - endpoint, payload, err = doHTTP(scheme, host, timeout, debug) - case "grpc", "grpcs": - endpoint, payload, err = doGRPC(scheme, host, timeout, debug) - default: - fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http|https)\n", scheme) - os.Exit(1) - } - } - if err != nil { - if err == flag.ErrHelp { - os.Exit(0) - } - fmt.Fprintln(os.Stderr, err.Error()) - fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") - os.Exit(1) - } - - data, err := endpoint(context.Background(), payload) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - if data != nil { - m, _ := json.MarshalIndent(data, "", " ") - fmt.Println(string(m)) - } -} - -func usage() { - fmt.Fprintf(os.Stderr, ` + "`" + `%s is a command line client for the SingleServerSingleHost API. - -Usage: - %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] - - -host HOST: server host (dev). valid values: dev - -url URL: specify service URL overriding host URL (http://localhost:8080) - -timeout: maximum number of seconds to wait for response (30) - -verbose|-v: print request and response details (false) - -Commands: -%s -Additional help: - %s SERVICE [ENDPOINT] --help - -Example: -%s -` + "`" + `, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) -} - -func indent(s string) string { - if s == "" { - return "" - } - return " " + strings.Replace(s, "\n", "\n ", -1) -} -` - - SingleServerSingleHostWithVariablesCLIMainCode = `func main() { - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - addrF = flag.String("url", "", "URL to service host") - - int_F = flag.String("int", "1", "") - uint_F = flag.String("uint", "1", "") - float32_F = flag.String("float32", "1.1", "") - int32_F = flag.String("int32", "1", "") - int64_F = flag.String("int64", "1", "") - uint32_F = flag.String("uint32", "1", "") - uint64_F = flag.String("uint64", "1", "") - float64_F = flag.String("float64", "1", "") - bool_F = flag.String("bool", "true", "") - verboseF = flag.Bool("verbose", false, "Print request and response details") - vF = flag.Bool("v", false, "Print request and response details") - timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") - ) - flag.Usage = usage - flag.Parse() - var ( - addr string - timeout int - debug bool - ) - { - addr = *addrF - if addr == "" { - switch *hostF { - case "dev": - addr = "http://example-{int}-{uint}-{float32}:8090" - addr = strings.Replace(addr, "{int}", *int_F, -1) - addr = strings.Replace(addr, "{uint}", *uint_F, -1) - addr = strings.Replace(addr, "{float32}", *float32_F, -1) - addr = strings.Replace(addr, "{int32}", *int32_F, -1) - addr = strings.Replace(addr, "{int64}", *int64_F, -1) - addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) - addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) - addr = strings.Replace(addr, "{float64}", *float64_F, -1) - addr = strings.Replace(addr, "{bool}", *bool_F, -1) - default: - fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF) - os.Exit(1) - } - } - timeout = *timeoutF - debug = *verboseF || *vF - } - - var ( - scheme string - host string - ) - { - u, err := url.Parse(addr) - if err != nil { - fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) - os.Exit(1) - } - scheme = u.Scheme - host = u.Host - } - var ( - endpoint goa.Endpoint - payload any - err error - ) - { - switch scheme { - case "http", "https": - endpoint, payload, err = doHTTP(scheme, host, timeout, debug) - default: - fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) - os.Exit(1) - } - } - if err != nil { - if err == flag.ErrHelp { - os.Exit(0) - } - fmt.Fprintln(os.Stderr, err.Error()) - fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") - os.Exit(1) - } - - data, err := endpoint(context.Background(), payload) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - if data != nil { - m, _ := json.MarshalIndent(data, "", " ") - fmt.Println(string(m)) - } -} - -func usage() { - fmt.Fprintf(os.Stderr, ` + "`" + `%s is a command line client for the SingleServerSingleHostWithVariables API. - -Usage: - %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-int INT][-uint UINT][-float32 FLOAT32][-int32 INT32][-int64 INT64][-uint32 UINT32][-uint64 UINT64][-float64 FLOAT64][-bool BOOL] SERVICE ENDPOINT [flags] - - -host HOST: server host (dev). valid values: dev - -url URL: specify service URL overriding host URL (http://localhost:8080) - -timeout: maximum number of seconds to wait for response (30) - -verbose|-v: print request and response details (false) - -int: (1) - -uint: (1) - -float32: (1.1) - -int32: (1) - -int64: (1) - -uint32: (1) - -uint64: (1) - -float64: (1) - -bool: (true) - -Commands: -%s -Additional help: - %s SERVICE [ENDPOINT] --help - -Example: -%s -` + "`" + `, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) -} - -func indent(s string) string { - if s == "" { - return "" - } - return " " + strings.Replace(s, "\n", "\n ", -1) -} -` - - SingleServerMultipleHostsCLIMainCode = `func main() { - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") - addrF = flag.String("url", "", "URL to service host") - - verboseF = flag.Bool("verbose", false, "Print request and response details") - vF = flag.Bool("v", false, "Print request and response details") - timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") - ) - flag.Usage = usage - flag.Parse() - var ( - addr string - timeout int - debug bool - ) - { - addr = *addrF - if addr == "" { - switch *hostF { - case "dev": - addr = "http://example:8090" - case "stage": - addr = "https://example" - default: - fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) - os.Exit(1) - } - } - timeout = *timeoutF - debug = *verboseF || *vF - } - - var ( - scheme string - host string - ) - { - u, err := url.Parse(addr) - if err != nil { - fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) - os.Exit(1) - } - scheme = u.Scheme - host = u.Host - } - var ( - endpoint goa.Endpoint - payload any - err error - ) - { - switch scheme { - case "http", "https": - endpoint, payload, err = doHTTP(scheme, host, timeout, debug) - default: - fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) - os.Exit(1) - } - } - if err != nil { - if err == flag.ErrHelp { - os.Exit(0) - } - fmt.Fprintln(os.Stderr, err.Error()) - fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") - os.Exit(1) - } - - data, err := endpoint(context.Background(), payload) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - if data != nil { - m, _ := json.MarshalIndent(data, "", " ") - fmt.Println(string(m)) - } -} - -func usage() { - fmt.Fprintf(os.Stderr, ` + "`" + `%s is a command line client for the SingleServerMultipleHosts API. - -Usage: - %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] - - -host HOST: server host (dev). valid values: dev, stage - -url URL: specify service URL overriding host URL (http://localhost:8080) - -timeout: maximum number of seconds to wait for response (30) - -verbose|-v: print request and response details (false) - -Commands: -%s -Additional help: - %s SERVICE [ENDPOINT] --help - -Example: -%s -` + "`" + `, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) -} - -func indent(s string) string { - if s == "" { - return "" - } - return " " + strings.Replace(s, "\n", "\n ", -1) -} -` - - SingleServerMultipleHostsWithVariablesCLIMainCode = `func main() { - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") - addrF = flag.String("url", "", "URL to service host") - - versionF = flag.String("version", "v1", "Version") - domainF = flag.String("domain", "test", "Domain") - portF = flag.String("port", "8080", "Port") - verboseF = flag.Bool("verbose", false, "Print request and response details") - vF = flag.Bool("v", false, "Print request and response details") - timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") - ) - flag.Usage = usage - flag.Parse() - var ( - addr string - timeout int - debug bool - ) - { - addr = *addrF - if addr == "" { - switch *hostF { - case "dev": - addr = "http://example-{version}:8090" - var versionSeen bool - { - for _, v := range []string{"v1", "v2"} { - if v == *versionF { - versionSeen = true - break - } - } - } - if !versionSeen { - fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF) - os.Exit(1) - } - addr = strings.Replace(addr, "{version}", *versionF, -1) - case "stage": - addr = "https://example-{domain}:{port}" - addr = strings.Replace(addr, "{domain}", *domainF, -1) - addr = strings.Replace(addr, "{port}", *portF, -1) - default: - fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) - os.Exit(1) - } - } - timeout = *timeoutF - debug = *verboseF || *vF - } - - var ( - scheme string - host string - ) - { - u, err := url.Parse(addr) - if err != nil { - fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) - os.Exit(1) - } - scheme = u.Scheme - host = u.Host - } - var ( - endpoint goa.Endpoint - payload any - err error - ) - { - switch scheme { - case "http", "https": - endpoint, payload, err = doHTTP(scheme, host, timeout, debug) - default: - fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme) - os.Exit(1) - } - } - if err != nil { - if err == flag.ErrHelp { - os.Exit(0) - } - fmt.Fprintln(os.Stderr, err.Error()) - fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") - os.Exit(1) - } - - data, err := endpoint(context.Background(), payload) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - if data != nil { - m, _ := json.MarshalIndent(data, "", " ") - fmt.Println(string(m)) - } -} - -func usage() { - fmt.Fprintf(os.Stderr, ` + "`" + `%s is a command line client for the SingleServerMultipleHostsWithVariables API. - -Usage: - %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-version VERSION][-domain DOMAIN][-port PORT] SERVICE ENDPOINT [flags] - - -host HOST: server host (dev). valid values: dev, stage - -url URL: specify service URL overriding host URL (http://localhost:8080) - -timeout: maximum number of seconds to wait for response (30) - -verbose|-v: print request and response details (false) - -version: Version (v1) - -domain: Domain (test) - -port: Port (8080) - -Commands: -%s -Additional help: - %s SERVICE [ENDPOINT] --help - -Example: -%s -` + "`" + `, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) -} - -func indent(s string) string { - if s == "" { - return "" - } - return " " + strings.Replace(s, "\n", "\n ", -1) -} -` -) diff --git a/codegen/example/testdata/example_server_code.go b/codegen/example/testdata/example_server_code.go deleted file mode 100644 index 282d72c96e..0000000000 --- a/codegen/example/testdata/example_server_code.go +++ /dev/null @@ -1,1558 +0,0 @@ -package testdata - -const ( - NoServerServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[testapi] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = testapi.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "localhost": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - SameAPIServiceNameServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[serviceapi] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = serviceapi.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "localhost": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - SingleServerSingleHostServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[singleserversinglehost] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = singleserversinglehost.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example:8090" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "https://example:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "443") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://example:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "http://[::1]:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - SingleServerSingleHostWithVariablesServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - int_F = flag.String("int", "1", "") - uint_F = flag.String("uint", "1", "") - float32_F = flag.String("float32", "1.1", "") - int32_F = flag.String("int32", "1", "") - int64_F = flag.String("int64", "1", "") - uint32_F = flag.String("uint32", "1", "") - uint64_F = flag.String("uint64", "1", "") - float64_F = flag.String("float64", "1", "") - bool_F = flag.String("bool", "true", "") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[singleserversinglehostwithvariables] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = singleserversinglehostwithvariables.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example-{int}-{uint}-{float32}:8090" - addr = strings.Replace(addr, "{int}", *int_F, -1) - addr = strings.Replace(addr, "{uint}", *uint_F, -1) - addr = strings.Replace(addr, "{float32}", *float32_F, -1) - addr = strings.Replace(addr, "{int32}", *int32_F, -1) - addr = strings.Replace(addr, "{int64}", *int64_F, -1) - addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) - addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) - addr = strings.Replace(addr, "{float64}", *float64_F, -1) - addr = strings.Replace(addr, "{bool}", *bool_F, -1) - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "https://example-{int32}-{int64}-{uint32}-{uint64}-{float64}:80/{bool}" - addr = strings.Replace(addr, "{int}", *int_F, -1) - addr = strings.Replace(addr, "{uint}", *uint_F, -1) - addr = strings.Replace(addr, "{float32}", *float32_F, -1) - addr = strings.Replace(addr, "{int32}", *int32_F, -1) - addr = strings.Replace(addr, "{int64}", *int64_F, -1) - addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) - addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) - addr = strings.Replace(addr, "{float64}", *float64_F, -1) - addr = strings.Replace(addr, "{bool}", *bool_F, -1) - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "443") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServerHostingServiceWithFileServerServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "svc", "Server host (valid values: svc)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[serverhostingservicewithfileserver] ", log.Ltime) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "svc": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: svc)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServerHostingServiceSubsetServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[serverhostingservicesubset] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = serverhostingservicesubset.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example:8090" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServerHostingMultipleServicesServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[serverhostingmultipleservices] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - anotherServiceSvc anotherservice.Service - ) - { - serviceSvc = serverhostingmultipleservices.NewService(logger) - anotherServiceSvc = serverhostingmultipleservices.NewAnotherService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - anotherServiceEndpoints *anotherservice.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - anotherServiceEndpoints = anotherservice.NewEndpoints(anotherServiceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example:8090" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - SingleServerMultipleHostsServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[singleservermultiplehosts] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = singleservermultiplehosts.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example:8090" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - case "stage": - { - addr := "https://example" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "443") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - SingleServerMultipleHostsWithVariablesServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - versionF = flag.String("version", "v1", "Version (valid values: v1, v2)") - domainF = flag.String("domain", "test", "Domain") - portF = flag.String("port", "8080", "Port") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[singleservermultiplehostswithvariables] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = singleservermultiplehostswithvariables.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "dev": - { - addr := "http://example-{version}:8090" - var versionSeen bool - { - for _, v := range []string{"v1", "v2"} { - if v == *versionF { - versionSeen = true - break - } - } - } - if !versionSeen { - logger.Fatalf("invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF) - } - addr = strings.Replace(addr, "{version}", *versionF, -1) - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - case "stage": - { - addr := "https://example-{domain}:{port}" - addr = strings.Replace(addr, "{domain}", *domainF, -1) - addr = strings.Replace(addr, "{port}", *portF, -1) - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "443") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - NamesWithSpacesServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "svc", "Server host (valid values: svc)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[apiwithspaces] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceWithSpacesSvc servicewithspaces.Service - ) - { - serviceWithSpacesSvc = apiwithspaces.NewServiceWithSpaces(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceWithSpacesEndpoints *servicewithspaces.Endpoints - ) - { - serviceWithSpacesEndpoints = servicewithspaces.NewEndpoints(serviceWithSpacesSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "svc": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: svc)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServiceForOnlyHTTPServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[testapi] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = testapi.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "localhost": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServiceForOnlyGRPCServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[testapi] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - ) - { - serviceSvc = testapi.NewService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "localhost": - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` - - ServiceForHTTPAndPartOfGRPCServerMainCode = `func main() { - // Define command line flags, add any other flag required to configure the - // service. - var ( - hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") - domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") - httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") - grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") - secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") - dbgF = flag.Bool("debug", false, "Log request and response bodies") - ) - flag.Parse() - - // Setup logger. Replace logger with your own log package of choice. - var ( - logger *log.Logger - ) - { - logger = log.New(os.Stderr, "[testapi] ", log.Ltime) - } - - // Initialize the services. - var ( - serviceSvc service.Service - anotherServiceSvc anotherservice.Service - ) - { - serviceSvc = testapi.NewService(logger) - anotherServiceSvc = testapi.NewAnotherService(logger) - } - - // Wrap the services in endpoints that can be invoked from other services - // potentially running in different processes. - var ( - serviceEndpoints *service.Endpoints - anotherServiceEndpoints *anotherservice.Endpoints - ) - { - serviceEndpoints = service.NewEndpoints(serviceSvc) - anotherServiceEndpoints = anotherservice.NewEndpoints(anotherServiceSvc) - } - - // Create channel used by both the signal handler and server goroutines - // to notify the main goroutine when to stop the server. - errc := make(chan error) - - // Setup interrupt handler. This optional step configures the process so - // that SIGINT and SIGTERM signals cause the services to stop gracefully. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - - // Start the servers and send errors (if any) to the error channel. - switch *hostF { - case "localhost": - { - addr := "http://localhost:80" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "https" - } - if *domainF != "" { - u.Host = *domainF - } - if *httpPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *httpPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "80") - } - handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF) - } - - { - addr := "grpc://localhost:8080" - u, err := url.Parse(addr) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", addr, err) - } - if *secureF { - u.Scheme = "grpcs" - } - if *domainF != "" { - u.Host = *domainF - } - if *grpcPortF != "" { - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) - } - u.Host = net.JoinHostPort(h, *grpcPortF) - } else if u.Port() == "" { - u.Host = net.JoinHostPort(u.Host, "8080") - } - handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF) - } - - default: - logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) - } - - // Wait for signal. - logger.Printf("exiting (%v)", <-errc) - - // Send cancellation signal to the goroutines. - cancel() - - wg.Wait() - logger.Println("exited") -} -` -) diff --git a/codegen/example/testdata/server-no-server.golden b/codegen/example/testdata/server-no-server.golden new file mode 100644 index 0000000000..203682e150 --- /dev/null +++ b/codegen/example/testdata/server-no-server.golden @@ -0,0 +1,123 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = testapi.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "localhost": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-same-api-service-name.golden b/codegen/example/testdata/server-same-api-service-name.golden new file mode 100644 index 0000000000..f10a89ed9e --- /dev/null +++ b/codegen/example/testdata/server-same-api-service-name.golden @@ -0,0 +1,123 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = serviceapi.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "localhost": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-sercice-for-only-grpc.golden b/codegen/example/testdata/server-sercice-for-only-grpc.golden new file mode 100644 index 0000000000..066f1130e7 --- /dev/null +++ b/codegen/example/testdata/server-sercice-for-only-grpc.golden @@ -0,0 +1,99 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = testapi.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "localhost": + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-server-hosting-multiple-services.golden b/codegen/example/testdata/server-server-hosting-multiple-services.golden new file mode 100644 index 0000000000..d24ffcee06 --- /dev/null +++ b/codegen/example/testdata/server-server-hosting-multiple-services.golden @@ -0,0 +1,129 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + anotherServiceSvc anotherservice.Service + ) + { + serviceSvc = serverhostingmultipleservices.NewService() + anotherServiceSvc = serverhostingmultipleservices.NewAnotherService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + anotherServiceEndpoints *anotherservice.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + anotherServiceEndpoints = anotherservice.NewEndpoints(anotherServiceSvc) + anotherServiceEndpoints.Use(debug.LogPayloads()) + anotherServiceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example:8090" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-server-hosting-service-subset.golden b/codegen/example/testdata/server-server-hosting-service-subset.golden new file mode 100644 index 0000000000..8f8acec3cd --- /dev/null +++ b/codegen/example/testdata/server-server-hosting-service-subset.golden @@ -0,0 +1,123 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = serverhostingservicesubset.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example:8090" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-server-hosting-service-with-file-server.golden b/codegen/example/testdata/server-server-hosting-service-with-file-server.golden new file mode 100644 index 0000000000..2e62b9eb34 --- /dev/null +++ b/codegen/example/testdata/server-server-hosting-service-with-file-server.golden @@ -0,0 +1,79 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "svc", "Server host (valid values: svc)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "svc": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: svc)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-service-for-http-and-part-of-grpc.golden b/codegen/example/testdata/server-service-for-http-and-part-of-grpc.golden new file mode 100644 index 0000000000..06abe18c71 --- /dev/null +++ b/codegen/example/testdata/server-service-for-http-and-part-of-grpc.golden @@ -0,0 +1,129 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + anotherServiceSvc anotherservice.Service + ) + { + serviceSvc = testapi.NewService() + anotherServiceSvc = testapi.NewAnotherService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + anotherServiceEndpoints *anotherservice.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + anotherServiceEndpoints = anotherservice.NewEndpoints(anotherServiceSvc) + anotherServiceEndpoints.Use(debug.LogPayloads()) + anotherServiceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "localhost": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-service-for-only-http.golden b/codegen/example/testdata/server-service-for-only-http.golden new file mode 100644 index 0000000000..b75f9bd6b6 --- /dev/null +++ b/codegen/example/testdata/server-service-for-only-http.golden @@ -0,0 +1,98 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = testapi.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "localhost": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-service-name-with-spaces.golden b/codegen/example/testdata/server-service-name-with-spaces.golden new file mode 100644 index 0000000000..75af0e5c18 --- /dev/null +++ b/codegen/example/testdata/server-service-name-with-spaces.golden @@ -0,0 +1,123 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "svc", "Server host (valid values: svc)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceWithSpacesSvc servicewithspaces.Service + ) + { + serviceWithSpacesSvc = apiwithspaces.NewServiceWithSpaces() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceWithSpacesEndpoints *servicewithspaces.Endpoints + ) + { + serviceWithSpacesEndpoints = servicewithspaces.NewEndpoints(serviceWithSpacesSvc) + serviceWithSpacesEndpoints.Use(debug.LogPayloads()) + serviceWithSpacesEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "svc": + { + addr := "http://localhost:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: svc)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-single-server-multiple-hosts-with-variables.golden b/codegen/example/testdata/server-single-server-multiple-hosts-with-variables.golden new file mode 100644 index 0000000000..eba9a19300 --- /dev/null +++ b/codegen/example/testdata/server-single-server-multiple-hosts-with-variables.golden @@ -0,0 +1,141 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + versionF = flag.String("version", "v1", "Version (valid values: v1, v2)") + domainF = flag.String("domain", "test", "Domain") + portF = flag.String("port", "8080", "Port") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = singleservermultiplehostswithvariables.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example-{version}:8090" + var versionSeen bool + { + for _, v := range []string{"v1", "v2"} { + if v == *versionF { + versionSeen = true + break + } + } + } + if !versionSeen { + log.Fatalf(ctx, "invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF) + } + addr = strings.Replace(addr, "{version}", *versionF, -1) + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + case "stage": + { + addr := "https://example-{domain}:{port}" + addr = strings.Replace(addr, "{domain}", *domainF, -1) + addr = strings.Replace(addr, "{port}", *portF, -1) + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "443") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-single-server-multiple-hosts.golden b/codegen/example/testdata/server-single-server-multiple-hosts.golden new file mode 100644 index 0000000000..5ad3ad26de --- /dev/null +++ b/codegen/example/testdata/server-single-server-multiple-hosts.golden @@ -0,0 +1,123 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev, stage)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = singleservermultiplehosts.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example:8090" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + case "stage": + { + addr := "https://example" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "443") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-single-server-single-host-with-variables.golden b/codegen/example/testdata/server-single-server-single-host-with-variables.golden new file mode 100644 index 0000000000..200b7918f0 --- /dev/null +++ b/codegen/example/testdata/server-single-server-single-host-with-variables.golden @@ -0,0 +1,149 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + int_F = flag.String("int", "1", "") + uint_F = flag.String("uint", "1", "") + float32_F = flag.String("float32", "1.1", "") + int32_F = flag.String("int32", "1", "") + int64_F = flag.String("int64", "1", "") + uint32_F = flag.String("uint32", "1", "") + uint64_F = flag.String("uint64", "1", "") + float64_F = flag.String("float64", "1", "") + bool_F = flag.String("bool", "true", "") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = singleserversinglehostwithvariables.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example-{int}-{uint}-{float32}:8090" + addr = strings.Replace(addr, "{int}", *int_F, -1) + addr = strings.Replace(addr, "{uint}", *uint_F, -1) + addr = strings.Replace(addr, "{float32}", *float32_F, -1) + addr = strings.Replace(addr, "{int32}", *int32_F, -1) + addr = strings.Replace(addr, "{int64}", *int64_F, -1) + addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) + addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) + addr = strings.Replace(addr, "{float64}", *float64_F, -1) + addr = strings.Replace(addr, "{bool}", *bool_F, -1) + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "https://example-{int32}-{int64}-{uint32}-{uint64}-{float64}:80/{bool}" + addr = strings.Replace(addr, "{int}", *int_F, -1) + addr = strings.Replace(addr, "{uint}", *uint_F, -1) + addr = strings.Replace(addr, "{float32}", *float32_F, -1) + addr = strings.Replace(addr, "{int32}", *int32_F, -1) + addr = strings.Replace(addr, "{int64}", *int64_F, -1) + addr = strings.Replace(addr, "{uint32}", *uint32_F, -1) + addr = strings.Replace(addr, "{uint64}", *uint64_F, -1) + addr = strings.Replace(addr, "{float64}", *float64_F, -1) + addr = strings.Replace(addr, "{bool}", *bool_F, -1) + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "443") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/codegen/example/testdata/server-single-server-single-host.golden b/codegen/example/testdata/server-single-server-single-host.golden new file mode 100644 index 0000000000..f921b9a035 --- /dev/null +++ b/codegen/example/testdata/server-single-server-single-host.golden @@ -0,0 +1,171 @@ +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "dev", "Server host (valid values: dev)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + format := log.FormatJSON + if log.IsTerminal() { + format = log.FormatTerminal + } + ctx := log.Context(context.Background(), log.WithFormat(format)) + if *dbgF { + ctx = log.Context(ctx, log.WithDebug()) + log.Debugf(ctx, "debug logs enabled") + } + log.Print(ctx, log.KV{K: "http-port", V: *httpPortF}) + + // Initialize the services. + var ( + serviceSvc service.Service + ) + { + serviceSvc = singleserversinglehost.NewService() + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + serviceEndpoints *service.Endpoints + ) + { + serviceEndpoints = service.NewEndpoints(serviceSvc) + serviceEndpoints.Use(debug.LogPayloads()) + serviceEndpoints.Use(log.Endpoint) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "dev": + { + addr := "http://example:8090" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "https://example:80" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "443") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "grpc://example:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *grpcPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "8080") + } + handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + { + addr := "http://[::1]:8080" + u, err := url.Parse(addr) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", addr, err) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h, _, err := net.SplitHostPort(u.Host) + if err != nil { + log.Fatalf(ctx, "invalid URL %#v: %s\n", u.Host, err) + } + u.Host = net.JoinHostPort(h, *httpPortF) + } else if u.Port() == "" { + u.Host = net.JoinHostPort(u.Host, "80") + } + handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, *dbgF) + } + + default: + log.Fatalf(ctx, "invalid host argument: %q (valid hosts: dev)\n", *hostF) + } + + // Wait for signal. + log.Printf(ctx, "exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + log.Printf(ctx, "exited") +} diff --git a/grpc/codegen/client-no-server-pkgpath.golden b/grpc/codegen/client-no-server-pkgpath.golden new file mode 100644 index 0000000000..d593940778 --- /dev/null +++ b/grpc/codegen/client-no-server-pkgpath.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "my/pkg/path/grpc/cli/test_api" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/client-no-server.golden b/grpc/codegen/client-no-server.golden new file mode 100644 index 0000000000..8d18d0d9aa --- /dev/null +++ b/grpc/codegen/client-no-server.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "grpc/cli/test_api" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/client-server-hosting-multiple-services-pkgpath.golden b/grpc/codegen/client-server-hosting-multiple-services-pkgpath.golden new file mode 100644 index 0000000000..6a85da9f60 --- /dev/null +++ b/grpc/codegen/client-server-hosting-multiple-services-pkgpath.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "my/pkg/path/grpc/cli/single_host" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/client-server-hosting-multiple-services.golden b/grpc/codegen/client-server-hosting-multiple-services.golden new file mode 100644 index 0000000000..910d906213 --- /dev/null +++ b/grpc/codegen/client-server-hosting-multiple-services.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "grpc/cli/single_host" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/client-server-hosting-service-subset-pkgpath.golden b/grpc/codegen/client-server-hosting-service-subset-pkgpath.golden new file mode 100644 index 0000000000..6a85da9f60 --- /dev/null +++ b/grpc/codegen/client-server-hosting-service-subset-pkgpath.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "my/pkg/path/grpc/cli/single_host" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/client-server-hosting-service-subset.golden b/grpc/codegen/client-server-hosting-service-subset.golden new file mode 100644 index 0000000000..910d906213 --- /dev/null +++ b/grpc/codegen/client-server-hosting-service-subset.golden @@ -0,0 +1,17 @@ +import ( + "fmt" + cli "grpc/cli/single_host" + "os" + + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/grpc/codegen/example_cli_test.go b/grpc/codegen/example_cli_test.go index 4b4841b374..9a9b59a787 100644 --- a/grpc/codegen/example_cli_test.go +++ b/grpc/codegen/example_cli_test.go @@ -4,11 +4,11 @@ import ( "bytes" "testing" + "github.com/stretchr/testify/require" "goa.design/goa/v3/codegen" "goa.design/goa/v3/codegen/example" ctestdata "goa.design/goa/v3/codegen/example/testdata" "goa.design/goa/v3/expr" - "goa.design/goa/v3/grpc/codegen/testdata" ) func TestExampleCLIFiles(t *testing.T) { @@ -16,14 +16,13 @@ func TestExampleCLIFiles(t *testing.T) { Name string DSL func() PkgPath string - Code string }{ - {"no-server", ctestdata.NoServerDSL, "", testdata.ExampleCLIImport + "\n" + testdata.ExampleCLICode}, - {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL, "", testdata.ExampleSingleHostCLIImport + "\n" + testdata.ExampleCLICode}, - {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL, "", testdata.ExampleSingleHostCLIImport + "\n" + testdata.ExampleCLICode}, - {"no-server-pkgpath", ctestdata.NoServerDSL, "my/pkg/path", testdata.ExamplePkgPathCLIImport + "\n" + testdata.ExampleCLICode}, - {"server-hosting-service-subset-pkgpath", ctestdata.ServerHostingServiceSubsetDSL, "my/pkg/path", testdata.ExampleSingleHostPkgPathCLIImport + "\n" + testdata.ExampleCLICode}, - {"server-hosting-multiple-services-pkgpath", ctestdata.ServerHostingMultipleServicesDSL, "my/pkg/path", testdata.ExampleSingleHostPkgPathCLIImport + "\n" + testdata.ExampleCLICode}, + {"no-server", ctestdata.NoServerDSL, ""}, + {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL, ""}, + {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL, ""}, + {"no-server-pkgpath", ctestdata.NoServerDSL, "my/pkg/path"}, + {"server-hosting-service-subset-pkgpath", ctestdata.ServerHostingServiceSubsetDSL, "my/pkg/path"}, + {"server-hosting-multiple-services-pkgpath", ctestdata.ServerHostingMultipleServicesDSL, "my/pkg/path"}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { @@ -31,22 +30,14 @@ func TestExampleCLIFiles(t *testing.T) { example.Servers = make(example.ServersData) codegen.RunDSL(t, c.DSL) fs := ExampleCLIFiles(c.PkgPath, expr.Root) - if len(fs) == 0 { - t.Fatalf("got 0 files, expected 1") - } - if len(fs[0].SectionTemplates) == 0 { - t.Fatalf("got 0 sections, expected at least 1") - } + require.Greater(t, len(fs), 0) + require.Greater(t, len(fs[0].SectionTemplates), 0) var buf bytes.Buffer for _, s := range fs[0].SectionTemplates { - if err := s.Write(&buf); err != nil { - t.Fatal(err) - } + require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, buf.String()) - if code != c.Code { - t.Errorf("invalid code for %s: got\n%s\ngot vs. expected:\n%s", fs[0].Path, code, codegen.Diff(t, code, c.Code)) - } + compareOrUpdateGolden(t, code, "client-"+c.Name+".golden") }) } } diff --git a/grpc/codegen/example_server.go b/grpc/codegen/example_server.go index 4d3096d2fb..f4a712bbed 100644 --- a/grpc/codegen/example_server.go +++ b/grpc/codegen/example_server.go @@ -44,14 +44,13 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co { specs = []*codegen.ImportSpec{ {Path: "context"}, - {Path: "log"}, + {Path: "fmt"}, {Path: "net"}, {Path: "net/url"}, - {Path: "os"}, {Path: "sync"}, - codegen.GoaImport("middleware"), codegen.GoaNamedImport("grpc", "goagrpc"), - codegen.GoaNamedImport("grpc/middleware", "grpcmdlwr"), + {Path: "goa.design/clue/debug"}, + {Path: "goa.design/clue/log"}, {Path: "google.golang.org/grpc"}, {Path: "google.golang.org/grpc/reflection"}, } @@ -106,9 +105,6 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co Data: map[string]any{ "Services": svcdata, }, - }, { - Name: "server-grpc-logger", - Source: readTemplate("server_grpc_logger"), }, { Name: "server-grpc-init", Source: readTemplate("server_grpc_init"), diff --git a/grpc/codegen/example_server_test.go b/grpc/codegen/example_server_test.go index b982a2cf6f..57e6c6d56c 100644 --- a/grpc/codegen/example_server_test.go +++ b/grpc/codegen/example_server_test.go @@ -2,25 +2,50 @@ package codegen import ( "bytes" + "flag" + "os" + "path/filepath" + "runtime" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "goa.design/goa/v3/codegen" "goa.design/goa/v3/codegen/example" ctestdata "goa.design/goa/v3/codegen/example/testdata" "goa.design/goa/v3/codegen/service" "goa.design/goa/v3/expr" - "goa.design/goa/v3/grpc/codegen/testdata" ) +var updateGolden = false + +func init() { + flag.BoolVar(&updateGolden, "w", false, "update golden files") +} + +func compareOrUpdateGolden(t *testing.T, code, golden string) { + t.Helper() + if updateGolden { + require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0750)) + require.NoError(t, os.WriteFile(golden, []byte(code), 0640)) + return + } + data, err := os.ReadFile(golden) + require.NoError(t, err) + if runtime.GOOS == "windows" { + data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n")) + } + assert.Equal(t, string(data), code) +} + func TestExampleServerFiles(t *testing.T) { cases := []struct { Name string DSL func() - Code string }{ - {"no-server", ctestdata.NoServerDSL, testdata.NoServerServerHandleCode}, - {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL, testdata.ServerHostingServiceSubsetServerHandleCode}, - {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL, testdata.ServerHostingMultipleServicesServerHandleCode}, + {"no-server", ctestdata.NoServerDSL}, + {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL}, + {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { @@ -30,22 +55,15 @@ func TestExampleServerFiles(t *testing.T) { example.Servers = make(example.ServersData) codegen.RunDSL(t, c.DSL) fs := ExampleServerFiles("", expr.Root) - if len(fs) == 0 { - t.Fatalf("got 0 files, expected 1") - } - if len(fs[0].SectionTemplates) == 0 { - t.Fatalf("got 0 sections, expected at least 1") - } + require.Greater(t, len(fs), 0) + require.Greater(t, len(fs[0].SectionTemplates), 0) var buf bytes.Buffer for _, s := range fs[0].SectionTemplates[1:] { - if err := s.Write(&buf); err != nil { - t.Fatal(err) - } + require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, "package foo\n"+buf.String()) - if code != c.Code { - t.Errorf("invalid code for %s: got\n%s\ngot vs. expected:\n%s", fs[0].Path, code, codegen.Diff(t, code, c.Code)) - } + golden := filepath.Join("testdata", "server-"+c.Name+".golden") + compareOrUpdateGolden(t, code, golden) }) } } diff --git a/grpc/codegen/proto_test.go b/grpc/codegen/proto_test.go index 19051d7d22..d3441e4119 100644 --- a/grpc/codegen/proto_test.go +++ b/grpc/codegen/proto_test.go @@ -47,7 +47,7 @@ func TestProtoFiles(t *testing.T) { if runtime.GOOS == "windows" { code = strings.ReplaceAll(code, "\r\n", "\n") } - assert.Equal(t, code, c.Code) + assert.Equal(t, c.Code, code) fpath := codegen.CreateTempFile(t, code) assert.NoError(t, protoc(fpath, nil), "error occurred when compiling proto file %q", fpath) }) diff --git a/grpc/codegen/templates/server_grpc_end.go.tpl b/grpc/codegen/templates/server_grpc_end.go.tpl index 247a32637a..f99557c205 100644 --- a/grpc/codegen/templates/server_grpc_end.go.tpl +++ b/grpc/codegen/templates/server_grpc_end.go.tpl @@ -9,12 +9,15 @@ if err != nil { errc <- err } + if lis == nil { + errc <- fmt.Errorf("failed to listen on %q", u.Host) + } logger.Printf("gRPC server listening on %q", u.Host) errc <- srv.Serve(lis) }() <-ctx.Done() - logger.Printf("shutting down gRPC server at %q", u.Host) + log.Printf(ctx, "shutting down gRPC server at %q", u.Host) srv.Stop() }() } diff --git a/grpc/codegen/templates/server_grpc_logger.go.tpl b/grpc/codegen/templates/server_grpc_logger.go.tpl deleted file mode 100644 index aefac80956..0000000000 --- a/grpc/codegen/templates/server_grpc_logger.go.tpl +++ /dev/null @@ -1,9 +0,0 @@ - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - diff --git a/grpc/codegen/templates/server_grpc_register.go.tpl b/grpc/codegen/templates/server_grpc_register.go.tpl index d4b1e1ebe6..8f95d0dc7d 100644 --- a/grpc/codegen/templates/server_grpc_register.go.tpl +++ b/grpc/codegen/templates/server_grpc_register.go.tpl @@ -1,17 +1,19 @@ - // Initialize gRPC server with the middleware. - srv := grpc.NewServer( - grpc.ChainUnaryInterceptor( - grpcmdlwr.UnaryRequestID(), - grpcmdlwr.UnaryServerLog(adapter), - ), - {{- if needStream .Services }} - grpc.ChainStreamInterceptor( - grpcmdlwr.StreamRequestID(), - grpcmdlwr.StreamServerLog(adapter), - ), + // Create interceptor which sets up the logger in each request context. + chain := grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx)) + if dbg { + // Log request and response content if debug logs are enabled. + chain = grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx), debug.UnaryServerInterceptor()) + } + {{- if needStream .Services}} + streamchain := grpc.ChainStreamInterceptor(log.StreamServerInterceptor(ctx)) + if dbg { + streamchain = grpc.ChainStreamInterceptor(log.StreamServerInterceptor(ctx), debug.StreamServerInterceptor()) + } {{- end }} - ) + + // Initialize gRPC server + srv := grpc.NewServer(chain{{ if needStream .Sevices }}, streamchain{{ end }}) // Register the servers. {{- range .Services }} @@ -20,7 +22,7 @@ for svc, info := range srv.GetServiceInfo() { for _, m := range info.Methods { - logger.Printf("serving gRPC method %s", svc + "/" + m.Name) + log.Printf(ctx, "serving gRPC method %s", svc + "/" + m.Name) } } diff --git a/grpc/codegen/templates/server_grpc_start.go.tpl b/grpc/codegen/templates/server_grpc_start.go.tpl index 038aa1b0d7..c963aed74b 100644 --- a/grpc/codegen/templates/server_grpc_start.go.tpl +++ b/grpc/codegen/templates/server_grpc_start.go.tpl @@ -1,2 +1,2 @@ {{ comment "handleGRPCServer starts configures and starts a gRPC server on the given URL. It shuts down the server if any error is received in the error channel." }} -func handleGRPCServer(ctx context.Context, u *url.URL{{ range $.Services }}{{ if .Service.Methods }}, {{ .Service.VarName }}Endpoints *{{ .Service.PkgName }}.Endpoints{{ end }}{{ end }}, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { +func handleGRPCServer(ctx context.Context, u *url.URL{{ range $.Services }}{{ if .Service.Methods }}, {{ .Service.VarName }}Endpoints *{{ .Service.PkgName }}.Endpoints{{ end }}{{ end }}, wg *sync.WaitGroup, errc chan error, dbg bool) { diff --git a/grpc/codegen/testdata/example_code.go b/grpc/codegen/testdata/example_code.go deleted file mode 100644 index 29851bd0a2..0000000000 --- a/grpc/codegen/testdata/example_code.go +++ /dev/null @@ -1,252 +0,0 @@ -package testdata - -const NoServerServerHandleCode = `// handleGRPCServer starts configures and starts a gRPC server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to gRPC requests and - // responses. - var ( - serviceServer *servicesvr.Server - ) - { - serviceServer = servicesvr.New(serviceEndpoints, nil) - } - - // Initialize gRPC server with the middleware. - srv := grpc.NewServer( - grpc.ChainUnaryInterceptor( - grpcmdlwr.UnaryRequestID(), - grpcmdlwr.UnaryServerLog(adapter), - ), - ) - - // Register the servers. - servicepb.RegisterServiceServer(srv, serviceServer) - - for svc, info := range srv.GetServiceInfo() { - for _, m := range info.Methods { - logger.Printf("serving gRPC method %s", svc+"/"+m.Name) - } - } - - // Register the server reflection service on the server. - // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. - reflection.Register(srv) - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start gRPC server in a separate goroutine. - go func() { - lis, err := net.Listen("tcp", u.Host) - if err != nil { - errc <- err - } - logger.Printf("gRPC server listening on %q", u.Host) - errc <- srv.Serve(lis) - }() - - <-ctx.Done() - logger.Printf("shutting down gRPC server at %q", u.Host) - srv.Stop() - }() -} -` - -const ServerHostingServiceSubsetServerHandleCode = `// handleGRPCServer starts configures and starts a gRPC server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to gRPC requests and - // responses. - var ( - serviceServer *servicesvr.Server - ) - { - serviceServer = servicesvr.New(serviceEndpoints, nil) - } - - // Initialize gRPC server with the middleware. - srv := grpc.NewServer( - grpc.ChainUnaryInterceptor( - grpcmdlwr.UnaryRequestID(), - grpcmdlwr.UnaryServerLog(adapter), - ), - ) - - // Register the servers. - servicepb.RegisterServiceServer(srv, serviceServer) - - for svc, info := range srv.GetServiceInfo() { - for _, m := range info.Methods { - logger.Printf("serving gRPC method %s", svc+"/"+m.Name) - } - } - - // Register the server reflection service on the server. - // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. - reflection.Register(srv) - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start gRPC server in a separate goroutine. - go func() { - lis, err := net.Listen("tcp", u.Host) - if err != nil { - errc <- err - } - logger.Printf("gRPC server listening on %q", u.Host) - errc <- srv.Serve(lis) - }() - - <-ctx.Done() - logger.Printf("shutting down gRPC server at %q", u.Host) - srv.Stop() - }() -} -` - -const ServerHostingMultipleServicesServerHandleCode = `// handleGRPCServer starts configures and starts a gRPC server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, anotherServiceEndpoints *anotherservice.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to gRPC requests and - // responses. - var ( - serviceServer *servicesvr.Server - anotherServiceServer *anotherservicesvr.Server - ) - { - serviceServer = servicesvr.New(serviceEndpoints, nil) - anotherServiceServer = anotherservicesvr.New(anotherServiceEndpoints, nil) - } - - // Initialize gRPC server with the middleware. - srv := grpc.NewServer( - grpc.ChainUnaryInterceptor( - grpcmdlwr.UnaryRequestID(), - grpcmdlwr.UnaryServerLog(adapter), - ), - ) - - // Register the servers. - servicepb.RegisterServiceServer(srv, serviceServer) - another_servicepb.RegisterAnotherServiceServer(srv, anotherServiceServer) - - for svc, info := range srv.GetServiceInfo() { - for _, m := range info.Methods { - logger.Printf("serving gRPC method %s", svc+"/"+m.Name) - } - } - - // Register the server reflection service on the server. - // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. - reflection.Register(srv) - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start gRPC server in a separate goroutine. - go func() { - lis, err := net.Listen("tcp", u.Host) - if err != nil { - errc <- err - } - logger.Printf("gRPC server listening on %q", u.Host) - errc <- srv.Serve(lis) - }() - - <-ctx.Done() - logger.Printf("shutting down gRPC server at %q", u.Host) - srv.Stop() - }() -} -` - -const ExampleCLIImport = `import ( - "fmt" - cli "grpc/cli/test_api" - "os" - - goa "goa.design/goa/v3/pkg" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) -` - -const ExampleSingleHostCLIImport = `import ( - "fmt" - cli "grpc/cli/single_host" - "os" - - goa "goa.design/goa/v3/pkg" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) -` - -const ExamplePkgPathCLIImport = `import ( - "fmt" - cli "my/pkg/path/grpc/cli/test_api" - "os" - - goa "goa.design/goa/v3/pkg" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) -` - -const ExampleSingleHostPkgPathCLIImport = `import ( - "fmt" - cli "my/pkg/path/grpc/cli/single_host" - "os" - - goa "goa.design/goa/v3/pkg" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) -` - -const ExampleCLICode = `func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { - conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) - } - return cli.ParseEndpoint(conn) -} -` diff --git a/grpc/codegen/testdata/server-no-server.golden b/grpc/codegen/testdata/server-no-server.golden new file mode 100644 index 0000000000..6e0e8f444f --- /dev/null +++ b/grpc/codegen/testdata/server-no-server.golden @@ -0,0 +1,60 @@ +// handleGRPCServer starts configures and starts a gRPC server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to gRPC requests and + // responses. + var ( + serviceServer *servicesvr.Server + ) + { + serviceServer = servicesvr.New(serviceEndpoints, nil) + } + + // Create interceptor which sets up the logger in each request context. + chain := grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx)) + if dbg { + // Log request and response content if debug logs are enabled. + chain = grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx), debug.UnaryServerInterceptor()) + } + + // Initialize gRPC server + srv := grpc.NewServer(chain) + + // Register the servers. + servicepb.RegisterServiceServer(srv, serviceServer) + + for svc, info := range srv.GetServiceInfo() { + for _, m := range info.Methods { + log.Printf(ctx, "serving gRPC method %s", svc+"/"+m.Name) + } + } + + // Register the server reflection service on the server. + // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. + reflection.Register(srv) + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start gRPC server in a separate goroutine. + go func() { + lis, err := net.Listen("tcp", u.Host) + if err != nil { + errc <- err + } + if lis == nil { + errc <- fmt.Errorf("failed to listen on %q", u.Host) + } + logger.Printf("gRPC server listening on %q", u.Host) + errc <- srv.Serve(lis) + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down gRPC server at %q", u.Host) + srv.Stop() + }() +} diff --git a/grpc/codegen/testdata/server-server-hosting-multiple-services.golden b/grpc/codegen/testdata/server-server-hosting-multiple-services.golden new file mode 100644 index 0000000000..a693a56143 --- /dev/null +++ b/grpc/codegen/testdata/server-server-hosting-multiple-services.golden @@ -0,0 +1,63 @@ +// handleGRPCServer starts configures and starts a gRPC server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, anotherServiceEndpoints *anotherservice.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to gRPC requests and + // responses. + var ( + serviceServer *servicesvr.Server + anotherServiceServer *anotherservicesvr.Server + ) + { + serviceServer = servicesvr.New(serviceEndpoints, nil) + anotherServiceServer = anotherservicesvr.New(anotherServiceEndpoints, nil) + } + + // Create interceptor which sets up the logger in each request context. + chain := grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx)) + if dbg { + // Log request and response content if debug logs are enabled. + chain = grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx), debug.UnaryServerInterceptor()) + } + + // Initialize gRPC server + srv := grpc.NewServer(chain) + + // Register the servers. + servicepb.RegisterServiceServer(srv, serviceServer) + another_servicepb.RegisterAnotherServiceServer(srv, anotherServiceServer) + + for svc, info := range srv.GetServiceInfo() { + for _, m := range info.Methods { + log.Printf(ctx, "serving gRPC method %s", svc+"/"+m.Name) + } + } + + // Register the server reflection service on the server. + // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. + reflection.Register(srv) + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start gRPC server in a separate goroutine. + go func() { + lis, err := net.Listen("tcp", u.Host) + if err != nil { + errc <- err + } + if lis == nil { + errc <- fmt.Errorf("failed to listen on %q", u.Host) + } + logger.Printf("gRPC server listening on %q", u.Host) + errc <- srv.Serve(lis) + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down gRPC server at %q", u.Host) + srv.Stop() + }() +} diff --git a/grpc/codegen/testdata/server-server-hosting-service-subset.golden b/grpc/codegen/testdata/server-server-hosting-service-subset.golden new file mode 100644 index 0000000000..6e0e8f444f --- /dev/null +++ b/grpc/codegen/testdata/server-server-hosting-service-subset.golden @@ -0,0 +1,60 @@ +// handleGRPCServer starts configures and starts a gRPC server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleGRPCServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to gRPC requests and + // responses. + var ( + serviceServer *servicesvr.Server + ) + { + serviceServer = servicesvr.New(serviceEndpoints, nil) + } + + // Create interceptor which sets up the logger in each request context. + chain := grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx)) + if dbg { + // Log request and response content if debug logs are enabled. + chain = grpc.ChainUnaryInterceptor(log.UnaryServerInterceptor(ctx), debug.UnaryServerInterceptor()) + } + + // Initialize gRPC server + srv := grpc.NewServer(chain) + + // Register the servers. + servicepb.RegisterServiceServer(srv, serviceServer) + + for svc, info := range srv.GetServiceInfo() { + for _, m := range info.Methods { + log.Printf(ctx, "serving gRPC method %s", svc+"/"+m.Name) + } + } + + // Register the server reflection service on the server. + // See https://grpc.github.io/grpc/core/md_doc_server-reflection.html. + reflection.Register(srv) + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start gRPC server in a separate goroutine. + go func() { + lis, err := net.Listen("tcp", u.Host) + if err != nil { + errc <- err + } + if lis == nil { + errc <- fmt.Errorf("failed to listen on %q", u.Host) + } + logger.Printf("gRPC server listening on %q", u.Host) + errc <- srv.Serve(lis) + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down gRPC server at %q", u.Host) + srv.Stop() + }() +} diff --git a/http/codegen/example_cli_test.go b/http/codegen/example_cli_test.go index ab81c138ca..c6de2bae7f 100644 --- a/http/codegen/example_cli_test.go +++ b/http/codegen/example_cli_test.go @@ -2,9 +2,9 @@ package codegen import ( "bytes" + "path/filepath" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "goa.design/goa/v3/codegen" @@ -18,13 +18,12 @@ func TestExampleCLIFiles(t *testing.T) { cases := []struct { Name string DSL func() - Code string }{ - {"no-server", ctestdata.NoServerDSL, testdata.ExampleCLICode}, - {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL, testdata.ExampleCLICode}, - {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL, testdata.ExampleCLICode}, - {"streaming", testdata.StreamingResultDSL, testdata.StreamingExampleCLICode}, - {"streaming-multiple-services", testdata.StreamingMultipleServicesDSL, testdata.StreamingMultipleServicesExampleCLICode}, + {"no-server", ctestdata.NoServerDSL}, + {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL}, + {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL}, + {"streaming", testdata.StreamingResultDSL}, + {"streaming-multiple-services", testdata.StreamingMultipleServicesDSL}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { @@ -39,7 +38,8 @@ func TestExampleCLIFiles(t *testing.T) { require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, "package foo\n"+buf.String()) - assert.Equal(t, c.Code, code) + golden := filepath.Join("testdata", "client-"+c.Name+".golden") + compareOrUpdateGolden(t, code, golden) }) } } diff --git a/http/codegen/example_server.go b/http/codegen/example_server.go index df469eb14a..ffa9835af7 100644 --- a/http/codegen/example_server.go +++ b/http/codegen/example_server.go @@ -33,14 +33,14 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co fpath := filepath.Join("cmd", svrdata.Dir, "http.go") specs := []*codegen.ImportSpec{ {Path: "context"}, - {Path: "log"}, {Path: "net/http"}, {Path: "net/url"}, {Path: "os"}, {Path: "sync"}, {Path: "time"}, codegen.GoaNamedImport("http", "goahttp"), - codegen.GoaNamedImport("http/middleware", "httpmdlwr"), + {Path: "goa.design/clue/debug"}, + {Path: "goa.design/clue/log"}, codegen.GoaImport("middleware"), {Path: "github.com/gorilla/websocket"}, } @@ -90,10 +90,6 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co "Services": svcdata, }, }, - { - Name: "server-http-logger", - Source: readTemplate("server_logger"), - }, { Name: "server-http-encoding", Source: readTemplate("server_encoding"), diff --git a/http/codegen/example_server_test.go b/http/codegen/example_server_test.go index a358c34670..ffd6e1e7be 100644 --- a/http/codegen/example_server_test.go +++ b/http/codegen/example_server_test.go @@ -2,6 +2,10 @@ package codegen import ( "bytes" + "flag" + "os" + "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -15,6 +19,27 @@ import ( "goa.design/goa/v3/http/codegen/testdata" ) +var updateGolden = false + +func init() { + flag.BoolVar(&updateGolden, "w", false, "update golden files") +} + +func compareOrUpdateGolden(t *testing.T, code, golden string) { + t.Helper() + if updateGolden { + require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0750)) + require.NoError(t, os.WriteFile(golden, []byte(code), 0640)) + return + } + data, err := os.ReadFile(golden) + require.NoError(t, err) + if runtime.GOOS == "windows" { + data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n")) + } + assert.Equal(t, string(data), code) +} + func TestExampleServerFiles(t *testing.T) { t.Run("package name check", func(t *testing.T) { cases := []struct { @@ -59,13 +84,12 @@ func TestExampleServerFiles(t *testing.T) { cases := []struct { Name string DSL func() - Code string }{ - {"no-server", ctestdata.NoServerDSL, testdata.NoServerServerHandleCode}, - {"server-hosting-service-with-file-server", ctestdata.ServerHostingServiceWithFileServerDSL, testdata.ServerHostingServiceWithFileServerHandlerCode}, - {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL, testdata.ServerHostingServiceSubsetServerHandleCode}, - {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL, testdata.ServerHostingMultipleServicesServerHandleCode}, - {"streaming", testdata.StreamingMultipleServicesDSL, testdata.StreamingServerHandleCode}, + {"no-server", ctestdata.NoServerDSL}, + {"server-hosting-service-with-file-server", ctestdata.ServerHostingServiceWithFileServerDSL}, + {"server-hosting-service-subset", ctestdata.ServerHostingServiceSubsetDSL}, + {"server-hosting-multiple-services", ctestdata.ServerHostingMultipleServicesDSL}, + {"streaming", testdata.StreamingMultipleServicesDSL}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { @@ -82,7 +106,8 @@ func TestExampleServerFiles(t *testing.T) { require.NoError(t, s.Write(&buf)) } code := codegen.FormatTestCode(t, "package foo\n"+buf.String()) - assert.Equal(t, c.Code, code) + golden := filepath.Join("testdata", "server-"+c.Name+".golden") + compareOrUpdateGolden(t, code, golden) }) } }) diff --git a/http/codegen/templates/server_configure.go.tpl b/http/codegen/templates/server_configure.go.tpl index fadd53d230..1f186a9a85 100644 --- a/http/codegen/templates/server_configure.go.tpl +++ b/http/codegen/templates/server_configure.go.tpl @@ -9,7 +9,7 @@ {{- end }} ) { - eh := errorHandler(logger) + eh := errorHandler(ctx) {{- if needStream .Services }} upgrader := &websocket.Upgrader{} {{- end }} @@ -20,17 +20,8 @@ {{ .Service.VarName }}Server = {{ .Service.PkgName }}svr.New(nil, mux, dec, enc, eh, nil{{ range .FileServers }}, nil{{ end }}) {{- end }} {{- end }} - {{- if .Services }} - if debug { - servers := goahttp.Servers{ - {{- range $svc := .Services }} - {{ .Service.VarName }}Server, - {{- end }} - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - {{- end }} } + // Configure the mux. {{- range .Services }} {{ .Service.PkgName }}svr.Mount(mux, {{ .Service.VarName }}Server) diff --git a/http/codegen/templates/server_end.go.tpl b/http/codegen/templates/server_end.go.tpl index 4e56003dab..7316147eb2 100644 --- a/http/codegen/templates/server_end.go.tpl +++ b/http/codegen/templates/server_end.go.tpl @@ -5,7 +5,7 @@ {{- range .Services }} for _, m := range {{ .Service.VarName }}Server.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) } {{- end }} @@ -15,12 +15,12 @@ {{ comment "Start HTTP server in a separate goroutine." }} go func() { - logger.Printf("HTTP server listening on %q", u.Host) + log.Printf(ctx, "HTTP server listening on %q", u.Host) errc <- srv.ListenAndServe() }() <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) {{ comment "Shutdown gracefully with a 30s timeout." }} ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) @@ -28,7 +28,7 @@ err := srv.Shutdown(ctx) if err != nil { - logger.Printf("failed to shutdown: %v", err) + log.Printf(ctx, "failed to shutdown: %v", err) } }() } diff --git a/http/codegen/templates/server_error_handler.go.tpl b/http/codegen/templates/server_error_handler.go.tpl index ca839b1d34..b2bc53aea1 100644 --- a/http/codegen/templates/server_error_handler.go.tpl +++ b/http/codegen/templates/server_error_handler.go.tpl @@ -2,10 +2,8 @@ // errorHandler returns a function that writes and logs the given error. // The function also writes and logs the error unique ID so that it's possible // to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) + log.Printf(logCtx, "ERROR: %s", err.Error()) } } diff --git a/http/codegen/templates/server_logger.go.tpl b/http/codegen/templates/server_logger.go.tpl deleted file mode 100644 index 5754ef6175..0000000000 --- a/http/codegen/templates/server_logger.go.tpl +++ /dev/null @@ -1,8 +0,0 @@ - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } diff --git a/http/codegen/templates/server_middleware.go.tpl b/http/codegen/templates/server_middleware.go.tpl index fde2d86cab..85cc521016 100644 --- a/http/codegen/templates/server_middleware.go.tpl +++ b/http/codegen/templates/server_middleware.go.tpl @@ -1,8 +1,6 @@ - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) } diff --git a/http/codegen/templates/server_mux.go.tpl b/http/codegen/templates/server_mux.go.tpl index 1b51d77c6e..b189203c72 100644 --- a/http/codegen/templates/server_mux.go.tpl +++ b/http/codegen/templates/server_mux.go.tpl @@ -1,7 +1,13 @@ - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. var mux goahttp.Muxer { mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } } diff --git a/http/codegen/templates/server_start.go.tpl b/http/codegen/templates/server_start.go.tpl index 5435c201c1..d1f8eb9b42 100644 --- a/http/codegen/templates/server_start.go.tpl +++ b/http/codegen/templates/server_start.go.tpl @@ -1,2 +1,2 @@ {{ comment "handleHTTPServer starts configures and starts a HTTP server on the given URL. It shuts down the server if any error is received in the error channel." }} -func handleHTTPServer(ctx context.Context, u *url.URL{{ range $.Services }}{{ if .Service.Methods }}, {{ .Service.VarName }}Endpoints *{{ .Service.PkgName }}.Endpoints{{ end }}{{ end }}, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { +func handleHTTPServer(ctx context.Context, u *url.URL{{ range $.Services }}{{ if .Service.Methods }}, {{ .Service.VarName }}Endpoints *{{ .Service.PkgName }}.Endpoints{{ end }}{{ end }}, wg *sync.WaitGroup, errc chan error, dbg bool) { \ No newline at end of file diff --git a/http/codegen/testdata/client-no-server.golden b/http/codegen/testdata/client-no-server.golden new file mode 100644 index 0000000000..1c52149542 --- /dev/null +++ b/http/codegen/testdata/client-no-server.golden @@ -0,0 +1,28 @@ +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + ) +} + +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/http/codegen/testdata/client-server-hosting-multiple-services.golden b/http/codegen/testdata/client-server-hosting-multiple-services.golden new file mode 100644 index 0000000000..1c52149542 --- /dev/null +++ b/http/codegen/testdata/client-server-hosting-multiple-services.golden @@ -0,0 +1,28 @@ +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + ) +} + +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/http/codegen/testdata/client-server-hosting-service-subset.golden b/http/codegen/testdata/client-server-hosting-service-subset.golden new file mode 100644 index 0000000000..1c52149542 --- /dev/null +++ b/http/codegen/testdata/client-server-hosting-service-subset.golden @@ -0,0 +1,28 @@ +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + ) +} + +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/http/codegen/testdata/client-streaming-multiple-services.golden b/http/codegen/testdata/client-streaming-multiple-services.golden new file mode 100644 index 0000000000..5a0b1e315c --- /dev/null +++ b/http/codegen/testdata/client-streaming-multiple-services.golden @@ -0,0 +1,38 @@ +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + } + + var ( + dialer *websocket.Dialer + ) + { + dialer = websocket.DefaultDialer + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + dialer, + nil, + nil, + ) +} + +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/http/codegen/testdata/client-streaming.golden b/http/codegen/testdata/client-streaming.golden new file mode 100644 index 0000000000..86cd799031 --- /dev/null +++ b/http/codegen/testdata/client-streaming.golden @@ -0,0 +1,37 @@ +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + } + + var ( + dialer *websocket.Dialer + ) + { + dialer = websocket.DefaultDialer + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + dialer, + nil, + ) +} + +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/http/codegen/testdata/example_code.go b/http/codegen/testdata/example_code.go deleted file mode 100644 index a97e34ed07..0000000000 --- a/http/codegen/testdata/example_code.go +++ /dev/null @@ -1,622 +0,0 @@ -package testdata - -const ( - NoServerServerHandleCode = `// handleHTTPServer starts configures and starts a HTTP server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Provide the transport specific request decoder and response encoder. - // The goa http package has built-in support for JSON, XML and gob. - // Other encodings can be used by providing the corresponding functions, - // see goa.design/implement/encoding. - var ( - dec = goahttp.RequestDecoder - enc = goahttp.ResponseEncoder - ) - - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. - var mux goahttp.Muxer - { - mux = goahttp.NewMuxer() - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to HTTP requests and - // responses. - var ( - serviceServer *servicesvr.Server - ) - { - eh := errorHandler(logger) - serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) - if debug { - servers := goahttp.Servers{ - serviceServer, - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - } - // Configure the mux. - servicesvr.Mount(mux, serviceServer) - - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. - var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) - } - - // Start HTTP server using default configuration, change the code to - // configure the server as required by your service. - srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} - for _, m := range serviceServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start HTTP server in a separate goroutine. - go func() { - logger.Printf("HTTP server listening on %q", u.Host) - errc <- srv.ListenAndServe() - }() - - <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) - - // Shutdown gracefully with a 30s timeout. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - err := srv.Shutdown(ctx) - if err != nil { - logger.Printf("failed to shutdown: %v", err) - } - }() -} - -// errorHandler returns a function that writes and logs the given error. -// The function also writes and logs the error unique ID so that it's possible -// to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { - return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) - } -} -` - - ServerHostingServiceWithFileServerHandlerCode = `// handleHTTPServer starts configures and starts a HTTP server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleHTTPServer(ctx context.Context, u *url.URL, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Provide the transport specific request decoder and response encoder. - // The goa http package has built-in support for JSON, XML and gob. - // Other encodings can be used by providing the corresponding functions, - // see goa.design/implement/encoding. - var ( - dec = goahttp.RequestDecoder - enc = goahttp.ResponseEncoder - ) - - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. - var mux goahttp.Muxer - { - mux = goahttp.NewMuxer() - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to HTTP requests and - // responses. - var ( - serviceServer *servicesvr.Server - ) - { - eh := errorHandler(logger) - serviceServer = servicesvr.New(nil, mux, dec, enc, eh, nil, nil) - if debug { - servers := goahttp.Servers{ - serviceServer, - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - } - // Configure the mux. - servicesvr.Mount(mux, serviceServer) - - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. - var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) - } - - // Start HTTP server using default configuration, change the code to - // configure the server as required by your service. - srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} - for _, m := range serviceServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start HTTP server in a separate goroutine. - go func() { - logger.Printf("HTTP server listening on %q", u.Host) - errc <- srv.ListenAndServe() - }() - - <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) - - // Shutdown gracefully with a 30s timeout. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - err := srv.Shutdown(ctx) - if err != nil { - logger.Printf("failed to shutdown: %v", err) - } - }() -} - -// errorHandler returns a function that writes and logs the given error. -// The function also writes and logs the error unique ID so that it's possible -// to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { - return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) - } -} -` - - ServerHostingServiceSubsetServerHandleCode = `// handleHTTPServer starts configures and starts a HTTP server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Provide the transport specific request decoder and response encoder. - // The goa http package has built-in support for JSON, XML and gob. - // Other encodings can be used by providing the corresponding functions, - // see goa.design/implement/encoding. - var ( - dec = goahttp.RequestDecoder - enc = goahttp.ResponseEncoder - ) - - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. - var mux goahttp.Muxer - { - mux = goahttp.NewMuxer() - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to HTTP requests and - // responses. - var ( - serviceServer *servicesvr.Server - ) - { - eh := errorHandler(logger) - serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) - if debug { - servers := goahttp.Servers{ - serviceServer, - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - } - // Configure the mux. - servicesvr.Mount(mux, serviceServer) - - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. - var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) - } - - // Start HTTP server using default configuration, change the code to - // configure the server as required by your service. - srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} - for _, m := range serviceServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start HTTP server in a separate goroutine. - go func() { - logger.Printf("HTTP server listening on %q", u.Host) - errc <- srv.ListenAndServe() - }() - - <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) - - // Shutdown gracefully with a 30s timeout. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - err := srv.Shutdown(ctx) - if err != nil { - logger.Printf("failed to shutdown: %v", err) - } - }() -} - -// errorHandler returns a function that writes and logs the given error. -// The function also writes and logs the error unique ID so that it's possible -// to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { - return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) - } -} -` - - ServerHostingMultipleServicesServerHandleCode = `// handleHTTPServer starts configures and starts a HTTP server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, anotherServiceEndpoints *anotherservice.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Provide the transport specific request decoder and response encoder. - // The goa http package has built-in support for JSON, XML and gob. - // Other encodings can be used by providing the corresponding functions, - // see goa.design/implement/encoding. - var ( - dec = goahttp.RequestDecoder - enc = goahttp.ResponseEncoder - ) - - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. - var mux goahttp.Muxer - { - mux = goahttp.NewMuxer() - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to HTTP requests and - // responses. - var ( - serviceServer *servicesvr.Server - anotherServiceServer *anotherservicesvr.Server - ) - { - eh := errorHandler(logger) - serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) - anotherServiceServer = anotherservicesvr.New(anotherServiceEndpoints, mux, dec, enc, eh, nil) - if debug { - servers := goahttp.Servers{ - serviceServer, - anotherServiceServer, - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - } - // Configure the mux. - servicesvr.Mount(mux, serviceServer) - anotherservicesvr.Mount(mux, anotherServiceServer) - - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. - var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) - } - - // Start HTTP server using default configuration, change the code to - // configure the server as required by your service. - srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} - for _, m := range serviceServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - for _, m := range anotherServiceServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start HTTP server in a separate goroutine. - go func() { - logger.Printf("HTTP server listening on %q", u.Host) - errc <- srv.ListenAndServe() - }() - - <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) - - // Shutdown gracefully with a 30s timeout. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - err := srv.Shutdown(ctx) - if err != nil { - logger.Printf("failed to shutdown: %v", err) - } - }() -} - -// errorHandler returns a function that writes and logs the given error. -// The function also writes and logs the error unique ID so that it's possible -// to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { - return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) - } -} -` - - StreamingServerHandleCode = `// handleHTTPServer starts configures and starts a HTTP server on the given -// URL. It shuts down the server if any error is received in the error channel. -func handleHTTPServer(ctx context.Context, u *url.URL, streamingServiceAEndpoints *streamingservicea.Endpoints, streamingServiceBEndpoints *streamingserviceb.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { - - // Setup goa log adapter. - var ( - adapter middleware.Logger - ) - { - adapter = middleware.NewLogger(logger) - } - - // Provide the transport specific request decoder and response encoder. - // The goa http package has built-in support for JSON, XML and gob. - // Other encodings can be used by providing the corresponding functions, - // see goa.design/implement/encoding. - var ( - dec = goahttp.RequestDecoder - enc = goahttp.ResponseEncoder - ) - - // Build the service HTTP request multiplexer and configure it to serve - // HTTP requests to the service endpoints. - var mux goahttp.Muxer - { - mux = goahttp.NewMuxer() - } - - // Wrap the endpoints with the transport specific layers. The generated - // server packages contains code generated from the design which maps - // the service input and output data structures to HTTP requests and - // responses. - var ( - streamingServiceAServer *streamingserviceasvr.Server - streamingServiceBServer *streamingservicebsvr.Server - ) - { - eh := errorHandler(logger) - upgrader := &websocket.Upgrader{} - streamingServiceAServer = streamingserviceasvr.New(streamingServiceAEndpoints, mux, dec, enc, eh, nil, upgrader, nil) - streamingServiceBServer = streamingservicebsvr.New(streamingServiceBEndpoints, mux, dec, enc, eh, nil, upgrader, nil) - if debug { - servers := goahttp.Servers{ - streamingServiceAServer, - streamingServiceBServer, - } - servers.Use(httpmdlwr.Debug(mux, os.Stdout)) - } - } - // Configure the mux. - streamingserviceasvr.Mount(mux, streamingServiceAServer) - streamingservicebsvr.Mount(mux, streamingServiceBServer) - - // Wrap the multiplexer with additional middlewares. Middlewares mounted - // here apply to all the service endpoints. - var handler http.Handler = mux - { - handler = httpmdlwr.Log(adapter)(handler) - handler = httpmdlwr.RequestID()(handler) - } - - // Start HTTP server using default configuration, change the code to - // configure the server as required by your service. - srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} - for _, m := range streamingServiceAServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - for _, m := range streamingServiceBServer.Mounts { - logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) - } - - (*wg).Add(1) - go func() { - defer (*wg).Done() - - // Start HTTP server in a separate goroutine. - go func() { - logger.Printf("HTTP server listening on %q", u.Host) - errc <- srv.ListenAndServe() - }() - - <-ctx.Done() - logger.Printf("shutting down HTTP server at %q", u.Host) - - // Shutdown gracefully with a 30s timeout. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - err := srv.Shutdown(ctx) - if err != nil { - logger.Printf("failed to shutdown: %v", err) - } - }() -} - -// errorHandler returns a function that writes and logs the given error. -// The function also writes and logs the error unique ID so that it's possible -// to correlate. -func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { - return func(ctx context.Context, w http.ResponseWriter, err error) { - id := ctx.Value(middleware.RequestIDKey).(string) - _, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) - logger.Printf("[%s] ERROR: %s", id, err.Error()) - } -} -` - - ExampleCLICode = `func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { - var ( - doer goahttp.Doer - ) - { - doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} - if debug { - doer = goahttp.NewDebugDoer(doer) - } - } - - return cli.ParseEndpoint( - scheme, - host, - doer, - goahttp.RequestEncoder, - goahttp.ResponseDecoder, - debug, - ) -} - -func httpUsageCommands() string { - return cli.UsageCommands() -} - -func httpUsageExamples() string { - return cli.UsageExamples() -} -` - - StreamingExampleCLICode = `func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { - var ( - doer goahttp.Doer - ) - { - doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} - if debug { - doer = goahttp.NewDebugDoer(doer) - } - } - - var ( - dialer *websocket.Dialer - ) - { - dialer = websocket.DefaultDialer - } - - return cli.ParseEndpoint( - scheme, - host, - doer, - goahttp.RequestEncoder, - goahttp.ResponseDecoder, - debug, - dialer, - nil, - ) -} - -func httpUsageCommands() string { - return cli.UsageCommands() -} - -func httpUsageExamples() string { - return cli.UsageExamples() -} -` - - StreamingMultipleServicesExampleCLICode = `func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { - var ( - doer goahttp.Doer - ) - { - doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} - if debug { - doer = goahttp.NewDebugDoer(doer) - } - } - - var ( - dialer *websocket.Dialer - ) - { - dialer = websocket.DefaultDialer - } - - return cli.ParseEndpoint( - scheme, - host, - doer, - goahttp.RequestEncoder, - goahttp.ResponseDecoder, - debug, - dialer, - nil, - nil, - ) -} - -func httpUsageCommands() string { - return cli.UsageCommands() -} - -func httpUsageExamples() string { - return cli.UsageExamples() -} -` -) diff --git a/http/codegen/testdata/server-no-server.golden b/http/codegen/testdata/server-no-server.golden new file mode 100644 index 0000000000..bec39c6092 --- /dev/null +++ b/http/codegen/testdata/server-no-server.golden @@ -0,0 +1,86 @@ +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/implement/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + serviceServer *servicesvr.Server + ) + { + eh := errorHandler(ctx) + serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) + } + + // Configure the mux. + servicesvr.Mount(mux, serviceServer) + + var handler http.Handler = mux + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} + for _, m := range serviceServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + log.Printf(ctx, "HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + err := srv.Shutdown(ctx) + if err != nil { + log.Printf(ctx, "failed to shutdown: %v", err) + } + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + log.Printf(logCtx, "ERROR: %s", err.Error()) + } +} diff --git a/http/codegen/testdata/server-server-hosting-multiple-services.golden b/http/codegen/testdata/server-server-hosting-multiple-services.golden new file mode 100644 index 0000000000..08507e3923 --- /dev/null +++ b/http/codegen/testdata/server-server-hosting-multiple-services.golden @@ -0,0 +1,92 @@ +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, anotherServiceEndpoints *anotherservice.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/implement/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + serviceServer *servicesvr.Server + anotherServiceServer *anotherservicesvr.Server + ) + { + eh := errorHandler(ctx) + serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) + anotherServiceServer = anotherservicesvr.New(anotherServiceEndpoints, mux, dec, enc, eh, nil) + } + + // Configure the mux. + servicesvr.Mount(mux, serviceServer) + anotherservicesvr.Mount(mux, anotherServiceServer) + + var handler http.Handler = mux + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} + for _, m := range serviceServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + for _, m := range anotherServiceServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + log.Printf(ctx, "HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + err := srv.Shutdown(ctx) + if err != nil { + log.Printf(ctx, "failed to shutdown: %v", err) + } + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + log.Printf(logCtx, "ERROR: %s", err.Error()) + } +} diff --git a/http/codegen/testdata/server-server-hosting-service-subset.golden b/http/codegen/testdata/server-server-hosting-service-subset.golden new file mode 100644 index 0000000000..bec39c6092 --- /dev/null +++ b/http/codegen/testdata/server-server-hosting-service-subset.golden @@ -0,0 +1,86 @@ +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/implement/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + serviceServer *servicesvr.Server + ) + { + eh := errorHandler(ctx) + serviceServer = servicesvr.New(serviceEndpoints, mux, dec, enc, eh, nil) + } + + // Configure the mux. + servicesvr.Mount(mux, serviceServer) + + var handler http.Handler = mux + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} + for _, m := range serviceServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + log.Printf(ctx, "HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + err := srv.Shutdown(ctx) + if err != nil { + log.Printf(ctx, "failed to shutdown: %v", err) + } + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + log.Printf(logCtx, "ERROR: %s", err.Error()) + } +} diff --git a/http/codegen/testdata/server-server-hosting-service-with-file-server.golden b/http/codegen/testdata/server-server-hosting-service-with-file-server.golden new file mode 100644 index 0000000000..6a7ffb8a4e --- /dev/null +++ b/http/codegen/testdata/server-server-hosting-service-with-file-server.golden @@ -0,0 +1,86 @@ +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/implement/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + serviceServer *servicesvr.Server + ) + { + eh := errorHandler(ctx) + serviceServer = servicesvr.New(nil, mux, dec, enc, eh, nil, nil) + } + + // Configure the mux. + servicesvr.Mount(mux, serviceServer) + + var handler http.Handler = mux + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} + for _, m := range serviceServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + log.Printf(ctx, "HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + err := srv.Shutdown(ctx) + if err != nil { + log.Printf(ctx, "failed to shutdown: %v", err) + } + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + log.Printf(logCtx, "ERROR: %s", err.Error()) + } +} diff --git a/http/codegen/testdata/server-streaming.golden b/http/codegen/testdata/server-streaming.golden new file mode 100644 index 0000000000..713eb24e5c --- /dev/null +++ b/http/codegen/testdata/server-streaming.golden @@ -0,0 +1,93 @@ +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, streamingServiceAEndpoints *streamingservicea.Endpoints, streamingServiceBEndpoints *streamingserviceb.Endpoints, wg *sync.WaitGroup, errc chan error, dbg bool) { + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/implement/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and mount debug and profiler + // endpoints in debug mode. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + if dbg { + // Mount pprof handlers for memory profiling under /debug/pprof. + debug.MountPprofHandlers(debug.Adapt(mux)) + // Mount /debug endpoint to enable or disable debug logs at runtime. + debug.MountDebugLogEnabler(debug.Adapt(mux)) + } + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + streamingServiceAServer *streamingserviceasvr.Server + streamingServiceBServer *streamingservicebsvr.Server + ) + { + eh := errorHandler(ctx) + upgrader := &websocket.Upgrader{} + streamingServiceAServer = streamingserviceasvr.New(streamingServiceAEndpoints, mux, dec, enc, eh, nil, upgrader, nil) + streamingServiceBServer = streamingservicebsvr.New(streamingServiceBEndpoints, mux, dec, enc, eh, nil, upgrader, nil) + } + + // Configure the mux. + streamingserviceasvr.Mount(mux, streamingServiceAServer) + streamingservicebsvr.Mount(mux, streamingServiceBServer) + + var handler http.Handler = mux + if dbg { + // Log query and response bodies if debug logs are enabled. + handler = debug.HTTP()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} + for _, m := range streamingServiceAServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + for _, m := range streamingServiceBServer.Mounts { + log.Printf(ctx, "HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + log.Printf(ctx, "HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + <-ctx.Done() + log.Printf(ctx, "shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + err := srv.Shutdown(ctx) + if err != nil { + log.Printf(ctx, "failed to shutdown: %v", err) + } + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logCtx context.Context) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + log.Printf(logCtx, "ERROR: %s", err.Error()) + } +}