Skip to content

Commit

Permalink
Merge pull request #78 from Clever/patch-type-multi
Browse files Browse the repository at this point in the history
Support wag-patch-type with multiple input fields
  • Loading branch information
kvigen authored Oct 28, 2016
2 parents e085509 + 1c3dcaf commit e754abe
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 8 deletions.
12 changes: 10 additions & 2 deletions models/genmodels.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/go-openapi/spec"

Expand Down Expand Up @@ -117,6 +118,10 @@ var _ = validate.Maximum
var _ = strfmt.NewFormats
`)

wagPatchTypes, err := swagger.WagPatchDataTypes(paths.Paths)
if err != nil {
return err
}
for _, pathKey := range swagger.SortedPathItemKeys(paths.Paths) {
path := paths.Paths[pathKey]
pathItemOps := swagger.PathItemOperations(path)
Expand All @@ -129,7 +134,7 @@ var _ = strfmt.NewFormats
if ssbp, _ := swagger.SingleSchemaedBodyParameter(op); ssbp {
continue
}
if err := printInputStruct(&g, op); err != nil {
if err := printInputStruct(&g, op, opKey, wagPatchTypes); err != nil {
return err
}
if err := printInputValidation(&g, op); err != nil {
Expand All @@ -141,7 +146,7 @@ var _ = strfmt.NewFormats
return g.WriteFile("models/inputs.go")
}

func printInputStruct(g *swagger.Generator, op *spec.Operation) error {
func printInputStruct(g *swagger.Generator, op *spec.Operation, method string, wagPatchTypes map[string]struct{}) error {
capOpID := swagger.Capitalize(op.ID)
g.Printf("// %sInput holds the input parameters for a %s operation.\n", capOpID, op.ID)
g.Printf("type %sInput struct {\n", capOpID)
Expand All @@ -163,6 +168,9 @@ func printInputStruct(g *swagger.Generator, op *spec.Operation) error {
if err != nil {
return err
}
if _, ok := wagPatchTypes[typeName]; ok && strings.ToUpper(method) == "PATCH" {
typeName = "Patch" + typeName
}
// All schema types are pointers
typeName = "*" + typeName
}
Expand Down
79 changes: 79 additions & 0 deletions samples/gen-wag-patch/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,82 @@ func (c *WagClient) Wagpatch(ctx context.Context, i *models.PatchData) (*models.
return nil, models.DefaultInternalError{Msg: "Unknown response"}
}
}

// Wagpatch2 makes a PATCH request to /wagpatch2.
// Wag patch with another argument
func (c *WagClient) Wagpatch2(ctx context.Context, i *models.Wagpatch2Input) (*models.Data, error) {
path := c.basePath + "/wagpatch2"
urlVals := url.Values{}
var body []byte

if i.Other != nil {
urlVals.Add("other", *i.Other)
}
path = path + "?" + urlVals.Encode()

if i.SpecialPatch != nil {

var err error
body, err = json.Marshal(i.SpecialPatch)

if err != nil {
return nil, err
}

}

client := &http.Client{Transport: c.transport}
req, err := http.NewRequest("PATCH", path, bytes.NewBuffer(body))

if err != nil {
return nil, err
}

// Add the opname for doers like tracing
ctx = context.WithValue(ctx, opNameCtx{}, "wagpatch2")
req = req.WithContext(ctx)
// Don't add the timeout in a "doer" because we don't want to call "defer.cancel()"
// until we've finished all the processing of the request object. Otherwise we'll cancel
// our own request before we've finished it.
if c.defaultTimeout != 0 {
ctx, cancel := context.WithTimeout(req.Context(), c.defaultTimeout)
defer cancel()
req = req.WithContext(ctx)
}
resp, err := c.requestDoer.Do(client, req)

if err != nil {
return nil, models.DefaultInternalError{Msg: err.Error()}
}

defer resp.Body.Close()
switch resp.StatusCode {
case 200:
var output models.Data

if err := json.NewDecoder(resp.Body).Decode(&output); err != nil {
return nil, models.DefaultInternalError{Msg: err.Error()}
}

return &output, nil
case 400:
var output models.DefaultBadRequest

if err := json.NewDecoder(resp.Body).Decode(&output); err != nil {
return nil, models.DefaultInternalError{Msg: err.Error()}
}

return nil, output
case 500:
var output models.DefaultInternalError

if err := json.NewDecoder(resp.Body).Decode(&output); err != nil {
return nil, models.DefaultInternalError{Msg: err.Error()}
}

return nil, output

default:
return nil, models.DefaultInternalError{Msg: "Unknown response"}
}
}
4 changes: 4 additions & 0 deletions samples/gen-wag-patch/client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ type Client interface {
// Wagpatch makes a PATCH request to /wagpatch.
// Special wag patch type
Wagpatch(ctx context.Context, i *models.PatchData) (*models.Data, error)

// Wagpatch2 makes a PATCH request to /wagpatch2.
// Wag patch with another argument
Wagpatch2(ctx context.Context, i *models.Wagpatch2Input) (*models.Data, error)
}
11 changes: 11 additions & 0 deletions samples/gen-wag-patch/client/mock_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,14 @@ func (_m *MockClient) Wagpatch(ctx context.Context, i *models.PatchData) (*model
func (_mr *_MockClientRecorder) Wagpatch(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Wagpatch", arg0, arg1)
}

func (_m *MockClient) Wagpatch2(ctx context.Context, i *models.Wagpatch2Input) (*models.Data, error) {
ret := _m.ctrl.Call(_m, "Wagpatch2", ctx, i)
ret0, _ := ret[0].(*models.Data)
ret1, _ := ret[1].(error)
return ret0, ret1
}

func (_mr *_MockClientRecorder) Wagpatch2(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Wagpatch2", arg0, arg1)
}
16 changes: 16 additions & 0 deletions samples/gen-wag-patch/models/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,19 @@ var _ = json.Marshal
var _ = strconv.FormatInt
var _ = validate.Maximum
var _ = strfmt.NewFormats

// Wagpatch2Input holds the input parameters for a wagpatch2 operation.
type Wagpatch2Input struct {
Other *string
SpecialPatch *PatchData
}

// Validate returns an error if any of the Wagpatch2Input parameters don't satisfy the
// requirements from the swagger yml file.
func (i Wagpatch2Input) Validate() error {
if err := i.SpecialPatch.Validate(nil); err != nil {
return err
}

return nil
}
91 changes: 91 additions & 0 deletions samples/gen-wag-patch/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,94 @@ func newWagpatchInput(r *http.Request) (*models.PatchData, error) {

return &input, nil
}

// statusCodeForWagpatch2 returns the status code corresponding to the returned
// object. It returns -1 if the type doesn't correspond to anything.
func statusCodeForWagpatch2(obj interface{}) int {

switch obj.(type) {

case *models.Data:
return 200

case models.Data:
return 200

case models.DefaultBadRequest:
return 400
case models.DefaultInternalError:
return 500
default:
return -1
}
}

func (h handler) Wagpatch2Handler(ctx context.Context, w http.ResponseWriter, r *http.Request) {

input, err := newWagpatch2Input(r)
if err != nil {
logger.FromContext(ctx).AddContext("error", err.Error())
http.Error(w, jsonMarshalNoError(models.DefaultBadRequest{Msg: err.Error()}), http.StatusBadRequest)
return
}

err = input.Validate()
if err != nil {
logger.FromContext(ctx).AddContext("error", err.Error())
http.Error(w, jsonMarshalNoError(models.DefaultBadRequest{Msg: err.Error()}), http.StatusBadRequest)
return
}

resp, err := h.Wagpatch2(ctx, input)

if err != nil {
logger.FromContext(ctx).AddContext("error", err.Error())
statusCode := statusCodeForWagpatch2(err)
if statusCode != -1 {
http.Error(w, err.Error(), statusCode)
} else {
http.Error(w, jsonMarshalNoError(models.DefaultInternalError{Msg: err.Error()}), http.StatusInternalServerError)
}
return
}

respBytes, err := json.Marshal(resp)
if err != nil {
logger.FromContext(ctx).AddContext("error", err.Error())
http.Error(w, jsonMarshalNoError(models.DefaultInternalError{Msg: err.Error()}), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCodeForWagpatch2(resp))
w.Write(respBytes)

}

// newWagpatch2Input takes in an http.Request an returns the input struct.
func newWagpatch2Input(r *http.Request) (*models.Wagpatch2Input, error) {
var input models.Wagpatch2Input

var err error
_ = err

otherStr := r.URL.Query().Get("other")
if len(otherStr) != 0 {
var otherTmp string
otherTmp, err = otherStr, error(nil)
if err != nil {
return nil, err
}
input.Other = &otherTmp

}
data, err := ioutil.ReadAll(r.Body)
if len(data) > 0 {
input.SpecialPatch = &models.PatchData{}
if err := json.NewDecoder(bytes.NewReader(data)).Decode(input.SpecialPatch); err != nil {
return nil, err
}
}

return &input, nil
}
4 changes: 4 additions & 0 deletions samples/gen-wag-patch/server/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ type Controller interface {
// Wagpatch makes a PATCH request to /wagpatch.
// Special wag patch type
Wagpatch(ctx context.Context, i *models.PatchData) (*models.Data, error)

// Wagpatch2 makes a PATCH request to /wagpatch2.
// Wag patch with another argument
Wagpatch2(ctx context.Context, i *models.Wagpatch2Input) (*models.Data, error)
}
11 changes: 11 additions & 0 deletions samples/gen-wag-patch/server/mock_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,14 @@ func (_m *MockController) Wagpatch(ctx context.Context, i *models.PatchData) (*m
func (_mr *_MockControllerRecorder) Wagpatch(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Wagpatch", arg0, arg1)
}

func (_m *MockController) Wagpatch2(ctx context.Context, i *models.Wagpatch2Input) (*models.Data, error) {
ret := _m.ctrl.Call(_m, "Wagpatch2", ctx, i)
ret0, _ := ret[0].(*models.Data)
ret1, _ := ret[1].(error)
return ret0, ret1
}

func (_mr *_MockControllerRecorder) Wagpatch2(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "Wagpatch2", arg0, arg1)
}
5 changes: 5 additions & 0 deletions samples/gen-wag-patch/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ func New(c Controller, addr string) *Server {
h.WagpatchHandler(r.Context(), w, r)
})

r.Methods("PATCH").Path("/wagpatch2").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger.FromContext(r.Context()).AddContext("op", "wagpatch2")
h.Wagpatch2Handler(r.Context(), w, r)
})

handler := withMiddleware("wag-patch", r)
return &Server{Handler: handler, addr: addr, l: l}
}
20 changes: 20 additions & 0 deletions samples/wag-patch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ paths:
schema:
$ref: "#/definitions/Data"

/wagpatch2:
patch:
operationId: wagpatch2
description: Wag patch with another argument
parameters:
- name: other
in: query
type: string
- name: specialPatch
x-wag-patch: true
in: body
schema:
$ref: "#/definitions/Data"
responses:
200:
description: OK response
schema:
$ref: "#/definitions/Data"


definitions:

Data:
Expand Down
20 changes: 14 additions & 6 deletions server/genserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,18 @@ func generateHandlers(packageName string, paths *spec.Paths) error {
BaseStringToTypeCode: swagger.BaseStringToTypeCode(),
}

wagPatchTypes, err := swagger.WagPatchDataTypes(paths.Paths)
if err != nil {
return err
}

for _, pathKey := range swagger.SortedPathItemKeys(paths.Paths) {
path := paths.Paths[pathKey]
pathItemOps := swagger.PathItemOperations(path)
for _, opKey := range swagger.SortedOperationsKeys(pathItemOps) {
op := pathItemOps[opKey]

// TODO: Fill this in...

operationHandler, err := generateOperationHandler(op)
operationHandler, err := generateOperationHandler(op, opKey, wagPatchTypes)
if err != nil {
return err
}
Expand All @@ -268,7 +271,7 @@ var jsonMarshalString = `
`

// generateOperationHandler generates the handler code for a single handler
func generateOperationHandler(op *spec.Operation) (string, error) {
func generateOperationHandler(op *spec.Operation, method string, wagPatchTypes map[string]struct{}) (string, error) {
typeToCode := make(map[string]int)
emptyResponseCode := 200
for code, typeStr := range swagger.CodeToTypeMap(op) {
Expand Down Expand Up @@ -298,7 +301,7 @@ func generateOperationHandler(op *spec.Operation) (string, error) {
return "", err
}

newInputCode, err := generateNewInput(op)
newInputCode, err := generateNewInput(op, method, wagPatchTypes)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -396,7 +399,7 @@ func (h handler) {{.Op}}Handler(ctx context.Context, w http.ResponseWriter, r *h
}
`

func generateNewInput(op *spec.Operation) (string, error) {
func generateNewInput(op *spec.Operation, method string, wagPatchTypes map[string]struct{}) (string, error) {
var buf bytes.Buffer
capOpID := swagger.Capitalize(op.ID)

Expand Down Expand Up @@ -476,6 +479,11 @@ func generateNewInput(op *spec.Operation) (string, error) {
if err != nil {
return "", err
}
noModelsTypeName, _ := swagger.TypeFromSchema(param.Schema, false)
if _, ok := wagPatchTypes[noModelsTypeName]; ok && strings.ToUpper(method) == "PATCH" {
// This is a hack that doesn't work on arrays
typeName = "models.Patch" + noModelsTypeName
}

buf.WriteString(fmt.Sprintf("\tdata, err := ioutil.ReadAll(r.Body)\n"))

Expand Down
Loading

0 comments on commit e754abe

Please sign in to comment.