forked from loafle/openapi-generator-original
[go-server] Support min/max/defaults for values (#15185)
* [go-server] Support min/max/defaults for values Enforce, for the go-server, to check the minimum and maximum values specified in the openapi description. Also apply the default if the parameter is not passed. Fix #14013 * Fix merge conflict Co-authored-by: Ween Jiann <16207788+lwj5@users.noreply.github.com> * Improve UnmarshalJSON implementation Co-authored-by: Ween Jiann <16207788+lwj5@users.noreply.github.com> * Improve default value handling for string Co-authored-by: Ween Jiann <16207788+lwj5@users.noreply.github.com> * Fix suggested changes * rework option pattern * add imports based on types/min max values --------- Co-authored-by: Ween Jiann <16207788+lwj5@users.noreply.github.com>
This commit is contained in:
@@ -17,19 +17,39 @@
|
||||
|
||||
package org.openapitools.codegen.languages;
|
||||
|
||||
import io.swagger.v3.oas.models.media.ComposedSchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import org.openapitools.codegen.*;
|
||||
import org.openapitools.codegen.meta.features.*;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.openapitools.codegen.CliOption;
|
||||
import org.openapitools.codegen.CodegenConstants;
|
||||
import org.openapitools.codegen.CodegenModel;
|
||||
import org.openapitools.codegen.CodegenOperation;
|
||||
import org.openapitools.codegen.CodegenParameter;
|
||||
import org.openapitools.codegen.CodegenProperty;
|
||||
import org.openapitools.codegen.CodegenType;
|
||||
import org.openapitools.codegen.SupportingFile;
|
||||
import org.openapitools.codegen.meta.features.DocumentationFeature;
|
||||
import org.openapitools.codegen.meta.features.GlobalFeature;
|
||||
import org.openapitools.codegen.meta.features.ParameterFeature;
|
||||
import org.openapitools.codegen.meta.features.SchemaSupportFeature;
|
||||
import org.openapitools.codegen.meta.features.SecurityFeature;
|
||||
import org.openapitools.codegen.meta.features.WireFormatFeature;
|
||||
import org.openapitools.codegen.model.ModelMap;
|
||||
import org.openapitools.codegen.model.ModelsMap;
|
||||
import org.openapitools.codegen.model.OperationMap;
|
||||
import org.openapitools.codegen.model.OperationsMap;
|
||||
import org.openapitools.codegen.utils.ModelUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import io.swagger.v3.oas.models.media.ComposedSchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
|
||||
public class GoServerCodegen extends AbstractGoCodegen {
|
||||
|
||||
@@ -114,7 +134,7 @@ public class GoServerCodegen extends AbstractGoCodegen {
|
||||
optAddResponseHeaders.defaultValue(addResponseHeaders.toString());
|
||||
cliOptions.add(optAddResponseHeaders);
|
||||
|
||||
|
||||
|
||||
// option to exclude service factories; only interfaces are rendered
|
||||
CliOption optOnlyInterfaces = new CliOption("onlyInterfaces", "Exclude default service creators from output; only generate interfaces");
|
||||
optOnlyInterfaces.setType("bool");
|
||||
@@ -287,6 +307,38 @@ public class GoServerCodegen extends AbstractGoCodegen {
|
||||
.doNotOverwrite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelsMap postProcessModels(ModelsMap objs) {
|
||||
// The superclass determines the list of required golang imports. The actual list of imports
|
||||
// depends on which types are used. So super.postProcessModels must be invoked at the beginning
|
||||
// of this method.
|
||||
objs = super.postProcessModels(objs);
|
||||
|
||||
List<Map<String, String>> imports = objs.getImports();
|
||||
|
||||
for (ModelMap m : objs.getModels()) {
|
||||
imports.add(createMapping("import", "encoding/json"));
|
||||
|
||||
CodegenModel model = m.getModel();
|
||||
if (model.isEnum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Boolean importErrors = false;
|
||||
|
||||
for (CodegenProperty param : Iterables.concat(model.vars, model.allVars, model.requiredVars, model.optionalVars)) {
|
||||
if (param.isNumeric && ((param.minimum != null && param.minimum != "") || (param.maximum != null && param.maximum != ""))) {
|
||||
importErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (importErrors) {
|
||||
imports.add(createMapping("import", "errors"));
|
||||
}
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
|
||||
objs = super.postProcessOperationsWithModels(objs, allModels);
|
||||
|
||||
@@ -93,35 +93,70 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
{{#allParams}}
|
||||
{{#isPathParam}}
|
||||
{{#isNumber}}
|
||||
{{paramName}}Param, err := parseFloat32Parameter({{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}}, {{required}})
|
||||
if err != nil {
|
||||
{{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 := parseFloat32Parameter({{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}}, {{required}})
|
||||
{{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 := parseFloat64Parameter({{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}}, {{required}})
|
||||
{{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 := parseInt64Parameter({{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}}, {{required}})
|
||||
{{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 := parseInt32Parameter({{#routers}}{{#mux}}params["{{baseName}}"]{{/mux}}{{#chi}}chi.URLParam(r, "{{baseName}}"){{/chi}}{{/routers}}, {{required}})
|
||||
{{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
|
||||
@@ -141,42 +176,82 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
{{/isPathParam}}
|
||||
{{#isQueryParam}}
|
||||
{{#isNumber}}
|
||||
{{paramName}}Param, err := parseFloat32Parameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseFloat32Parameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseFloat64Parameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseInt64Parameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseInt32Parameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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}}"), {{required}})
|
||||
{{paramName}}Param, err := parseBoolParameter(
|
||||
query.Get("{{baseName}}"),{{#defaultValue}}
|
||||
WithDefaultOrParse[bool]({{defaultValue}}, parseBool),{{/defaultValue}}{{^defaultValue}}{{#required}}
|
||||
WithRequire[bool](parseBool),{{/required}}{{/defaultValue}}{{^defaultValue}}{{^required}}
|
||||
WithParse[float32](parseBool),{{/required}}{{/defaultValue}}
|
||||
)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
@@ -184,35 +259,60 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
{{/isBoolean}}
|
||||
{{#isArray}}
|
||||
{{#items.isNumber}}
|
||||
{{paramName}}Param, err := parseFloat32ArrayParameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseFloat32ArrayParameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseFloat64ArrayParameter(query.Get("{{baseName}}"), {{required}})
|
||||
{{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 := parseInt64ArrayParameter(query.Get("{{baseName}}"), ",", {{required}})
|
||||
{{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 := parseInt32ArrayParameter(query.Get("{{baseName}}"), ",", {{required}})
|
||||
{{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
|
||||
@@ -237,7 +337,15 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
{{^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}}
|
||||
@@ -257,21 +365,29 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
}
|
||||
{{/isFile}}
|
||||
{{#isLong}}{{#isArray}}
|
||||
{{paramName}}Param, err := parseInt64ArrayParameter(r.FormValue("{{baseName}}"), ",", {{required}}){{/isArray}}{{^isArray}}
|
||||
{{paramName}}Param, err := parseInt64Parameter(r.FormValue("{{baseName}}"), {{required}}){{/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
|
||||
}
|
||||
{{/isLong}}
|
||||
{{/isArray}}{{/isLong}}
|
||||
{{#isInteger}}{{#isArray}}
|
||||
{{paramName}}Param, err := parseInt32ArrayParameter(r.FormValue("{{baseName}}"), ",", {{required}}){{/isArray}}{{^isArray}}
|
||||
{{paramName}}Param, err := parseInt32Parameter(r.FormValue("{{baseName}}"), {{required}}){{/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
|
||||
}
|
||||
{{/isInteger}}
|
||||
{{/isArray}}{{/isInteger}}
|
||||
{{^isFile}}
|
||||
{{^isLong}}
|
||||
{{paramName}}Param := r.FormValue("{{baseName}}")
|
||||
@@ -306,6 +422,10 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
|
||||
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}}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{{>partial_header}}
|
||||
package {{packageName}}
|
||||
|
||||
{{#models}}{{#imports}}
|
||||
{{#-first}}import (
|
||||
{{/-first}} "{{import}}"{{#-last}}
|
||||
)
|
||||
{{/-last}}{{/imports}}{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}}
|
||||
{{/-last}}{{/imports}}
|
||||
|
||||
{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}}
|
||||
type {{{classname}}} {{^format}}{{dataType}}{{/format}}{{{format}}}
|
||||
|
||||
// List of {{{classname}}}
|
||||
@@ -36,6 +39,30 @@ type {{classname}} struct {
|
||||
{{/vars}}
|
||||
}{{/isEnum}}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *{{classname}}) UnmarshalJSON(data []byte) error {
|
||||
{{#vars}}
|
||||
{{#defaultValue}}
|
||||
{{^isArray}}
|
||||
{{#isBoolean}}
|
||||
m.{{name}} = {{{.}}}
|
||||
{{/isBoolean}}
|
||||
{{#isNumeric}}
|
||||
m.{{name}} = {{{.}}}
|
||||
{{/isNumeric}}
|
||||
{{^isBoolean}}
|
||||
{{^isNumeric}}
|
||||
m.{{name}} = "{{{.}}}"
|
||||
{{/isNumeric}}
|
||||
{{/isBoolean}}
|
||||
{{/isArray}}
|
||||
{{/defaultValue}}
|
||||
{{/vars}}
|
||||
|
||||
type Alias {{classname}} // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// Assert{{classname}}Required checks if the required fields are not zero-ed
|
||||
func Assert{{classname}}Required(obj {{classname}}) error {
|
||||
{{#hasRequired}}
|
||||
@@ -117,6 +144,23 @@ func Assert{{classname}}Required(obj {{classname}}) error {
|
||||
{{/items.isModel}}
|
||||
{{/isArray}}
|
||||
{{/isNullable}}
|
||||
{{/Vars}}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assert{{classname}}Constraints checks if the values respects the defined constraints
|
||||
func Assert{{classname}}Constraints(obj {{classname}}) error {
|
||||
{{#Vars}}
|
||||
{{#minimum}}
|
||||
if {{#isNullable}}obj.{{name}} != nil && *{{/isNullable}}obj.{{name}} < {{minimum}} {
|
||||
return &ParsingError{Err: errors.New(errMsgMinValueConstraint)}
|
||||
}
|
||||
{{/minimum}}
|
||||
{{#maximum}}
|
||||
if {{#isNullable}}obj.{{name}} != nil && *{{/isNullable}}obj.{{name}} > {{maximum}} {
|
||||
return &ParsingError{Err: errors.New(errMsgMaxValueConstraint)}
|
||||
}
|
||||
{{/maximum}}
|
||||
{{/Vars}}
|
||||
return nil
|
||||
}{{/model}}{{/models}}
|
||||
|
||||
@@ -43,6 +43,8 @@ type Router interface {
|
||||
}
|
||||
|
||||
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) {{#routers}}{{#mux}}*mux.Router{{/mux}}{{#chi}}chi.Router{{/chi}}{{/routers}} {
|
||||
@@ -168,74 +170,119 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// parseFloatParameter parses a string parameter to an int64.
|
||||
func parseFloatParameter(param string, bitSize int, required bool) (float64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseFloat(param, bitSize)
|
||||
type Number interface {
|
||||
~int32 | ~int64 | ~float32 | ~float64
|
||||
}
|
||||
|
||||
// parseFloat64Parameter parses a string parameter to an float64.
|
||||
func parseFloat64Parameter(param string, required bool) (float64, error) {
|
||||
return parseFloatParameter(param, 64, required)
|
||||
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) {
|
||||
return strconv.ParseFloat(param, 64)
|
||||
}
|
||||
|
||||
// parseFloat32Parameter parses a string parameter to an float32.
|
||||
func parseFloat32Parameter(param string, required bool) (float32, error) {
|
||||
val, err := parseFloatParameter(param, 32, required)
|
||||
return float32(val), err
|
||||
// parseFloat32 parses a string parameter to an float32.
|
||||
func parseFloat32(param string) (float32, error) {
|
||||
v, err := strconv.ParseFloat(param, 32)
|
||||
return float32(v), err
|
||||
}
|
||||
|
||||
// parseIntParameter parses a string parameter to an int64.
|
||||
func parseIntParameter(param string, bitSize int, required bool) (int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseInt(param, 10, bitSize)
|
||||
// parseInt64 parses a string parameter to an int64.
|
||||
func parseInt64(param string) (int64, error) {
|
||||
return strconv.ParseInt(param, 10, 64)
|
||||
}
|
||||
|
||||
// parseInt64Parameter parses a string parameter to an int64.
|
||||
func parseInt64Parameter(param string, required bool) (int64, error) {
|
||||
return parseIntParameter(param, 64, required)
|
||||
}
|
||||
|
||||
// parseInt32Parameter parses a string parameter to an int32.
|
||||
func parseInt32Parameter(param string, required bool) (int32, error) {
|
||||
val, err := parseIntParameter(param, 32, required)
|
||||
// parseInt32 parses a string parameter to an int32.
|
||||
func parseInt32(param string) (int32, error) {
|
||||
val, err := strconv.ParseInt(param, 10, 32)
|
||||
return int32(val), err
|
||||
}
|
||||
|
||||
// parseBoolParameter parses a string parameter to a bool
|
||||
func parseBoolParameter(param string, required bool) (bool, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return false, errors.New(errMsgRequiredMissing)
|
||||
// parseBool parses a string parameter to an bool.
|
||||
func parseBool(param string) (bool, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
v, err := parse(actual)
|
||||
return v, false, err
|
||||
}
|
||||
}
|
||||
|
||||
val, err := strconv.ParseBool(param)
|
||||
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 false, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return bool(val), nil
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// parseFloat64ArrayParameter parses a string parameter containing array of values to []Float64.
|
||||
func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64, error) {
|
||||
// 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)
|
||||
@@ -245,88 +292,24 @@ func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64,
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float64, len(str))
|
||||
values := make([]T, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 64); err != nil {
|
||||
v, ok, err := fn(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = v
|
||||
}
|
||||
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values[i] = v
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
// parseFloat32ArrayParameter parses a string parameter containing array of values to []float32.
|
||||
func parseFloat32ArrayParameter(param, delim string, required bool) ([]float32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = float32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
|
||||
// parseInt64ArrayParameter parses a string parameter containing array of values to []int64.
|
||||
func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int64, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
}
|
||||
|
||||
// parseInt32ArrayParameter parses a string parameter containing array of values to []int32.
|
||||
func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = int32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
return values, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/OpenAPITools/openapi-generator/samples/client/petstore/go/go-petstore v0.0.0-00010101000000-000000000000
|
||||
github.com/go-openapi/swag v0.22.3
|
||||
github.com/stretchr/testify v1.8.1
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/oauth2 v0.2.0
|
||||
|
||||
@@ -40,6 +40,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -51,6 +52,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -106,14 +109,20 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -405,6 +414,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.AddPet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -119,7 +123,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
// DeletePet - Deletes a pet
|
||||
func (c *PetAPIController) DeletePet(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
petIdParam, err := parseInt64Parameter(params["petId"], true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
params["petId"],
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -167,7 +174,10 @@ func (c *PetAPIController) FindPetsByTags(w http.ResponseWriter, r *http.Request
|
||||
// GetPetById - Find pet by ID
|
||||
func (c *PetAPIController) GetPetById(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
petIdParam, err := parseInt64Parameter(params["petId"], true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
params["petId"],
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -195,6 +205,10 @@ func (c *PetAPIController) UpdatePet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdatePet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -212,13 +226,20 @@ func (c *PetAPIController) UpdatePetWithForm(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
params := mux.Vars(r)
|
||||
petIdParam, err := parseInt64Parameter(params["petId"], true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
params["petId"],
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
nameParam := r.FormValue("name")
|
||||
statusParam := r.FormValue("status")
|
||||
|
||||
|
||||
nameParam := r.FormValue("name")
|
||||
|
||||
|
||||
statusParam := r.FormValue("status")
|
||||
result, err := c.service.UpdatePetWithForm(r.Context(), petIdParam, nameParam, statusParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -236,19 +257,26 @@ func (c *PetAPIController) UploadFile(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
params := mux.Vars(r)
|
||||
petIdParam, err := parseInt64Parameter(params["petId"], true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
params["petId"],
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
fileParam, err := ReadFormFileToTempFile(r, "file")
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
|
||||
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, err, &result)
|
||||
|
||||
@@ -102,7 +102,12 @@ func (c *StoreAPIController) GetInventory(w http.ResponseWriter, r *http.Request
|
||||
// GetOrderById - Find purchase order by ID
|
||||
func (c *StoreAPIController) GetOrderById(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
orderIdParam, err := parseInt64Parameter(params["orderId"], true)
|
||||
orderIdParam, err := parseNumericParameter[int64](
|
||||
params["orderId"],
|
||||
WithRequire[int64](parseInt64),
|
||||
WithMinimum[int64](1),
|
||||
WithMaximum[int64](5),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -130,6 +135,10 @@ func (c *StoreAPIController) PlaceOrder(w http.ResponseWriter, r *http.Request)
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertOrderConstraints(orderParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.PlaceOrder(r.Context(), orderParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *UserAPIController) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.CreateUser(r.Context(), userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -236,6 +240,10 @@ func (c *UserAPIController) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdateUser(r.Context(), usernameParam, userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// ApiResponse - Describes the result of uploading an image resource
|
||||
type ApiResponse struct {
|
||||
|
||||
@@ -19,7 +26,19 @@ type ApiResponse struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *ApiResponse) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias ApiResponse // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertApiResponseRequired checks if the required fields are not zero-ed
|
||||
func AssertApiResponseRequired(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertApiResponseConstraints checks if the values respects the defined constraints
|
||||
func AssertApiResponseConstraints(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Category - A category for a pet
|
||||
type Category struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Category struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Category) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Category // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertCategoryRequired checks if the required fields are not zero-ed
|
||||
func AssertCategoryRequired(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertCategoryConstraints checks if the values respects the defined constraints
|
||||
func AssertCategoryConstraints(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Order - An order for a pets from the pet store
|
||||
type Order struct {
|
||||
|
||||
@@ -30,7 +34,20 @@ type Order struct {
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Order) UnmarshalJSON(data []byte) error {
|
||||
m.Complete = false
|
||||
|
||||
type Alias Order // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertOrderRequired checks if the required fields are not zero-ed
|
||||
func AssertOrderRequired(obj Order) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertOrderConstraints checks if the values respects the defined constraints
|
||||
func AssertOrderConstraints(obj Order) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Pet - A pet for sale in the pet store
|
||||
type Pet struct {
|
||||
|
||||
@@ -27,6 +34,13 @@ type Pet struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Pet) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Pet // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertPetRequired checks if the required fields are not zero-ed
|
||||
func AssertPetRequired(obj Pet) error {
|
||||
elements := map[string]interface{}{
|
||||
@@ -49,3 +63,8 @@ func AssertPetRequired(obj Pet) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertPetConstraints checks if the values respects the defined constraints
|
||||
func AssertPetConstraints(obj Pet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Tag - A tag for a pet
|
||||
type Tag struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Tag struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Tag) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Tag // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertTagRequired checks if the required fields are not zero-ed
|
||||
func AssertTagRequired(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertTagConstraints checks if the values respects the defined constraints
|
||||
func AssertTagConstraints(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// User - A User who is purchasing from the pet store
|
||||
type User struct {
|
||||
|
||||
@@ -30,7 +37,19 @@ type User struct {
|
||||
UserStatus int32 `json:"userStatus,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *User) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias User // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertUserRequired checks if the required fields are not zero-ed
|
||||
func AssertUserRequired(obj User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertUserConstraints checks if the values respects the defined constraints
|
||||
func AssertUserConstraints(obj User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ type Router interface {
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -136,74 +138,119 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// parseFloatParameter parses a string parameter to an int64.
|
||||
func parseFloatParameter(param string, bitSize int, required bool) (float64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseFloat(param, bitSize)
|
||||
type Number interface {
|
||||
~int32 | ~int64 | ~float32 | ~float64
|
||||
}
|
||||
|
||||
// parseFloat64Parameter parses a string parameter to an float64.
|
||||
func parseFloat64Parameter(param string, required bool) (float64, error) {
|
||||
return parseFloatParameter(param, 64, required)
|
||||
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) {
|
||||
return strconv.ParseFloat(param, 64)
|
||||
}
|
||||
|
||||
// parseFloat32Parameter parses a string parameter to an float32.
|
||||
func parseFloat32Parameter(param string, required bool) (float32, error) {
|
||||
val, err := parseFloatParameter(param, 32, required)
|
||||
return float32(val), err
|
||||
// parseFloat32 parses a string parameter to an float32.
|
||||
func parseFloat32(param string) (float32, error) {
|
||||
v, err := strconv.ParseFloat(param, 32)
|
||||
return float32(v), err
|
||||
}
|
||||
|
||||
// parseIntParameter parses a string parameter to an int64.
|
||||
func parseIntParameter(param string, bitSize int, required bool) (int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseInt(param, 10, bitSize)
|
||||
// parseInt64 parses a string parameter to an int64.
|
||||
func parseInt64(param string) (int64, error) {
|
||||
return strconv.ParseInt(param, 10, 64)
|
||||
}
|
||||
|
||||
// parseInt64Parameter parses a string parameter to an int64.
|
||||
func parseInt64Parameter(param string, required bool) (int64, error) {
|
||||
return parseIntParameter(param, 64, required)
|
||||
}
|
||||
|
||||
// parseInt32Parameter parses a string parameter to an int32.
|
||||
func parseInt32Parameter(param string, required bool) (int32, error) {
|
||||
val, err := parseIntParameter(param, 32, required)
|
||||
// parseInt32 parses a string parameter to an int32.
|
||||
func parseInt32(param string) (int32, error) {
|
||||
val, err := strconv.ParseInt(param, 10, 32)
|
||||
return int32(val), err
|
||||
}
|
||||
|
||||
// parseBoolParameter parses a string parameter to a bool
|
||||
func parseBoolParameter(param string, required bool) (bool, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return false, errors.New(errMsgRequiredMissing)
|
||||
// parseBool parses a string parameter to an bool.
|
||||
func parseBool(param string) (bool, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
v, err := parse(actual)
|
||||
return v, false, err
|
||||
}
|
||||
}
|
||||
|
||||
val, err := strconv.ParseBool(param)
|
||||
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 false, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return bool(val), nil
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// parseFloat64ArrayParameter parses a string parameter containing array of values to []Float64.
|
||||
func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64, error) {
|
||||
// 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)
|
||||
@@ -213,88 +260,24 @@ func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64,
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float64, len(str))
|
||||
values := make([]T, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 64); err != nil {
|
||||
v, ok, err := fn(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = v
|
||||
}
|
||||
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values[i] = v
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
// parseFloat32ArrayParameter parses a string parameter containing array of values to []float32.
|
||||
func parseFloat32ArrayParameter(param, delim string, required bool) ([]float32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = float32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
|
||||
// parseInt64ArrayParameter parses a string parameter containing array of values to []int64.
|
||||
func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int64, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
}
|
||||
|
||||
// parseInt32ArrayParameter parses a string parameter containing array of values to []int32.
|
||||
func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = int32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
return values, nil
|
||||
}
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.AddPet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -118,7 +122,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// DeletePet - Deletes a pet
|
||||
func (c *PetAPIController) DeletePet(w http.ResponseWriter, r *http.Request) {
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -165,7 +172,10 @@ func (c *PetAPIController) FindPetsByTags(w http.ResponseWriter, r *http.Request
|
||||
|
||||
// GetPetById - Find pet by ID
|
||||
func (c *PetAPIController) GetPetById(w http.ResponseWriter, r *http.Request) {
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -193,6 +203,10 @@ func (c *PetAPIController) UpdatePet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdatePet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -209,13 +223,20 @@ func (c *PetAPIController) UpdatePetWithForm(w http.ResponseWriter, r *http.Requ
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
nameParam := r.FormValue("name")
|
||||
statusParam := r.FormValue("status")
|
||||
|
||||
|
||||
nameParam := r.FormValue("name")
|
||||
|
||||
|
||||
statusParam := r.FormValue("status")
|
||||
result, err := c.service.UpdatePetWithForm(r.Context(), petIdParam, nameParam, statusParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -232,19 +253,26 @@ func (c *PetAPIController) UploadFile(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
fileParam, err := ReadFormFileToTempFile(r, "file")
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
|
||||
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, err, &result)
|
||||
|
||||
@@ -100,7 +100,12 @@ func (c *StoreAPIController) GetInventory(w http.ResponseWriter, r *http.Request
|
||||
|
||||
// GetOrderById - Find purchase order by ID
|
||||
func (c *StoreAPIController) GetOrderById(w http.ResponseWriter, r *http.Request) {
|
||||
orderIdParam, err := parseInt64Parameter(chi.URLParam(r, "orderId"), true)
|
||||
orderIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "orderId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
WithMinimum[int64](1),
|
||||
WithMaximum[int64](5),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -128,6 +133,10 @@ func (c *StoreAPIController) PlaceOrder(w http.ResponseWriter, r *http.Request)
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertOrderConstraints(orderParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.PlaceOrder(r.Context(), orderParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *UserAPIController) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.CreateUser(r.Context(), userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -233,6 +237,10 @@ func (c *UserAPIController) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdateUser(r.Context(), usernameParam, userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// ApiResponse - Describes the result of uploading an image resource
|
||||
type ApiResponse struct {
|
||||
|
||||
@@ -19,7 +26,19 @@ type ApiResponse struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *ApiResponse) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias ApiResponse // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertApiResponseRequired checks if the required fields are not zero-ed
|
||||
func AssertApiResponseRequired(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertApiResponseConstraints checks if the values respects the defined constraints
|
||||
func AssertApiResponseConstraints(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Category - A category for a pet
|
||||
type Category struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Category struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Category) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Category // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertCategoryRequired checks if the required fields are not zero-ed
|
||||
func AssertCategoryRequired(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertCategoryConstraints checks if the values respects the defined constraints
|
||||
func AssertCategoryConstraints(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Order - An order for a pets from the pet store
|
||||
type Order struct {
|
||||
|
||||
@@ -30,7 +34,20 @@ type Order struct {
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Order) UnmarshalJSON(data []byte) error {
|
||||
m.Complete = false
|
||||
|
||||
type Alias Order // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertOrderRequired checks if the required fields are not zero-ed
|
||||
func AssertOrderRequired(obj Order) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertOrderConstraints checks if the values respects the defined constraints
|
||||
func AssertOrderConstraints(obj Order) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Pet - A pet for sale in the pet store
|
||||
type Pet struct {
|
||||
|
||||
@@ -27,6 +34,13 @@ type Pet struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Pet) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Pet // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertPetRequired checks if the required fields are not zero-ed
|
||||
func AssertPetRequired(obj Pet) error {
|
||||
elements := map[string]interface{}{
|
||||
@@ -49,3 +63,8 @@ func AssertPetRequired(obj Pet) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertPetConstraints checks if the values respects the defined constraints
|
||||
func AssertPetConstraints(obj Pet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Tag - A tag for a pet
|
||||
type Tag struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Tag struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Tag) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Tag // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertTagRequired checks if the required fields are not zero-ed
|
||||
func AssertTagRequired(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertTagConstraints checks if the values respects the defined constraints
|
||||
func AssertTagConstraints(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// User - A User who is purchasing from the pet store
|
||||
type User struct {
|
||||
|
||||
@@ -30,7 +37,19 @@ type User struct {
|
||||
UserStatus int32 `json:"userStatus,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *User) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias User // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertUserRequired checks if the required fields are not zero-ed
|
||||
func AssertUserRequired(obj User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertUserConstraints checks if the values respects the defined constraints
|
||||
func AssertUserConstraints(obj User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ type Router interface {
|
||||
}
|
||||
|
||||
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) chi.Router {
|
||||
@@ -132,74 +134,119 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// parseFloatParameter parses a string parameter to an int64.
|
||||
func parseFloatParameter(param string, bitSize int, required bool) (float64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseFloat(param, bitSize)
|
||||
type Number interface {
|
||||
~int32 | ~int64 | ~float32 | ~float64
|
||||
}
|
||||
|
||||
// parseFloat64Parameter parses a string parameter to an float64.
|
||||
func parseFloat64Parameter(param string, required bool) (float64, error) {
|
||||
return parseFloatParameter(param, 64, required)
|
||||
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) {
|
||||
return strconv.ParseFloat(param, 64)
|
||||
}
|
||||
|
||||
// parseFloat32Parameter parses a string parameter to an float32.
|
||||
func parseFloat32Parameter(param string, required bool) (float32, error) {
|
||||
val, err := parseFloatParameter(param, 32, required)
|
||||
return float32(val), err
|
||||
// parseFloat32 parses a string parameter to an float32.
|
||||
func parseFloat32(param string) (float32, error) {
|
||||
v, err := strconv.ParseFloat(param, 32)
|
||||
return float32(v), err
|
||||
}
|
||||
|
||||
// parseIntParameter parses a string parameter to an int64.
|
||||
func parseIntParameter(param string, bitSize int, required bool) (int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseInt(param, 10, bitSize)
|
||||
// parseInt64 parses a string parameter to an int64.
|
||||
func parseInt64(param string) (int64, error) {
|
||||
return strconv.ParseInt(param, 10, 64)
|
||||
}
|
||||
|
||||
// parseInt64Parameter parses a string parameter to an int64.
|
||||
func parseInt64Parameter(param string, required bool) (int64, error) {
|
||||
return parseIntParameter(param, 64, required)
|
||||
}
|
||||
|
||||
// parseInt32Parameter parses a string parameter to an int32.
|
||||
func parseInt32Parameter(param string, required bool) (int32, error) {
|
||||
val, err := parseIntParameter(param, 32, required)
|
||||
// parseInt32 parses a string parameter to an int32.
|
||||
func parseInt32(param string) (int32, error) {
|
||||
val, err := strconv.ParseInt(param, 10, 32)
|
||||
return int32(val), err
|
||||
}
|
||||
|
||||
// parseBoolParameter parses a string parameter to a bool
|
||||
func parseBoolParameter(param string, required bool) (bool, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return false, errors.New(errMsgRequiredMissing)
|
||||
// parseBool parses a string parameter to an bool.
|
||||
func parseBool(param string) (bool, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
v, err := parse(actual)
|
||||
return v, false, err
|
||||
}
|
||||
}
|
||||
|
||||
val, err := strconv.ParseBool(param)
|
||||
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 false, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return bool(val), nil
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// parseFloat64ArrayParameter parses a string parameter containing array of values to []Float64.
|
||||
func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64, error) {
|
||||
// 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)
|
||||
@@ -209,88 +256,24 @@ func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64,
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float64, len(str))
|
||||
values := make([]T, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 64); err != nil {
|
||||
v, ok, err := fn(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = v
|
||||
}
|
||||
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values[i] = v
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
// parseFloat32ArrayParameter parses a string parameter containing array of values to []float32.
|
||||
func parseFloat32ArrayParameter(param, delim string, required bool) ([]float32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = float32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
|
||||
// parseInt64ArrayParameter parses a string parameter containing array of values to []int64.
|
||||
func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int64, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
}
|
||||
|
||||
// parseInt32ArrayParameter parses a string parameter containing array of values to []int32.
|
||||
func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = int32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
return values, nil
|
||||
}
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.AddPet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -118,7 +122,10 @@ func (c *PetAPIController) AddPet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// DeletePet - Deletes a pet
|
||||
func (c *PetAPIController) DeletePet(w http.ResponseWriter, r *http.Request) {
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -165,7 +172,10 @@ func (c *PetAPIController) FindPetsByTags(w http.ResponseWriter, r *http.Request
|
||||
|
||||
// GetPetById - Find pet by ID
|
||||
func (c *PetAPIController) GetPetById(w http.ResponseWriter, r *http.Request) {
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -193,6 +203,10 @@ func (c *PetAPIController) UpdatePet(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertPetConstraints(petParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdatePet(r.Context(), petParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -209,13 +223,20 @@ func (c *PetAPIController) UpdatePetWithForm(w http.ResponseWriter, r *http.Requ
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
nameParam := r.FormValue("name")
|
||||
statusParam := r.FormValue("status")
|
||||
|
||||
|
||||
nameParam := r.FormValue("name")
|
||||
|
||||
|
||||
statusParam := r.FormValue("status")
|
||||
result, err := c.service.UpdatePetWithForm(r.Context(), petIdParam, nameParam, statusParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -232,19 +253,26 @@ func (c *PetAPIController) UploadFile(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
petIdParam, err := parseInt64Parameter(chi.URLParam(r, "petId"), true)
|
||||
petIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "petId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
|
||||
additionalMetadataParam := r.FormValue("additionalMetadata")
|
||||
|
||||
fileParam, err := ReadFormFileToTempFile(r, "file")
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
|
||||
|
||||
result, err := c.service.UploadFile(r.Context(), petIdParam, additionalMetadataParam, fileParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, err, &result)
|
||||
|
||||
@@ -100,7 +100,12 @@ func (c *StoreAPIController) GetInventory(w http.ResponseWriter, r *http.Request
|
||||
|
||||
// GetOrderById - Find purchase order by ID
|
||||
func (c *StoreAPIController) GetOrderById(w http.ResponseWriter, r *http.Request) {
|
||||
orderIdParam, err := parseInt64Parameter(chi.URLParam(r, "orderId"), true)
|
||||
orderIdParam, err := parseNumericParameter[int64](
|
||||
chi.URLParam(r, "orderId"),
|
||||
WithRequire[int64](parseInt64),
|
||||
WithMinimum[int64](1),
|
||||
WithMaximum[int64](5),
|
||||
)
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||
return
|
||||
@@ -128,6 +133,10 @@ func (c *StoreAPIController) PlaceOrder(w http.ResponseWriter, r *http.Request)
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertOrderConstraints(orderParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.PlaceOrder(r.Context(), orderParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -106,6 +106,10 @@ func (c *UserAPIController) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.CreateUser(r.Context(), userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
@@ -233,6 +237,10 @@ func (c *UserAPIController) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
if err := AssertUserConstraints(userParam); err != nil {
|
||||
c.errorHandler(w, r, err, nil)
|
||||
return
|
||||
}
|
||||
result, err := c.service.UpdateUser(r.Context(), usernameParam, userParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// AnObject - An array 3-deep.
|
||||
type AnObject struct {
|
||||
|
||||
@@ -18,6 +25,13 @@ type AnObject struct {
|
||||
Pet []Pet `json:"Pet,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *AnObject) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias AnObject // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertAnObjectRequired checks if the required fields are not zero-ed
|
||||
func AssertAnObjectRequired(obj AnObject) error {
|
||||
if err := AssertTagRequired(obj.Tag); err != nil {
|
||||
@@ -30,3 +44,8 @@ func AssertAnObjectRequired(obj AnObject) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertAnObjectConstraints checks if the values respects the defined constraints
|
||||
func AssertAnObjectConstraints(obj AnObject) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// ApiResponse - Describes the result of uploading an image resource
|
||||
type ApiResponse struct {
|
||||
|
||||
@@ -19,7 +26,19 @@ type ApiResponse struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *ApiResponse) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias ApiResponse // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertApiResponseRequired checks if the required fields are not zero-ed
|
||||
func AssertApiResponseRequired(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertApiResponseConstraints checks if the values respects the defined constraints
|
||||
func AssertApiResponseConstraints(obj ApiResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Category - A category for a pet
|
||||
type Category struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Category struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Category) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Category // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertCategoryRequired checks if the required fields are not zero-ed
|
||||
func AssertCategoryRequired(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertCategoryConstraints checks if the values respects the defined constraints
|
||||
func AssertCategoryConstraints(obj Category) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Order - An order for a pets from the pet store
|
||||
type Order struct {
|
||||
SpecialInfo
|
||||
@@ -33,6 +37,14 @@ type Order struct {
|
||||
ShipDate time.Time `json:"shipDate,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Order) UnmarshalJSON(data []byte) error {
|
||||
m.Complete = false
|
||||
|
||||
type Alias Order // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertOrderRequired checks if the required fields are not zero-ed
|
||||
func AssertOrderRequired(obj Order) error {
|
||||
elements := map[string]interface{}{
|
||||
@@ -50,3 +62,8 @@ func AssertOrderRequired(obj Order) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertOrderConstraints checks if the values respects the defined constraints
|
||||
func AssertOrderConstraints(obj Order) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// OrderInfo - An order info for a pets from the pet store
|
||||
type OrderInfo struct {
|
||||
|
||||
@@ -23,7 +27,19 @@ type OrderInfo struct {
|
||||
ShipDate time.Time `json:"shipDate,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *OrderInfo) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias OrderInfo // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertOrderInfoRequired checks if the required fields are not zero-ed
|
||||
func AssertOrderInfoRequired(obj OrderInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertOrderInfoConstraints checks if the values respects the defined constraints
|
||||
func AssertOrderInfoConstraints(obj OrderInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Pet - A pet for sale in the pet store
|
||||
type Pet struct {
|
||||
|
||||
@@ -26,6 +33,13 @@ type Pet struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Pet) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Pet // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertPetRequired checks if the required fields are not zero-ed
|
||||
func AssertPetRequired(obj Pet) error {
|
||||
elements := map[string]interface{}{
|
||||
@@ -52,3 +66,8 @@ func AssertPetRequired(obj Pet) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertPetConstraints checks if the values respects the defined constraints
|
||||
func AssertPetConstraints(obj Pet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// SpecialInfo - An order info for a pets from the pet store
|
||||
type SpecialInfo struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type SpecialInfo struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *SpecialInfo) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias SpecialInfo // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertSpecialInfoRequired checks if the required fields are not zero-ed
|
||||
func AssertSpecialInfoRequired(obj SpecialInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertSpecialInfoConstraints checks if the values respects the defined constraints
|
||||
func AssertSpecialInfoConstraints(obj SpecialInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// Tag - A tag for a pet
|
||||
type Tag struct {
|
||||
|
||||
@@ -17,7 +24,19 @@ type Tag struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *Tag) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias Tag // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertTagRequired checks if the required fields are not zero-ed
|
||||
func AssertTagRequired(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertTagConstraints checks if the values respects the defined constraints
|
||||
func AssertTagConstraints(obj Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
package petstoreserver
|
||||
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
// User - A User who is purchasing from the pet store
|
||||
type User struct {
|
||||
|
||||
@@ -36,6 +43,13 @@ type User struct {
|
||||
DeepSliceMap [][]AnObject `json:"deepSliceMap,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data while respecting defaults if specified.
|
||||
func (m *User) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type Alias User // To avoid infinite recursion
|
||||
return json.Unmarshal(data, (*Alias)(m))
|
||||
}
|
||||
|
||||
// AssertUserRequired checks if the required fields are not zero-ed
|
||||
func AssertUserRequired(obj User) error {
|
||||
elements := map[string]interface{}{
|
||||
@@ -57,3 +71,8 @@ func AssertUserRequired(obj User) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertUserConstraints checks if the values respects the defined constraints
|
||||
func AssertUserConstraints(obj User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ type Router interface {
|
||||
}
|
||||
|
||||
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) chi.Router {
|
||||
@@ -132,74 +134,119 @@ func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// parseFloatParameter parses a string parameter to an int64.
|
||||
func parseFloatParameter(param string, bitSize int, required bool) (float64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseFloat(param, bitSize)
|
||||
type Number interface {
|
||||
~int32 | ~int64 | ~float32 | ~float64
|
||||
}
|
||||
|
||||
// parseFloat64Parameter parses a string parameter to an float64.
|
||||
func parseFloat64Parameter(param string, required bool) (float64, error) {
|
||||
return parseFloatParameter(param, 64, required)
|
||||
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) {
|
||||
return strconv.ParseFloat(param, 64)
|
||||
}
|
||||
|
||||
// parseFloat32Parameter parses a string parameter to an float32.
|
||||
func parseFloat32Parameter(param string, required bool) (float32, error) {
|
||||
val, err := parseFloatParameter(param, 32, required)
|
||||
return float32(val), err
|
||||
// parseFloat32 parses a string parameter to an float32.
|
||||
func parseFloat32(param string) (float32, error) {
|
||||
v, err := strconv.ParseFloat(param, 32)
|
||||
return float32(v), err
|
||||
}
|
||||
|
||||
// parseIntParameter parses a string parameter to an int64.
|
||||
func parseIntParameter(param string, bitSize int, required bool) (int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return 0, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return strconv.ParseInt(param, 10, bitSize)
|
||||
// parseInt64 parses a string parameter to an int64.
|
||||
func parseInt64(param string) (int64, error) {
|
||||
return strconv.ParseInt(param, 10, 64)
|
||||
}
|
||||
|
||||
// parseInt64Parameter parses a string parameter to an int64.
|
||||
func parseInt64Parameter(param string, required bool) (int64, error) {
|
||||
return parseIntParameter(param, 64, required)
|
||||
}
|
||||
|
||||
// parseInt32Parameter parses a string parameter to an int32.
|
||||
func parseInt32Parameter(param string, required bool) (int32, error) {
|
||||
val, err := parseIntParameter(param, 32, required)
|
||||
// parseInt32 parses a string parameter to an int32.
|
||||
func parseInt32(param string) (int32, error) {
|
||||
val, err := strconv.ParseInt(param, 10, 32)
|
||||
return int32(val), err
|
||||
}
|
||||
|
||||
// parseBoolParameter parses a string parameter to a bool
|
||||
func parseBoolParameter(param string, required bool) (bool, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return false, errors.New(errMsgRequiredMissing)
|
||||
// parseBool parses a string parameter to an bool.
|
||||
func parseBool(param string) (bool, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
v, err := parse(actual)
|
||||
return v, false, err
|
||||
}
|
||||
}
|
||||
|
||||
val, err := strconv.ParseBool(param)
|
||||
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 false, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return bool(val), nil
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// parseFloat64ArrayParameter parses a string parameter containing array of values to []Float64.
|
||||
func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64, error) {
|
||||
// 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)
|
||||
@@ -209,88 +256,24 @@ func parseFloat64ArrayParameter(param, delim string, required bool) ([]float64,
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float64, len(str))
|
||||
values := make([]T, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 64); err != nil {
|
||||
v, ok, err := fn(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = v
|
||||
}
|
||||
|
||||
if !ok {
|
||||
for _, check := range checks {
|
||||
if err := check(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values[i] = v
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
// parseFloat32ArrayParameter parses a string parameter containing array of values to []float32.
|
||||
func parseFloat32ArrayParameter(param, delim string, required bool) ([]float32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
floats := make([]float32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseFloat(s, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floats[i] = float32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return floats, nil
|
||||
}
|
||||
|
||||
|
||||
// parseInt64ArrayParameter parses a string parameter containing array of values to []int64.
|
||||
func parseInt64ArrayParameter(param, delim string, required bool) ([]int64, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int64, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
}
|
||||
|
||||
// parseInt32ArrayParameter parses a string parameter containing array of values to []int32.
|
||||
func parseInt32ArrayParameter(param, delim string, required bool) ([]int32, error) {
|
||||
if param == "" {
|
||||
if required {
|
||||
return nil, errors.New(errMsgRequiredMissing)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str := strings.Split(param, delim)
|
||||
ints := make([]int32, len(str))
|
||||
|
||||
for i, s := range str {
|
||||
if v, err := strconv.ParseInt(s, 10, 32); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ints[i] = int32(v)
|
||||
}
|
||||
}
|
||||
|
||||
return ints, nil
|
||||
return values, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user