diff --git a/.github/workflows/samples-go.yaml b/.github/workflows/samples-go.yaml index 4c0fdaf1d8f..3d3418767e5 100644 --- a/.github/workflows/samples-go.yaml +++ b/.github/workflows/samples-go.yaml @@ -6,13 +6,13 @@ 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/**' + - 'samples/server/others/go-server/**' 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/**' + - 'samples/server/others/go-server/**' jobs: build: @@ -26,6 +26,7 @@ jobs: - samples/server/petstore/go-api-server/ - samples/server/petstore/go-chi-server/ - samples/server/others/go-server/no-body-path-params/ + - samples/server/others/go-server/optional-body/ steps: - uses: actions/checkout@v5 - uses: actions/setup-go@v6 @@ -58,4 +59,4 @@ jobs: go mod tidy - name: Run tests working-directory: ${{ matrix.sample }} - run: go test ./samples_tests -v \ No newline at end of file + run: go test ./samples_tests -v diff --git a/bin/configs/go-server-optional-body.yaml b/bin/configs/go-server-optional-body.yaml new file mode 100644 index 00000000000..4dc5cea0aa2 --- /dev/null +++ b/bin/configs/go-server-optional-body.yaml @@ -0,0 +1,8 @@ +generatorName: go-server +outputDir: samples/server/others/go-server/optional-body +inputSpec: modules/openapi-generator/src/test/resources/3_0/optional_body.yaml +templateDir: modules/openapi-generator/src/main/resources/go-server +additionalProperties: + hideGenerationTimestamp: "true" + packageName: petstoreserver + addResponseHeaders: true diff --git a/modules/openapi-generator/src/test/resources/3_0/optional_body.yaml b/modules/openapi-generator/src/test/resources/3_0/optional_body.yaml new file mode 100644 index 00000000000..655517114b5 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/optional_body.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +info: + title: optional body + version: 1.0.1 +servers: + - url: https://api.123.com/api/v1 +tags: + - name: just-api + description: Everything about API functions +paths: + /silly: + post: + tags: + - just-api + operationId: send_optional_payload + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Payload" + responses: + '200': + description: Successful operation + content: + application/text: + schema: + type: string +components: + schemas: + Payload: + type: object + properties: + token: + type: string + description: Some kind of token - usually received by Email diff --git a/samples/server/others/go-server/optional-body/.openapi-generator-ignore b/samples/server/others/go-server/optional-body/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/others/go-server/optional-body/.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/optional-body/.openapi-generator/FILES b/samples/server/others/go-server/optional-body/.openapi-generator/FILES new file mode 100644 index 00000000000..3e3160203f9 --- /dev/null +++ b/samples/server/others/go-server/optional-body/.openapi-generator/FILES @@ -0,0 +1,14 @@ +Dockerfile +README.md +api/openapi.yaml +go.mod +go/api.go +go/api_just_api.go +go/api_just_api_service.go +go/error.go +go/helpers.go +go/impl.go +go/logger.go +go/model_payload.go +go/routers.go +main.go diff --git a/samples/server/others/go-server/optional-body/.openapi-generator/VERSION b/samples/server/others/go-server/optional-body/.openapi-generator/VERSION new file mode 100644 index 00000000000..5e528295308 --- /dev/null +++ b/samples/server/others/go-server/optional-body/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.16.0-SNAPSHOT diff --git a/samples/server/others/go-server/optional-body/Dockerfile b/samples/server/others/go-server/optional-body/Dockerfile new file mode 100644 index 00000000000..2a71bb77629 --- /dev/null +++ b/samples/server/others/go-server/optional-body/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/optional-body/README.md b/samples/server/others/go-server/optional-body/README.md new file mode 100644 index 00000000000..ecdd96b5437 --- /dev/null +++ b/samples/server/others/go-server/optional-body/README.md @@ -0,0 +1,35 @@ +# 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.1 +- Generator version: 7.16.0-SNAPSHOT + + +### Running the server +To run the server, follow these simple steps: + +``` +go run main.go +``` + +The server will be available on `http://localhost:8080`. + +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/optional-body/api/openapi.yaml b/samples/server/others/go-server/optional-body/api/openapi.yaml new file mode 100644 index 00000000000..084355d969f --- /dev/null +++ b/samples/server/others/go-server/optional-body/api/openapi.yaml @@ -0,0 +1,37 @@ +openapi: 3.0.3 +info: + title: optional body + version: 1.0.1 +servers: +- url: https://api.123.com/api/v1 +tags: +- description: Everything about API functions + name: just-api +paths: + /silly: + post: + operationId: send_optional_payload + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Payload" + responses: + "200": + content: + application/text: + schema: + type: string + description: Successful operation + tags: + - just-api +components: + schemas: + Payload: + example: + token: token + properties: + token: + description: Some kind of token - usually received by Email + type: string + type: object diff --git a/samples/server/others/go-server/optional-body/go.mod b/samples/server/others/go-server/optional-body/go.mod new file mode 100644 index 00000000000..f71ce9c8223 --- /dev/null +++ b/samples/server/others/go-server/optional-body/go.mod @@ -0,0 +1,4 @@ +module github.com/GIT_USER_ID/GIT_REPO_ID + +go 1.18 + diff --git a/samples/server/others/go-server/optional-body/go/api.go b/samples/server/others/go-server/optional-body/go/api.go new file mode 100644 index 00000000000..6db70acf47f --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/api.go @@ -0,0 +1,34 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "context" + "net/http" +) + + + +// JustApiAPIRouter defines the required methods for binding the api requests to a responses for the JustApiAPI +// The JustApiAPIRouter implementation should parse necessary information from the http request, +// pass the data to a JustApiAPIServicer to perform the required actions, then write the service results to the http response. +type JustApiAPIRouter interface { + SendOptionalPayload(http.ResponseWriter, *http.Request) +} + + +// JustApiAPIServicer defines the api actions for the JustApiAPI 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 JustApiAPIServicer interface { + SendOptionalPayload(context.Context, Payload) (ImplResponse, error) +} diff --git a/samples/server/others/go-server/optional-body/go/api_just_api.go b/samples/server/others/go-server/optional-body/go/api_just_api.go new file mode 100644 index 00000000000..86181fe26f0 --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/api_just_api.go @@ -0,0 +1,102 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "encoding/json" + "errors" + "io" + "net/http" + "strings" +) + +// JustApiAPIController binds http requests to an api service and writes the service results to the http response +type JustApiAPIController struct { + service JustApiAPIServicer + errorHandler ErrorHandler +} + +// JustApiAPIOption for how the controller is set up. +type JustApiAPIOption func(*JustApiAPIController) + +// WithJustApiAPIErrorHandler inject ErrorHandler into controller +func WithJustApiAPIErrorHandler(h ErrorHandler) JustApiAPIOption { + return func(c *JustApiAPIController) { + c.errorHandler = h + } +} + +// NewJustApiAPIController creates a default api controller +func NewJustApiAPIController(s JustApiAPIServicer, opts ...JustApiAPIOption) *JustApiAPIController { + controller := &JustApiAPIController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the JustApiAPIController +func (c *JustApiAPIController) Routes() Routes { + return Routes{ + "SendOptionalPayload": Route{ + "SendOptionalPayload", + strings.ToUpper("Post"), + "/api/v1/silly", + c.SendOptionalPayload, + }, + } +} + +// OrderedRoutes returns all the api routes in a deterministic order for the JustApiAPIController +func (c *JustApiAPIController) OrderedRoutes() []Route { + return []Route{ + Route{ + "SendOptionalPayload", + strings.ToUpper("Post"), + "/api/v1/silly", + c.SendOptionalPayload, + }, + } +} + + + +// SendOptionalPayload - +func (c *JustApiAPIController) SendOptionalPayload(w http.ResponseWriter, r *http.Request) { + var payloadParam Payload + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&payloadParam); err != nil && !errors.Is(err, io.EOF) { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPayloadRequired(payloadParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + if err := AssertPayloadConstraints(payloadParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.SendOptionalPayload(r.Context(), payloadParam) + // 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/optional-body/go/api_just_api_service.go b/samples/server/others/go-server/optional-body/go/api_just_api_service.go new file mode 100644 index 00000000000..9ffb48b193b --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/api_just_api_service.go @@ -0,0 +1,39 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// JustApiAPIService is a service that implements the logic for the JustApiAPIServicer +// This service should implement the business logic for every endpoint for the JustApiAPI API. +// Include any external packages or services that will be required by this service. +type JustApiAPIService struct { +} + +// NewJustApiAPIService creates a default api service +func NewJustApiAPIService() *JustApiAPIService { + return &JustApiAPIService{} +} + +// SendOptionalPayload - +func (s *JustApiAPIService) SendOptionalPayload(ctx context.Context, payload Payload) (ImplResponse, error) { + // TODO - update SendOptionalPayload with the required logic for this service method. + // Add api_just_api_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(200, string{}) or use other options such as http.Ok ... + // return Response(200, string{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("SendOptionalPayload method not implemented") +} diff --git a/samples/server/others/go-server/optional-body/go/error.go b/samples/server/others/go-server/optional-body/go/error.go new file mode 100644 index 00000000000..6a1d17d783c --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/error.go @@ -0,0 +1,74 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +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 { + Param string + Err error +} + +func (e *ParsingError) Unwrap() error { + return e.Err +} + +func (e *ParsingError) Error() string { + if e.Param == "" { + return e.Err.Error() + } + + return e.Param + ": " + 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, _ *http.Request, err error, result *ImplResponse) { + var parsingErr *ParsingError + if ok := errors.As(err, &parsingErr); ok { + // Handle parsing errors + _ = EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), map[string][]string{}, w) + return + } + + var requiredErr *RequiredError + if ok := errors.As(err, &requiredErr); ok { + // Handle missing required errors + _ = EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), map[string][]string{}, w) + return + } + + // Handle all other errors + _ = EncodeJSONResponse(err.Error(), &result.Code, result.Headers, w) +} diff --git a/samples/server/others/go-server/optional-body/go/helpers.go b/samples/server/others/go-server/optional-body/go/helpers.go new file mode 100644 index 00000000000..cf8d1f5ae9f --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/helpers.go @@ -0,0 +1,371 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "encoding/json" + "errors" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "reflect" + "strconv" + "strings" + "time" +) + +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" + +// 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++ { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} + +// 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 parameters and returns an error if any malformed value pairs are encountered. +func parseQuery(rawQuery string) (url.Values, error) { + return url.ParseQuery(rawQuery) +} diff --git a/samples/server/others/go-server/optional-body/go/impl.go b/samples/server/others/go-server/optional-body/go/impl.go new file mode 100644 index 00000000000..f3c7cddd76f --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/impl.go @@ -0,0 +1,18 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +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/optional-body/go/logger.go b/samples/server/others/go-server/optional-body/go/logger.go new file mode 100644 index 00000000000..e8f8a441e9c --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/logger.go @@ -0,0 +1,33 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "net/http" + "log" + "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/optional-body/go/model_payload.go b/samples/server/others/go-server/optional-body/go/model_payload.go new file mode 100644 index 00000000000..89ce13937f8 --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/model_payload.go @@ -0,0 +1,30 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + + + + +type Payload struct { + + // Some kind of token - usually received by Email + Token string `json:"token,omitempty"` +} + +// AssertPayloadRequired checks if the required fields are not zero-ed +func AssertPayloadRequired(obj Payload) error { + return nil +} + +// AssertPayloadConstraints checks if the values respects the defined constraints +func AssertPayloadConstraints(obj Payload) error { + return nil +} diff --git a/samples/server/others/go-server/optional-body/go/routers.go b/samples/server/others/go-server/optional-body/go/routers.go new file mode 100644 index 00000000000..8bb178252c7 --- /dev/null +++ b/samples/server/others/go-server/optional-body/go/routers.go @@ -0,0 +1,52 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package petstoreserver + +import ( + "net/http" + "github.com/gorilla/mux" +) + +// A Route defines the parameters for an api endpoint +type Route struct { + Name string + 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 + OrderedRoutes() []Route +} + +// 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 _, route := range api.OrderedRoutes() { + var handler http.Handler = route.HandlerFunc + handler = Logger(handler, route.Name) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + } + } + + return router +} diff --git a/samples/server/others/go-server/optional-body/main.go b/samples/server/others/go-server/optional-body/main.go new file mode 100644 index 00000000000..c5d456f7c5a --- /dev/null +++ b/samples/server/others/go-server/optional-body/main.go @@ -0,0 +1,29 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +/* + * optional body + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 1.0.1 + */ + +package main + +import ( + "log" + "net/http" + + petstoreserver "github.com/GIT_USER_ID/GIT_REPO_ID/go" +) + +func main() { + log.Printf("Server started") + + JustApiAPIService := petstoreserver.NewJustApiAPIService() + JustApiAPIController := petstoreserver.NewJustApiAPIController(JustApiAPIService) + + router := petstoreserver.NewRouter(JustApiAPIController) + + log.Fatal(http.ListenAndServe(":8080", router)) +}