diff --git a/httpstatus/README.md b/httpstatus/README.md new file mode 100644 index 00000000..227a5041 --- /dev/null +++ b/httpstatus/README.md @@ -0,0 +1,4 @@ +Sample endpoints that return multiple status codes in HTTP transport. +=== + +This sample changes the response status according to the contents of the request body. See the [Tag documentation](https://pkg.go.dev/goa.design/goa/v3/dsl#Tag) for details. \ No newline at end of file diff --git a/httpstatus/cmd/hello-cli/http.go b/httpstatus/cmd/hello-cli/http.go new file mode 100644 index 00000000..25ba981c --- /dev/null +++ b/httpstatus/cmd/hello-cli/http.go @@ -0,0 +1,39 @@ +package main + +import ( + "net/http" + "time" + + cli "goa.design/examples/httpstatus/gen/http/cli/hello" + goahttp "goa.design/goa/v3/http" + goa "goa.design/goa/v3/pkg" +) + +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/httpstatus/cmd/hello-cli/main.go b/httpstatus/cmd/hello-cli/main.go new file mode 100644 index 00000000..0835a4ef --- /dev/null +++ b/httpstatus/cmd/hello-cli/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "net/url" + "os" + "strings" + + goa "goa.design/goa/v3/pkg" +) + +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) + 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 hello 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/httpstatus/cmd/hello/http.go b/httpstatus/cmd/hello/http.go new file mode 100644 index 00000000..ec329841 --- /dev/null +++ b/httpstatus/cmd/hello/http.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "log" + "net/http" + "net/url" + "os" + "sync" + "time" + + hello "goa.design/examples/httpstatus/gen/hello" + hellosvr "goa.design/examples/httpstatus/gen/http/hello/server" + goahttp "goa.design/goa/v3/http" + httpmdlwr "goa.design/goa/v3/http/middleware" + "goa.design/goa/v3/middleware" +) + +// 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, helloEndpoints *hello.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 ( + helloServer *hellosvr.Server + ) + { + eh := errorHandler(logger) + helloServer = hellosvr.New(helloEndpoints, mux, dec, enc, eh, nil) + if debug { + servers := goahttp.Servers{ + helloServer, + } + servers.Use(httpmdlwr.Debug(mux, os.Stdout)) + } + } + // Configure the mux. + hellosvr.Mount(mux, helloServer) + + // 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 helloServer.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()) + } +} diff --git a/httpstatus/cmd/hello/main.go b/httpstatus/cmd/hello/main.go new file mode 100644 index 00000000..fb00c99e --- /dev/null +++ b/httpstatus/cmd/hello/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net" + "net/url" + "os" + "os/signal" + "sync" + "syscall" + + helloapi "goa.design/examples/httpstatus" + hello "goa.design/examples/httpstatus/gen/hello" +) + +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, "[helloapi] ", log.Ltime) + } + + // Initialize the services. + var ( + helloSvc hello.Service + ) + { + helloSvc = helloapi.NewHello(logger) + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + helloEndpoints *hello.Endpoints + ) + { + helloEndpoints = hello.NewEndpoints(helloSvc) + } + + // 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, helloEndpoints, &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/httpstatus/design/design.go b/httpstatus/design/design.go new file mode 100644 index 00000000..3d5f3506 --- /dev/null +++ b/httpstatus/design/design.go @@ -0,0 +1,41 @@ +package design + +import ( + . "goa.design/goa/v3/dsl" +) + +var GreetingResult = ResultType("application/vnd.hello", func() { + Attribute("greeting", String, "The greeting message") + Attribute("outcome", func() { + Meta("struct:tag:json", "-") // hide from response + Meta("swagger:example", "false") // hide from swagger + Meta("swagger:generate", "false") + }) + Required("outcome", "greeting") +}) + +var _ = Service("hello", func() { + Description("The hello service returns greetings with various statuses.") + + // Method describes a service method (endpoint) + Method("hello", func() { + Payload(func() { + Attribute("greeting", String, "The greeting message") + Required("greeting") + }) + Result(GreetingResult) + HTTP(func() { + GET("/hello/{greeting}") + Params(func() { + Param("greeting") + }) + Response(StatusCreated, func() { + Tag("outcome", "created") + }) + Response(StatusAccepted, func() { + Tag("outcome", "accepted") + }) + Response(StatusOK) + }) + }) +}) diff --git a/httpstatus/gen/hello/client.go b/httpstatus/gen/hello/client.go new file mode 100644 index 00000000..2727eacf --- /dev/null +++ b/httpstatus/gen/hello/client.go @@ -0,0 +1,36 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello client +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package hello + +import ( + "context" + + goa "goa.design/goa/v3/pkg" +) + +// Client is the "hello" service client. +type Client struct { + HelloEndpointEndpoint goa.Endpoint +} + +// NewClient initializes a "hello" service client given the endpoints. +func NewClient(helloEndpoint goa.Endpoint) *Client { + return &Client{ + HelloEndpointEndpoint: helloEndpoint, + } +} + +// HelloEndpoint calls the "hello" endpoint of the "hello" service. +func (c *Client) HelloEndpoint(ctx context.Context, p *HelloPayload) (res *Hello, err error) { + var ires any + ires, err = c.HelloEndpointEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Hello), nil +} diff --git a/httpstatus/gen/hello/endpoints.go b/httpstatus/gen/hello/endpoints.go new file mode 100644 index 00000000..e8289ed4 --- /dev/null +++ b/httpstatus/gen/hello/endpoints.go @@ -0,0 +1,45 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello endpoints +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package hello + +import ( + "context" + + goa "goa.design/goa/v3/pkg" +) + +// Endpoints wraps the "hello" service endpoints. +type Endpoints struct { + HelloEndpoint goa.Endpoint +} + +// NewEndpoints wraps the methods of the "hello" service with endpoints. +func NewEndpoints(s Service) *Endpoints { + return &Endpoints{ + HelloEndpoint: NewHelloEndpointEndpoint(s), + } +} + +// Use applies the given middleware to all the "hello" service endpoints. +func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) { + e.HelloEndpoint = m(e.HelloEndpoint) +} + +// NewHelloEndpointEndpoint returns an endpoint function that calls the method +// "hello" of service "hello". +func NewHelloEndpointEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req any) (any, error) { + p := req.(*HelloPayload) + res, err := s.HelloEndpoint(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedHello(res, "default") + return vres, nil + } +} diff --git a/httpstatus/gen/hello/service.go b/httpstatus/gen/hello/service.go new file mode 100644 index 00000000..893cedc3 --- /dev/null +++ b/httpstatus/gen/hello/service.go @@ -0,0 +1,83 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello service +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package hello + +import ( + "context" + + helloviews "goa.design/examples/httpstatus/gen/hello/views" +) + +// The hello service returns greetings with various statuses. +type Service interface { + // Hello implements hello. + HelloEndpoint(context.Context, *HelloPayload) (res *Hello, err error) +} + +// APIName is the name of the API as defined in the design. +const APIName = "hello" + +// APIVersion is the version of the API as defined in the design. +const APIVersion = "0.0.1" + +// ServiceName is the name of the service as defined in the design. This is the +// same value that is set in the endpoint request contexts under the ServiceKey +// key. +const ServiceName = "hello" + +// MethodNames lists the service method names as defined in the design. These +// are the same values that are set in the endpoint request contexts under the +// MethodKey key. +var MethodNames = [1]string{"hello"} + +// Hello is the result type of the hello service hello method. +type Hello struct { + // The greeting message + Greeting string + Outcome string `json:"-"` +} + +// HelloPayload is the payload type of the hello service hello method. +type HelloPayload struct { + // The greeting message + Greeting string +} + +// NewHello initializes result type Hello from viewed result type Hello. +func NewHello(vres *helloviews.Hello) *Hello { + return newHello(vres.Projected) +} + +// NewViewedHello initializes viewed result type Hello from result type Hello +// using the given view. +func NewViewedHello(res *Hello, view string) *helloviews.Hello { + p := newHelloView(res) + return &helloviews.Hello{Projected: p, View: "default"} +} + +// newHello converts projected type Hello to service type Hello. +func newHello(vres *helloviews.HelloView) *Hello { + res := &Hello{} + if vres.Greeting != nil { + res.Greeting = *vres.Greeting + } + if vres.Outcome != nil { + res.Outcome = *vres.Outcome + } + return res +} + +// newHelloView projects result type Hello to projected type HelloView using +// the "default" view. +func newHelloView(res *Hello) *helloviews.HelloView { + vres := &helloviews.HelloView{ + Greeting: &res.Greeting, + Outcome: &res.Outcome, + } + return vres +} diff --git a/httpstatus/gen/hello/views/view.go b/httpstatus/gen/hello/views/view.go new file mode 100644 index 00000000..a82a7a55 --- /dev/null +++ b/httpstatus/gen/hello/views/view.go @@ -0,0 +1,60 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello views +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package views + +import ( + goa "goa.design/goa/v3/pkg" +) + +// Hello is the viewed result type that is projected based on a view. +type Hello struct { + // Type to project + Projected *HelloView + // View to render + View string +} + +// HelloView is a type that runs validations on a projected type. +type HelloView struct { + // The greeting message + Greeting *string + Outcome *string `json:"-"` +} + +var ( + // HelloMap is a map indexing the attribute names of Hello by view name. + HelloMap = map[string][]string{ + "default": { + "greeting", + "outcome", + }, + } +) + +// ValidateHello runs the validations defined on the viewed result type Hello. +func ValidateHello(result *Hello) (err error) { + switch result.View { + case "default", "": + err = ValidateHelloView(result.Projected) + default: + err = goa.InvalidEnumValueError("view", result.View, []any{"default"}) + } + return +} + +// ValidateHelloView runs the validations defined on HelloView using the +// "default" view. +func ValidateHelloView(result *HelloView) (err error) { + if result.Outcome == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("outcome", "result")) + } + if result.Greeting == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("greeting", "result")) + } + return +} diff --git a/httpstatus/gen/http/cli/hello/cli.go b/httpstatus/gen/http/cli/hello/cli.go new file mode 100644 index 00000000..da43da0d --- /dev/null +++ b/httpstatus/gen/http/cli/hello/cli.go @@ -0,0 +1,150 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP client CLI support package +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package cli + +import ( + "flag" + "fmt" + "net/http" + "os" + + helloc "goa.design/examples/httpstatus/gen/http/hello/client" + goahttp "goa.design/goa/v3/http" + goa "goa.design/goa/v3/pkg" +) + +// UsageCommands returns the set of commands and sub-commands using the format +// +// command (subcommand1|subcommand2|...) +func UsageCommands() string { + return `hello hello +` +} + +// UsageExamples produces an example of a valid invocation of the CLI tool. +func UsageExamples() string { + return os.Args[0] + ` hello hello --greeting "Mollitia sint omnis modi consequatur harum."` + "\n" + + "" +} + +// ParseEndpoint returns the endpoint and payload as specified on the command +// line. +func ParseEndpoint( + scheme, host string, + doer goahttp.Doer, + enc func(*http.Request) goahttp.Encoder, + dec func(*http.Response) goahttp.Decoder, + restore bool, +) (goa.Endpoint, any, error) { + var ( + helloFlags = flag.NewFlagSet("hello", flag.ContinueOnError) + + helloHelloFlags = flag.NewFlagSet("hello", flag.ExitOnError) + helloHelloGreetingFlag = helloHelloFlags.String("greeting", "REQUIRED", "The greeting message") + ) + helloFlags.Usage = helloUsage + helloHelloFlags.Usage = helloHelloUsage + + if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { + return nil, nil, err + } + + if flag.NArg() < 2 { // two non flag args are required: SERVICE and ENDPOINT (aka COMMAND) + return nil, nil, fmt.Errorf("not enough arguments") + } + + var ( + svcn string + svcf *flag.FlagSet + ) + { + svcn = flag.Arg(0) + switch svcn { + case "hello": + svcf = helloFlags + default: + return nil, nil, fmt.Errorf("unknown service %q", svcn) + } + } + if err := svcf.Parse(flag.Args()[1:]); err != nil { + return nil, nil, err + } + + var ( + epn string + epf *flag.FlagSet + ) + { + epn = svcf.Arg(0) + switch svcn { + case "hello": + switch epn { + case "hello": + epf = helloHelloFlags + + } + + } + } + if epf == nil { + return nil, nil, fmt.Errorf("unknown %q endpoint %q", svcn, epn) + } + + // Parse endpoint flags if any + if svcf.NArg() > 1 { + if err := epf.Parse(svcf.Args()[1:]); err != nil { + return nil, nil, err + } + } + + var ( + data any + endpoint goa.Endpoint + err error + ) + { + switch svcn { + case "hello": + c := helloc.NewClient(scheme, host, doer, enc, dec, restore) + switch epn { + case "hello": + endpoint = c.HelloEndpoint() + data, err = helloc.BuildHelloEndpointPayload(*helloHelloGreetingFlag) + } + } + } + if err != nil { + return nil, nil, err + } + + return endpoint, data, nil +} + +// helloUsage displays the usage of the hello command and its subcommands. +func helloUsage() { + fmt.Fprintf(os.Stderr, `The hello service returns greetings with various statuses. +Usage: + %[1]s [globalflags] hello COMMAND [flags] + +COMMAND: + hello: Hello implements hello. + +Additional help: + %[1]s hello COMMAND --help +`, os.Args[0]) +} +func helloHelloUsage() { + fmt.Fprintf(os.Stderr, `%[1]s [flags] hello hello -greeting STRING + +Hello implements hello. + -greeting STRING: The greeting message + +Example: + %[1]s hello hello --greeting "Mollitia sint omnis modi consequatur harum." +`, os.Args[0]) +} diff --git a/httpstatus/gen/http/hello/client/cli.go b/httpstatus/gen/http/hello/client/cli.go new file mode 100644 index 00000000..5b1c40f5 --- /dev/null +++ b/httpstatus/gen/http/hello/client/cli.go @@ -0,0 +1,25 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP client CLI support package +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package client + +import ( + hello "goa.design/examples/httpstatus/gen/hello" +) + +// BuildHelloEndpointPayload builds the payload for the hello hello endpoint +// from CLI flags. +func BuildHelloEndpointPayload(helloHelloGreeting string) (*hello.HelloPayload, error) { + var greeting string + { + greeting = helloHelloGreeting + } + v := &hello.HelloPayload{} + v.Greeting = greeting + + return v, nil +} diff --git a/httpstatus/gen/http/hello/client/client.go b/httpstatus/gen/http/hello/client/client.go new file mode 100644 index 00000000..10760741 --- /dev/null +++ b/httpstatus/gen/http/hello/client/client.go @@ -0,0 +1,70 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello client HTTP transport +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package client + +import ( + "context" + "net/http" + + goahttp "goa.design/goa/v3/http" + goa "goa.design/goa/v3/pkg" +) + +// Client lists the hello service endpoint HTTP clients. +type Client struct { + // HelloEndpoint Doer is the HTTP client used to make requests to the hello + // endpoint. + HelloEndpointDoer goahttp.Doer + + // RestoreResponseBody controls whether the response bodies are reset after + // decoding so they can be read again. + RestoreResponseBody bool + + scheme string + host string + encoder func(*http.Request) goahttp.Encoder + decoder func(*http.Response) goahttp.Decoder +} + +// NewClient instantiates HTTP clients for all the hello service servers. +func NewClient( + scheme string, + host string, + doer goahttp.Doer, + enc func(*http.Request) goahttp.Encoder, + dec func(*http.Response) goahttp.Decoder, + restoreBody bool, +) *Client { + return &Client{ + HelloEndpointDoer: doer, + RestoreResponseBody: restoreBody, + scheme: scheme, + host: host, + decoder: dec, + encoder: enc, + } +} + +// HelloEndpoint returns an endpoint that makes HTTP requests to the hello +// service hello server. +func (c *Client) HelloEndpoint() goa.Endpoint { + var ( + decodeResponse = DecodeHelloEndpointResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v any) (any, error) { + req, err := c.BuildHelloEndpointRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.HelloEndpointDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("hello", "hello", err) + } + return decodeResponse(resp) + } +} diff --git a/httpstatus/gen/http/hello/client/encode_decode.go b/httpstatus/gen/http/hello/client/encode_decode.go new file mode 100644 index 00000000..fe201321 --- /dev/null +++ b/httpstatus/gen/http/hello/client/encode_decode.go @@ -0,0 +1,125 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP client encoders and decoders +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package client + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + + hello "goa.design/examples/httpstatus/gen/hello" + helloviews "goa.design/examples/httpstatus/gen/hello/views" + goahttp "goa.design/goa/v3/http" +) + +// BuildHelloEndpointRequest instantiates a HTTP request object with method and +// path set to call the "hello" service "hello" endpoint +func (c *Client) BuildHelloEndpointRequest(ctx context.Context, v any) (*http.Request, error) { + var ( + greeting string + ) + { + p, ok := v.(*hello.HelloPayload) + if !ok { + return nil, goahttp.ErrInvalidType("hello", "hello", "*hello.HelloPayload", v) + } + greeting = p.Greeting + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: HelloEndpointHelloPath(greeting)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("hello", "hello", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeHelloEndpointResponse returns a decoder for responses returned by the +// hello hello endpoint. restoreBody controls whether the response body should +// be restored after having been read. +func DecodeHelloEndpointResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (any, error) { + return func(resp *http.Response) (any, error) { + if restoreBody { + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = io.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = io.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusCreated: + var ( + body HelloCreatedResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("hello", "hello", err) + } + p := NewHelloViewCreated(&body) + tmp := "created" + p.Outcome = &tmp + view := "default" + vres := &helloviews.Hello{Projected: p, View: view} + if err = helloviews.ValidateHello(vres); err != nil { + return nil, goahttp.ErrValidationError("hello", "hello", err) + } + res := hello.NewHello(vres) + return res, nil + case http.StatusAccepted: + var ( + body HelloAcceptedResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("hello", "hello", err) + } + p := NewHelloViewAccepted(&body) + tmp := "accepted" + p.Outcome = &tmp + view := "default" + vres := &helloviews.Hello{Projected: p, View: view} + if err = helloviews.ValidateHello(vres); err != nil { + return nil, goahttp.ErrValidationError("hello", "hello", err) + } + res := hello.NewHello(vres) + return res, nil + case http.StatusOK: + var ( + body HelloOKResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("hello", "hello", err) + } + p := NewHelloViewOK(&body) + view := "default" + vres := &helloviews.Hello{Projected: p, View: view} + if err = helloviews.ValidateHello(vres); err != nil { + return nil, goahttp.ErrValidationError("hello", "hello", err) + } + res := hello.NewHello(vres) + return res, nil + default: + body, _ := io.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("hello", "hello", resp.StatusCode, string(body)) + } + } +} diff --git a/httpstatus/gen/http/hello/client/paths.go b/httpstatus/gen/http/hello/client/paths.go new file mode 100644 index 00000000..4328feb0 --- /dev/null +++ b/httpstatus/gen/http/hello/client/paths.go @@ -0,0 +1,17 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// HTTP request path constructors for the hello service. +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package client + +import ( + "fmt" +) + +// HelloEndpointHelloPath returns the URL path to the hello service hello HTTP endpoint. +func HelloEndpointHelloPath(greeting string) string { + return fmt.Sprintf("/hello/%v", greeting) +} diff --git a/httpstatus/gen/http/hello/client/types.go b/httpstatus/gen/http/hello/client/types.go new file mode 100644 index 00000000..385a97a7 --- /dev/null +++ b/httpstatus/gen/http/hello/client/types.go @@ -0,0 +1,69 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP client types +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package client + +import ( + helloviews "goa.design/examples/httpstatus/gen/hello/views" +) + +// HelloCreatedResponseBody is the type of the "hello" service "hello" endpoint +// HTTP response body. +type HelloCreatedResponseBody struct { + // The greeting message + Greeting *string `form:"greeting,omitempty" json:"greeting,omitempty" xml:"greeting,omitempty"` + Outcome *string `json:"-"` +} + +// HelloAcceptedResponseBody is the type of the "hello" service "hello" +// endpoint HTTP response body. +type HelloAcceptedResponseBody struct { + // The greeting message + Greeting *string `form:"greeting,omitempty" json:"greeting,omitempty" xml:"greeting,omitempty"` + Outcome *string `json:"-"` +} + +// HelloOKResponseBody is the type of the "hello" service "hello" endpoint HTTP +// response body. +type HelloOKResponseBody struct { + // The greeting message + Greeting *string `form:"greeting,omitempty" json:"greeting,omitempty" xml:"greeting,omitempty"` + Outcome *string `json:"-"` +} + +// NewHelloViewCreated builds a "hello" service "hello" endpoint result from a +// HTTP "Created" response. +func NewHelloViewCreated(body *HelloCreatedResponseBody) *helloviews.HelloView { + v := &helloviews.HelloView{ + Greeting: body.Greeting, + Outcome: body.Outcome, + } + + return v +} + +// NewHelloViewAccepted builds a "hello" service "hello" endpoint result from a +// HTTP "Accepted" response. +func NewHelloViewAccepted(body *HelloAcceptedResponseBody) *helloviews.HelloView { + v := &helloviews.HelloView{ + Greeting: body.Greeting, + Outcome: body.Outcome, + } + + return v +} + +// NewHelloViewOK builds a "hello" service "hello" endpoint result from a HTTP +// "OK" response. +func NewHelloViewOK(body *HelloOKResponseBody) *helloviews.HelloView { + v := &helloviews.HelloView{ + Greeting: body.Greeting, + Outcome: body.Outcome, + } + + return v +} diff --git a/httpstatus/gen/http/hello/server/encode_decode.go b/httpstatus/gen/http/hello/server/encode_decode.go new file mode 100644 index 00000000..995f4cc7 --- /dev/null +++ b/httpstatus/gen/http/hello/server/encode_decode.go @@ -0,0 +1,56 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP server encoders and decoders +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package server + +import ( + "context" + "net/http" + + helloviews "goa.design/examples/httpstatus/gen/hello/views" + goahttp "goa.design/goa/v3/http" +) + +// EncodeHelloEndpointResponse returns an encoder for responses returned by the +// hello hello endpoint. +func EncodeHelloEndpointResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, any) error { + return func(ctx context.Context, w http.ResponseWriter, v any) error { + res := v.(*helloviews.Hello) + if res.Projected.Outcome != nil && *res.Projected.Outcome == "created" { + enc := encoder(ctx, w) + body := NewHelloCreatedResponseBody(res.Projected) + w.WriteHeader(http.StatusCreated) + return enc.Encode(body) + } + if res.Projected.Outcome != nil && *res.Projected.Outcome == "accepted" { + enc := encoder(ctx, w) + body := NewHelloAcceptedResponseBody(res.Projected) + w.WriteHeader(http.StatusAccepted) + return enc.Encode(body) + } + enc := encoder(ctx, w) + body := NewHelloOKResponseBody(res.Projected) + w.WriteHeader(http.StatusOK) + return enc.Encode(body) + } +} + +// DecodeHelloEndpointRequest returns a decoder for requests sent to the hello +// hello endpoint. +func DecodeHelloEndpointRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (any, error) { + return func(r *http.Request) (any, error) { + var ( + greeting string + + params = mux.Vars(r) + ) + greeting = params["greeting"] + payload := NewHelloPayload(greeting) + + return payload, nil + } +} diff --git a/httpstatus/gen/http/hello/server/paths.go b/httpstatus/gen/http/hello/server/paths.go new file mode 100644 index 00000000..49da1642 --- /dev/null +++ b/httpstatus/gen/http/hello/server/paths.go @@ -0,0 +1,17 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// HTTP request path constructors for the hello service. +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package server + +import ( + "fmt" +) + +// HelloEndpointHelloPath returns the URL path to the hello service hello HTTP endpoint. +func HelloEndpointHelloPath(greeting string) string { + return fmt.Sprintf("/hello/%v", greeting) +} diff --git a/httpstatus/gen/http/hello/server/server.go b/httpstatus/gen/http/hello/server/server.go new file mode 100644 index 00000000..179d7fa0 --- /dev/null +++ b/httpstatus/gen/http/hello/server/server.go @@ -0,0 +1,128 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP server +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package server + +import ( + "context" + "net/http" + + hello "goa.design/examples/httpstatus/gen/hello" + goahttp "goa.design/goa/v3/http" + goa "goa.design/goa/v3/pkg" +) + +// Server lists the hello service endpoint HTTP handlers. +type Server struct { + Mounts []*MountPoint + HelloEndpoint http.Handler +} + +// MountPoint holds information about the mounted endpoints. +type MountPoint struct { + // Method is the name of the service method served by the mounted HTTP handler. + Method string + // Verb is the HTTP method used to match requests to the mounted handler. + Verb string + // Pattern is the HTTP request path pattern used to match requests to the + // mounted handler. + Pattern string +} + +// New instantiates HTTP handlers for all the hello service endpoints using the +// provided encoder and decoder. The handlers are mounted on the given mux +// using the HTTP verb and path defined in the design. errhandler is called +// whenever a response fails to be encoded. formatter is used to format errors +// returned by the service methods prior to encoding. Both errhandler and +// formatter are optional and can be nil. +func New( + e *hello.Endpoints, + mux goahttp.Muxer, + decoder func(*http.Request) goahttp.Decoder, + encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, + errhandler func(context.Context, http.ResponseWriter, error), + formatter func(ctx context.Context, err error) goahttp.Statuser, +) *Server { + return &Server{ + Mounts: []*MountPoint{ + {"HelloEndpoint", "GET", "/hello/{greeting}"}, + }, + HelloEndpoint: NewHelloEndpointHandler(e.HelloEndpoint, mux, decoder, encoder, errhandler, formatter), + } +} + +// Service returns the name of the service served. +func (s *Server) Service() string { return "hello" } + +// Use wraps the server handlers with the given middleware. +func (s *Server) Use(m func(http.Handler) http.Handler) { + s.HelloEndpoint = m(s.HelloEndpoint) +} + +// MethodNames returns the methods served. +func (s *Server) MethodNames() []string { return hello.MethodNames[:] } + +// Mount configures the mux to serve the hello endpoints. +func Mount(mux goahttp.Muxer, h *Server) { + MountHelloEndpointHandler(mux, h.HelloEndpoint) +} + +// Mount configures the mux to serve the hello endpoints. +func (s *Server) Mount(mux goahttp.Muxer) { + Mount(mux, s) +} + +// MountHelloEndpointHandler configures the mux to serve the "hello" service +// "hello" endpoint. +func MountHelloEndpointHandler(mux goahttp.Muxer, h http.Handler) { + f, ok := h.(http.HandlerFunc) + if !ok { + f = func(w http.ResponseWriter, r *http.Request) { + h.ServeHTTP(w, r) + } + } + mux.Handle("GET", "/hello/{greeting}", f) +} + +// NewHelloEndpointHandler creates a HTTP handler which loads the HTTP request +// and calls the "hello" service "hello" endpoint. +func NewHelloEndpointHandler( + endpoint goa.Endpoint, + mux goahttp.Muxer, + decoder func(*http.Request) goahttp.Decoder, + encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, + errhandler func(context.Context, http.ResponseWriter, error), + formatter func(ctx context.Context, err error) goahttp.Statuser, +) http.Handler { + var ( + decodeRequest = DecodeHelloEndpointRequest(mux, decoder) + encodeResponse = EncodeHelloEndpointResponse(encoder) + encodeError = goahttp.ErrorEncoder(encoder, formatter) + ) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), goahttp.AcceptTypeKey, r.Header.Get("Accept")) + ctx = context.WithValue(ctx, goa.MethodKey, "hello") + ctx = context.WithValue(ctx, goa.ServiceKey, "hello") + payload, err := decodeRequest(r) + if err != nil { + if err := encodeError(ctx, w, err); err != nil { + errhandler(ctx, w, err) + } + return + } + res, err := endpoint(ctx, payload) + if err != nil { + if err := encodeError(ctx, w, err); err != nil { + errhandler(ctx, w, err) + } + return + } + if err := encodeResponse(ctx, w, res); err != nil { + errhandler(ctx, w, err) + } + }) +} diff --git a/httpstatus/gen/http/hello/server/types.go b/httpstatus/gen/http/hello/server/types.go new file mode 100644 index 00000000..3ae06f5e --- /dev/null +++ b/httpstatus/gen/http/hello/server/types.go @@ -0,0 +1,75 @@ +// Code generated by goa v3.15.2, DO NOT EDIT. +// +// hello HTTP server types +// +// Command: +// $ goa gen goa.design/examples/httpstatus/design + +package server + +import ( + hello "goa.design/examples/httpstatus/gen/hello" + helloviews "goa.design/examples/httpstatus/gen/hello/views" +) + +// HelloCreatedResponseBody is the type of the "hello" service "hello" endpoint +// HTTP response body. +type HelloCreatedResponseBody struct { + // The greeting message + Greeting string `form:"greeting" json:"greeting" xml:"greeting"` + Outcome string `json:"-"` +} + +// HelloAcceptedResponseBody is the type of the "hello" service "hello" +// endpoint HTTP response body. +type HelloAcceptedResponseBody struct { + // The greeting message + Greeting string `form:"greeting" json:"greeting" xml:"greeting"` + Outcome string `json:"-"` +} + +// HelloOKResponseBody is the type of the "hello" service "hello" endpoint HTTP +// response body. +type HelloOKResponseBody struct { + // The greeting message + Greeting string `form:"greeting" json:"greeting" xml:"greeting"` + Outcome string `json:"-"` +} + +// NewHelloCreatedResponseBody builds the HTTP response body from the result of +// the "hello" endpoint of the "hello" service. +func NewHelloCreatedResponseBody(res *helloviews.HelloView) *HelloCreatedResponseBody { + body := &HelloCreatedResponseBody{ + Greeting: *res.Greeting, + Outcome: *res.Outcome, + } + return body +} + +// NewHelloAcceptedResponseBody builds the HTTP response body from the result +// of the "hello" endpoint of the "hello" service. +func NewHelloAcceptedResponseBody(res *helloviews.HelloView) *HelloAcceptedResponseBody { + body := &HelloAcceptedResponseBody{ + Greeting: *res.Greeting, + Outcome: *res.Outcome, + } + return body +} + +// NewHelloOKResponseBody builds the HTTP response body from the result of the +// "hello" endpoint of the "hello" service. +func NewHelloOKResponseBody(res *helloviews.HelloView) *HelloOKResponseBody { + body := &HelloOKResponseBody{ + Greeting: *res.Greeting, + Outcome: *res.Outcome, + } + return body +} + +// NewHelloPayload builds a hello service hello endpoint payload. +func NewHelloPayload(greeting string) *hello.HelloPayload { + v := &hello.HelloPayload{} + v.Greeting = greeting + + return v +} diff --git a/httpstatus/gen/http/openapi.json b/httpstatus/gen/http/openapi.json new file mode 100644 index 00000000..eafb24b0 --- /dev/null +++ b/httpstatus/gen/http/openapi.json @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"","version":"0.0.1"},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/hello/{greeting}":{"get":{"tags":["hello"],"summary":"hello hello","operationId":"hello#hello","parameters":[{"name":"greeting","in":"path","description":"The greeting message","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/HelloHelloOKResponseBody"}},"201":{"description":"Created response.","schema":{"$ref":"#/definitions/HelloHelloCreatedResponseBody"}},"202":{"description":"Accepted response.","schema":{"$ref":"#/definitions/HelloHelloAcceptedResponseBody"}}},"schemes":["http"]}}},"definitions":{"HelloHelloAcceptedResponseBody":{"title":"Mediatype identifier: application/vnd.hello; view=default","type":"object","properties":{"greeting":{"type":"string","description":"The greeting message","example":"Voluptatem voluptatibus itaque omnis."}},"description":"HelloAcceptedResponseBody result type (default view)","example":{"greeting":"Sed molestiae."},"required":["greeting"]},"HelloHelloCreatedResponseBody":{"title":"Mediatype identifier: application/vnd.hello; view=default","type":"object","properties":{"greeting":{"type":"string","description":"The greeting message","example":"Et ab soluta in."}},"description":"HelloCreatedResponseBody result type (default view)","example":{"greeting":"Aut officiis."},"required":["greeting"]},"HelloHelloOKResponseBody":{"title":"Mediatype identifier: application/vnd.hello; view=default","type":"object","properties":{"greeting":{"type":"string","description":"The greeting message","example":"Et cum autem ipsa laboriosam."}},"description":"HelloOKResponseBody result type (default view)","example":{"greeting":"Reprehenderit aut quae ad voluptas."},"required":["greeting"]}}} \ No newline at end of file diff --git a/httpstatus/gen/http/openapi.yaml b/httpstatus/gen/http/openapi.yaml new file mode 100644 index 00000000..46ead559 --- /dev/null +++ b/httpstatus/gen/http/openapi.yaml @@ -0,0 +1,81 @@ +swagger: "2.0" +info: + title: "" + version: 0.0.1 +host: localhost:80 +consumes: + - application/json + - application/xml + - application/gob +produces: + - application/json + - application/xml + - application/gob +paths: + /hello/{greeting}: + get: + tags: + - hello + summary: hello hello + operationId: hello#hello + parameters: + - name: greeting + in: path + description: The greeting message + required: true + type: string + responses: + "200": + description: OK response. + schema: + $ref: '#/definitions/HelloHelloOKResponseBody' + "201": + description: Created response. + schema: + $ref: '#/definitions/HelloHelloCreatedResponseBody' + "202": + description: Accepted response. + schema: + $ref: '#/definitions/HelloHelloAcceptedResponseBody' + schemes: + - http +definitions: + HelloHelloAcceptedResponseBody: + title: 'Mediatype identifier: application/vnd.hello; view=default' + type: object + properties: + greeting: + type: string + description: The greeting message + example: Voluptatem voluptatibus itaque omnis. + description: HelloAcceptedResponseBody result type (default view) + example: + greeting: Sed molestiae. + required: + - greeting + HelloHelloCreatedResponseBody: + title: 'Mediatype identifier: application/vnd.hello; view=default' + type: object + properties: + greeting: + type: string + description: The greeting message + example: Et ab soluta in. + description: HelloCreatedResponseBody result type (default view) + example: + greeting: Aut officiis. + required: + - greeting + HelloHelloOKResponseBody: + title: 'Mediatype identifier: application/vnd.hello; view=default' + type: object + properties: + greeting: + type: string + description: The greeting message + example: Et cum autem ipsa laboriosam. + description: HelloOKResponseBody result type (default view) + example: + greeting: Reprehenderit aut quae ad voluptas. + required: + - greeting diff --git a/httpstatus/gen/http/openapi3.json b/httpstatus/gen/http/openapi3.json new file mode 100644 index 00000000..a6cf0e06 --- /dev/null +++ b/httpstatus/gen/http/openapi3.json @@ -0,0 +1 @@ +{"openapi":"3.0.3","info":{"title":"Goa API","version":"0.0.1"},"servers":[{"url":"http://localhost:80","description":"Default server for hello"}],"paths":{"/hello/{greeting}":{"get":{"tags":["hello"],"summary":"hello hello","operationId":"hello#hello","parameters":[{"name":"greeting","in":"path","description":"The greeting message","required":true,"schema":{"type":"string","description":"The greeting message","example":"Porro quas nobis ea."},"example":"Quae est possimus eius architecto."}],"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Hello"},"example":{"greeting":"Ullam sequi quidem qui in et excepturi."}}}},"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Hello"},"example":{"greeting":"Aut iste labore provident incidunt aut enim."}}}},"202":{"description":"Accepted response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Hello"},"example":{"greeting":"Iste ratione voluptatum."}}}}}}}},"components":{"schemas":{"Hello":{"type":"object","properties":{"greeting":{"type":"string","description":"The greeting message","example":"Perspiciatis laboriosam voluptatem sunt maxime quae."}},"example":{"greeting":"Et molestiae."},"required":["greeting"]}}},"tags":[{"name":"hello","description":"The hello service returns greetings with various statuses."}]} \ No newline at end of file diff --git a/httpstatus/gen/http/openapi3.yaml b/httpstatus/gen/http/openapi3.yaml new file mode 100644 index 00000000..33d7e026 --- /dev/null +++ b/httpstatus/gen/http/openapi3.yaml @@ -0,0 +1,65 @@ +openapi: 3.0.3 +info: + title: Goa API + version: 0.0.1 +servers: + - url: http://localhost:80 + description: Default server for hello +paths: + /hello/{greeting}: + get: + tags: + - hello + summary: hello hello + operationId: hello#hello + parameters: + - name: greeting + in: path + description: The greeting message + required: true + schema: + type: string + description: The greeting message + example: Porro quas nobis ea. + example: Quae est possimus eius architecto. + responses: + "200": + description: OK response. + content: + application/json: + schema: + $ref: '#/components/schemas/Hello' + example: + greeting: Ullam sequi quidem qui in et excepturi. + "201": + description: Created response. + content: + application/json: + schema: + $ref: '#/components/schemas/Hello' + example: + greeting: Aut iste labore provident incidunt aut enim. + "202": + description: Accepted response. + content: + application/json: + schema: + $ref: '#/components/schemas/Hello' + example: + greeting: Iste ratione voluptatum. +components: + schemas: + Hello: + type: object + properties: + greeting: + type: string + description: The greeting message + example: Perspiciatis laboriosam voluptatem sunt maxime quae. + example: + greeting: Et molestiae. + required: + - greeting +tags: + - name: hello + description: The hello service returns greetings with various statuses. diff --git a/httpstatus/go.mod b/httpstatus/go.mod new file mode 100644 index 00000000..473806b8 --- /dev/null +++ b/httpstatus/go.mod @@ -0,0 +1,20 @@ +module goa.design/examples/httpstatus + +go 1.22.2 + +require goa.design/goa/v3 v3.15.2 + +require ( + github.com/AnatolyRugalev/goregen v0.1.0 // indirect + github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 // indirect + github.com/go-chi/chi/v5 v5.0.12 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect + github.com/sergi/go-diff v1.3.1 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.19.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/httpstatus/go.sum b/httpstatus/go.sum new file mode 100644 index 00000000..39c7c1ea --- /dev/null +++ b/httpstatus/go.sum @@ -0,0 +1,49 @@ +github.com/AnatolyRugalev/goregen v0.1.0 h1:xrdXkLaskMnbxW0x4FWNj2yoednv0X2bcTBWpuJGYfE= +github.com/AnatolyRugalev/goregen v0.1.0/go.mod h1:sVlY1tjcirqLBRZnCcIq1+7/Lwmqz5g7IK8AStjOVzI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= +github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d h1:Zj+PHjnhRYWBK6RqCDBcAhLXoi3TzC27Zad/Vn+gnVQ= +github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d/go.mod h1:WZy8Q5coAB1zhY9AOBJP0O6J4BuDfbupUDavKY+I3+s= +github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b h1:3E44bLeN8uKYdfQqVQycPnaVviZdBLbizFhU49mtbe4= +github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b/go.mod h1:Bj8LjjP0ReT1eKt5QlKjwgi5AFm5mI6O1A2G4ChI0Ag= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +goa.design/goa/v3 v3.15.2 h1:ziyJuVR+GSBBmQ/Nkr7FDZx9qtEBpRA6wle3hYqJT9Q= +goa.design/goa/v3 v3.15.2/go.mod h1:zZLxqfk8mZu0Q6fmnnflXYbIJ6BA3SVSB6LSz7Tzcv4= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/httpstatus/hello.go b/httpstatus/hello.go new file mode 100644 index 00000000..3f3e0aa0 --- /dev/null +++ b/httpstatus/hello.go @@ -0,0 +1,34 @@ +package helloapi + +import ( + "context" + "log" + + "goa.design/examples/httpstatus/gen/hello" +) + +// hello service example implementation. +// The example methods log the requests and return zero values. +type hellosrvc struct { + logger *log.Logger +} + +// NewHello returns the hello service implementation. +func NewHello(logger *log.Logger) hello.Service { + return &hellosrvc{logger} +} + +// Hello implements hello. +func (s *hellosrvc) HelloEndpoint(ctx context.Context, p *hello.HelloPayload) (*hello.Hello, error) { + var res hello.Hello + res.Greeting = p.Greeting + switch p.Greeting { + case "hello": + res.Outcome = "created" + case "bye": + res.Outcome = "accepted" + default: + res.Outcome = "defaultStatus" + } + return &res, nil +}