diff --git a/bin/configs/go-server-required.yaml b/bin/configs/go-server-required.yaml new file mode 100644 index 000000000000..fd90bbbeb83a --- /dev/null +++ b/bin/configs/go-server-required.yaml @@ -0,0 +1,9 @@ +generatorName: go-server +outputDir: samples/server/petstore/go-server-required +inputSpec: modules/openapi-generator/src/test/resources/3_0/server-required.yaml +templateDir: modules/openapi-generator/src/main/resources/go-server +additionalProperties: + hideGenerationTimestamp: "true" + packageName: petstoreserver + addResponseHeaders: true + router: "chi" diff --git a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache index 2bfa53cd5035..68ed25142006 100644 --- a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache @@ -174,14 +174,36 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re {{paramName}} := r.Header.Get("{{baseName}}") {{/isHeaderParam}} {{#isBodyParam}} - {{paramName}} := &{{dataType}}{} - if err := json.NewDecoder(r.Body).Decode(&{{paramName}}); err != nil { + {{paramName}} := {{dataType}}{} + d := json.NewDecoder(r.Body) + {{^isAdditionalPropertiesTrue}} + d.DisallowUnknownFields() + {{/isAdditionalPropertiesTrue}} + if err := d.Decode(&{{paramName}}); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } + {{#isArray}} + {{#items.isModel}} + for _, el := range {{paramName}} { + if err := Assert{{baseType}}Required(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + {{/items.isModel}} + {{/isArray}} + {{^isArray}} + {{#isModel}} + if err := Assert{{baseType}}Required({{paramName}}); err != nil { + c.errorHandler(w, r, err, nil) + return + } + {{/isModel}} + {{/isArray}} {{/isBodyParam}} {{/allParams}} - result, err := c.service.{{nickname}}(r.Context(){{#allParams}}, {{#isBodyParam}}*{{/isBodyParam}}{{paramName}}{{/allParams}}) + result, err := c.service.{{nickname}}(r.Context(){{#allParams}}, {{paramName}}{{/allParams}}) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/modules/openapi-generator/src/main/resources/go-server/error.mustache b/modules/openapi-generator/src/main/resources/go-server/error.mustache index cf22da170fa8..a4ffa03f53f9 100644 --- a/modules/openapi-generator/src/main/resources/go-server/error.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/error.mustache @@ -2,9 +2,16 @@ package {{packageName}} import ( + "errors" + "fmt" "net/http" ) +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + // ParsingError indicates that an error has occurred when parsing request parameters type ParsingError struct { Err error @@ -18,6 +25,15 @@ func (e *ParsingError) Error() string { return e.Err.Error() } +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + // ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if // you would like errors to be handled differently from the DefaultErrorHandler type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) @@ -28,6 +44,9 @@ func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, resu if _, ok := err.(*ParsingError); ok { // Handle parsing errors EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest),{{#addResponseHeaders}} map[string][]string{},{{/addResponseHeaders}} w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity),{{#addResponseHeaders}} map[string][]string{},{{/addResponseHeaders}} w) } else { // Handle all other errors EncodeJSONResponse(err.Error(), &result.Code,{{#addResponseHeaders}} result.Headers,{{/addResponseHeaders}} w) diff --git a/modules/openapi-generator/src/main/resources/go-server/helpers.mustache b/modules/openapi-generator/src/main/resources/go-server/helpers.mustache index 13115656bc0a..63b91ebb9911 100644 --- a/modules/openapi-generator/src/main/resources/go-server/helpers.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/helpers.mustache @@ -1,7 +1,11 @@ {{>partial_header}} package {{packageName}} -//Response return a ImplResponse struct filled +import ( + "reflect" +) + +// Response return a ImplResponse struct filled func Response(code int, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -13,7 +17,7 @@ func Response(code int, body interface{}) ImplResponse { } {{#addResponseHeaders}} -//ResponseWithHeaders return a ImplResponse struct filled, including headers +// ResponseWithHeaders return a ImplResponse struct filled, including headers func ResponseWithHeaders(code int, headers map[string][]string, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -22,3 +26,35 @@ func ResponseWithHeaders(code int, headers map[string][]string, body interface{} } } {{/addResponseHeaders}} + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertNestedValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + if err := callback(value.Interface()); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/modules/openapi-generator/src/main/resources/go-server/model.mustache b/modules/openapi-generator/src/main/resources/go-server/model.mustache index afcfcf7338e0..c13d81795add 100644 --- a/modules/openapi-generator/src/main/resources/go-server/model.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/model.mustache @@ -34,4 +34,101 @@ type {{classname}} struct { {{/deprecated}} {{name}} {{#isNullable}}*{{/isNullable}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"{{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}` {{/vars}} -}{{/isEnum}}{{/model}}{{/models}} +}{{/isEnum}} + +// Assert{{classname}}Required checks if the required fields are not zero-ed +func Assert{{classname}}Required(obj {{classname}}) error { +{{#hasRequired}} + elements := map[string]interface{}{ +{{#requiredVars}} "{{baseName}}": obj.{{name}}, +{{/requiredVars}} } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + +{{/hasRequired}} +{{#parent}} + {{^isMap}} + {{^isArray}} + if err := Assert{{{parent}}}Required(obj.{{{parent}}}); err != nil { + return err + } + + {{/isArray}} + {{/isMap}} +{{/parent}} +{{#Vars}} + {{#isNullable}} + {{#isModel}} + if obj.{{name}} != nil { + {{/isModel}} + {{#isArray}} + {{#items.isModel}} + if obj.{{name}} != nil { + {{/items.isModel}} + {{^items.isModel}} + {{#mostInnerItems.isModel}} + {{^mostInnerItems.isPrimitiveType}} + if obj.{{name}} != nil { + {{/mostInnerItems.isPrimitiveType}} + {{/mostInnerItems.isModel}} + {{/items.isModel}} + {{/isArray}} + {{/isNullable}} + {{#isModel}} +{{#isNullable}} {{/isNullable}} if err := Assert{{baseType}}Required({{#isNullable}}*{{/isNullable}}obj.{{name}}); err != nil { +{{#isNullable}} {{/isNullable}} return err +{{#isNullable}} {{/isNullable}} } + {{/isModel}} + {{#isArray}} + {{#items.isModel}} +{{#isNullable}} {{/isNullable}} for _, el := range {{#isNullable}}*{{/isNullable}}obj.{{name}} { +{{#isNullable}} {{/isNullable}} if err := Assert{{items.baseType}}Required(el); err != nil { +{{#isNullable}} {{/isNullable}} return err +{{#isNullable}} {{/isNullable}} } +{{#isNullable}} {{/isNullable}} } + {{/items.isModel}} + {{^items.isModel}} + {{#mostInnerItems.isModel}} + {{^mostInnerItems.isPrimitiveType}} +{{#isNullable}} {{/isNullable}} if err := AssertRecurse{{mostInnerItems.dataType}}Required({{#isNullable}}*{{/isNullable}}obj.{{name}}); err != nil { +{{#isNullable}} {{/isNullable}} return err +{{#isNullable}} {{/isNullable}} } + {{/mostInnerItems.isPrimitiveType}} + {{/mostInnerItems.isModel}} + {{/items.isModel}} + {{/isArray}} + {{#isNullable}} + {{#isModel}} + } + {{/isModel}} + {{#isArray}} + {{#items.isModel}} + } + {{/items.isModel}} + {{^items.isModel}} + {{#mostInnerItems.isModel}} + {{^mostInnerItems.isPrimitiveType}} + } + {{/mostInnerItems.isPrimitiveType}} + {{/mostInnerItems.isModel}} + {{/items.isModel}} + {{/isArray}} + {{/isNullable}} +{{/Vars}} + return nil +} + +// AssertRecurse{{classname}}Required recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of {{classname}} (e.g. [][]{{classname}}), otherwise ErrTypeAssertionError is thrown. +func AssertRecurse{{classname}}Required(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + a{{classname}}, ok := obj.({{classname}}) + if !ok { + return ErrTypeAssertionError + } + return Assert{{classname}}Required(a{{classname}}) + }) +}{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/test/resources/3_0/server-required.yaml b/modules/openapi-generator/src/test/resources/3_0/server-required.yaml new file mode 100644 index 000000000000..5ced38f18d35 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/server-required.yaml @@ -0,0 +1,800 @@ +openapi: 3.0.0 +servers: + - url: "http://petstore.swagger.io/v2" +info: + description: >- + This is a sample server Petstore server. For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: "https://www.apache.org/licenses/LICENSE-2.0.html" +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: "" + operationId: addPet + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Pet" + application/json: + schema: + $ref: "#/components/schemas/Pet" + "405": + description: Invalid input + security: + - petstore_auth: + - "write:pets" + - "read:pets" + requestBody: + $ref: "#/components/requestBodies/Pet" + put: + tags: + - pet + summary: Update an existing pet + description: "" + operationId: updatePet + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Pet" + application/json: + schema: + $ref: "#/components/schemas/Pet" + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + security: + - petstore_auth: + - "write:pets" + - "read:pets" + requestBody: + $ref: "#/components/requestBodies/Pet" + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + style: form + explode: false + schema: + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + responses: + "200": + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: "#/components/schemas/Pet" + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Pet" + "400": + description: Invalid status value + security: + - petstore_auth: + - "read:pets" + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: >- + Multiple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + "200": + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: "#/components/schemas/Pet" + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Pet" + "400": + description: Invalid tag value + security: + - petstore_auth: + - "read:pets" + deprecated: true + "/pet/{petId}": + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Pet" + application/json: + schema: + $ref: "#/components/schemas/Pet" + "400": + description: Invalid ID supplied + "404": + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: "" + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + responses: + "405": + description: Invalid input + security: + - petstore_auth: + - "write:pets" + - "read:pets" + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + delete: + tags: + - pet + summary: Deletes a pet + description: "" + operationId: deletePet + parameters: + - name: api_key + in: header + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + "400": + description: Invalid pet value + security: + - petstore_auth: + - "write:pets" + - "read:pets" + "/pet/{petId}/uploadImage": + post: + tags: + - pet + summary: uploads an image + description: "" + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponse" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + type: string + format: binary + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: "" + operationId: placeOrder + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Order" + application/json: + schema: + $ref: "#/components/schemas/Order" + "400": + description: Invalid Order + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Order" + description: order placed for purchasing the pet + required: true + "/store/order/{orderId}": + get: + tags: + - store + summary: Find purchase order by ID + description: >- + For valid response try integer IDs with value <= 5 or > 10. Other values + will generated exceptions + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + minimum: 1 + maximum: 5 + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/Order" + application/json: + schema: + $ref: "#/components/schemas/Order" + "400": + description: Invalid ID supplied + "404": + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: >- + For valid response try integer IDs with value < 1000. Anything above + 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: string + responses: + "400": + description: Invalid ID supplied + "404": + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + description: Created user object + required: true + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: "" + operationId: createUsersWithArrayInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: "#/components/requestBodies/UserArray" + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: "" + operationId: createUsersWithListInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: "#/components/requestBodies/UserArray" + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: "" + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: true + schema: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + - name: password + in: query + description: The password for login in clear text + required: true + schema: + type: string + responses: + "200": + description: successful operation + headers: + Set-Cookie: + description: >- + Cookie authentication key for use with the `api_key` + apiKey authentication. + schema: + type: string + example: AUTH_KEY=abcde12345; Path=/; HttpOnly + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when toekn expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + "400": + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: "" + operationId: logoutUser + responses: + default: + description: successful operation + security: + - api_key: [] + "/user/{username}": + get: + tags: + - user + summary: Get user by user name + description: "" + operationId: getUserByName + parameters: + - name: username + in: path + description: The name that needs to be fetched. Use user1 for testing. + required: true + schema: + type: string + responses: + "200": + description: successful operation + content: + application/xml: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + "400": + description: Invalid username supplied + "404": + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + responses: + "400": + description: Invalid user supplied + "404": + description: User not found + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + description: Updated user object + required: true + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + "400": + description: Invalid username supplied + "404": + description: User not found + security: + - api_key: [] +externalDocs: + description: Find out more about Swagger + url: "http://swagger.io" +components: + requestBodies: + UserArray: + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + description: List of user object + required: true + Pet: + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + application/xml: + schema: + $ref: "#/components/schemas/Pet" + description: Pet object that needs to be added to the store + required: true + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: "http://petstore.swagger.io/api/oauth/dialog" + scopes: + "write:pets": modify pets in your account + "read:pets": read your pets + api_key: + type: apiKey + name: api_key + in: header + schemas: + OrderInfo: + title: Pet Order Info + description: An order info for a pets from the pet store + type: object + properties: + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + xml: + name: OrderInfo + SpecialInfo: + title: Pet Order Info + description: An order info for a pets from the pet store + discriminator: + propertyName: type + type: object + properties: + promotion: + type: boolean + type: + type: string + xml: + name: OrderInfo + Order: + title: Pet Order + description: An order for a pets from the pet store + allOf: + - $ref: '#/components/schemas/OrderInfo' + - $ref: '#/components/schemas/SpecialInfo' + type: object + properties: + id: + type: integer + format: int64 + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + comment: + type: string + nullable: true + xml: + name: Order + required: + - comment + Category: + title: Pet category + description: A category for a pet + type: object + nullable: true + properties: + id: + type: integer + format: int64 + name: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + nullable: true + userStatus: + type: integer + format: int32 + description: User Status + deepSliceModel: + nullable: true + type: array + description: An array 1-deep. + items: + type: array + description: An array 2-deep. + items: + type: array + description: An array 3-deep. + items: + $ref: "#/components/schemas/Tag" + deepSliceMap: + type: array + description: An array 1-deep. + items: + type: array + description: An array 2-deep. + items: + title: an Object + type: object + description: An array 3-deep. + properties: + tag: + $ref: "#/components/schemas/Tag" + Pet: + type: array + description: An array of pet. + items: + $ref: "#/components/schemas/Pet" + xml: + name: User + required: + - deepSliceModel + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: "#/components/schemas/Category" + name: + type: string + example: doggie + photoUrls: + nullable: true + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + nullable: true + type: array + xml: + name: tag + wrapped: true + items: + $ref: "#/components/schemas/Tag" + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string diff --git a/samples/server/petstore/go-api-server/go/api_pet.go b/samples/server/petstore/go-api-server/go/api_pet.go index 863b9095b897..babc46eda263 100644 --- a/samples/server/petstore/go-api-server/go/api_pet.go +++ b/samples/server/petstore/go-api-server/go/api_pet.go @@ -103,12 +103,18 @@ func (c *PetApiController) Routes() Routes { // AddPet - Add a new pet to the store func (c *PetApiController) AddPet(w http.ResponseWriter, r *http.Request) { - pet := &Pet{} - if err := json.NewDecoder(r.Body).Decode(&pet); err != nil { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.AddPet(r.Context(), *pet) + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.AddPet(r.Context(), pet) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -193,12 +199,18 @@ func (c *PetApiController) GetPetById(w http.ResponseWriter, r *http.Request) { // UpdatePet - Update an existing pet func (c *PetApiController) UpdatePet(w http.ResponseWriter, r *http.Request) { - pet := &Pet{} - if err := json.NewDecoder(r.Body).Decode(&pet); err != nil { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.UpdatePet(r.Context(), *pet) + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdatePet(r.Context(), pet) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-api-server/go/api_store.go b/samples/server/petstore/go-api-server/go/api_store.go index f36cd41c5237..134c0820607b 100644 --- a/samples/server/petstore/go-api-server/go/api_store.go +++ b/samples/server/petstore/go-api-server/go/api_store.go @@ -128,12 +128,18 @@ func (c *StoreApiController) GetOrderById(w http.ResponseWriter, r *http.Request // PlaceOrder - Place an order for a pet func (c *StoreApiController) PlaceOrder(w http.ResponseWriter, r *http.Request) { - order := &Order{} - if err := json.NewDecoder(r.Body).Decode(&order); err != nil { + order := Order{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&order); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.PlaceOrder(r.Context(), *order) + if err := AssertOrderRequired(order); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.PlaceOrder(r.Context(), order) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-api-server/go/api_user.go b/samples/server/petstore/go-api-server/go/api_user.go index ebbcd8ad4941..558fcf91ba0a 100644 --- a/samples/server/petstore/go-api-server/go/api_user.go +++ b/samples/server/petstore/go-api-server/go/api_user.go @@ -103,12 +103,18 @@ func (c *UserApiController) Routes() Routes { // CreateUser - Create user func (c *UserApiController) CreateUser(w http.ResponseWriter, r *http.Request) { - user := &User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUser(r.Context(), *user) + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.CreateUser(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -121,12 +127,20 @@ func (c *UserApiController) CreateUser(w http.ResponseWriter, r *http.Request) { // CreateUsersWithArrayInput - Creates list of users with given input array func (c *UserApiController) CreateUsersWithArrayInput(w http.ResponseWriter, r *http.Request) { - user := &[]User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUsersWithArrayInput(r.Context(), *user) + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithArrayInput(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -139,12 +153,20 @@ func (c *UserApiController) CreateUsersWithArrayInput(w http.ResponseWriter, r * // CreateUsersWithListInput - Creates list of users with given input array func (c *UserApiController) CreateUsersWithListInput(w http.ResponseWriter, r *http.Request) { - user := &[]User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUsersWithListInput(r.Context(), *user) + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithListInput(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -221,12 +243,18 @@ func (c *UserApiController) UpdateUser(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) username := params["username"] - user := &User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.UpdateUser(r.Context(), username, *user) + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdateUser(r.Context(), username, user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-api-server/go/error.go b/samples/server/petstore/go-api-server/go/error.go index 7dd60bc08500..2c0b010fe8da 100644 --- a/samples/server/petstore/go-api-server/go/error.go +++ b/samples/server/petstore/go-api-server/go/error.go @@ -10,9 +10,16 @@ package petstoreserver import ( + "errors" + "fmt" "net/http" ) +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + // ParsingError indicates that an error has occurred when parsing request parameters type ParsingError struct { Err error @@ -26,6 +33,15 @@ func (e *ParsingError) Error() string { return e.Err.Error() } +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + // ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if // you would like errors to be handled differently from the DefaultErrorHandler type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) @@ -36,6 +52,9 @@ func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, resu if _, ok := err.(*ParsingError); ok { // Handle parsing errors EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), map[string][]string{}, w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), map[string][]string{}, w) } else { // Handle all other errors EncodeJSONResponse(err.Error(), &result.Code, result.Headers, w) diff --git a/samples/server/petstore/go-api-server/go/helpers.go b/samples/server/petstore/go-api-server/go/helpers.go index 179ee893f7f5..ef41ca1c8975 100644 --- a/samples/server/petstore/go-api-server/go/helpers.go +++ b/samples/server/petstore/go-api-server/go/helpers.go @@ -9,7 +9,11 @@ package petstoreserver -//Response return a ImplResponse struct filled +import ( + "reflect" +) + +// Response return a ImplResponse struct filled func Response(code int, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -18,7 +22,7 @@ func Response(code int, body interface{}) ImplResponse { } } -//ResponseWithHeaders return a ImplResponse struct filled, including headers +// ResponseWithHeaders return a ImplResponse struct filled, including headers func ResponseWithHeaders(code int, headers map[string][]string, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -26,3 +30,35 @@ func ResponseWithHeaders(code int, headers map[string][]string, body interface{} Body: body, } } + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertNestedValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + if err := callback(value.Interface()); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/samples/server/petstore/go-api-server/go/model_api_response.go b/samples/server/petstore/go-api-server/go/model_api_response.go index 2379e169080d..324cfbe08444 100644 --- a/samples/server/petstore/go-api-server/go/model_api_response.go +++ b/samples/server/petstore/go-api-server/go/model_api_response.go @@ -18,3 +18,20 @@ type ApiResponse struct { Message string `json:"message,omitempty"` } + +// AssertApiResponseRequired checks if the required fields are not zero-ed +func AssertApiResponseRequired(obj ApiResponse) error { + return nil +} + +// AssertRecurseApiResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ApiResponse (e.g. [][]ApiResponse), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseApiResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aApiResponse, ok := obj.(ApiResponse) + if !ok { + return ErrTypeAssertionError + } + return AssertApiResponseRequired(aApiResponse) + }) +} diff --git a/samples/server/petstore/go-api-server/go/model_category.go b/samples/server/petstore/go-api-server/go/model_category.go index 5e6f9d247fa4..3ae01892a3fc 100644 --- a/samples/server/petstore/go-api-server/go/model_category.go +++ b/samples/server/petstore/go-api-server/go/model_category.go @@ -16,3 +16,20 @@ type Category struct { Name string `json:"name,omitempty"` } + +// AssertCategoryRequired checks if the required fields are not zero-ed +func AssertCategoryRequired(obj Category) error { + return nil +} + +// AssertRecurseCategoryRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Category (e.g. [][]Category), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseCategoryRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aCategory, ok := obj.(Category) + if !ok { + return ErrTypeAssertionError + } + return AssertCategoryRequired(aCategory) + }) +} diff --git a/samples/server/petstore/go-api-server/go/model_order.go b/samples/server/petstore/go-api-server/go/model_order.go index a9d8dbb79579..80ecfea3dc7f 100644 --- a/samples/server/petstore/go-api-server/go/model_order.go +++ b/samples/server/petstore/go-api-server/go/model_order.go @@ -29,3 +29,20 @@ type Order struct { Complete bool `json:"complete,omitempty"` } + +// AssertOrderRequired checks if the required fields are not zero-ed +func AssertOrderRequired(obj Order) error { + return nil +} + +// AssertRecurseOrderRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Order (e.g. [][]Order), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseOrderRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aOrder, ok := obj.(Order) + if !ok { + return ErrTypeAssertionError + } + return AssertOrderRequired(aOrder) + }) +} diff --git a/samples/server/petstore/go-api-server/go/model_pet.go b/samples/server/petstore/go-api-server/go/model_pet.go index 3fb69fafe385..8e3dc7ad66f1 100644 --- a/samples/server/petstore/go-api-server/go/model_pet.go +++ b/samples/server/petstore/go-api-server/go/model_pet.go @@ -26,3 +26,38 @@ type Pet struct { // Deprecated Status string `json:"status,omitempty"` } + +// AssertPetRequired checks if the required fields are not zero-ed +func AssertPetRequired(obj Pet) error { + elements := map[string]interface{}{ + "name": obj.Name, + "photoUrls": obj.PhotoUrls, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + if err := AssertCategoryRequired(obj.Category); err != nil { + return err + } + for _, el := range obj.Tags { + if err := AssertTagRequired(el); err != nil { + return err + } + } + return nil +} + +// AssertRecursePetRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Pet (e.g. [][]Pet), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePetRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPet, ok := obj.(Pet) + if !ok { + return ErrTypeAssertionError + } + return AssertPetRequired(aPet) + }) +} diff --git a/samples/server/petstore/go-api-server/go/model_tag.go b/samples/server/petstore/go-api-server/go/model_tag.go index 2fb07fb32c46..0631ccc4866f 100644 --- a/samples/server/petstore/go-api-server/go/model_tag.go +++ b/samples/server/petstore/go-api-server/go/model_tag.go @@ -16,3 +16,20 @@ type Tag struct { Name string `json:"name,omitempty"` } + +// AssertTagRequired checks if the required fields are not zero-ed +func AssertTagRequired(obj Tag) error { + return nil +} + +// AssertRecurseTagRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Tag (e.g. [][]Tag), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseTagRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aTag, ok := obj.(Tag) + if !ok { + return ErrTypeAssertionError + } + return AssertTagRequired(aTag) + }) +} diff --git a/samples/server/petstore/go-api-server/go/model_user.go b/samples/server/petstore/go-api-server/go/model_user.go index 8f40d0ac04cb..83ac114e3c59 100644 --- a/samples/server/petstore/go-api-server/go/model_user.go +++ b/samples/server/petstore/go-api-server/go/model_user.go @@ -29,3 +29,20 @@ type User struct { // User Status UserStatus int32 `json:"userStatus,omitempty"` } + +// AssertUserRequired checks if the required fields are not zero-ed +func AssertUserRequired(obj User) error { + return nil +} + +// AssertRecurseUserRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of User (e.g. [][]User), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseUserRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aUser, ok := obj.(User) + if !ok { + return ErrTypeAssertionError + } + return AssertUserRequired(aUser) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/api_pet.go b/samples/server/petstore/go-chi-server/go/api_pet.go index b698977898fc..074dfbb0eac9 100644 --- a/samples/server/petstore/go-chi-server/go/api_pet.go +++ b/samples/server/petstore/go-chi-server/go/api_pet.go @@ -103,12 +103,18 @@ func (c *PetApiController) Routes() Routes { // AddPet - Add a new pet to the store func (c *PetApiController) AddPet(w http.ResponseWriter, r *http.Request) { - pet := &Pet{} - if err := json.NewDecoder(r.Body).Decode(&pet); err != nil { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.AddPet(r.Context(), *pet) + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.AddPet(r.Context(), pet) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -191,12 +197,18 @@ func (c *PetApiController) GetPetById(w http.ResponseWriter, r *http.Request) { // UpdatePet - Update an existing pet func (c *PetApiController) UpdatePet(w http.ResponseWriter, r *http.Request) { - pet := &Pet{} - if err := json.NewDecoder(r.Body).Decode(&pet); err != nil { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.UpdatePet(r.Context(), *pet) + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdatePet(r.Context(), pet) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-chi-server/go/api_store.go b/samples/server/petstore/go-chi-server/go/api_store.go index cd2ee935689e..af34ada596f6 100644 --- a/samples/server/petstore/go-chi-server/go/api_store.go +++ b/samples/server/petstore/go-chi-server/go/api_store.go @@ -126,12 +126,18 @@ func (c *StoreApiController) GetOrderById(w http.ResponseWriter, r *http.Request // PlaceOrder - Place an order for a pet func (c *StoreApiController) PlaceOrder(w http.ResponseWriter, r *http.Request) { - order := &Order{} - if err := json.NewDecoder(r.Body).Decode(&order); err != nil { + order := Order{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&order); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.PlaceOrder(r.Context(), *order) + if err := AssertOrderRequired(order); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.PlaceOrder(r.Context(), order) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-chi-server/go/api_user.go b/samples/server/petstore/go-chi-server/go/api_user.go index 440cf54a4370..f2c41d1695e9 100644 --- a/samples/server/petstore/go-chi-server/go/api_user.go +++ b/samples/server/petstore/go-chi-server/go/api_user.go @@ -103,12 +103,18 @@ func (c *UserApiController) Routes() Routes { // CreateUser - Create user func (c *UserApiController) CreateUser(w http.ResponseWriter, r *http.Request) { - user := &User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUser(r.Context(), *user) + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.CreateUser(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -121,12 +127,20 @@ func (c *UserApiController) CreateUser(w http.ResponseWriter, r *http.Request) { // CreateUsersWithArrayInput - Creates list of users with given input array func (c *UserApiController) CreateUsersWithArrayInput(w http.ResponseWriter, r *http.Request) { - user := &[]User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUsersWithArrayInput(r.Context(), *user) + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithArrayInput(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -139,12 +153,20 @@ func (c *UserApiController) CreateUsersWithArrayInput(w http.ResponseWriter, r * // CreateUsersWithListInput - Creates list of users with given input array func (c *UserApiController) CreateUsersWithListInput(w http.ResponseWriter, r *http.Request) { - user := &[]User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.CreateUsersWithListInput(r.Context(), *user) + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithListInput(r.Context(), user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) @@ -218,12 +240,18 @@ func (c *UserApiController) LogoutUser(w http.ResponseWriter, r *http.Request) { func (c *UserApiController) UpdateUser(w http.ResponseWriter, r *http.Request) { username := chi.URLParam(r, "username") - user := &User{} - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { c.errorHandler(w, r, &ParsingError{Err: err}, nil) return } - result, err := c.service.UpdateUser(r.Context(), username, *user) + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdateUser(r.Context(), username, user) // If an error occurred, encode the error with the status code if err != nil { c.errorHandler(w, r, err, &result) diff --git a/samples/server/petstore/go-chi-server/go/error.go b/samples/server/petstore/go-chi-server/go/error.go index 7dd60bc08500..2c0b010fe8da 100644 --- a/samples/server/petstore/go-chi-server/go/error.go +++ b/samples/server/petstore/go-chi-server/go/error.go @@ -10,9 +10,16 @@ package petstoreserver import ( + "errors" + "fmt" "net/http" ) +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + // ParsingError indicates that an error has occurred when parsing request parameters type ParsingError struct { Err error @@ -26,6 +33,15 @@ func (e *ParsingError) Error() string { return e.Err.Error() } +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + // ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if // you would like errors to be handled differently from the DefaultErrorHandler type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) @@ -36,6 +52,9 @@ func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, resu if _, ok := err.(*ParsingError); ok { // Handle parsing errors EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), map[string][]string{}, w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), map[string][]string{}, w) } else { // Handle all other errors EncodeJSONResponse(err.Error(), &result.Code, result.Headers, w) diff --git a/samples/server/petstore/go-chi-server/go/helpers.go b/samples/server/petstore/go-chi-server/go/helpers.go index 179ee893f7f5..ef41ca1c8975 100644 --- a/samples/server/petstore/go-chi-server/go/helpers.go +++ b/samples/server/petstore/go-chi-server/go/helpers.go @@ -9,7 +9,11 @@ package petstoreserver -//Response return a ImplResponse struct filled +import ( + "reflect" +) + +// Response return a ImplResponse struct filled func Response(code int, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -18,7 +22,7 @@ func Response(code int, body interface{}) ImplResponse { } } -//ResponseWithHeaders return a ImplResponse struct filled, including headers +// ResponseWithHeaders return a ImplResponse struct filled, including headers func ResponseWithHeaders(code int, headers map[string][]string, body interface{}) ImplResponse { return ImplResponse { Code: code, @@ -26,3 +30,35 @@ func ResponseWithHeaders(code int, headers map[string][]string, body interface{} Body: body, } } + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertNestedValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + if err := callback(value.Interface()); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/samples/server/petstore/go-chi-server/go/model_api_response.go b/samples/server/petstore/go-chi-server/go/model_api_response.go index 2379e169080d..324cfbe08444 100644 --- a/samples/server/petstore/go-chi-server/go/model_api_response.go +++ b/samples/server/petstore/go-chi-server/go/model_api_response.go @@ -18,3 +18,20 @@ type ApiResponse struct { Message string `json:"message,omitempty"` } + +// AssertApiResponseRequired checks if the required fields are not zero-ed +func AssertApiResponseRequired(obj ApiResponse) error { + return nil +} + +// AssertRecurseApiResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ApiResponse (e.g. [][]ApiResponse), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseApiResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aApiResponse, ok := obj.(ApiResponse) + if !ok { + return ErrTypeAssertionError + } + return AssertApiResponseRequired(aApiResponse) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/model_category.go b/samples/server/petstore/go-chi-server/go/model_category.go index 5e6f9d247fa4..3ae01892a3fc 100644 --- a/samples/server/petstore/go-chi-server/go/model_category.go +++ b/samples/server/petstore/go-chi-server/go/model_category.go @@ -16,3 +16,20 @@ type Category struct { Name string `json:"name,omitempty"` } + +// AssertCategoryRequired checks if the required fields are not zero-ed +func AssertCategoryRequired(obj Category) error { + return nil +} + +// AssertRecurseCategoryRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Category (e.g. [][]Category), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseCategoryRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aCategory, ok := obj.(Category) + if !ok { + return ErrTypeAssertionError + } + return AssertCategoryRequired(aCategory) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/model_order.go b/samples/server/petstore/go-chi-server/go/model_order.go index a9d8dbb79579..80ecfea3dc7f 100644 --- a/samples/server/petstore/go-chi-server/go/model_order.go +++ b/samples/server/petstore/go-chi-server/go/model_order.go @@ -29,3 +29,20 @@ type Order struct { Complete bool `json:"complete,omitempty"` } + +// AssertOrderRequired checks if the required fields are not zero-ed +func AssertOrderRequired(obj Order) error { + return nil +} + +// AssertRecurseOrderRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Order (e.g. [][]Order), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseOrderRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aOrder, ok := obj.(Order) + if !ok { + return ErrTypeAssertionError + } + return AssertOrderRequired(aOrder) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/model_pet.go b/samples/server/petstore/go-chi-server/go/model_pet.go index 3fb69fafe385..8e3dc7ad66f1 100644 --- a/samples/server/petstore/go-chi-server/go/model_pet.go +++ b/samples/server/petstore/go-chi-server/go/model_pet.go @@ -26,3 +26,38 @@ type Pet struct { // Deprecated Status string `json:"status,omitempty"` } + +// AssertPetRequired checks if the required fields are not zero-ed +func AssertPetRequired(obj Pet) error { + elements := map[string]interface{}{ + "name": obj.Name, + "photoUrls": obj.PhotoUrls, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + if err := AssertCategoryRequired(obj.Category); err != nil { + return err + } + for _, el := range obj.Tags { + if err := AssertTagRequired(el); err != nil { + return err + } + } + return nil +} + +// AssertRecursePetRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Pet (e.g. [][]Pet), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePetRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPet, ok := obj.(Pet) + if !ok { + return ErrTypeAssertionError + } + return AssertPetRequired(aPet) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/model_tag.go b/samples/server/petstore/go-chi-server/go/model_tag.go index 2fb07fb32c46..0631ccc4866f 100644 --- a/samples/server/petstore/go-chi-server/go/model_tag.go +++ b/samples/server/petstore/go-chi-server/go/model_tag.go @@ -16,3 +16,20 @@ type Tag struct { Name string `json:"name,omitempty"` } + +// AssertTagRequired checks if the required fields are not zero-ed +func AssertTagRequired(obj Tag) error { + return nil +} + +// AssertRecurseTagRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Tag (e.g. [][]Tag), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseTagRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aTag, ok := obj.(Tag) + if !ok { + return ErrTypeAssertionError + } + return AssertTagRequired(aTag) + }) +} diff --git a/samples/server/petstore/go-chi-server/go/model_user.go b/samples/server/petstore/go-chi-server/go/model_user.go index 8f40d0ac04cb..83ac114e3c59 100644 --- a/samples/server/petstore/go-chi-server/go/model_user.go +++ b/samples/server/petstore/go-chi-server/go/model_user.go @@ -29,3 +29,20 @@ type User struct { // User Status UserStatus int32 `json:"userStatus,omitempty"` } + +// AssertUserRequired checks if the required fields are not zero-ed +func AssertUserRequired(obj User) error { + return nil +} + +// AssertRecurseUserRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of User (e.g. [][]User), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseUserRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aUser, ok := obj.(User) + if !ok { + return ErrTypeAssertionError + } + return AssertUserRequired(aUser) + }) +} diff --git a/samples/server/petstore/go-server-required/.openapi-generator-ignore b/samples/server/petstore/go-server-required/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/go-server-required/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/go-server-required/.openapi-generator/FILES b/samples/server/petstore/go-server-required/.openapi-generator/FILES new file mode 100644 index 000000000000..26b2edca0b85 --- /dev/null +++ b/samples/server/petstore/go-server-required/.openapi-generator/FILES @@ -0,0 +1,25 @@ +Dockerfile +README.md +api/openapi.yaml +go.mod +go/api.go +go/api_pet.go +go/api_pet_service.go +go/api_store.go +go/api_store_service.go +go/api_user.go +go/api_user_service.go +go/error.go +go/helpers.go +go/impl.go +go/logger.go +go/model_api_response.go +go/model_category.go +go/model_order.go +go/model_order_info.go +go/model_pet.go +go/model_special_info.go +go/model_tag.go +go/model_user.go +go/routers.go +main.go diff --git a/samples/server/petstore/go-server-required/.openapi-generator/VERSION b/samples/server/petstore/go-server-required/.openapi-generator/VERSION new file mode 100644 index 000000000000..862529f8cacd --- /dev/null +++ b/samples/server/petstore/go-server-required/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.2.1-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/go-server-required/Dockerfile b/samples/server/petstore/go-server-required/Dockerfile new file mode 100644 index 000000000000..cfdfbaed0804 --- /dev/null +++ b/samples/server/petstore/go-server-required/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.10 AS build +WORKDIR /go/src +COPY go ./go +COPY main.go . + +ENV CGO_ENABLED=0 +RUN go get -d -v ./... + +RUN go build -a -installsuffix cgo -o petstoreserver . + +FROM scratch AS runtime +COPY --from=build /go/src/petstoreserver ./ +EXPOSE 8080/tcp +ENTRYPOINT ["./petstoreserver"] diff --git a/samples/server/petstore/go-server-required/README.md b/samples/server/petstore/go-server-required/README.md new file mode 100644 index 000000000000..fbcf4ae661b0 --- /dev/null +++ b/samples/server/petstore/go-server-required/README.md @@ -0,0 +1,35 @@ +# Go API Server for petstoreserver + +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + +## Overview +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. +By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +- + +To see how to make this your own, look here: + +[README](https://openapi-generator.tech) + +- API version: 1.0.0 + + +### Running the server +To run the server, follow these simple steps: + +``` +go run main.go +``` + +To run the server in a docker container +``` +docker build --network=host -t petstoreserver . +``` + +Once image is built use +``` +docker run --rm -it petstoreserver +``` + + diff --git a/samples/server/petstore/go-server-required/api/openapi.yaml b/samples/server/petstore/go-server-required/api/openapi.yaml new file mode 100644 index 000000000000..a30214167176 --- /dev/null +++ b/samples/server/petstore/go-server-required/api/openapi.yaml @@ -0,0 +1,906 @@ +openapi: 3.0.0 +info: + description: This is a sample server Petstore server. For this sample, you can use + the api key `special-key` to test the authorization filters. + license: + name: Apache-2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + title: OpenAPI Petstore + version: 1.0.0 +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +servers: +- url: http://petstore.swagger.io/v2 +tags: +- description: Everything about your Pets + name: pet +- description: Access to Petstore orders + name: store +- description: Operations about user + name: user +paths: + /pet: + post: + operationId: addPet + requestBody: + $ref: '#/components/requestBodies/Pet' + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: successful operation + "405": + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Add a new pet to the store + tags: + - pet + put: + operationId: updatePet + requestBody: + $ref: '#/components/requestBodies/Pet' + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: successful operation + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + summary: Update an existing pet + tags: + - pet + /pet/findByStatus: + get: + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - description: Status values that need to be considered for filter + explode: false + in: query + name: status + required: true + schema: + items: + default: available + enum: + - available + - pending + - sold + type: string + type: array + style: form + responses: + "200": + content: + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + "400": + description: Invalid status value + security: + - petstore_auth: + - read:pets + summary: Finds Pets by status + tags: + - pet + /pet/findByTags: + get: + deprecated: true + description: Multiple tags can be provided with comma separated strings. Use + tag1, tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - description: Tags to filter by + explode: false + in: query + name: tags + required: true + schema: + items: + type: string + type: array + style: form + responses: + "200": + content: + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + "400": + description: Invalid tag value + security: + - petstore_auth: + - read:pets + summary: Finds Pets by tags + tags: + - pet + /pet/{petId}: + delete: + operationId: deletePet + parameters: + - explode: false + in: header + name: api_key + required: false + schema: + type: string + style: simple + - description: Pet id to delete + explode: false + in: path + name: petId + required: true + schema: + format: int64 + type: integer + style: simple + responses: + "400": + description: Invalid pet value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Deletes a pet + tags: + - pet + get: + description: Returns a single pet + operationId: getPetById + parameters: + - description: ID of pet to return + explode: false + in: path + name: petId + required: true + schema: + format: int64 + type: integer + style: simple + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: successful operation + "400": + description: Invalid ID supplied + "404": + description: Pet not found + security: + - api_key: [] + summary: Find pet by ID + tags: + - pet + post: + operationId: updatePetWithForm + parameters: + - description: ID of pet that needs to be updated + explode: false + in: path + name: petId + required: true + schema: + format: int64 + type: integer + style: simple + requestBody: + $ref: '#/components/requestBodies/inline_object' + content: + application/x-www-form-urlencoded: + schema: + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + type: object + responses: + "405": + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Updates a pet in the store with form data + tags: + - pet + /pet/{petId}/uploadImage: + post: + operationId: uploadFile + parameters: + - description: ID of pet to update + explode: false + in: path + name: petId + required: true + schema: + format: int64 + type: integer + style: simple + requestBody: + $ref: '#/components/requestBodies/inline_object_1' + content: + multipart/form-data: + schema: + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + format: binary + type: string + type: object + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + description: successful operation + security: + - petstore_auth: + - write:pets + - read:pets + summary: uploads an image + tags: + - pet + /store/inventory: + get: + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + "200": + content: + application/json: + schema: + additionalProperties: + format: int32 + type: integer + type: object + description: successful operation + security: + - api_key: [] + summary: Returns pet inventories by status + tags: + - store + /store/order: + post: + operationId: placeOrder + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + "400": + description: Invalid Order + summary: Place an order for a pet + tags: + - store + /store/order/{orderId}: + delete: + description: For valid response try integer IDs with value < 1000. Anything + above 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - description: ID of the order that needs to be deleted + explode: false + in: path + name: orderId + required: true + schema: + type: string + style: simple + responses: + "400": + description: Invalid ID supplied + "404": + description: Order not found + summary: Delete purchase order by ID + tags: + - store + get: + description: For valid response try integer IDs with value <= 5 or > 10. Other + values will generated exceptions + operationId: getOrderById + parameters: + - description: ID of pet that needs to be fetched + explode: false + in: path + name: orderId + required: true + schema: + format: int64 + maximum: 5 + minimum: 1 + type: integer + style: simple + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + "400": + description: Invalid ID supplied + "404": + description: Order not found + summary: Find purchase order by ID + tags: + - store + /user: + post: + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + responses: + default: + description: successful operation + security: + - api_key: [] + summary: Create user + tags: + - user + /user/createWithArray: + post: + operationId: createUsersWithArrayInput + requestBody: + $ref: '#/components/requestBodies/UserArray' + responses: + default: + description: successful operation + security: + - api_key: [] + summary: Creates list of users with given input array + tags: + - user + /user/createWithList: + post: + operationId: createUsersWithListInput + requestBody: + $ref: '#/components/requestBodies/UserArray' + responses: + default: + description: successful operation + security: + - api_key: [] + summary: Creates list of users with given input array + tags: + - user + /user/login: + get: + operationId: loginUser + parameters: + - description: The user name for login + explode: true + in: query + name: username + required: true + schema: + pattern: ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ + type: string + style: form + - description: The password for login in clear text + explode: true + in: query + name: password + required: true + schema: + type: string + style: form + responses: + "200": + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + description: successful operation + headers: + Set-Cookie: + description: Cookie authentication key for use with the `api_key` apiKey + authentication. + explode: false + schema: + example: AUTH_KEY=abcde12345; Path=/; HttpOnly + type: string + style: simple + X-Rate-Limit: + description: calls per hour allowed by the user + explode: false + schema: + format: int32 + type: integer + style: simple + X-Expires-After: + description: date in UTC when toekn expires + explode: false + schema: + format: date-time + type: string + style: simple + "400": + description: Invalid username/password supplied + summary: Logs user into the system + tags: + - user + /user/logout: + get: + operationId: logoutUser + responses: + default: + description: successful operation + security: + - api_key: [] + summary: Logs out current logged in user session + tags: + - user + /user/{username}: + delete: + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - description: The name that needs to be deleted + explode: false + in: path + name: username + required: true + schema: + type: string + style: simple + responses: + "400": + description: Invalid username supplied + "404": + description: User not found + security: + - api_key: [] + summary: Delete user + tags: + - user + get: + operationId: getUserByName + parameters: + - description: The name that needs to be fetched. Use user1 for testing. + explode: false + in: path + name: username + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + description: successful operation + "400": + description: Invalid username supplied + "404": + description: User not found + summary: Get user by user name + tags: + - user + put: + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - description: name that need to be deleted + explode: false + in: path + name: username + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + responses: + "400": + description: Invalid user supplied + "404": + description: User not found + security: + - api_key: [] + summary: Updated user + tags: + - user +components: + requestBodies: + UserArray: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/User' + type: array + description: List of user object + required: true + Pet: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + inline_object: + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/inline_object' + inline_object_1: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/inline_object_1' + schemas: + OrderInfo: + description: An order info for a pets from the pet store + properties: + petId: + format: int64 + type: integer + quantity: + format: int32 + type: integer + shipDate: + format: date-time + type: string + title: Pet Order Info + type: object + xml: + name: OrderInfo + SpecialInfo: + description: An order info for a pets from the pet store + discriminator: + propertyName: type + properties: + promotion: + type: boolean + type: + type: string + title: Pet Order Info + type: object + xml: + name: OrderInfo + Order: + allOf: + - $ref: '#/components/schemas/OrderInfo' + - $ref: '#/components/schemas/SpecialInfo' + description: An order for a pets from the pet store + example: + comment: comment + id: 0 + complete: false + status: placed + properties: + id: + format: int64 + type: integer + status: + description: Order Status + enum: + - placed + - approved + - delivered + type: string + complete: + default: false + type: boolean + comment: + nullable: true + type: string + required: + - comment + title: Pet Order + type: object + xml: + name: Order + Category: + description: A category for a pet + example: + name: name + id: 6 + nullable: true + properties: + id: + format: int64 + type: integer + name: + pattern: ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ + type: string + title: Pet category + type: object + xml: + name: Category + User: + description: A User who is purchasing from the pet store + example: + firstName: firstName + lastName: lastName + password: password + userStatus: 6 + phone: phone + deepSliceModel: + - - - name: name + id: 1 + - name: name + id: 1 + - - name: name + id: 1 + - name: name + id: 1 + - - - name: name + id: 1 + - name: name + id: 1 + - - name: name + id: 1 + - name: name + id: 1 + id: 0 + deepSliceMap: + - - '{}' + - '{}' + - - '{}' + - '{}' + email: email + username: username + properties: + id: + format: int64 + type: integer + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + nullable: true + type: string + userStatus: + description: User Status + format: int32 + type: integer + deepSliceModel: + description: An array 1-deep. + items: + description: An array 2-deep. + items: + description: An array 3-deep. + items: + $ref: '#/components/schemas/Tag' + type: array + type: array + nullable: true + type: array + deepSliceMap: + description: An array 1-deep. + items: + description: An array 2-deep. + items: + description: An array 3-deep. + properties: + tag: + $ref: '#/components/schemas/Tag' + Pet: + description: An array of pet. + items: + $ref: '#/components/schemas/Pet' + type: array + title: an Object + type: object + type: array + type: array + required: + - deepSliceModel + title: a User + type: object + xml: + name: User + Tag: + description: A tag for a pet + example: + name: name + id: 1 + properties: + id: + format: int64 + type: integer + name: + type: string + title: Pet Tag + type: object + xml: + name: Tag + Pet: + description: A pet for sale in the pet store + example: + photoUrls: + - photoUrls + - photoUrls + name: doggie + id: 0 + category: + name: name + id: 6 + tags: + - name: name + id: 1 + - name: name + id: 1 + status: available + properties: + id: + format: int64 + type: integer + category: + $ref: '#/components/schemas/Category' + name: + example: doggie + type: string + photoUrls: + items: + type: string + nullable: true + type: array + xml: + name: photoUrl + wrapped: true + tags: + items: + $ref: '#/components/schemas/Tag' + nullable: true + type: array + xml: + name: tag + wrapped: true + status: + description: pet status in the store + enum: + - available + - pending + - sold + type: string + required: + - name + - photoUrls + title: a Pet + type: object + xml: + name: Pet + ApiResponse: + description: Describes the result of uploading an image resource + example: + code: 0 + type: type + message: message + properties: + code: + format: int32 + type: integer + type: + type: string + message: + type: string + title: An uploaded response + type: object + inline_object: + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + type: object + inline_object_1: + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + format: binary + type: string + type: object + securitySchemes: + petstore_auth: + flows: + implicit: + authorizationUrl: http://petstore.swagger.io/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + type: oauth2 + api_key: + in: header + name: api_key + type: apiKey diff --git a/samples/server/petstore/go-server-required/go.mod b/samples/server/petstore/go-server-required/go.mod new file mode 100644 index 000000000000..fa1221d2bedc --- /dev/null +++ b/samples/server/petstore/go-server-required/go.mod @@ -0,0 +1,5 @@ +module github.com/GIT_USER_ID/GIT_REPO_ID + +go 1.13 + +require github.com/go-chi/chi/v5 v5.0.3 diff --git a/samples/server/petstore/go-server-required/go/api.go b/samples/server/petstore/go-server-required/go/api.go new file mode 100644 index 000000000000..5556474295eb --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api.go @@ -0,0 +1,100 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "context" + "net/http" + "os" +) + + + +// PetApiRouter defines the required methods for binding the api requests to a responses for the PetApi +// The PetApiRouter implementation should parse necessary information from the http request, +// pass the data to a PetApiServicer to perform the required actions, then write the service results to the http response. +type PetApiRouter interface { + AddPet(http.ResponseWriter, *http.Request) + DeletePet(http.ResponseWriter, *http.Request) + FindPetsByStatus(http.ResponseWriter, *http.Request) + // Deprecated + FindPetsByTags(http.ResponseWriter, *http.Request) + GetPetById(http.ResponseWriter, *http.Request) + UpdatePet(http.ResponseWriter, *http.Request) + UpdatePetWithForm(http.ResponseWriter, *http.Request) + UploadFile(http.ResponseWriter, *http.Request) +} +// StoreApiRouter defines the required methods for binding the api requests to a responses for the StoreApi +// The StoreApiRouter implementation should parse necessary information from the http request, +// pass the data to a StoreApiServicer to perform the required actions, then write the service results to the http response. +type StoreApiRouter interface { + DeleteOrder(http.ResponseWriter, *http.Request) + GetInventory(http.ResponseWriter, *http.Request) + GetOrderById(http.ResponseWriter, *http.Request) + PlaceOrder(http.ResponseWriter, *http.Request) +} +// UserApiRouter defines the required methods for binding the api requests to a responses for the UserApi +// The UserApiRouter implementation should parse necessary information from the http request, +// pass the data to a UserApiServicer to perform the required actions, then write the service results to the http response. +type UserApiRouter interface { + CreateUser(http.ResponseWriter, *http.Request) + CreateUsersWithArrayInput(http.ResponseWriter, *http.Request) + CreateUsersWithListInput(http.ResponseWriter, *http.Request) + DeleteUser(http.ResponseWriter, *http.Request) + GetUserByName(http.ResponseWriter, *http.Request) + LoginUser(http.ResponseWriter, *http.Request) + LogoutUser(http.ResponseWriter, *http.Request) + UpdateUser(http.ResponseWriter, *http.Request) +} + + +// PetApiServicer defines the api actions for the PetApi service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type PetApiServicer interface { + AddPet(context.Context, Pet) (ImplResponse, error) + DeletePet(context.Context, int64, string) (ImplResponse, error) + FindPetsByStatus(context.Context, []string) (ImplResponse, error) + // Deprecated + FindPetsByTags(context.Context, []string) (ImplResponse, error) + GetPetById(context.Context, int64) (ImplResponse, error) + UpdatePet(context.Context, Pet) (ImplResponse, error) + UpdatePetWithForm(context.Context, int64, string, string) (ImplResponse, error) + UploadFile(context.Context, int64, string, *os.File) (ImplResponse, error) +} + + +// StoreApiServicer defines the api actions for the StoreApi service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type StoreApiServicer interface { + DeleteOrder(context.Context, string) (ImplResponse, error) + GetInventory(context.Context) (ImplResponse, error) + GetOrderById(context.Context, int64) (ImplResponse, error) + PlaceOrder(context.Context, Order) (ImplResponse, error) +} + + +// UserApiServicer defines the api actions for the UserApi service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type UserApiServicer interface { + CreateUser(context.Context, User) (ImplResponse, error) + CreateUsersWithArrayInput(context.Context, []User) (ImplResponse, error) + CreateUsersWithListInput(context.Context, []User) (ImplResponse, error) + DeleteUser(context.Context, string) (ImplResponse, error) + GetUserByName(context.Context, string) (ImplResponse, error) + LoginUser(context.Context, string, string) (ImplResponse, error) + LogoutUser(context.Context) (ImplResponse, error) + UpdateUser(context.Context, string, User) (ImplResponse, error) +} diff --git a/samples/server/petstore/go-server-required/go/api_pet.go b/samples/server/petstore/go-server-required/go/api_pet.go new file mode 100644 index 000000000000..074dfbb0eac9 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_pet.go @@ -0,0 +1,275 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/go-chi/chi/v5" +) + +// PetApiController binds http requests to an api service and writes the service results to the http response +type PetApiController struct { + service PetApiServicer + errorHandler ErrorHandler +} + +// PetApiOption for how the controller is set up. +type PetApiOption func(*PetApiController) + +// WithPetApiErrorHandler inject ErrorHandler into controller +func WithPetApiErrorHandler(h ErrorHandler) PetApiOption { + return func(c *PetApiController) { + c.errorHandler = h + } +} + +// NewPetApiController creates a default api controller +func NewPetApiController(s PetApiServicer, opts ...PetApiOption) Router { + controller := &PetApiController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all of the api route for the PetApiController +func (c *PetApiController) Routes() Routes { + return Routes{ + { + "AddPet", + strings.ToUpper("Post"), + "/v2/pet", + c.AddPet, + }, + { + "DeletePet", + strings.ToUpper("Delete"), + "/v2/pet/{petId}", + c.DeletePet, + }, + { + "FindPetsByStatus", + strings.ToUpper("Get"), + "/v2/pet/findByStatus", + c.FindPetsByStatus, + }, + { + "FindPetsByTags", + strings.ToUpper("Get"), + "/v2/pet/findByTags", + c.FindPetsByTags, + }, + { + "GetPetById", + strings.ToUpper("Get"), + "/v2/pet/{petId}", + c.GetPetById, + }, + { + "UpdatePet", + strings.ToUpper("Put"), + "/v2/pet", + c.UpdatePet, + }, + { + "UpdatePetWithForm", + strings.ToUpper("Post"), + "/v2/pet/{petId}", + c.UpdatePetWithForm, + }, + { + "UploadFile", + strings.ToUpper("Post"), + "/v2/pet/{petId}/uploadImage", + c.UploadFile, + }, + } +} + +// AddPet - Add a new pet to the store +func (c *PetApiController) AddPet(w http.ResponseWriter, r *http.Request) { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.AddPet(r.Context(), pet) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// DeletePet - Deletes a pet +func (c *PetApiController) DeletePet(w http.ResponseWriter, r *http.Request) { + petId, err := parseInt64Parameter(chi.URLParam(r, "petId"), true) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + + apiKey := r.Header.Get("api_key") + result, err := c.service.DeletePet(r.Context(), petId, apiKey) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// FindPetsByStatus - Finds Pets by status +func (c *PetApiController) FindPetsByStatus(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + status := strings.Split(query.Get("status"), ",") + result, err := c.service.FindPetsByStatus(r.Context(), status) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// FindPetsByTags - Finds Pets by tags +// Deprecated +func (c *PetApiController) FindPetsByTags(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + tags := strings.Split(query.Get("tags"), ",") + result, err := c.service.FindPetsByTags(r.Context(), tags) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// GetPetById - Find pet by ID +func (c *PetApiController) GetPetById(w http.ResponseWriter, r *http.Request) { + petId, err := parseInt64Parameter(chi.URLParam(r, "petId"), true) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + + result, err := c.service.GetPetById(r.Context(), petId) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// UpdatePet - Update an existing pet +func (c *PetApiController) UpdatePet(w http.ResponseWriter, r *http.Request) { + pet := Pet{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&pet); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPetRequired(pet); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdatePet(r.Context(), pet) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// UpdatePetWithForm - Updates a pet in the store with form data +func (c *PetApiController) UpdatePetWithForm(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + petId, err := parseInt64Parameter(chi.URLParam(r, "petId"), true) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + + name := r.FormValue("name") + status := r.FormValue("status") + result, err := c.service.UpdatePetWithForm(r.Context(), petId, name, status) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// UploadFile - uploads an image +func (c *PetApiController) UploadFile(w http.ResponseWriter, r *http.Request) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + petId, err := parseInt64Parameter(chi.URLParam(r, "petId"), true) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + + additionalMetadata := r.FormValue("additionalMetadata") + + file, err := ReadFormFileToTempFile(r, "file") + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + result, err := c.service.UploadFile(r.Context(), petId, additionalMetadata, file) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} diff --git a/samples/server/petstore/go-server-required/go/api_pet_service.go b/samples/server/petstore/go-server-required/go/api_pet_service.go new file mode 100644 index 000000000000..c612d222a875 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_pet_service.go @@ -0,0 +1,142 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" + "os" +) + +// PetApiService is a service that implents the logic for the PetApiServicer +// This service should implement the business logic for every endpoint for the PetApi API. +// Include any external packages or services that will be required by this service. +type PetApiService struct { +} + +// NewPetApiService creates a default api service +func NewPetApiService() PetApiServicer { + return &PetApiService{} +} + +// AddPet - Add a new pet to the store +func (s *PetApiService) AddPet(ctx context.Context, pet Pet) (ImplResponse, error) { + // TODO - update AddPet with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, Pet{}) or use other options such as http.Ok ... + //return Response(200, Pet{}), nil + + //TODO: Uncomment the next line to return response Response(405, {}) or use other options such as http.Ok ... + //return Response(405, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("AddPet method not implemented") +} + +// DeletePet - Deletes a pet +func (s *PetApiService) DeletePet(ctx context.Context, petId int64, apiKey string) (ImplResponse, error) { + // TODO - update DeletePet with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("DeletePet method not implemented") +} + +// FindPetsByStatus - Finds Pets by status +func (s *PetApiService) FindPetsByStatus(ctx context.Context, status []string) (ImplResponse, error) { + // TODO - update FindPetsByStatus with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, []Pet{}) or use other options such as http.Ok ... + //return Response(200, []Pet{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("FindPetsByStatus method not implemented") +} + +// FindPetsByTags - Finds Pets by tags +// Deprecated +func (s *PetApiService) FindPetsByTags(ctx context.Context, tags []string) (ImplResponse, error) { + // TODO - update FindPetsByTags with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, []Pet{}) or use other options such as http.Ok ... + //return Response(200, []Pet{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("FindPetsByTags method not implemented") +} + +// GetPetById - Find pet by ID +func (s *PetApiService) GetPetById(ctx context.Context, petId int64) (ImplResponse, error) { + // TODO - update GetPetById with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, Pet{}) or use other options such as http.Ok ... + //return Response(200, Pet{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetPetById method not implemented") +} + +// UpdatePet - Update an existing pet +func (s *PetApiService) UpdatePet(ctx context.Context, pet Pet) (ImplResponse, error) { + // TODO - update UpdatePet with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, Pet{}) or use other options such as http.Ok ... + //return Response(200, Pet{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + //TODO: Uncomment the next line to return response Response(405, {}) or use other options such as http.Ok ... + //return Response(405, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("UpdatePet method not implemented") +} + +// UpdatePetWithForm - Updates a pet in the store with form data +func (s *PetApiService) UpdatePetWithForm(ctx context.Context, petId int64, name string, status string) (ImplResponse, error) { + // TODO - update UpdatePetWithForm with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(405, {}) or use other options such as http.Ok ... + //return Response(405, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("UpdatePetWithForm method not implemented") +} + +// UploadFile - uploads an image +func (s *PetApiService) UploadFile(ctx context.Context, petId int64, additionalMetadata string, file *os.File) (ImplResponse, error) { + // TODO - update UploadFile with the required logic for this service method. + // Add api_pet_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, ApiResponse{}) or use other options such as http.Ok ... + //return Response(200, ApiResponse{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("UploadFile method not implemented") +} + diff --git a/samples/server/petstore/go-server-required/go/api_store.go b/samples/server/petstore/go-server-required/go/api_store.go new file mode 100644 index 000000000000..af34ada596f6 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_store.go @@ -0,0 +1,149 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/go-chi/chi/v5" +) + +// StoreApiController binds http requests to an api service and writes the service results to the http response +type StoreApiController struct { + service StoreApiServicer + errorHandler ErrorHandler +} + +// StoreApiOption for how the controller is set up. +type StoreApiOption func(*StoreApiController) + +// WithStoreApiErrorHandler inject ErrorHandler into controller +func WithStoreApiErrorHandler(h ErrorHandler) StoreApiOption { + return func(c *StoreApiController) { + c.errorHandler = h + } +} + +// NewStoreApiController creates a default api controller +func NewStoreApiController(s StoreApiServicer, opts ...StoreApiOption) Router { + controller := &StoreApiController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all of the api route for the StoreApiController +func (c *StoreApiController) Routes() Routes { + return Routes{ + { + "DeleteOrder", + strings.ToUpper("Delete"), + "/v2/store/order/{orderId}", + c.DeleteOrder, + }, + { + "GetInventory", + strings.ToUpper("Get"), + "/v2/store/inventory", + c.GetInventory, + }, + { + "GetOrderById", + strings.ToUpper("Get"), + "/v2/store/order/{orderId}", + c.GetOrderById, + }, + { + "PlaceOrder", + strings.ToUpper("Post"), + "/v2/store/order", + c.PlaceOrder, + }, + } +} + +// DeleteOrder - Delete purchase order by ID +func (c *StoreApiController) DeleteOrder(w http.ResponseWriter, r *http.Request) { + orderId := chi.URLParam(r, "orderId") + + result, err := c.service.DeleteOrder(r.Context(), orderId) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// GetInventory - Returns pet inventories by status +func (c *StoreApiController) GetInventory(w http.ResponseWriter, r *http.Request) { + result, err := c.service.GetInventory(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// GetOrderById - Find purchase order by ID +func (c *StoreApiController) GetOrderById(w http.ResponseWriter, r *http.Request) { + orderId, err := parseInt64Parameter(chi.URLParam(r, "orderId"), true) + if err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + + result, err := c.service.GetOrderById(r.Context(), orderId) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// PlaceOrder - Place an order for a pet +func (c *StoreApiController) PlaceOrder(w http.ResponseWriter, r *http.Request) { + order := Order{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&order); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertOrderRequired(order); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.PlaceOrder(r.Context(), order) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} diff --git a/samples/server/petstore/go-server-required/go/api_store_service.go b/samples/server/petstore/go-server-required/go/api_store_service.go new file mode 100644 index 000000000000..27da55939a35 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_store_service.go @@ -0,0 +1,84 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// StoreApiService is a service that implents the logic for the StoreApiServicer +// This service should implement the business logic for every endpoint for the StoreApi API. +// Include any external packages or services that will be required by this service. +type StoreApiService struct { +} + +// NewStoreApiService creates a default api service +func NewStoreApiService() StoreApiServicer { + return &StoreApiService{} +} + +// DeleteOrder - Delete purchase order by ID +func (s *StoreApiService) DeleteOrder(ctx context.Context, orderId string) (ImplResponse, error) { + // TODO - update DeleteOrder with the required logic for this service method. + // Add api_store_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("DeleteOrder method not implemented") +} + +// GetInventory - Returns pet inventories by status +func (s *StoreApiService) GetInventory(ctx context.Context) (ImplResponse, error) { + // TODO - update GetInventory with the required logic for this service method. + // Add api_store_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, map[string]int32{}) or use other options such as http.Ok ... + //return Response(200, map[string]int32{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetInventory method not implemented") +} + +// GetOrderById - Find purchase order by ID +func (s *StoreApiService) GetOrderById(ctx context.Context, orderId int64) (ImplResponse, error) { + // TODO - update GetOrderById with the required logic for this service method. + // Add api_store_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, Order{}) or use other options such as http.Ok ... + //return Response(200, Order{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetOrderById method not implemented") +} + +// PlaceOrder - Place an order for a pet +func (s *StoreApiService) PlaceOrder(ctx context.Context, order Order) (ImplResponse, error) { + // TODO - update PlaceOrder with the required logic for this service method. + // Add api_store_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, Order{}) or use other options such as http.Ok ... + //return Response(200, Order{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("PlaceOrder method not implemented") +} + diff --git a/samples/server/petstore/go-server-required/go/api_user.go b/samples/server/petstore/go-server-required/go/api_user.go new file mode 100644 index 000000000000..f2c41d1695e9 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_user.go @@ -0,0 +1,263 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/go-chi/chi/v5" +) + +// UserApiController binds http requests to an api service and writes the service results to the http response +type UserApiController struct { + service UserApiServicer + errorHandler ErrorHandler +} + +// UserApiOption for how the controller is set up. +type UserApiOption func(*UserApiController) + +// WithUserApiErrorHandler inject ErrorHandler into controller +func WithUserApiErrorHandler(h ErrorHandler) UserApiOption { + return func(c *UserApiController) { + c.errorHandler = h + } +} + +// NewUserApiController creates a default api controller +func NewUserApiController(s UserApiServicer, opts ...UserApiOption) Router { + controller := &UserApiController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all of the api route for the UserApiController +func (c *UserApiController) Routes() Routes { + return Routes{ + { + "CreateUser", + strings.ToUpper("Post"), + "/v2/user", + c.CreateUser, + }, + { + "CreateUsersWithArrayInput", + strings.ToUpper("Post"), + "/v2/user/createWithArray", + c.CreateUsersWithArrayInput, + }, + { + "CreateUsersWithListInput", + strings.ToUpper("Post"), + "/v2/user/createWithList", + c.CreateUsersWithListInput, + }, + { + "DeleteUser", + strings.ToUpper("Delete"), + "/v2/user/{username}", + c.DeleteUser, + }, + { + "GetUserByName", + strings.ToUpper("Get"), + "/v2/user/{username}", + c.GetUserByName, + }, + { + "LoginUser", + strings.ToUpper("Get"), + "/v2/user/login", + c.LoginUser, + }, + { + "LogoutUser", + strings.ToUpper("Get"), + "/v2/user/logout", + c.LogoutUser, + }, + { + "UpdateUser", + strings.ToUpper("Put"), + "/v2/user/{username}", + c.UpdateUser, + }, + } +} + +// CreateUser - Create user +func (c *UserApiController) CreateUser(w http.ResponseWriter, r *http.Request) { + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.CreateUser(r.Context(), user) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// CreateUsersWithArrayInput - Creates list of users with given input array +func (c *UserApiController) CreateUsersWithArrayInput(w http.ResponseWriter, r *http.Request) { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithArrayInput(r.Context(), user) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// CreateUsersWithListInput - Creates list of users with given input array +func (c *UserApiController) CreateUsersWithListInput(w http.ResponseWriter, r *http.Request) { + user := []User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + for _, el := range user { + if err := AssertUserRequired(el); err != nil { + c.errorHandler(w, r, err, nil) + return + } + } + result, err := c.service.CreateUsersWithListInput(r.Context(), user) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// DeleteUser - Delete user +func (c *UserApiController) DeleteUser(w http.ResponseWriter, r *http.Request) { + username := chi.URLParam(r, "username") + + result, err := c.service.DeleteUser(r.Context(), username) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// GetUserByName - Get user by user name +func (c *UserApiController) GetUserByName(w http.ResponseWriter, r *http.Request) { + username := chi.URLParam(r, "username") + + result, err := c.service.GetUserByName(r.Context(), username) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// LoginUser - Logs user into the system +func (c *UserApiController) LoginUser(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + username := query.Get("username") + password := query.Get("password") + result, err := c.service.LoginUser(r.Context(), username, password) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// LogoutUser - Logs out current logged in user session +func (c *UserApiController) LogoutUser(w http.ResponseWriter, r *http.Request) { + result, err := c.service.LogoutUser(r.Context()) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} + +// UpdateUser - Updated user +func (c *UserApiController) UpdateUser(w http.ResponseWriter, r *http.Request) { + username := chi.URLParam(r, "username") + + user := User{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&user); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertUserRequired(user); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.UpdateUser(r.Context(), username, user) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, result.Headers, w) + +} diff --git a/samples/server/petstore/go-server-required/go/api_user_service.go b/samples/server/petstore/go-server-required/go/api_user_service.go new file mode 100644 index 000000000000..c14d96ab3682 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/api_user_service.go @@ -0,0 +1,131 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "context" + "net/http" + "errors" +) + +// UserApiService is a service that implents the logic for the UserApiServicer +// This service should implement the business logic for every endpoint for the UserApi API. +// Include any external packages or services that will be required by this service. +type UserApiService struct { +} + +// NewUserApiService creates a default api service +func NewUserApiService() UserApiServicer { + return &UserApiService{} +} + +// CreateUser - Create user +func (s *UserApiService) CreateUser(ctx context.Context, user User) (ImplResponse, error) { + // TODO - update CreateUser with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(0, {}) or use other options such as http.Ok ... + //return Response(0, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("CreateUser method not implemented") +} + +// CreateUsersWithArrayInput - Creates list of users with given input array +func (s *UserApiService) CreateUsersWithArrayInput(ctx context.Context, user []User) (ImplResponse, error) { + // TODO - update CreateUsersWithArrayInput with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(0, {}) or use other options such as http.Ok ... + //return Response(0, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("CreateUsersWithArrayInput method not implemented") +} + +// CreateUsersWithListInput - Creates list of users with given input array +func (s *UserApiService) CreateUsersWithListInput(ctx context.Context, user []User) (ImplResponse, error) { + // TODO - update CreateUsersWithListInput with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(0, {}) or use other options such as http.Ok ... + //return Response(0, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("CreateUsersWithListInput method not implemented") +} + +// DeleteUser - Delete user +func (s *UserApiService) DeleteUser(ctx context.Context, username string) (ImplResponse, error) { + // TODO - update DeleteUser with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("DeleteUser method not implemented") +} + +// GetUserByName - Get user by user name +func (s *UserApiService) GetUserByName(ctx context.Context, username string) (ImplResponse, error) { + // TODO - update GetUserByName with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, User{}) or use other options such as http.Ok ... + //return Response(200, User{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetUserByName method not implemented") +} + +// LoginUser - Logs user into the system +func (s *UserApiService) LoginUser(ctx context.Context, username string, password string) (ImplResponse, error) { + // TODO - update LoginUser with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, string{}) or use other options such as http.Ok ... + //return Response(200, string{}), nil + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("LoginUser method not implemented") +} + +// LogoutUser - Logs out current logged in user session +func (s *UserApiService) LogoutUser(ctx context.Context) (ImplResponse, error) { + // TODO - update LogoutUser with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(0, {}) or use other options such as http.Ok ... + //return Response(0, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("LogoutUser method not implemented") +} + +// UpdateUser - Updated user +func (s *UserApiService) UpdateUser(ctx context.Context, username string, user User) (ImplResponse, error) { + // TODO - update UpdateUser with the required logic for this service method. + // Add api_user_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(400, {}) or use other options such as http.Ok ... + //return Response(400, nil),nil + + //TODO: Uncomment the next line to return response Response(404, {}) or use other options such as http.Ok ... + //return Response(404, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("UpdateUser method not implemented") +} + diff --git a/samples/server/petstore/go-server-required/go/error.go b/samples/server/petstore/go-server-required/go/error.go new file mode 100644 index 000000000000..2c0b010fe8da --- /dev/null +++ b/samples/server/petstore/go-server-required/go/error.go @@ -0,0 +1,62 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "errors" + "fmt" + "net/http" +) + +var ( + // ErrTypeAssertionError is thrown when type an interface does not match the asserted type + ErrTypeAssertionError = errors.New("unable to assert type") +) + +// ParsingError indicates that an error has occurred when parsing request parameters +type ParsingError struct { + Err error +} + +func (e *ParsingError) Unwrap() error { + return e.Err +} + +func (e *ParsingError) Error() string { + return e.Err.Error() +} + +// RequiredError indicates that an error has occurred when parsing request parameters +type RequiredError struct { + Field string +} + +func (e *RequiredError) Error() string { + return fmt.Sprintf("required field '%s' is zero value.", e.Field) +} + +// ErrorHandler defines the required method for handling error. You may implement it and inject this into a controller if +// you would like errors to be handled differently from the DefaultErrorHandler +type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) + +// DefaultErrorHandler defines the default logic on how to handle errors from the controller. Any errors from parsing +// request params will return a StatusBadRequest. Otherwise, the error code originating from the servicer will be used. +func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, result *ImplResponse) { + if _, ok := err.(*ParsingError); ok { + // Handle parsing errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusBadRequest), map[string][]string{}, w) + } else if _, ok := err.(*RequiredError); ok { + // Handle missing required errors + EncodeJSONResponse(err.Error(), func(i int) *int { return &i }(http.StatusUnprocessableEntity), map[string][]string{}, w) + } else { + // Handle all other errors + EncodeJSONResponse(err.Error(), &result.Code, result.Headers, w) + } +} diff --git a/samples/server/petstore/go-server-required/go/helpers.go b/samples/server/petstore/go-server-required/go/helpers.go new file mode 100644 index 000000000000..ef41ca1c8975 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/helpers.go @@ -0,0 +1,64 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "reflect" +) + +// Response return a ImplResponse struct filled +func Response(code int, body interface{}) ImplResponse { + return ImplResponse { + Code: code, + Headers: nil, + Body: body, + } +} + +// ResponseWithHeaders return a ImplResponse struct filled, including headers +func ResponseWithHeaders(code int, headers map[string][]string, body interface{}) ImplResponse { + return ImplResponse { + Code: code, + Headers: headers, + Body: body, + } +} + +// IsZeroValue checks if the val is the zero-ed value. +func IsZeroValue(val interface{}) bool { + return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) +} + +// AssertInterfaceRequired recursively checks each struct in a slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseInterfaceRequired(obj interface{}, callback func(interface{}) error) error { + return AssertRecurseValueRequired(reflect.ValueOf(obj), callback) +} + +// AssertNestedValueRequired checks each struct in the nested slice against the callback. +// This method traverse nested slices in a preorder fashion. +func AssertRecurseValueRequired(value reflect.Value, callback func(interface{}) error) error { + switch value.Kind() { + // If it is a struct we check using callback + case reflect.Struct: + if err := callback(value.Interface()); err != nil { + return err + } + + // If it is a slice we continue recursion + case reflect.Slice: + for i := 0; i < value.Len(); i += 1 { + if err := AssertRecurseValueRequired(value.Index(i), callback); err != nil { + return err + } + } + } + return nil +} diff --git a/samples/server/petstore/go-server-required/go/impl.go b/samples/server/petstore/go-server-required/go/impl.go new file mode 100644 index 000000000000..1da96f41252a --- /dev/null +++ b/samples/server/petstore/go-server-required/go/impl.go @@ -0,0 +1,17 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +//Implementation response defines an error code with the associated body +type ImplResponse struct { + Code int + Headers map[string][]string + Body interface{} +} diff --git a/samples/server/petstore/go-server-required/go/logger.go b/samples/server/petstore/go-server-required/go/logger.go new file mode 100644 index 000000000000..08cc0ea3f1d2 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/logger.go @@ -0,0 +1,32 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "log" + "net/http" + "time" +) + +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s %s %s %s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_api_response.go b/samples/server/petstore/go-server-required/go/model_api_response.go new file mode 100644 index 000000000000..324cfbe08444 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_api_response.go @@ -0,0 +1,37 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// ApiResponse - Describes the result of uploading an image resource +type ApiResponse struct { + + Code int32 `json:"code,omitempty"` + + Type string `json:"type,omitempty"` + + Message string `json:"message,omitempty"` +} + +// AssertApiResponseRequired checks if the required fields are not zero-ed +func AssertApiResponseRequired(obj ApiResponse) error { + return nil +} + +// AssertRecurseApiResponseRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of ApiResponse (e.g. [][]ApiResponse), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseApiResponseRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aApiResponse, ok := obj.(ApiResponse) + if !ok { + return ErrTypeAssertionError + } + return AssertApiResponseRequired(aApiResponse) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_category.go b/samples/server/petstore/go-server-required/go/model_category.go new file mode 100644 index 000000000000..3ae01892a3fc --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_category.go @@ -0,0 +1,35 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// Category - A category for a pet +type Category struct { + + Id int64 `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +// AssertCategoryRequired checks if the required fields are not zero-ed +func AssertCategoryRequired(obj Category) error { + return nil +} + +// AssertRecurseCategoryRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Category (e.g. [][]Category), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseCategoryRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aCategory, ok := obj.(Category) + if !ok { + return ErrTypeAssertionError + } + return AssertCategoryRequired(aCategory) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_order.go b/samples/server/petstore/go-server-required/go/model_order.go new file mode 100644 index 000000000000..f3441fa8c7b5 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_order.go @@ -0,0 +1,64 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "time" +) + +// Order - An order for a pets from the pet store +type Order struct { + SpecialInfo + + Id int64 `json:"id,omitempty"` + + // Order Status + Status string `json:"status,omitempty"` + + Complete bool `json:"complete,omitempty"` + + Comment *string `json:"comment"` + + PetId int64 `json:"petId,omitempty"` + + Quantity int32 `json:"quantity,omitempty"` + + ShipDate time.Time `json:"shipDate,omitempty"` +} + +// AssertOrderRequired checks if the required fields are not zero-ed +func AssertOrderRequired(obj Order) error { + elements := map[string]interface{}{ + "comment": obj.Comment, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + if err := AssertSpecialInfoRequired(obj.SpecialInfo); err != nil { + return err + } + + return nil +} + +// AssertRecurseOrderRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Order (e.g. [][]Order), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseOrderRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aOrder, ok := obj.(Order) + if !ok { + return ErrTypeAssertionError + } + return AssertOrderRequired(aOrder) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_order_info.go b/samples/server/petstore/go-server-required/go/model_order_info.go new file mode 100644 index 000000000000..3c6bb2704c94 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_order_info.go @@ -0,0 +1,41 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "time" +) + +// OrderInfo - An order info for a pets from the pet store +type OrderInfo struct { + + PetId int64 `json:"petId,omitempty"` + + Quantity int32 `json:"quantity,omitempty"` + + ShipDate time.Time `json:"shipDate,omitempty"` +} + +// AssertOrderInfoRequired checks if the required fields are not zero-ed +func AssertOrderInfoRequired(obj OrderInfo) error { + return nil +} + +// AssertRecurseOrderInfoRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of OrderInfo (e.g. [][]OrderInfo), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseOrderInfoRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aOrderInfo, ok := obj.(OrderInfo) + if !ok { + return ErrTypeAssertionError + } + return AssertOrderInfoRequired(aOrderInfo) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_pet.go b/samples/server/petstore/go-server-required/go/model_pet.go new file mode 100644 index 000000000000..5c71af290f61 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_pet.go @@ -0,0 +1,66 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// Pet - A pet for sale in the pet store +type Pet struct { + + Id int64 `json:"id,omitempty"` + + Category *Category `json:"category,omitempty"` + + Name string `json:"name"` + + PhotoUrls *[]string `json:"photoUrls"` + + Tags *[]Tag `json:"tags,omitempty"` + + // pet status in the store + Status string `json:"status,omitempty"` +} + +// AssertPetRequired checks if the required fields are not zero-ed +func AssertPetRequired(obj Pet) error { + elements := map[string]interface{}{ + "name": obj.Name, + "photoUrls": obj.PhotoUrls, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + if obj.Category != nil { + if err := AssertCategoryRequired(*obj.Category); err != nil { + return err + } + } + if obj.Tags != nil { + for _, el := range *obj.Tags { + if err := AssertTagRequired(el); err != nil { + return err + } + } + } + return nil +} + +// AssertRecursePetRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Pet (e.g. [][]Pet), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePetRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPet, ok := obj.(Pet) + if !ok { + return ErrTypeAssertionError + } + return AssertPetRequired(aPet) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_special_info.go b/samples/server/petstore/go-server-required/go/model_special_info.go new file mode 100644 index 000000000000..73bea15959c1 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_special_info.go @@ -0,0 +1,35 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// SpecialInfo - An order info for a pets from the pet store +type SpecialInfo struct { + + Promotion bool `json:"promotion,omitempty"` + + Type string `json:"type,omitempty"` +} + +// AssertSpecialInfoRequired checks if the required fields are not zero-ed +func AssertSpecialInfoRequired(obj SpecialInfo) error { + return nil +} + +// AssertRecurseSpecialInfoRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of SpecialInfo (e.g. [][]SpecialInfo), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseSpecialInfoRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aSpecialInfo, ok := obj.(SpecialInfo) + if !ok { + return ErrTypeAssertionError + } + return AssertSpecialInfoRequired(aSpecialInfo) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_tag.go b/samples/server/petstore/go-server-required/go/model_tag.go new file mode 100644 index 000000000000..0631ccc4866f --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_tag.go @@ -0,0 +1,35 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// Tag - A tag for a pet +type Tag struct { + + Id int64 `json:"id,omitempty"` + + Name string `json:"name,omitempty"` +} + +// AssertTagRequired checks if the required fields are not zero-ed +func AssertTagRequired(obj Tag) error { + return nil +} + +// AssertRecurseTagRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of Tag (e.g. [][]Tag), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseTagRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aTag, ok := obj.(Tag) + if !ok { + return ErrTypeAssertionError + } + return AssertTagRequired(aTag) + }) +} diff --git a/samples/server/petstore/go-server-required/go/model_user.go b/samples/server/petstore/go-server-required/go/model_user.go new file mode 100644 index 000000000000..037b2f1ede62 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/model_user.go @@ -0,0 +1,68 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +// User - A User who is purchasing from the pet store +type User struct { + + Id int64 `json:"id,omitempty"` + + Username string `json:"username,omitempty"` + + FirstName string `json:"firstName,omitempty"` + + LastName string `json:"lastName,omitempty"` + + Email string `json:"email,omitempty"` + + Password string `json:"password,omitempty"` + + Phone *string `json:"phone,omitempty"` + + // User Status + UserStatus int32 `json:"userStatus,omitempty"` + + // An array 1-deep. + DeepSliceModel *[][][]Tag `json:"deepSliceModel"` + + // An array 1-deep. + DeepSliceMap [][]map[string]interface{} `json:"deepSliceMap,omitempty"` +} + +// AssertUserRequired checks if the required fields are not zero-ed +func AssertUserRequired(obj User) error { + elements := map[string]interface{}{ + "deepSliceModel": obj.DeepSliceModel, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + if obj.DeepSliceModel != nil { + if err := AssertRecurseTagRequired(*obj.DeepSliceModel); err != nil { + return err + } + } + return nil +} + +// AssertRecurseUserRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of User (e.g. [][]User), otherwise ErrTypeAssertionError is thrown. +func AssertRecurseUserRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aUser, ok := obj.(User) + if !ok { + return ErrTypeAssertionError + } + return AssertUserRequired(aUser) + }) +} diff --git a/samples/server/petstore/go-server-required/go/routers.go b/samples/server/petstore/go-server-required/go/routers.go new file mode 100644 index 000000000000..3213d5dde239 --- /dev/null +++ b/samples/server/petstore/go-server-required/go/routers.go @@ -0,0 +1,221 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package petstoreserver + +import ( + "encoding/json" + "errors" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "strconv" + "strings" +) + +// A Route defines the parameters for an api endpoint +type Route struct { + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +// Routes are a collection of defined api endpoints +type Routes []Route + +// Router defines the required methods for retrieving api routes +type Router interface { + Routes() Routes +} + +const errMsgRequiredMissing = "required parameter is missing" + +// NewRouter creates a new router for any number of api routers +func NewRouter(routers ...Router) chi.Router { + router := chi.NewRouter() + router.Use(middleware.Logger) + for _, api := range routers { + for _, route := range api.Routes() { + var handler http.Handler + handler = route.HandlerFunc + router.Method(route.Method, route.Pattern, handler) + } + } + + return router +} + +// EncodeJSONResponse uses the json encoder to write an interface to the http response with an optional status code +func EncodeJSONResponse(i interface{}, status *int, headers map[string][]string, w http.ResponseWriter) error { + wHeader := w.Header() + if headers != nil { + for key, values := range headers { + for _, value := range values { + wHeader.Add(key, value) + } + } + } + wHeader.Set("Content-Type", "application/json; charset=UTF-8") + if status != nil { + w.WriteHeader(*status) + } else { + w.WriteHeader(http.StatusOK) + } + + return json.NewEncoder(w).Encode(i) +} + +// ReadFormFileToTempFile reads file data from a request form and writes it to a temporary file +func ReadFormFileToTempFile(r *http.Request, key string) (*os.File, error) { + _, fileHeader, err := r.FormFile(key) + if err != nil { + return nil, err + } + + return readFileHeaderToTempFile(fileHeader) +} + +// ReadFormFilesToTempFiles reads files array data from a request form and writes it to a temporary files +func ReadFormFilesToTempFiles(r *http.Request, key string) ([]*os.File, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + + files := make([]*os.File, 0, len(r.MultipartForm.File[key])) + + for _, fileHeader := range r.MultipartForm.File[key] { + file, err := readFileHeaderToTempFile(fileHeader) + if err != nil { + return nil, err + } + + files = append(files, file) + } + + return files, nil +} + +// readFileHeaderToTempFile reads multipart.FileHeader and writes it to a temporary file +func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error) { + formFile, err := fileHeader.Open() + if err != nil { + return nil, err + } + + defer formFile.Close() + + fileBytes, err := ioutil.ReadAll(formFile) + if err != nil { + return nil, err + } + + file, err := ioutil.TempFile("", fileHeader.Filename) + if err != nil { + return nil, err + } + + defer file.Close() + + file.Write(fileBytes) + + return file, nil +} + +// parseInt64Parameter parses a string parameter to an int64. +func parseInt64Parameter(param string, required bool) (int64, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } + + return 0, nil + } + + return strconv.ParseInt(param, 10, 64) +} + +// parseInt32Parameter parses a string parameter to an int32. +func parseInt32Parameter(param string, required bool) (int32, error) { + if param == "" { + if required { + return 0, errors.New(errMsgRequiredMissing) + } + + return 0, nil + } + + val, err := strconv.ParseInt(param, 10, 32) + if err != nil { + return -1, err + } + + return int32(val), nil +} + +// parseBoolParameter parses a string parameter to a bool +func parseBoolParameter(param string) (bool, error) { + val, err := strconv.ParseBool(param) + if err != nil { + return false, err + } + + return bool(val), 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 +} \ No newline at end of file diff --git a/samples/server/petstore/go-server-required/main.go b/samples/server/petstore/go-server-required/main.go new file mode 100644 index 000000000000..ddc5258297ca --- /dev/null +++ b/samples/server/petstore/go-server-required/main.go @@ -0,0 +1,34 @@ +/* + * OpenAPI Petstore + * + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package main + +import ( + "log" + "net/http" + + petstoreserver "github.com/GIT_USER_ID/GIT_REPO_ID/go" +) + +func main() { + log.Printf("Server started") + + PetApiService := petstoreserver.NewPetApiService() + PetApiController := petstoreserver.NewPetApiController(PetApiService) + + StoreApiService := petstoreserver.NewStoreApiService() + StoreApiController := petstoreserver.NewStoreApiController(StoreApiService) + + UserApiService := petstoreserver.NewUserApiService() + UserApiController := petstoreserver.NewUserApiController(UserApiService) + + router := petstoreserver.NewRouter(PetApiController, StoreApiController, UserApiController) + + log.Fatal(http.ListenAndServe(":8080", router)) +}