Skip to content

Commit

Permalink
Fix gob encoding on operations
Browse files Browse the repository at this point in the history
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
  • Loading branch information
fredbi committed Dec 1, 2018
1 parent c3754ea commit 4967c03
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ linters:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
10 changes: 10 additions & 0 deletions info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,13 @@ func TestIntegrationInfo_Deserialize(t *testing.T) {
assert.EqualValues(t, info, actual)
}
}

func TestInfoGobEncoding(t *testing.T) {
var src, dst Info
if assert.NoError(t, json.Unmarshal([]byte(infoJSON), &src)) {
assert.EqualValues(t, src, info)
} else {
t.FailNow()
}
doTestAnyGobEncoding(t, &src, &dst)
}
131 changes: 131 additions & 0 deletions operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@
package spec

import (
"bytes"
"encoding/gob"
"encoding/json"

"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)

func init() {
//gob.Register(map[string][]interface{}{})
gob.Register(map[string]interface{}{})
gob.Register([]interface{}{})
}

// OperationProps describes an operation
type OperationProps struct {
Description string `json:"description,omitempty"`
Expand Down Expand Up @@ -257,3 +265,126 @@ func (o *Operation) RespondsWith(code int, response *Response) *Operation {
o.Responses.StatusCodeResponses[code] = *response
return o
}

type opsAlias OperationProps

type gobAlias struct {
Security []map[string]struct {
List []string
Pad bool
}
Alias *opsAlias
SecurityIsEmpty bool
}

// GobEncode provides a safe gob encoder for Operation, including empty security requirements
func (o Operation) GobEncode() ([]byte, error) {
raw := struct {
Ext VendorExtensible
Props OperationProps
}{
Ext: o.VendorExtensible,
Props: o.OperationProps,
}
var b bytes.Buffer
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}

// GobDecode provides a safe gob decoder for Operation, including empty security requirements
func (o *Operation) GobDecode(b []byte) error {
var raw struct {
Ext VendorExtensible
Props OperationProps
}

buf := bytes.NewBuffer(b)
err := gob.NewDecoder(buf).Decode(&raw)
if err != nil {
return err
}
o.VendorExtensible = raw.Ext
o.OperationProps = raw.Props
return nil
}

// GobEncode provides a safe gob encoder for Operation, including empty security requirements
func (op OperationProps) GobEncode() ([]byte, error) {
raw := gobAlias{
Alias: (*opsAlias)(&op),
}

var b bytes.Buffer
if op.Security == nil {
// nil security requirement
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}

if len(op.Security) == 0 {
// empty, but non-nil security requirement
raw.SecurityIsEmpty = true
raw.Alias.Security = nil
err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}

raw.Security = make([]map[string]struct {
List []string
Pad bool
}, 0, len(op.Security))
for _, req := range op.Security {
v := make(map[string]struct {
List []string
Pad bool
}, len(req))
for k, val := range req {
v[k] = struct {
List []string
Pad bool
}{
List: val,
}
}
raw.Security = append(raw.Security, v)
}

err := gob.NewEncoder(&b).Encode(raw)
return b.Bytes(), err
}

// GobDecode provides a safe gob decoder for Operation, including empty security requirements
func (op *OperationProps) GobDecode(b []byte) error {
var raw gobAlias

buf := bytes.NewBuffer(b)
err := gob.NewDecoder(buf).Decode(&raw)
if err != nil {
return err
}
if raw.Alias == nil {
return nil
}

switch {
case raw.SecurityIsEmpty:
// empty, but non-nil security requirement
raw.Alias.Security = []map[string][]string{}
case len(raw.Alias.Security) == 0:
// nil security requirement
raw.Alias.Security = nil
default:
raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
for _, req := range raw.Security {
v := make(map[string][]string, len(req))
for k, val := range req {
v[k] = make([]string, 0, len(val.List))
v[k] = append(v[k], val.List...)
}
raw.Alias.Security = append(raw.Alias.Security, v)
}
}

*op = *(*OperationProps)(raw.Alias)
return nil
}
97 changes: 97 additions & 0 deletions operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package spec

import (
"bytes"
"encoding/gob"
"encoding/json"
"testing"

Expand Down Expand Up @@ -103,5 +105,100 @@ func TestSecurityProperty(t *testing.T) {
assert.Equal(t, securityContainsEmptyArray, props)
}
}
}

func TestOperationGobEncoding(t *testing.T) {
// 1. empty scope in security requirements: "security": [ { "apiKey": [] } ],
doTestOperationGobEncoding(t, operationJSON)

// 2. nil security requirements
doTestOperationGobEncoding(t, `{
"description": "operation description",
"x-framework": "go-swagger",
"consumes": [ "application/json", "application/x-yaml" ],
"produces": [ "application/json", "application/x-yaml" ],
"schemes": ["http", "https"],
"tags": ["dogs"],
"summary": "the summary of the operation",
"operationId": "sendCat",
"deprecated": true,
"parameters": [{"$ref":"Cat"}],
"responses": {
"default": {
"description": "void response"
}
}
}`)

// 3. empty security requirement
doTestOperationGobEncoding(t, `{
"description": "operation description",
"x-framework": "go-swagger",
"consumes": [ "application/json", "application/x-yaml" ],
"produces": [ "application/json", "application/x-yaml" ],
"schemes": ["http", "https"],
"tags": ["dogs"],
"security": [],
"summary": "the summary of the operation",
"operationId": "sendCat",
"deprecated": true,
"parameters": [{"$ref":"Cat"}],
"responses": {
"default": {
"description": "void response"
}
}
}`)

// 4. non-empty security requirements
doTestOperationGobEncoding(t, `{
"description": "operation description",
"x-framework": "go-swagger",
"consumes": [ "application/json", "application/x-yaml" ],
"produces": [ "application/json", "application/x-yaml" ],
"schemes": ["http", "https"],
"tags": ["dogs"],
"summary": "the summary of the operation",
"security": [ { "scoped-auth": [ "phone", "email" ] , "api-key": []} ],
"operationId": "sendCat",
"deprecated": true,
"parameters": [{"$ref":"Cat"}],
"responses": {
"default": {
"description": "void response"
}
}
}`)

}

func doTestOperationGobEncoding(t *testing.T, fixture string) {
var src, dst Operation

if !assert.NoError(t, json.Unmarshal([]byte(fixture), &src)) {
t.FailNow()
}

doTestAnyGobEncoding(t, &src, &dst)
}

func doTestAnyGobEncoding(t *testing.T, src, dst interface{}) {
expectedJSON, _ := json.MarshalIndent(src, "", " ")

var b bytes.Buffer
err := gob.NewEncoder(&b).Encode(src)
if !assert.NoError(t, err) {
t.FailNow()
}

err = gob.NewDecoder(&b).Decode(dst)
if !assert.NoError(t, err) {
t.FailNow()
}

jazon, err := json.MarshalIndent(dst, "", " ")
if !assert.NoError(t, err) {
t.FailNow()
}
assert.JSONEq(t, string(expectedJSON), string(jazon))
}
8 changes: 8 additions & 0 deletions parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,11 @@ func TestParameterSerialization(t *testing.T) {
`{"type":"object","in":"body","schema":{"type":"array","items":{"$ref":"Cat"}}}`)

}

func TestParameterGobEncoding(t *testing.T) {
var src, dst Parameter
if !assert.NoError(t, json.Unmarshal([]byte(parameterJSON), &src)) {
t.FailNow()
}
doTestAnyGobEncoding(t, &src, &dst)
}

0 comments on commit 4967c03

Please sign in to comment.