From 90f7ddcd8d52274e7e9f9e2c805d4bc6bdad5b47 Mon Sep 17 00:00:00 2001 From: Andrea Lamparelli Date: Thu, 15 Feb 2024 11:17:29 +0100 Subject: [PATCH] Automate openapi server generation (#308) * Fix type_assert.patch * Improve openapi-server generation * Automate openapi server generation if something changed --- .pre-commit-config.yaml | 2 + Dockerfile | 3 + Makefile | 18 +- .../openapi/api_model_registry_service.go | 5 +- internal/server/openapi/type_asserts.go | 19 + patches/type_asserts.patch | 5 +- scripts/gen_openapi_server.sh | 22 + templates/go-server/controller-api.mustache | 450 ++++++++++++++++++ 8 files changed, 511 insertions(+), 13 deletions(-) create mode 100755 scripts/gen_openapi_server.sh create mode 100644 templates/go-server/controller-api.mustache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b40ec0727..783a3b439 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,5 @@ + +exclude: '^patches/' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 diff --git a/Dockerfile b/Dockerfile index 93f0143ba..e12191d60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,6 +34,9 @@ COPY cmd/ cmd/ COPY api/ api/ COPY internal/ internal/ COPY pkg/ pkg/ +COPY scripts/ scripts/ +COPY patches/ patches/ +COPY templates/ templates/ # Build USER root diff --git a/Makefile b/Makefile index 4e59ae604..e99a021d9 100644 --- a/Makefile +++ b/Makefile @@ -67,18 +67,18 @@ gen/converter: gen/grpc internal/converter/generated/converter.go # validate the openapi schema .PHONY: openapi/validate openapi/validate: bin/openapi-generator-cli - openapi-generator-cli validate -i api/openapi/model-registry.yaml + @openapi-generator-cli validate -i api/openapi/model-registry.yaml # generate the openapi server implementation -# note: run manually only when model-registry.yaml api changes, for model changes gen/openapi is run automatically .PHONY: gen/openapi-server gen/openapi-server: bin/openapi-generator-cli openapi/validate - openapi-generator-cli generate \ - -i api/openapi/model-registry.yaml -g go-server -o internal/server/openapi --package-name openapi --global-property models \ - --ignore-file-override ./.openapi-generator-ignore --additional-properties=outputAsLibrary=true,enumClassPrefix=true,router=chi,sourceFolder=,onlyInterfaces=true,isGoSubmodule=true,enumClassPrefix=true,useOneOfDiscriminatorLookup=true \ - --template-dir ./templates/go-server - ./scripts/gen_type_asserts.sh - gofmt -w internal/server/openapi + @if git diff --cached --name-only | grep -q "api/openapi/model-registry.yaml" || \ + git diff --name-only | grep -q "api/openapi/model-registry.yaml" || \ + [ -n "${FORCE_SERVER_GENERATION}" ]; then \ + ROOT_FOLDER="." ./scripts/gen_openapi_server.sh; \ + else \ + echo "INFO api/openapi/model-registry.yaml is not staged or modified, will not re-generate server"; \ + fi # generate the openapi schema model and client .PHONY: gen/openapi @@ -156,7 +156,7 @@ build/odh: vet go build .PHONY: gen -gen: deps gen/grpc gen/openapi gen/converter +gen: deps gen/grpc gen/openapi gen/openapi-server gen/converter go generate ./... .PHONY: lint diff --git a/internal/server/openapi/api_model_registry_service.go b/internal/server/openapi/api_model_registry_service.go index e079b0902..62990ee91 100644 --- a/internal/server/openapi/api_model_registry_service.go +++ b/internal/server/openapi/api_model_registry_service.go @@ -3,7 +3,7 @@ * * REST API for Model Registry to create and manage ML model metadata * - * API version: 1.0.0 + * API version: v1alpha1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ @@ -15,6 +15,7 @@ import ( "strings" "github.com/go-chi/chi/v5" + model "github.com/opendatahub-io/model-registry/pkg/openapi" ) @@ -692,7 +693,7 @@ func (c *ModelRegistryServiceAPIController) GetModelVersion(w http.ResponseWrite EncodeJSONResponse(result.Body, &result.Code, w) } -// GetModelVersionArtifacts - List All ModelVersion's artifacts +// GetModelVersionArtifacts - List all artifacts associated with the `ModelVersion` func (c *ModelRegistryServiceAPIController) GetModelVersionArtifacts(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() modelversionIdParam := chi.URLParam(r, "modelversionId") diff --git a/internal/server/openapi/type_asserts.go b/internal/server/openapi/type_asserts.go index 38ed51dd9..6d08bbb2c 100644 --- a/internal/server/openapi/type_asserts.go +++ b/internal/server/openapi/type_asserts.go @@ -191,6 +191,25 @@ func AssertBaseResourceUpdateConstraints(obj model.BaseResourceUpdate) error { return nil } +// AssertDocArtifactRequired checks if the required fields are not zero-ed +func AssertDocArtifactRequired(obj model.DocArtifact) error { + elements := map[string]interface{}{ + "artifactType": obj.ArtifactType, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + return nil +} + +// AssertDocArtifactConstraints checks if the values respects the defined constraints +func AssertDocArtifactConstraints(obj model.DocArtifact) error { + return nil +} + // AssertErrorRequired checks if the required fields are not zero-ed func AssertErrorRequired(obj model.Error) error { elements := map[string]interface{}{ diff --git a/patches/type_asserts.patch b/patches/type_asserts.patch index 8735b1c23..c7e5d9464 100644 --- a/patches/type_asserts.patch +++ b/patches/type_asserts.patch @@ -1,9 +1,9 @@ diff --git a/internal/server/openapi/type_asserts.go b/internal/server/openapi/type_asserts.go -index 4318f15..2aba64a 100644 +index 6e8ecb1..6d08bbb 100644 --- a/internal/server/openapi/type_asserts.go +++ b/internal/server/openapi/type_asserts.go @@ -18,15 +18,15 @@ import ( - + // AssertArtifactRequired checks if the required fields are not zero-ed func AssertArtifactRequired(obj model.Artifact) error { - elements := map[string]interface{}{ @@ -26,3 +26,4 @@ index 4318f15..2aba64a 100644 + // } return nil } + diff --git a/scripts/gen_openapi_server.sh b/scripts/gen_openapi_server.sh new file mode 100755 index 000000000..6bac6684c --- /dev/null +++ b/scripts/gen_openapi_server.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +echo "Generating the OpenAPI server" + +ROOT_FOLDER="${ROOT_FOLDER:-..}" + +openapi-generator-cli generate \ + -i $ROOT_FOLDER/api/openapi/model-registry.yaml -g go-server -o $ROOT_FOLDER/internal/server/openapi --package-name openapi --global-property models,apis \ + --ignore-file-override $ROOT_FOLDER/.openapi-generator-ignore --additional-properties=outputAsLibrary=true,enumClassPrefix=true,router=chi,sourceFolder=,onlyInterfaces=true,isGoSubmodule=true,enumClassPrefix=true,useOneOfDiscriminatorLookup=true \ + --template-dir $ROOT_FOLDER/templates/go-server + +sed -i 's/, orderByParam/, model.OrderByField(orderByParam)/g' $ROOT_FOLDER/internal/server/openapi/api_model_registry_service.go +sed -i 's/, sortOrderParam/, model.SortOrder(sortOrderParam)/g' $ROOT_FOLDER/internal/server/openapi/api_model_registry_service.go + +echo "Assembling type_assert Go file" +./scripts/gen_type_asserts.sh + +gofmt -w $ROOT_FOLDER/internal/server/openapi + +echo "OpenAPI server generation completed" diff --git a/templates/go-server/controller-api.mustache b/templates/go-server/controller-api.mustache new file mode 100644 index 000000000..4735921ac --- /dev/null +++ b/templates/go-server/controller-api.mustache @@ -0,0 +1,450 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "encoding/json" + {{#isBodyParam}} + {{^required}} + "errors" + "io" + {{/required}} + {{/isBodyParam}} + "net/http" + "strings" + +{{#routers}} + {{#mux}} + "github.com/gorilla/mux" + {{/mux}} + {{#chi}} + "github.com/go-chi/chi/v5" + {{/chi}} + + model "github.com/opendatahub-io/model-registry/pkg/openapi" +{{/routers}} +) + +// {{classname}}Controller binds http requests to an api service and writes the service results to the http response +type {{classname}}Controller struct { + service {{classname}}Servicer + errorHandler ErrorHandler +} + +// {{classname}}Option for how the controller is set up. +type {{classname}}Option func(*{{classname}}Controller) + +// With{{classname}}ErrorHandler inject ErrorHandler into controller +func With{{classname}}ErrorHandler(h ErrorHandler) {{classname}}Option { + return func(c *{{classname}}Controller) { + c.errorHandler = h + } +} + +// New{{classname}}Controller creates a default api controller +func New{{classname}}Controller(s {{classname}}Servicer, opts ...{{classname}}Option) Router { + controller := &{{classname}}Controller{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the {{classname}}Controller +func (c *{{classname}}Controller) Routes() Routes { + return Routes{ +{{#operations}} + {{#operation}} + "{{operationId}}": Route{ + strings.ToUpper("{{httpMethod}}"), + "{{{basePathWithoutHost}}}{{{path}}}", + c.{{operationId}}, + }, + {{/operation}} +{{/operations}} + } +}{{#operations}}{{#operation}} + +// {{nickname}} - {{{summary}}} +{{#isDeprecated}} +// Deprecated +{{/isDeprecated}} +func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Request) { + {{#hasFormParams}} + {{#isMultipart}} + if err := r.ParseMultipartForm(32 << 20); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isMultipart}} + {{^isMultipart}} + if err := r.ParseForm(); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isMultipart}} + {{/hasFormParams}} + {{#routers}} + {{#mux}} + {{#hasPathParams}} + params := mux.Vars(r) + {{/hasPathParams}} + {{/mux}} + {{/routers}} + {{#hasQueryParams}} + query := r.URL.Query() + {{/hasQueryParams}} + {{#allParams}} + {{#isPathParam}} + {{#isNumber}} + {{paramName}}Param, err := parseNumericParameter[float32]( + {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}},{{#defaultValue}} + WithDefaultOrParse[float32]({{defaultValue}}, parseFloat32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float32](parseFloat32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float32](parseFloat32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isNumber}} + {{#isFloat}} + {{paramName}}Param, err := parseNumericParameter[float32]( + {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}},{{#defaultValue}} + WithDefaultOrParse[float32]({{defaultValue}}, parseFloat32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float32](parseFloat32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float32](parseFloat32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isFloat}} + {{#isDouble}} + {{paramName}}Param, err := parseNumericParameter[float64]( + {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}},{{#defaultValue}} + WithDefaultOrParse[float64]({{defaultValue}}, parseFloat64),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float64](parseFloat64),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float64](parseFloat64),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isDouble}} + {{#isLong}} + {{paramName}}Param, err := parseNumericParameter[int64]( + {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}},{{#defaultValue}} + WithDefaultOrParse[int64]({{defaultValue}}, parseInt64),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[int64](parseInt64),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[int64](parseInt64),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[int64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isLong}} + {{#isInteger}} + {{paramName}}Param, err := parseNumericParameter[int32]( + {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}},{{#defaultValue}} + WithDefaultOrParse[int32]({{defaultValue}}, parseInt32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[int32](parseInt32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[int32](parseInt32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[int32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isInteger}} + {{^isNumber}} + {{^isFloat}} + {{^isDouble}} + {{^isLong}} + {{^isInteger}} + {{paramName}}Param := {{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}} + {{/isInteger}} + {{/isLong}} + {{/isDouble}} + {{/isFloat}} + {{/isNumber}} + {{/isPathParam}} + {{#isQueryParam}} + {{#isNumber}} + {{paramName}}Param, err := parseNumericParameter[float32]( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[float32]({{defaultValue}}, parseFloat32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float32](parseFloat32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float32](parseFloat32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isNumber}} + {{#isFloat}} + {{paramName}}Param, err := parseNumericParameter[float32]( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[float32]({{defaultValue}}, parseFloat32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float32](parseFloat32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float32](parseFloat32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isFloat}} + {{#isDouble}} + {{paramName}}Param, err := parseNumericParameter[float64]( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[float64]({{defaultValue}}, parseFloat64),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[float64](parseFloat64),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[float64](parseFloat64),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[float64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isDouble}} + {{#isLong}} + {{paramName}}Param, err := parseNumericParameter[int64]( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[int64]({{defaultValue}}, parseInt64),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[int64](parseInt64),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[int64](parseInt64),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[int64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isLong}} + {{#isInteger}} + {{paramName}}Param, err := parseNumericParameter[int32]( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[int32]({{defaultValue}}, parseInt32),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[int32](parseInt32),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[int32](parseInt32),{{/required}}{{/defaultValue}}{{#minimum}} + WithMinimum[int32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isInteger}} + {{#isBoolean}} + {{paramName}}Param, err := parseBoolParameter( + query.Get("{{baseName}}"),{{#defaultValue}} + WithDefaultOrParse[bool]({{defaultValue}}, parseBool),{{/defaultValue}}{{^defaultValue}}{{#required}} + WithRequire[bool](parseBool),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}} + WithParse[bool](parseBool),{{/required}}{{/defaultValue}} + ) + if err != nil { + w.WriteHeader(500) + return + } + {{/isBoolean}} + {{#isArray}} + {{#items.isNumber}} + {{paramName}}Param, err := parseNumericArrayParameter[float32]( + query.Get("{{baseName}}"), ",", {{required}}, + WithParse[float32](parseFloat32),{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/items.isNumber}} + {{#items.isFloat}} + {{paramName}}Param, err := parseNumericArrayParameter[float32]( + query.Get("{{baseName}}"), ",", {{required}}, + WithParse[float32](parseFloat32),{{#minimum}} + WithMinimum[float32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/items.isFloat}} + {{#items.isDouble}} + {{paramName}}Param, err := parseNumericArrayParameter[float64]( + query.Get("{{baseName}}"), ",", {{required}}, + WithParse[float64](parseFloat64),{{#minimum}} + WithMinimum[float64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[float64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/items.isDouble}} + {{#items.isLong}} + {{paramName}}Param, err := parseNumericArrayParameter[int64]( + query.Get("{{baseName}}"), ",", {{required}}, + WithParse[int64](parseInt64),{{#minimum}} + WithMinimum[int64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/items.isLong}} + {{#items.isInteger}} + {{paramName}}Param, err := parseNumericArrayParameter[int32]( + query.Get("{{baseName}}"), ",", {{required}}, + WithParse[int32](parseInt32),{{#minimum}} + WithMinimum[int32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/items.isInteger}} + {{^items.isNumber}} + {{^items.isFloat}} + {{^items.isDouble}} + {{^items.isLong}} + {{^items.isInteger}} + {{paramName}}Param := strings.Split(query.Get("{{baseName}}"), ",") + {{/items.isInteger}} + {{/items.isLong}} + {{/items.isDouble}} + {{/items.isFloat}} + {{/items.isNumber}} + {{/isArray}} + {{^isNumber}} + {{^isFloat}} + {{^isDouble}} + {{^isLong}} + {{^isInteger}} + {{^isBoolean}} + {{^isArray}} + {{#defaultValue}} + {{paramName}}Param := "{{defaultValue}}" + if query.Has("{{baseName}}") { + {{paramName}}Param = query.Get("{{baseName}}") + } + {{/defaultValue}} + {{^defaultValue}} + {{paramName}}Param := query.Get("{{baseName}}") + {{/defaultValue}} + {{/isArray}} + {{/isBoolean}} + {{/isInteger}} + {{/isLong}} + {{/isDouble}} + {{/isFloat}} + {{/isNumber}} + {{/isQueryParam}} + {{#isFormParam}} + {{#isFile}}{{#isArray}} + {{paramName}}Param, err := ReadFormFilesToTempFiles(r, "{{baseName}}"){{/isArray}}{{^isArray}} + {{paramName}}Param, err := ReadFormFileToTempFile(r, "{{baseName}}") + {{/isArray}} + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isFile}} + {{#isLong}}{{#isArray}} + {{paramName}}Param, err := parseNumericArrayParameter[int64]( + r.FormValue("{{baseName}}"), ",", {{required}}, + WithParse[int64](parseInt64),{{#minimum}} + WithMinimum[int64]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int64]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isArray}}{{/isLong}} + {{#isInteger}}{{#isArray}} + {{paramName}}Param, err := parseNumericArrayParameter[int32]( + r.FormValue("{{baseName}}"), ",", {{required}}, + WithParse[int32](parseInt32),{{#minimum}} + WithMinimum[int32]({{minimum}}),{{/minimum}}{{#maximum}} + WithMaximum[int32]({{maximum}}),{{/maximum}} + ) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{/isArray}}{{/isInteger}} + {{^isFile}} + {{^isLong}} + {{paramName}}Param := r.FormValue("{{baseName}}") + {{/isLong}} + {{/isFile}} + {{/isFormParam}} + {{#isHeaderParam}} + {{paramName}}Param := r.Header.Get("{{baseName}}") + {{/isHeaderParam}} + {{#isBodyParam}} + {{paramName}}Param := model.{{dataType}}{} + d := json.NewDecoder(r.Body) + {{^isAdditionalPropertiesTrue}} + d.DisallowUnknownFields() + {{/isAdditionalPropertiesTrue}} + if err := d.Decode(&{{paramName}}Param); err != nil {{^required}}&& !errors.Is(err, io.EOF) {{/required}}{ + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + {{#isArray}} + {{#items.isModel}} + for _, el := range {{paramName}}Param { + if err := Assert{{baseType}}Required(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + {{/items.isModel}} + {{/isArray}} + {{^isArray}} + {{#isModel}} + if err := Assert{{baseType}}Required({{paramName}}Param); err != nil { + c.errorHandler(w, r, err, nil) + return + } + if err := Assert{{baseType}}Constraints({{paramName}}Param); err != nil { + c.errorHandler(w, r, err, nil) + return + } + {{/isModel}} + {{/isArray}} + {{/isBodyParam}} + {{/allParams}} + result, err := c.service.{{nickname}}(r.Context(){{#allParams}}, {{paramName}}Param{{/allParams}}) + // 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,{{#addResponseHeaders}} result.Headers,{{/addResponseHeaders}} w) +}{{/operation}}{{/operations}}