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

Added logic for working with Tarantool schema via Box #426

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
### Added

- Extend box with replication information (#427).
- Implemented all box.schema.user operations requests and sugar interface (#426).
- Implemented box.session.su request and sugar interface only for current session granting (#426).

### Changed

Expand Down
6 changes: 6 additions & 0 deletions box/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ func New(conn tarantool.Doer) *Box {
}
}

// Schema returns a new Schema instance, providing access to schema-related operations.
// It uses the connection from the Box instance to communicate with Tarantool.
func (b *Box) Schema() *Schema {
return NewSchema(b.conn)
}

// Info retrieves the current information of the Tarantool instance.
// It calls the "box.info" function and parses the result into the Info structure.
func (b *Box) Info() (Info, error) {
Expand Down
189 changes: 186 additions & 3 deletions box/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
// Terminal 2:
// $ go test -v example_test.go

package box_test

import (
Expand All @@ -18,7 +19,7 @@ import (
"github.com/tarantool/go-tarantool/v2/box"
)

func Example() {
func ExampleBox_Info() {
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Expand Down Expand Up @@ -55,6 +56,188 @@ func Example() {
log.Fatalf("Box info uuids are not equal")
}

fmt.Printf("Box info uuids are equal")
fmt.Printf("Current box info: %+v\n", resp.Info)
fmt.Printf("Box info uuids are equal\n")
fmt.Printf("Current box ro: %+v", resp.Info.RO)
// Output:
// Box info uuids are equal
// Current box ro: false
}

func ExampleSchemaUser_Exists() {
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Password: "test",
}
ctx := context.Background()

client, err := tarantool.Connect(ctx, dialer, tarantool.Opts{})

if err != nil {
log.Fatalf("Failed to connect: %s", err)
}

// You can use UserExistsRequest type and call it directly.
fut := client.Do(box.NewUserExistsRequest("user"))

resp := &box.UserExistsResponse{}

err = fut.GetTyped(resp)
if err != nil {
log.Fatalf("Failed get box schema user exists with error: %s", err)
}

// Or use simple User implementation.
b := box.New(client)
exists, err := b.Schema().User().Exists(ctx, "user")
if err != nil {
log.Fatalf("Failed get box schema user exists with error: %s", err)
}

if exists != resp.Exists {
log.Fatalf("Box schema users exists are not equal")
}

fmt.Printf("Box schema users exists are equal\n")
fmt.Printf("Current exists state: %+v", exists)
// Output:
// Box schema users exists are equal
// Current exists state: false
}

func ExampleSchemaUser_Create() {
// Connect to Tarantool.
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Password: "test",
}
ctx := context.Background()

client, err := tarantool.Connect(ctx, dialer, tarantool.Opts{})
if err != nil {
log.Fatalf("Failed to connect: %s", err)
}

// Create SchemaUser.
schemaUser := box.NewSchemaUser(client)

// Create a new user.
username := "new_user"
options := box.UserCreateOptions{
IfNotExists: true,
Password: "secure_password",
}
err = schemaUser.Create(ctx, username, options)
if err != nil {
log.Fatalf("Failed to create user: %s", err)
}

fmt.Printf("User '%s' created successfully\n", username)
// Output:
// User 'new_user' created successfully
}

func ExampleSchemaUser_Drop() {
// Connect to Tarantool.
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Password: "test",
}
ctx := context.Background()

client, err := tarantool.Connect(ctx, dialer, tarantool.Opts{})
if err != nil {
log.Fatalf("Failed to connect: %s", err)
}

// Create SchemaUser.
schemaUser := box.NewSchemaUser(client)

// Drop an existing user.
username := "new_user"
options := box.UserDropOptions{
IfExists: true,
}
err = schemaUser.Drop(ctx, username, options)
if err != nil {
log.Fatalf("Failed to drop user: %s", err)
}

exists, err := schemaUser.Exists(ctx, username)
if err != nil {
log.Fatalf("Failed to get user exists: %s", err)
}

fmt.Printf("User '%s' dropped successfully\n", username)
fmt.Printf("User '%s' exists status: %v \n", username, exists)
// Output:
// User 'new_user' dropped successfully
// User 'new_user' exists status: false
}

func ExampleSchemaUser_Password() {
// Connect to Tarantool.
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Password: "test",
}
ctx := context.Background()

client, err := tarantool.Connect(ctx, dialer, tarantool.Opts{})
if err != nil {
log.Fatalf("Failed to connect: %s", err)
}

// Create SchemaUser.
schemaUser := box.NewSchemaUser(client)

// Get the password hash.
password := "my-password"
passwordHash, err := schemaUser.Password(ctx, password)
if err != nil {
log.Fatalf("Failed to get password hash: %s", err)
}

fmt.Printf("Password '%s' hash: %s", password, passwordHash)
// Output:
// Password 'my-password' hash: 3PHNAQGFWFo0KRfToxNgDXHj2i8=
}

func ExampleSchemaUser_Info() {
// Connect to Tarantool.
dialer := tarantool.NetDialer{
Address: "127.0.0.1:3013",
User: "test",
Password: "test",
}
ctx := context.Background()

client, err := tarantool.Connect(ctx, dialer, tarantool.Opts{})
if err != nil {
log.Fatalf("Failed to connect: %s", err)
}

// Create SchemaUser.
schemaUser := box.NewSchemaUser(client)

info, err := schemaUser.Info(ctx, "test")
if err != nil {
log.Fatalf("Failed to get password hash: %s", err)
}

hasSuper := false
for _, i := range info {
if i.Name == "super" && i.Type == box.PrivilegeRole {
hasSuper = true
}
}

if hasSuper {
fmt.Printf("User have super privileges")
}
// Output:
// User have super privileges
}
16 changes: 6 additions & 10 deletions box/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,14 @@ func (ir *InfoResponse) DecodeMsgpack(d *msgpack.Decoder) error {
// InfoRequest represents a request to retrieve information about the Tarantool instance.
// It implements the tarantool.Request interface.
type InfoRequest struct {
baseRequest
}

// Body method is used to serialize the request's body.
// It is part of the tarantool.Request interface implementation.
func (i InfoRequest) Body(res tarantool.SchemaResolver, enc *msgpack.Encoder) error {
return i.impl.Body(res, enc)
*tarantool.CallRequest // Underlying Tarantool call request.
}

// NewInfoRequest returns a new empty info request.
func NewInfoRequest() InfoRequest {
req := InfoRequest{}
req.impl = newCall("box.info")
return req
callReq := tarantool.NewCallRequest("box.info")

return InfoRequest{
callReq,
}
}
38 changes: 0 additions & 38 deletions box/request.go

This file was deleted.

21 changes: 21 additions & 0 deletions box/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package box

import "github.com/tarantool/go-tarantool/v2"

// Schema represents the schema-related operations in Tarantool.
// It holds a connection to interact with the Tarantool instance.
type Schema struct {
conn tarantool.Doer // Connection interface for interacting with Tarantool.
}

// NewSchema creates a new Schema instance with the provided Tarantool connection.
// It initializes a Schema object that can be used for schema-related operations
// such as managing users, tables, and other schema elements in the Tarantool instance.
func NewSchema(conn tarantool.Doer) *Schema {
return &Schema{conn: conn} // Pass the connection to the Schema.
}

// User returns a new SchemaUser instance, allowing schema-related user operations.
func (s *Schema) User() *SchemaUser {
return NewSchemaUser(s.conn)
}
45 changes: 45 additions & 0 deletions box/schema_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package box

import (
"context"
"testing"

"github.com/stretchr/testify/require"
"github.com/tarantool/go-tarantool/v2"
"github.com/tarantool/go-tarantool/v2/test_helpers"
)

func TestNewSchema(t *testing.T) {
ctx := context.Background()

// Create a schema instance with a nil connection. This should lead to a panic later.
b := NewSchema(nil)

// Ensure the schema is not nil (which it shouldn't be), but this is not meaningful
// since we will panic when we call the schema methods with the nil connection.
t.Run("internal sugar sub-objects not panics", func(t *testing.T) {
require.NotNil(t, b)
require.NotNil(t, b.User())
})

t.Run("check that connections are equal", func(t *testing.T) {
var tCases []tarantool.Doer
for i := 0; i < 10; i++ {

doer := test_helpers.NewMockDoer(t)
tCases = append(tCases, &doer)
}

for _, tCase := range tCases {
sch := NewSchema(tCase)
require.Equal(t, tCase, sch.conn)
require.Equal(t, tCase, sch.User().conn)
}
})

t.Run("nil conn panics", func(t *testing.T) {
Comment on lines +20 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, split it into 3 separate tests.

require.Panics(t, func() {
_, _ = b.User().Info(ctx, "panic-on")
})
})
}
Loading