Skip to content

Commit

Permalink
add bson protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentcau committed Jan 22, 2025
1 parent 3f818c3 commit 5462e87
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 0 deletions.
4 changes: 4 additions & 0 deletions binding/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
MIMEYAML = "application/x-yaml"
MIMEYAML2 = "application/yaml"
MIMETOML = "application/toml"
MIMEBSON = "application/bson"
)

// Binding describes the interface which needs to be implemented for binding the
Expand Down Expand Up @@ -86,6 +87,7 @@ var (
Header Binding = headerBinding{}
Plain BindingBody = plainBinding{}
TOML BindingBody = tomlBinding{}
BSON BindingBody = bsonBinding{}
)

// Default returns the appropriate Binding instance based on the HTTP method
Expand All @@ -110,6 +112,8 @@ func Default(method, contentType string) Binding {
return TOML
case MIMEMultipartPOSTForm:
return FormMultipart
case MIMEBSON:
return BSON
default: // case MIMEPOSTForm:
return Form
}
Expand Down
14 changes: 14 additions & 0 deletions binding/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/gin-gonic/gin/testdata/protoexample"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -170,6 +171,9 @@ func TestBindingDefault(t *testing.T) {

assert.Equal(t, TOML, Default(http.MethodPost, MIMETOML))
assert.Equal(t, TOML, Default(http.MethodPut, MIMETOML))

assert.Equal(t, BSON, Default(http.MethodPost, MIMEBSON))
assert.Equal(t, BSON, Default(http.MethodPut, MIMEBSON))
}

func TestBindingJSONNilBody(t *testing.T) {
Expand Down Expand Up @@ -729,6 +733,16 @@ func TestBindingProtoBufFail(t *testing.T) {
string(data), string(data[1:]))
}

func TestBindingBSON(t *testing.T) {
var obj FooStruct
obj.Foo = "bar"
data, _ := bson.Marshal(&obj)
testBodyBinding(t,
BSON, "bson",
"/", "/",
string(data), string(data[1:]))
}

func TestValidationFails(t *testing.T) {
var obj FooStruct
req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
Expand Down
33 changes: 33 additions & 0 deletions binding/bson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import (
"io"
"net/http"

"go.mongodb.org/mongo-driver/bson"
)

type bsonBinding struct{}

func (bsonBinding) Name() string {
return "bson"
}

func (b bsonBinding) Bind(req *http.Request, obj any) error {
buf, err := io.ReadAll(req.Body)
if err != nil {
return err
}
return b.BindBody(buf, obj)
}

func (bsonBinding) BindBody(body []byte, obj any) error {
if err := bson.Unmarshal(body, obj); err != nil {
return err
}
return nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.31.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
Expand Down
37 changes: 37 additions & 0 deletions render/bson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2018 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package render

import (
"net/http"

"go.mongodb.org/mongo-driver/bson"
"google.golang.org/protobuf/proto"
)

// BSONBuf contains the given interface object.
type BSONBuf struct {
Data any
}

var bsonContentType = []string{"application/bson"}

// Render (BSON) marshals the given interface object and writes data with custom ContentType.
func (r BSONBuf) Render(w http.ResponseWriter) error {
r.WriteContentType(w)

bytes, err := bson.Marshal(r.Data.(proto.Message))
if err != nil {
return err
}

_, err = w.Write(bytes)
return err
}

// WriteContentType (BSONBuf) writes BSONBuf ContentType.
func (r BSONBuf) WriteContentType(w http.ResponseWriter) {
writeContentType(w, bsonContentType)
}
22 changes: 22 additions & 0 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
testdata "github.com/gin-gonic/gin/testdata/protoexample"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -352,6 +353,27 @@ func TestRenderProtoBufFail(t *testing.T) {
require.Error(t, err)
}

func TestRenderBSON(t *testing.T) {
w := httptest.NewRecorder()
reps := []int64{int64(1), int64(2)}
label := "test"
data := &testdata.Test{
Label: &label,
Reps: reps,
}

(BSONBuf{data}).WriteContentType(w)
bsonData, err := bson.Marshal(data)
require.NoError(t, err)
assert.Equal(t, "application/bson", w.Header().Get("Content-Type"))

err = (BSONBuf{data}).Render(w)

require.NoError(t, err)
assert.Equal(t, string(bsonData), w.Body.String())
assert.Equal(t, "application/bson", w.Header().Get("Content-Type"))
}

func TestRenderXML(t *testing.T) {
w := httptest.NewRecorder()
data := xmlmap{
Expand Down

0 comments on commit 5462e87

Please sign in to comment.