Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crud: improvments #273

Merged
merged 5 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions crud/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,37 +59,30 @@ import (
"github.com/tarantool/go-tarantool"
)

// Tuple is a type to describe tuple for CRUD methods.
type Tuple = []interface{}

type baseRequest struct {
impl *tarantool.CallRequest
}

func (req *baseRequest) initImpl(methodName string) {
req.impl = tarantool.NewCall17Request(methodName)
func newCall(method string) *tarantool.CallRequest {
return tarantool.NewCall17Request(method)
}

// Code returns IPROTO code for CRUD request.
func (req *baseRequest) Code() int32 {
func (req baseRequest) Code() int32 {
return req.impl.Code()
}

// Ctx returns a context of CRUD request.
func (req *baseRequest) Ctx() context.Context {
func (req baseRequest) Ctx() context.Context {
return req.impl.Ctx()
}

// Async returns is CRUD request expects a response.
func (req *baseRequest) Async() bool {
func (req baseRequest) Async() bool {
return req.impl.Async()
}

type spaceRequest struct {
baseRequest
space string
}

func (req *spaceRequest) setSpace(space string) {
req.space = space
}
34 changes: 20 additions & 14 deletions crud/count.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,23 @@ type CountOpts struct {
func (opts CountOpts) EncodeMsgpack(enc *encoder) error {
const optsCnt = 9

options := [optsCnt]option{opts.Timeout, opts.VshardRouter,
opts.Mode, opts.PreferReplica, opts.Balance,
opts.YieldEvery, opts.BucketId, opts.ForceMapCall,
opts.Fullscan}
names := [optsCnt]string{timeoutOptName, vshardRouterOptName,
modeOptName, preferReplicaOptName, balanceOptName,
yieldEveryOptName, bucketIdOptName, forceMapCallOptName,
fullscanOptName}
values := [optsCnt]interface{}{}
exists := [optsCnt]bool{}
values[0], exists[0] = opts.Timeout.Get()
values[1], exists[1] = opts.VshardRouter.Get()
values[2], exists[2] = opts.Mode.Get()
values[3], exists[3] = opts.PreferReplica.Get()
values[4], exists[4] = opts.Balance.Get()
values[5], exists[5] = opts.YieldEvery.Get()
values[6], exists[6] = opts.BucketId.Get()
values[7], exists[7] = opts.ForceMapCall.Get()
values[8], exists[8] = opts.Fullscan.Get()

return encodeOptions(enc, options[:], names[:], values[:])
return encodeOptions(enc, names[:], values[:], exists[:])
}

// CountRequest helps you to create request object to call `crud.count`
Expand All @@ -70,39 +76,39 @@ type countArgs struct {
Opts CountOpts
}

// NewCountRequest returns a new empty CountRequest.
func NewCountRequest(space string) *CountRequest {
req := new(CountRequest)
req.initImpl("crud.count")
req.setSpace(space)
// MakeCountRequest returns a new empty CountRequest.
func MakeCountRequest(space string) CountRequest {
req := CountRequest{}
req.impl = newCall("crud.count")
req.space = space
req.conditions = nil
req.opts = CountOpts{}
return req
}

// Conditions sets the conditions for the CountRequest request.
// Note: default value is nil.
func (req *CountRequest) Conditions(conditions []Condition) *CountRequest {
func (req CountRequest) Conditions(conditions []Condition) CountRequest {
req.conditions = conditions
return req
}

// Opts sets the options for the CountRequest request.
// Note: default value is nil.
func (req *CountRequest) Opts(opts CountOpts) *CountRequest {
func (req CountRequest) Opts(opts CountOpts) CountRequest {
req.opts = opts
return req
}

// Body fills an encoder with the call request body.
func (req *CountRequest) Body(res tarantool.SchemaResolver, enc *encoder) error {
func (req CountRequest) Body(res tarantool.SchemaResolver, enc *encoder) error {
args := countArgs{Space: req.space, Conditions: req.conditions, Opts: req.opts}
req.impl = req.impl.Args(args)
return req.impl.Body(res, enc)
}

// Context sets a passed context to CRUD request.
func (req *CountRequest) Context(ctx context.Context) *CountRequest {
func (req CountRequest) Context(ctx context.Context) CountRequest {
req.impl = req.impl.Context(ctx)

return req
Expand Down
25 changes: 12 additions & 13 deletions crud/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import (
"github.com/tarantool/go-tarantool"
)

// DeleteResult describes result for `crud.delete` method.
type DeleteResult = Result

// DeleteOpts describes options for `crud.delete` method.
type DeleteOpts = SimpleOperationOpts

Expand All @@ -27,39 +24,41 @@ type deleteArgs struct {
Opts DeleteOpts
}

// NewDeleteRequest returns a new empty DeleteRequest.
func NewDeleteRequest(space string) *DeleteRequest {
req := new(DeleteRequest)
req.initImpl("crud.delete")
req.setSpace(space)
req.key = Tuple{}
// MakeDeleteRequest returns a new empty DeleteRequest.
func MakeDeleteRequest(space string) DeleteRequest {
req := DeleteRequest{}
req.impl = newCall("crud.delete")
req.space = space
req.opts = DeleteOpts{}
return req
}

// Key sets the key for the DeleteRequest request.
// Note: default value is nil.
func (req *DeleteRequest) Key(key Tuple) *DeleteRequest {
func (req DeleteRequest) Key(key Tuple) DeleteRequest {
req.key = key
return req
}

// Opts sets the options for the DeleteRequest request.
// Note: default value is nil.
func (req *DeleteRequest) Opts(opts DeleteOpts) *DeleteRequest {
func (req DeleteRequest) Opts(opts DeleteOpts) DeleteRequest {
req.opts = opts
return req
}

// Body fills an encoder with the call request body.
func (req *DeleteRequest) Body(res tarantool.SchemaResolver, enc *encoder) error {
func (req DeleteRequest) Body(res tarantool.SchemaResolver, enc *encoder) error {
if req.key == nil {
req.key = []interface{}{}
}
args := deleteArgs{Space: req.space, Key: req.key, Opts: req.opts}
req.impl = req.impl.Args(args)
return req.impl.Body(res, enc)
}

// Context sets a passed context to CRUD request.
func (req *DeleteRequest) Context(ctx context.Context) *DeleteRequest {
func (req DeleteRequest) Context(ctx context.Context) DeleteRequest {
req.impl = req.impl.Context(ctx)

return req
Expand Down
24 changes: 24 additions & 0 deletions crud/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ type ErrorMany struct {
Errors []Error
}

// DecodeMsgpack provides custom msgpack decoder.
func (e *ErrorMany) DecodeMsgpack(d *decoder) error {
l, err := d.DecodeArrayLen()
if err != nil {
return err
}

var errs []Error
for i := 0; i < l; i++ {
var crudErr *Error = nil
if err := d.Decode(&crudErr); err != nil {
return err
} else if crudErr != nil {
errs = append(errs, *crudErr)
}
}

if len(errs) > 0 {
*e = ErrorMany{Errors: errs}
}

return nil
}

// Error converts an Error to a string.
func (errs ErrorMany) Error() string {
var str []string
Expand Down
150 changes: 150 additions & 0 deletions crud/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package crud_test

import (
"fmt"
"reflect"
"time"

"github.com/tarantool/go-tarantool"
"github.com/tarantool/go-tarantool/crud"
)

const (
exampleServer = "127.0.0.1:3013"
exampleSpace = "test"
)

var exampleOpts = tarantool.Opts{
Timeout: 500 * time.Millisecond,
User: "test",
Pass: "test",
}

func exampleConnect() *tarantool.Connection {
conn, err := tarantool.Connect(exampleServer, exampleOpts)
if err != nil {
panic("Connection is not established: " + err.Error())
}
return conn
}

// ExampleResult_rowsInterface demonstrates how to use a helper type Result
// to decode a crud response. In this example, rows are decoded as an
// interface{} type.
func ExampleResult_rowsInterface() {
conn := exampleConnect()
req := crud.MakeReplaceRequest(exampleSpace).
Tuple([]interface{}{uint(2010), nil, "bla"})

ret := crud.Result{}
if err := conn.Do(req).GetTyped(&ret); err != nil {
fmt.Printf("Failed to execute request: %s", err)
return
}

fmt.Println(ret.Metadata)
fmt.Println(ret.Rows)
// Output:
// [{id unsigned false} {bucket_id unsigned true} {name string false}]
// [[2010 45 bla]]
}

// ExampleResult_rowsCustomType demonstrates how to use a helper type Result
// to decode a crud response. In this example, rows are decoded as a
// custom type.
func ExampleResult_rowsCustomType() {
conn := exampleConnect()
req := crud.MakeReplaceRequest(exampleSpace).
Tuple([]interface{}{uint(2010), nil, "bla"})

type Tuple struct {
_msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
Id uint64
BucketId uint64
Name string
}
ret := crud.MakeResult(reflect.TypeOf(Tuple{}))

if err := conn.Do(req).GetTyped(&ret); err != nil {
fmt.Printf("Failed to execute request: %s", err)
return
}

fmt.Println(ret.Metadata)
rows := ret.Rows.([]Tuple)
fmt.Println(rows)
// Output:
// [{id unsigned false} {bucket_id unsigned true} {name string false}]
// [{{} 2010 45 bla}]
}

// ExampleResult_many demonstrates that there is no difference in a
// response from *ManyRequest.
func ExampleResult_many() {
conn := exampleConnect()
req := crud.MakeReplaceManyRequest(exampleSpace).
Tuples([]crud.Tuple{
[]interface{}{uint(2010), nil, "bla"},
[]interface{}{uint(2011), nil, "bla"},
})

ret := crud.Result{}
if err := conn.Do(req).GetTyped(&ret); err != nil {
fmt.Printf("Failed to execute request: %s", err)
return
}

fmt.Println(ret.Metadata)
fmt.Println(ret.Rows)
// Output:
// [{id unsigned false} {bucket_id unsigned true} {name string false}]
// [[2010 45 bla] [2011 4 bla]]
}

// ExampleResult_error demonstrates how to use a helper type Result
// to handle a crud error.
func ExampleResult_error() {
conn := exampleConnect()
req := crud.MakeReplaceRequest("not_exist").
Tuple([]interface{}{uint(2010), nil, "bla"})

ret := crud.Result{}
if err := conn.Do(req).GetTyped(&ret); err != nil {
crudErr := err.(crud.Error)
fmt.Printf("Failed to execute request: %s", crudErr)
} else {
fmt.Println(ret.Metadata)
fmt.Println(ret.Rows)
}
// Output:
// Failed to execute request: ReplaceError: Space "not_exist" doesn't exist
}

// ExampleResult_errorMany demonstrates how to use a helper type Result
// to handle a crud error for a *ManyRequest.
func ExampleResult_errorMany() {
conn := exampleConnect()
initReq := crud.MakeReplaceRequest("not_exist").
Tuple([]interface{}{uint(2010), nil, "bla"})
if _, err := conn.Do(initReq).Get(); err != nil {
fmt.Printf("Failed to initialize the example: %s\n", err)
}

req := crud.MakeInsertManyRequest(exampleSpace).
Tuples([]crud.Tuple{
[]interface{}{uint(2010), nil, "bla"},
[]interface{}{uint(2010), nil, "bla"},
})
ret := crud.Result{}
if err := conn.Do(req).GetTyped(&ret); err != nil {
crudErr := err.(crud.ErrorMany)
// We need to trim the error message to make the example repeatable.
errmsg := crudErr.Error()[:10]
fmt.Printf("Failed to execute request: %s", errmsg)
} else {
fmt.Println(ret.Metadata)
fmt.Println(ret.Rows)
}
// Output:
// Failed to execute request: CallError:
}
Loading