diff --git a/.github/workflows/samples-go.yaml b/.github/workflows/samples-go.yaml index 62218739f56..c1b8973ba06 100644 --- a/.github/workflows/samples-go.yaml +++ b/.github/workflows/samples-go.yaml @@ -1,4 +1,4 @@ -name: Samples Go +name: Samples Go on: push: @@ -6,15 +6,17 @@ on: - 'samples/server/petstore/go-echo-server/**' - 'samples/server/petstore/go-api-server/**' - 'samples/server/petstore/go-chi-server/**' + - 'samples/server/others/go-server/no-body-path-params/**' pull_request: paths: - 'samples/server/petstore/go-echo-server/**' - 'samples/server/petstore/go-api-server/**' - 'samples/server/petstore/go-chi-server/**' + - 'samples/server/others/go-server/no-body-path-params/**' jobs: build: - name: Build Go + name: Build Go runs-on: ubuntu-latest strategy: fail-fast: false @@ -23,6 +25,7 @@ jobs: - samples/server/petstore/go-echo-server/ - samples/server/petstore/go-api-server/ - samples/server/petstore/go-chi-server/ + - samples/server/others/go-server/no-body-path-params/ steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/bin/configs/go-server-no-body-path-params.yaml b/bin/configs/go-server-no-body-path-params.yaml new file mode 100644 index 00000000000..06b9d63908b --- /dev/null +++ b/bin/configs/go-server-no-body-path-params.yaml @@ -0,0 +1,8 @@ +generatorName: go-server +outputDir: samples/server/others/go-server/no-body-path-params +inputSpec: modules/openapi-generator/src/test/resources/3_0/go-server/no-body-path-params.yaml +templateDir: modules/openapi-generator/src/main/resources/go-server +additionalProperties: + hideGenerationTimestamp: "true" + packageName: petstoreserver + addResponseHeaders: true diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java index f2670ba2538..b0c7b93edca 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java @@ -398,9 +398,29 @@ public class GoServerCodegen extends AbstractGoCodegen { } } + this.addConditionalImportInformation(objs); + return objs; } + private void addConditionalImportInformation(OperationsMap operations) { + boolean hasPathParams = false; + boolean hasBodyParams = false; + + for (CodegenOperation op : operations.getOperations().getOperation()) { + if (op.getHasPathParams()) { + hasPathParams = true; + } + if (op.getHasBodyParam()) { + hasBodyParams = true; + } + } + + additionalProperties.put("hasPathParams", hasPathParams); + additionalProperties.put("hasBodyParams", hasBodyParams); + } + + @Override public String apiPackage() { return sourceFolder; diff --git a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache index 583de2dca7c..d2e90618492 100644 --- a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache @@ -3,7 +3,9 @@ package {{packageName}} {{#operations}} import ( + {{#hasBodyParams}} "encoding/json" + {{/hasBodyParams}} {{#isBodyParam}} {{^required}} "errors" @@ -14,6 +16,7 @@ import ( "strings" {{#imports}} "{{import}}" {{/imports}} +{{#hasPathParams}} {{#routers}} {{#mux}} @@ -23,6 +26,7 @@ import ( "github.com/go-chi/chi/v5" {{/chi}} {{/routers}} +{{/hasPathParams}} ) {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/go-server/go.mod.mustache b/modules/openapi-generator/src/main/resources/go-server/go.mod.mustache index bb85cc224ae..3096fb82bd4 100644 --- a/modules/openapi-generator/src/main/resources/go-server/go.mod.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/go.mod.mustache @@ -2,6 +2,7 @@ module {{gitHost}}/{{gitUserId}}/{{gitRepoId}} go 1.18 +{{#hasPathParams}} {{#routers}} {{#mux}} require github.com/gorilla/mux v1.8.0 @@ -16,3 +17,4 @@ require github.com/go-chi/cors v1.2.1 {{/featureCORS}} {{/chi}} {{/routers}} +{{/hasPathParams}} diff --git a/modules/openapi-generator/src/test/resources/3_0/go-server/no-body-path-params.yaml b/modules/openapi-generator/src/test/resources/3_0/go-server/no-body-path-params.yaml new file mode 100644 index 00000000000..10c9ff27f6a --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/go-server/no-body-path-params.yaml @@ -0,0 +1,91 @@ +openapi: 3.0.0 + +info: + version: 1.0.0 + title: Simple no path and body param spec + +paths: + /none/endpoint: + get: + tags: + - none + summary: summary + description: description + operationId: one + responses: + '204': + description: successful operation + /path/endpoint/{pathParam}: + get: + tags: + - path + summary: summary + description: description + operationId: path + parameters: + - name: pathParam + in: path + required: true + style: form + explode: false + schema: + type: string + responses: + '204': + description: successful operation + + /body/endpoint: + post: + tags: + - body + summary: summary + description: description + operationId: body + requestBody: + description: Optional description in *Markdown* + required: true + content: + application/json: + schema: + type: object + properties: + param: + type: string + text/plain: + schema: + type: string + responses: + '204': + description: successful operation + + /both/endpoint/{pathParam}: + post: + tags: + - both + summary: summary + description: description + operationId: both + parameters: + - name: pathParam + in: path + required: true + style: form + explode: false + schema: + type: string + requestBody: + description: Optional description in *Markdown* + required: true + content: + application/json: + schema: + type: object + properties: + param: + type: string + text/plain: + schema: + type: string + responses: + '204': + description: successful operation diff --git a/samples/server/others/go-server/no-body-path-params/.openapi-generator-ignore b/samples/server/others/go-server/no-body-path-params/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/others/go-server/no-body-path-params/.openapi-generator/FILES b/samples/server/others/go-server/no-body-path-params/.openapi-generator/FILES new file mode 100644 index 00000000000..a772660d829 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/.openapi-generator/FILES @@ -0,0 +1,20 @@ +Dockerfile +README.md +api/openapi.yaml +go.mod +go/api.go +go/api_body.go +go/api_body_service.go +go/api_both.go +go/api_both_service.go +go/api_none.go +go/api_none_service.go +go/api_path.go +go/api_path_service.go +go/error.go +go/helpers.go +go/impl.go +go/logger.go +go/model_body_request.go +go/routers.go +main.go diff --git a/samples/server/others/go-server/no-body-path-params/.openapi-generator/VERSION b/samples/server/others/go-server/no-body-path-params/.openapi-generator/VERSION new file mode 100644 index 00000000000..ecb21862b1e --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.6.0-SNAPSHOT diff --git a/samples/server/others/go-server/no-body-path-params/Dockerfile b/samples/server/others/go-server/no-body-path-params/Dockerfile new file mode 100644 index 00000000000..2a71bb77629 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.19 AS build +WORKDIR /go/src +COPY go ./go +COPY main.go . +COPY go.sum . +COPY go.mod . + +ENV CGO_ENABLED=0 + +RUN go build -o petstoreserver . + +FROM scratch AS runtime +COPY --from=build /go/src/petstoreserver ./ +EXPOSE 8080/tcp +ENTRYPOINT ["./petstoreserver"] diff --git a/samples/server/others/go-server/no-body-path-params/README.md b/samples/server/others/go-server/no-body-path-params/README.md new file mode 100644 index 00000000000..fd9ea3942b8 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/README.md @@ -0,0 +1,34 @@ +# Go API Server for petstoreserver + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +## Overview +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. +By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +- + +To see how to make this your own, look here: + +[README](https://openapi-generator.tech) + +- API version: 1.0.0 +- Generator version: 7.6.0-SNAPSHOT + + +### Running the server +To run the server, follow these simple steps: + +``` +go run main.go +``` + +To run the server in a docker container +``` +docker build --network=host -t petstoreserver . +``` + +Once image is built use +``` +docker run --rm -it petstoreserver +``` diff --git a/samples/server/others/go-server/no-body-path-params/api/openapi.yaml b/samples/server/others/go-server/no-body-path-params/api/openapi.yaml new file mode 100644 index 00000000000..281f5424039 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/api/openapi.yaml @@ -0,0 +1,90 @@ +openapi: 3.0.0 +info: + title: Simple no path and body param spec + version: 1.0.0 +servers: +- url: / +paths: + /none/endpoint: + get: + description: description + operationId: one + responses: + "204": + description: successful operation + summary: summary + tags: + - none + /path/endpoint/{pathParam}: + get: + description: description + operationId: path + parameters: + - explode: false + in: path + name: pathParam + required: true + schema: + type: string + style: form + responses: + "204": + description: successful operation + summary: summary + tags: + - path + /body/endpoint: + post: + description: description + operationId: body + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/body_request' + text/plain: + schema: + type: string + description: Optional description in *Markdown* + required: true + responses: + "204": + description: successful operation + summary: summary + tags: + - body + /both/endpoint/{pathParam}: + post: + description: description + operationId: both + parameters: + - explode: false + in: path + name: pathParam + required: true + schema: + type: string + style: form + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/body_request' + text/plain: + schema: + type: string + description: Optional description in *Markdown* + required: true + responses: + "204": + description: successful operation + summary: summary + tags: + - both +components: + schemas: + body_request: + properties: + param: + type: string + type: object diff --git a/samples/server/others/go-server/no-body-path-params/go.mod b/samples/server/others/go-server/no-body-path-params/go.mod new file mode 100644 index 00000000000..65afa7e7071 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go.mod @@ -0,0 +1,5 @@ +module github.com/GIT_USER_ID/GIT_REPO_ID + +go 1.18 + +require github.com/gorilla/mux v1.8.0 diff --git a/samples/server/others/go-server/no-body-path-params/go/api.go b/samples/server/others/go-server/no-body-path-params/go/api.go new file mode 100644 index 00000000000..c27ecc34176 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api.go @@ -0,0 +1,79 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "context" + "net/http" +) + + + +// BodyAPIRouter defines the required methods for binding the api requests to a responses for the BodyAPI +// The BodyAPIRouter implementation should parse necessary information from the http request, +// pass the data to a BodyAPIServicer to perform the required actions, then write the service results to the http response. +type BodyAPIRouter interface { + Body(http.ResponseWriter, *http.Request) +} +// BothAPIRouter defines the required methods for binding the api requests to a responses for the BothAPI +// The BothAPIRouter implementation should parse necessary information from the http request, +// pass the data to a BothAPIServicer to perform the required actions, then write the service results to the http response. +type BothAPIRouter interface { + Both(http.ResponseWriter, *http.Request) +} +// NoneAPIRouter defines the required methods for binding the api requests to a responses for the NoneAPI +// The NoneAPIRouter implementation should parse necessary information from the http request, +// pass the data to a NoneAPIServicer to perform the required actions, then write the service results to the http response. +type NoneAPIRouter interface { + One(http.ResponseWriter, *http.Request) +} +// PathAPIRouter defines the required methods for binding the api requests to a responses for the PathAPI +// The PathAPIRouter implementation should parse necessary information from the http request, +// pass the data to a PathAPIServicer to perform the required actions, then write the service results to the http response. +type PathAPIRouter interface { + Path(http.ResponseWriter, *http.Request) +} + + +// BodyAPIServicer defines the api actions for the BodyAPI service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type BodyAPIServicer interface { + Body(context.Context, BodyRequest) (ImplResponse, error) +} + + +// BothAPIServicer defines the api actions for the BothAPI service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type BothAPIServicer interface { + Both(context.Context, string, BodyRequest) (ImplResponse, error) +} + + +// NoneAPIServicer defines the api actions for the NoneAPI service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type NoneAPIServicer interface { + One(context.Context) (ImplResponse, error) +} + + +// PathAPIServicer defines the api actions for the PathAPI service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type PathAPIServicer interface { + Path(context.Context, string) (ImplResponse, error) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_body.go b/samples/server/others/go-server/no-body-path-params/go/api_body.go new file mode 100644 index 00000000000..6830786d1ba --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_body.go @@ -0,0 +1,85 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "encoding/json" + "net/http" + "strings" +) + +// BodyAPIController binds http requests to an api service and writes the service results to the http response +type BodyAPIController struct { + service BodyAPIServicer + errorHandler ErrorHandler +} + +// BodyAPIOption for how the controller is set up. +type BodyAPIOption func(*BodyAPIController) + +// WithBodyAPIErrorHandler inject ErrorHandler into controller +func WithBodyAPIErrorHandler(h ErrorHandler) BodyAPIOption { + return func(c *BodyAPIController) { + c.errorHandler = h + } +} + +// NewBodyAPIController creates a default api controller +func NewBodyAPIController(s BodyAPIServicer, opts ...BodyAPIOption) Router { + controller := &BodyAPIController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the BodyAPIController +func (c *BodyAPIController) Routes() Routes { + return Routes{ + "Body": Route{ + strings.ToUpper("Post"), + "/body/endpoint", + c.Body, + }, + } +} + +// Body - summary +func (c *BodyAPIController) Body(w http.ResponseWriter, r *http.Request) { + bodyRequestParam := BodyRequest{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&bodyRequestParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertBodyRequestRequired(bodyRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + if err := AssertBodyRequestConstraints(bodyRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.Body(r.Context(), bodyRequestParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_body_service.go b/samples/server/others/go-server/no-body-path-params/go/api_body_service.go new file mode 100644 index 00000000000..cd0de685ae4 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_body_service.go @@ -0,0 +1,39 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// BodyAPIService is a service that implements the logic for the BodyAPIServicer +// This service should implement the business logic for every endpoint for the BodyAPI API. +// Include any external packages or services that will be required by this service. +type BodyAPIService struct { +} + +// NewBodyAPIService creates a default api service +func NewBodyAPIService() BodyAPIServicer { + return &BodyAPIService{} +} + +// Body - summary +func (s *BodyAPIService) Body(ctx context.Context, bodyRequest BodyRequest) (ImplResponse, error) { + // TODO - update Body with the required logic for this service method. + // Add api_body_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + // TODO: Uncomment the next line to return response Response(204, {}) or use other options such as http.Ok ... + // return Response(204, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("Body method not implemented") +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_both.go b/samples/server/others/go-server/no-body-path-params/go/api_both.go new file mode 100644 index 00000000000..5d5d2d3cad5 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_both.go @@ -0,0 +1,93 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/gorilla/mux" +) + +// BothAPIController binds http requests to an api service and writes the service results to the http response +type BothAPIController struct { + service BothAPIServicer + errorHandler ErrorHandler +} + +// BothAPIOption for how the controller is set up. +type BothAPIOption func(*BothAPIController) + +// WithBothAPIErrorHandler inject ErrorHandler into controller +func WithBothAPIErrorHandler(h ErrorHandler) BothAPIOption { + return func(c *BothAPIController) { + c.errorHandler = h + } +} + +// NewBothAPIController creates a default api controller +func NewBothAPIController(s BothAPIServicer, opts ...BothAPIOption) Router { + controller := &BothAPIController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the BothAPIController +func (c *BothAPIController) Routes() Routes { + return Routes{ + "Both": Route{ + strings.ToUpper("Post"), + "/both/endpoint/{pathParam}", + c.Both, + }, + } +} + +// Both - summary +func (c *BothAPIController) Both(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + pathParamParam := params["pathParam"] + if pathParamParam == "" { + c.errorHandler(w, r, &RequiredError{"pathParam"}, nil) + return + } + bodyRequestParam := BodyRequest{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&bodyRequestParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertBodyRequestRequired(bodyRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + if err := AssertBodyRequestConstraints(bodyRequestParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.Both(r.Context(), pathParamParam, bodyRequestParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_both_service.go b/samples/server/others/go-server/no-body-path-params/go/api_both_service.go new file mode 100644 index 00000000000..6ba8112cbb8 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_both_service.go @@ -0,0 +1,39 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// BothAPIService is a service that implements the logic for the BothAPIServicer +// This service should implement the business logic for every endpoint for the BothAPI API. +// Include any external packages or services that will be required by this service. +type BothAPIService struct { +} + +// NewBothAPIService creates a default api service +func NewBothAPIService() BothAPIServicer { + return &BothAPIService{} +} + +// Both - summary +func (s *BothAPIService) Both(ctx context.Context, pathParam string, bodyRequest BodyRequest) (ImplResponse, error) { + // TODO - update Both with the required logic for this service method. + // Add api_both_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + // TODO: Uncomment the next line to return response Response(204, {}) or use other options such as http.Ok ... + // return Response(204, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("Both method not implemented") +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_none.go b/samples/server/others/go-server/no-body-path-params/go/api_none.go new file mode 100644 index 00000000000..be2813a2871 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_none.go @@ -0,0 +1,69 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "net/http" + "strings" +) + +// NoneAPIController binds http requests to an api service and writes the service results to the http response +type NoneAPIController struct { + service NoneAPIServicer + errorHandler ErrorHandler +} + +// NoneAPIOption for how the controller is set up. +type NoneAPIOption func(*NoneAPIController) + +// WithNoneAPIErrorHandler inject ErrorHandler into controller +func WithNoneAPIErrorHandler(h ErrorHandler) NoneAPIOption { + return func(c *NoneAPIController) { + c.errorHandler = h + } +} + +// NewNoneAPIController creates a default api controller +func NewNoneAPIController(s NoneAPIServicer, opts ...NoneAPIOption) Router { + controller := &NoneAPIController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the NoneAPIController +func (c *NoneAPIController) Routes() Routes { + return Routes{ + "One": Route{ + strings.ToUpper("Get"), + "/none/endpoint", + c.One, + }, + } +} + +// One - summary +func (c *NoneAPIController) One(w http.ResponseWriter, r *http.Request) { + result, err := c.service.One(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_none_service.go b/samples/server/others/go-server/no-body-path-params/go/api_none_service.go new file mode 100644 index 00000000000..80b0b25e75e --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_none_service.go @@ -0,0 +1,39 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// NoneAPIService is a service that implements the logic for the NoneAPIServicer +// This service should implement the business logic for every endpoint for the NoneAPI API. +// Include any external packages or services that will be required by this service. +type NoneAPIService struct { +} + +// NewNoneAPIService creates a default api service +func NewNoneAPIService() NoneAPIServicer { + return &NoneAPIService{} +} + +// One - summary +func (s *NoneAPIService) One(ctx context.Context) (ImplResponse, error) { + // TODO - update One with the required logic for this service method. + // Add api_none_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + // TODO: Uncomment the next line to return response Response(204, {}) or use other options such as http.Ok ... + // return Response(204, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("One method not implemented") +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_path.go b/samples/server/others/go-server/no-body-path-params/go/api_path.go new file mode 100644 index 00000000000..f361bf7ac1f --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_path.go @@ -0,0 +1,77 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "net/http" + "strings" + + "github.com/gorilla/mux" +) + +// PathAPIController binds http requests to an api service and writes the service results to the http response +type PathAPIController struct { + service PathAPIServicer + errorHandler ErrorHandler +} + +// PathAPIOption for how the controller is set up. +type PathAPIOption func(*PathAPIController) + +// WithPathAPIErrorHandler inject ErrorHandler into controller +func WithPathAPIErrorHandler(h ErrorHandler) PathAPIOption { + return func(c *PathAPIController) { + c.errorHandler = h + } +} + +// NewPathAPIController creates a default api controller +func NewPathAPIController(s PathAPIServicer, opts ...PathAPIOption) Router { + controller := &PathAPIController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the PathAPIController +func (c *PathAPIController) Routes() Routes { + return Routes{ + "Path": Route{ + strings.ToUpper("Get"), + "/path/endpoint/{pathParam}", + c.Path, + }, + } +} + +// Path - summary +func (c *PathAPIController) Path(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + pathParamParam := params["pathParam"] + if pathParamParam == "" { + c.errorHandler(w, r, &RequiredError{"pathParam"}, nil) + return + } + result, err := c.service.Path(r.Context(), pathParamParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/api_path_service.go b/samples/server/others/go-server/no-body-path-params/go/api_path_service.go new file mode 100644 index 00000000000..611c5c38a16 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/api_path_service.go @@ -0,0 +1,39 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// PathAPIService is a service that implements the logic for the PathAPIServicer +// This service should implement the business logic for every endpoint for the PathAPI API. +// Include any external packages or services that will be required by this service. +type PathAPIService struct { +} + +// NewPathAPIService creates a default api service +func NewPathAPIService() PathAPIServicer { + return &PathAPIService{} +} + +// Path - summary +func (s *PathAPIService) Path(ctx context.Context, pathParam string) (ImplResponse, error) { + // TODO - update Path with the required logic for this service method. + // Add api_path_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + // TODO: Uncomment the next line to return response Response(204, {}) or use other options such as http.Ok ... + // return Response(204, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("Path method not implemented") +} diff --git a/samples/server/others/go-server/no-body-path-params/go/error.go b/samples/server/others/go-server/no-body-path-params/go/error.go new file mode 100644 index 00000000000..6febc76299c --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/error.go @@ -0,0 +1,63 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "errors" + "fmt" + "net/http" +) + +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + +// ParsingError indicates that an error has occurred when parsing request parameters +type ParsingError struct { + Err error +} + +func (e *ParsingError) Unwrap() error { + return e.Err +} + +func (e *ParsingError) Error() string { + return e.Err.Error() +} + +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + +// ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if +// you would like errors to be handled differently from the DefaultErrorHandler +type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) + +// DefaultErrorHandler defines the default logic on how to handle errors from the controller. Any errors from parsing +// request params will return a StatusBadRequest. Otherwise, the error code originating from the servicer will be used. +func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) { + if _, ok := err.(*ParsingError); ok { + // Handle parsing errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), map[string][]string{}, w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), map[string][]string{}, w) + } else { + // Handle all other errors + EncodeJSONResponse(err.Error(), &result.Code, result.Headers, w) + } +} diff --git a/samples/server/others/go-server/no-body-path-params/go/helpers.go b/samples/server/others/go-server/no-body-path-params/go/helpers.go new file mode 100644 index 00000000000..4a3079c45b5 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/helpers.go @@ -0,0 +1,71 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "reflect" +) + +// Response return a ImplResponse struct filled +func Response(code int, body interface{}) ImplResponse { + return ImplResponse { + Code: code, + Headers: nil, + Body: body, + } +} + +// ResponseWithHeaders return a ImplResponse struct filled, including headers +func ResponseWithHeaders(code int, headers map[string][]string, body interface{}) ImplResponse { + return ImplResponse { + Code: code, + Headers: headers, + Body: body, + } +} + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertRecurseInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired[T any](obj interface{}, callback func(T) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertRecurseValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. ErrTypeAssertionError is thrown if +// the underlying struct does not match type T. +func AssertRecurseValueRequired[T any](value reflect.Value, callback func(T) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + obj, ok := value.Interface().(T) + if !ok { + return ErrTypeAssertionError + } + + if err := callback(obj); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/samples/server/others/go-server/no-body-path-params/go/impl.go b/samples/server/others/go-server/no-body-path-params/go/impl.go new file mode 100644 index 00000000000..f03a4af7f41 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/impl.go @@ -0,0 +1,18 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +// ImplResponse defines an implementation response with error code and the associated body +type ImplResponse struct { + Code int + Headers map[string][]string + Body interface{} +} diff --git a/samples/server/others/go-server/no-body-path-params/go/logger.go b/samples/server/others/go-server/no-body-path-params/go/logger.go new file mode 100644 index 00000000000..09211a7dc10 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/logger.go @@ -0,0 +1,33 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "log" + "net/http" + "time" +) + +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s %s %s %s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} diff --git a/samples/server/others/go-server/no-body-path-params/go/model_body_request.go b/samples/server/others/go-server/no-body-path-params/go/model_body_request.go new file mode 100644 index 00000000000..92b220c01b2 --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/model_body_request.go @@ -0,0 +1,29 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + + + + +type BodyRequest struct { + + Param string `json:"param,omitempty"` +} + +// AssertBodyRequestRequired checks if the required fields are not zero-ed +func AssertBodyRequestRequired(obj BodyRequest) error { + return nil +} + +// AssertBodyRequestConstraints checks if the values respects the defined constraints +func AssertBodyRequestConstraints(obj BodyRequest) error { + return nil +} diff --git a/samples/server/others/go-server/no-body-path-params/go/routers.go b/samples/server/others/go-server/no-body-path-params/go/routers.go new file mode 100644 index 00000000000..536b14449fe --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/go/routers.go @@ -0,0 +1,351 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package petstoreserver + +import ( + "encoding/json" + "errors" + "time" + "github.com/gorilla/mux" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "strconv" + "strings" +) + +// A Route defines the parameters for an api endpoint +type Route struct { + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +// Routes is a map of defined api endpoints +type Routes map[string]Route + +// Router defines the required methods for retrieving api routes +type Router interface { + Routes() Routes +} + +const errMsgRequiredMissing = "required parameter is missing" +const errMsgMinValueConstraint = "provided parameter is not respecting minimum value constraint" +const errMsgMaxValueConstraint = "provided parameter is not respecting maximum value constraint" + +// NewRouter creates a new router for any number of api routers +func NewRouter(routers ...Router) *mux.Router { + router := mux.NewRouter().StrictSlash(true) + for _, api := range routers { + for name, route := range api.Routes() { + var handler http.Handler + handler = route.HandlerFunc + handler = Logger(handler, name) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(name). + Handler(handler) + } + } + + return router +} + +// EncodeJSONResponse uses the json encoder to write an interface to the http response with an optional status code +func EncodeJSONResponse(i interface{}, status *int, headers map[string][]string, w http.ResponseWriter) error { + wHeader := w.Header() + for key, values := range headers { + for _, value := range values { + wHeader.Add(key, value) + } + } + + f, ok := i.(*os.File) + if ok { + data, err := io.ReadAll(f) + if err != nil { + return err + } + wHeader.Set("Content-Type", http.DetectContentType(data)) + wHeader.Set("Content-Disposition", "attachment; filename="+f.Name()) + if status != nil { + w.WriteHeader(*status) + } else { + w.WriteHeader(http.StatusOK) + } + _, err = w.Write(data) + return err + } + wHeader.Set("Content-Type", "application/json; charset=UTF-8") + + if status != nil { + w.WriteHeader(*status) + } else { + w.WriteHeader(http.StatusOK) + } + + if i != nil { + return json.NewEncoder(w).Encode(i) + } + + return nil +} + +// ReadFormFileToTempFile reads file data from a request form and writes it to a temporary file +func ReadFormFileToTempFile(r *http.Request, key string) (*os.File, error) { + _, fileHeader, err := r.FormFile(key) + if err != nil { + return nil, err + } + + return readFileHeaderToTempFile(fileHeader) +} + +// ReadFormFilesToTempFiles reads files array data from a request form and writes it to a temporary files +func ReadFormFilesToTempFiles(r *http.Request, key string) ([]*os.File, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + + files := make([]*os.File, 0, len(r.MultipartForm.File[key])) + + for _, fileHeader := range r.MultipartForm.File[key] { + file, err := readFileHeaderToTempFile(fileHeader) + if err != nil { + return nil, err + } + + files = append(files, file) + } + + return files, nil +} + +// readFileHeaderToTempFile reads multipart.FileHeader and writes it to a temporary file +func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error) { + formFile, err := fileHeader.Open() + if err != nil { + return nil, err + } + + defer formFile.Close() + + // Use .* as suffix, because the asterisk is a placeholder for the random value, + // and the period allows consumers of this file to remove the suffix to obtain the original file name + file, err := os.CreateTemp("", fileHeader.Filename+".*") + if err != nil { + return nil, err + } + + defer file.Close() + + _, err = io.Copy(file, formFile) + if err != nil { + return nil, err + } + + return file, nil +} + +func parseTimes(param string) ([]time.Time, error) { + splits := strings.Split(param, ",") + times := make([]time.Time, 0, len(splits)) + for _, v := range splits { + t, err := parseTime(v) + if err != nil { + return nil, err + } + times = append(times, t) + } + return times, nil +} + +// parseTime will parses a string parameter into a time.Time using the RFC3339 format +func parseTime(param string) (time.Time, error) { + if param == "" { + return time.Time{}, nil + } + return time.Parse(time.RFC3339, param) +} + +type Number interface { + ~int32 | ~int64 | ~float32 | ~float64 +} + +type ParseString[T Number | string | bool] func(v string) (T, error) + +// parseFloat64 parses a string parameter to an float64. +func parseFloat64(param string) (float64, error) { + if param == "" { + return 0, nil + } + + return strconv.ParseFloat(param, 64) +} + +// parseFloat32 parses a string parameter to an float32. +func parseFloat32(param string) (float32, error) { + if param == "" { + return 0, nil + } + + v, err := strconv.ParseFloat(param, 32) + return float32(v), err +} + +// parseInt64 parses a string parameter to an int64. +func parseInt64(param string) (int64, error) { + if param == "" { + return 0, nil + } + + return strconv.ParseInt(param, 10, 64) +} + +// parseInt32 parses a string parameter to an int32. +func parseInt32(param string) (int32, error) { + if param == "" { + return 0, nil + } + + val, err := strconv.ParseInt(param, 10, 32) + return int32(val), err +} + +// parseBool parses a string parameter to an bool. +func parseBool(param string) (bool, error) { + if param == "" { + return false, nil + } + + return strconv.ParseBool(param) +} + +type Operation[T Number | string | bool] func(actual string) (T, bool, error) + +func WithRequire[T Number | string | bool](parse ParseString[T]) Operation[T] { + var empty T + return func(actual string) (T, bool, error) { + if actual == "" { + return empty, false, errors.New(errMsgRequiredMissing) + } + + v, err := parse(actual) + return v, false, err + } +} + +func WithDefaultOrParse[T Number | string | bool](def T, parse ParseString[T]) Operation[T] { + return func(actual string) (T, bool, error) { + if actual == "" { + return def, true, nil + } + + v, err := parse(actual) + return v, false, err + } +} + +func WithParse[T Number | string | bool](parse ParseString[T]) Operation[T] { + return func(actual string) (T, bool, error) { + v, err := parse(actual) + return v, false, err + } +} + +type Constraint[T Number | string | bool] func(actual T) error + +func WithMinimum[T Number](expected T) Constraint[T] { + return func(actual T) error { + if actual < expected { + return errors.New(errMsgMinValueConstraint) + } + + return nil + } +} + +func WithMaximum[T Number](expected T) Constraint[T] { + return func(actual T) error { + if actual > expected { + return errors.New(errMsgMaxValueConstraint) + } + + return nil + } +} + +// parseNumericParameter parses a numeric parameter to its respective type. +func parseNumericParameter[T Number](param string, fn Operation[T], checks ...Constraint[T]) (T, error) { + v, ok, err := fn(param) + if err != nil { + return 0, err + } + + if !ok { + for _, check := range checks { + if err := check(v); err != nil { + return 0, err + } + } + } + + return v, nil +} + +// parseBoolParameter parses a string parameter to a bool +func parseBoolParameter(param string, fn Operation[bool]) (bool, error) { + v, _, err := fn(param) + return v, err +} + +// parseNumericArrayParameter parses a string parameter containing array of values to its respective type. +func parseNumericArrayParameter[T Number](param, delim string, required bool, fn Operation[T], checks ...Constraint[T]) ([]T, error) { + if param == "" { + if required { + return nil, errors.New(errMsgRequiredMissing) + } + + return nil, nil + } + + str := strings.Split(param, delim) + values := make([]T, len(str)) + + for i, s := range str { + v, ok, err := fn(s) + if err != nil { + return nil, err + } + + if !ok { + for _, check := range checks { + if err := check(v); err != nil { + return nil, err + } + } + } + + values[i] = v + } + + return values, nil +} + + +// parseQuery parses query paramaters and returns an error if any malformed value pairs are encountered. +func parseQuery(rawQuery string) (url.Values, error) { + return url.ParseQuery(rawQuery) +} \ No newline at end of file diff --git a/samples/server/others/go-server/no-body-path-params/main.go b/samples/server/others/go-server/no-body-path-params/main.go new file mode 100644 index 00000000000..cdd7b015aaf --- /dev/null +++ b/samples/server/others/go-server/no-body-path-params/main.go @@ -0,0 +1,38 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * Simple no path and body param spec + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.0 + */ + +package main + +import ( + "log" + "net/http" + + petstoreserver "github.com/GIT_USER_ID/GIT_REPO_ID/go" +) + +func main() { + log.Printf("Server started") + + BodyAPIService := petstoreserver.NewBodyAPIService() + BodyAPIController := petstoreserver.NewBodyAPIController(BodyAPIService) + + BothAPIService := petstoreserver.NewBothAPIService() + BothAPIController := petstoreserver.NewBothAPIController(BothAPIService) + + NoneAPIService := petstoreserver.NewNoneAPIService() + NoneAPIController := petstoreserver.NewNoneAPIController(NoneAPIService) + + PathAPIService := petstoreserver.NewPathAPIService() + PathAPIController := petstoreserver.NewPathAPIController(PathAPIService) + + router := petstoreserver.NewRouter(BodyAPIController, BothAPIController, NoneAPIController, PathAPIController) + + log.Fatal(http.ListenAndServe(":8080", router)) +}