Skip to content

Commit

Permalink
feat(be): bolt helper functions like for sql
Browse files Browse the repository at this point in the history
  • Loading branch information
fiftin committed May 8, 2021
1 parent 55e8024 commit 0cc640b
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 27 deletions.
159 changes: 154 additions & 5 deletions db/bolt/BoltDb.go
@@ -1,24 +1,29 @@
package bolt

import (
"encoding/json"
"fmt"
"github.com/ansible-semaphore/semaphore/db"
"github.com/ansible-semaphore/semaphore/util"
"go.etcd.io/bbolt"
"reflect"
"sort"
"strconv"
)

type BoltDb struct {
db *bbolt.DB
}

func makeObjectId(tableName string, ids ...int) ([]byte, error) {
func makeBucketId(obj db.ObjectProperties, ids ...int) []byte {
n := len(ids)

id := tableName
id := obj.TableName
for i := 0; i < n; i++ {
id += fmt.Sprintf("_%010d", ids[i])
}

return []byte(id), nil
return []byte(id)
}

func (d *BoltDb) Migrate() error {
Expand All @@ -30,14 +35,158 @@ func (d *BoltDb) Connect() error {
if err != nil {
return err
}
db, err := bbolt.Open(config.Hostname, 0666, nil)
d.db, err = bbolt.Open(config.Hostname, 0666, nil)
if err != nil {
return err
}
d.db = db
return nil
}

func (d *BoltDb) Close() error {
return d.db.Close()
}

func (d *BoltDb) getObject(projectID int, props db.ObjectProperties, objectID int, object interface{}) (err error) {
err = d.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(props, projectID))
if b == nil {
return db.ErrNotFound
}

id := []byte(strconv.Itoa(objectID))
str := b.Get(id)
if str == nil {
return db.ErrNotFound
}

return json.Unmarshal(str, &object)
})

return
}

func getFieldNameByTag(t reflect.Type, tag string, value string) (string, error) {
n := t.NumField()
for i := 0; i < n; i++ {
if t.Field(i).Tag.Get(tag) == value {
return t.Field(i).Name, nil
}
}
return "", fmt.Errorf("")
}

func sortObjects(objects interface{}, sortBy string, sortInverted bool) error {
objectsValue := reflect.ValueOf(objects).Elem()
objType := objectsValue.Type().Elem()
fieldName, err := getFieldNameByTag(objType, "db", sortBy)
if err != nil {
return err
}

sort.SliceStable(objectsValue.Interface(), func (i, j int) bool {
fieldI := objectsValue.Index(i).FieldByName(fieldName)
fieldJ := objectsValue.Index(j).FieldByName(fieldName)
switch fieldJ.Kind() {
case reflect.Int:
case reflect.Int8:
case reflect.Int16:
case reflect.Int32:
case reflect.Int64:
case reflect.Uint:
case reflect.Uint8:
case reflect.Uint16:
case reflect.Uint32:
case reflect.Uint64:
return fieldI.Int() < fieldJ.Int()
case reflect.Float32:
case reflect.Float64:
return fieldI.Float() < fieldJ.Float()
case reflect.String:
return fieldI.String() < fieldJ.String()
}
return false
})

return nil
}

func (d *BoltDb) getObjects(projectID int, props db.ObjectProperties, params db.RetrieveQueryParams, objects interface{}) (err error) {
objectsValue := reflect.ValueOf(objects).Elem()
objType := objectsValue.Type().Elem()

// Read elements from database
err = d.db.View(func(tx *bbolt.Tx) error {

b := tx.Bucket(makeBucketId(props, projectID))
c := b.Cursor()
i := 0 // current item index
n := 0 // number of added items

for k, v := c.First(); k != nil; k, v = c.Next() {
if i < params.Offset {
continue
}

obj := reflect.New(objType).Elem()
err2 := json.Unmarshal(v, &obj)
if err2 == nil {
return err2
}

objectsValue.Set(reflect.Append(objectsValue, obj))

n++

if n > params.Count {
break
}
}

return nil
})

if err != nil {
return
}


// Sort elements
err = sortObjects(objects, params.SortBy, params.SortInverted)

return
}


func (d *BoltDb) isObjectInUse(projectID int, props db.ObjectProperties, objectID int) (inUse bool, err error) {
err = d.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(props, projectID))
inUse = b != nil && b.Get([]byte(strconv.Itoa(objectID))) != nil
return nil
})

return
}

func (d *BoltDb) deleteObject(projectID int, props db.ObjectProperties, objectID int) error {
inUse, err := d.isObjectInUse(projectID, props, objectID)

if err != nil {
return err
}

if inUse {
return db.ErrInvalidOperation
}

return d.db.Update(func (tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(db.InventoryObject, projectID))
if b == nil {
return db.ErrNotFound
}
return b.Delete([]byte(strconv.Itoa(objectID)))
})
}

func (d *BoltDb) deleteObjectSoft(projectID int, props db.ObjectProperties, objectID int) error {
return d.deleteObject(projectID, props, objectID)
}
48 changes: 48 additions & 0 deletions db/bolt/BoltDb_test.go
@@ -0,0 +1,48 @@
package bolt

import (
"fmt"
"github.com/ansible-semaphore/semaphore/db"
"testing"
)

func TestApiPing(t *testing.T) {
objects := []db.Inventory{
{
ID: 1,
Name: "x",
},
{
ID: 2,
Name: "a",
},
{
ID: 3,
Name: "d",
},
{
ID: 4,
Name: "b",
},
{
ID: 5,
Name: "r",
},
}

err := sortObjects(&objects, "name", false)
if err != nil {
t.Fatal(err)
}

expected := objects[0].Name == "a" &&
objects[1].Name == "b" &&
objects[2].Name == "d" &&
objects[3].Name == "r" &&
objects[4].Name == "x"


if !expected {
t.Fatal(fmt.Errorf("objects not sorted"))
}
}
60 changes: 60 additions & 0 deletions db/bolt/access_key.go
@@ -0,0 +1,60 @@
package bolt

import "github.com/ansible-semaphore/semaphore/db"

func (d *BoltDb) GetAccessKey(projectID int, accessKeyID int) (db.AccessKey, error) {
var key db.AccessKey
err := d.getObject(projectID, db.AccessKeyObject, accessKeyID, &key)
return key, err
}

func (d *BoltDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([]db.AccessKey, error) {
var keys []db.AccessKey
err := d.getObjects(projectID, db.AccessKeyObject, params, &keys)
return keys, err
}

func (d *BoltDb) UpdateAccessKey(key db.AccessKey) error {
return nil
}

func (d *BoltDb) CreateAccessKey(key db.AccessKey) (newKey db.AccessKey, err error) {
return
}

func (d *BoltDb) DeleteAccessKey(projectID int, accessKeyID int) error {
return d.deleteObject(projectID, db.AccessKeyObject, accessKeyID)
}

func (d *BoltDb) DeleteAccessKeySoft(projectID int, accessKeyID int) error {
return d.deleteObjectSoft(projectID, db.AccessKeyObject, accessKeyID)
}


func (d *BoltDb) GetGlobalAccessKey(accessKeyID int) (db.AccessKey, error) {
var key db.AccessKey
err := d.getObject(0, db.GlobalAccessKeyObject, accessKeyID, &key)
return key, err
}

func (d *BoltDb) GetGlobalAccessKeys(params db.RetrieveQueryParams) ([]db.AccessKey, error) {
var keys []db.AccessKey
err := d.getObjects(0, db.GlobalAccessKeyObject, params, &keys)
return keys, err
}

func (d *BoltDb) UpdateGlobalAccessKey(key db.AccessKey) error {
return nil
}

func (d *BoltDb) CreateGlobalAccessKey(key db.AccessKey) (newKey db.AccessKey, err error) {
return
}

func (d *BoltDb) DeleteGlobalAccessKey(accessKeyID int) error {
return d.deleteObject(0, db.GlobalAccessKeyObject, accessKeyID)
}

func (d *BoltDb) DeleteGlobalAccessKeySoft(accessKeyID int) error {
return d.deleteObjectSoft(0, db.GlobalAccessKeyObject, accessKeyID)
}
43 changes: 21 additions & 22 deletions db/bolt/inventory.go
Expand Up @@ -3,16 +3,14 @@ package bolt
import (
"encoding/json"
"github.com/ansible-semaphore/semaphore/db"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt"
"strconv"
)


func (d *BoltDb) GetInventory(projectID int, inventoryID int) (inventory db.Inventory, err error) {
id, err := makeObjectId("inventory", projectID)

err = d.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(id)
err = d.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(db.InventoryObject, projectID))
if b == nil {
return db.ErrNotFound
}
Expand All @@ -30,23 +28,23 @@ func (d *BoltDb) GetInventory(projectID int, inventoryID int) (inventory db.Inve
return
}

//if inventory.KeyID != nil {
// inventory.Key, err = d.GetAccessKey(projectID, *inventory.KeyID)
// if err != nil {
// return
// }
//}
//
//if inventory.SSHKeyID != nil {
// inventory.SSHKey, err = d.GetAccessKey(projectID, *inventory.SSHKeyID)
//}
if inventory.KeyID != nil {
inventory.Key, err = d.GetAccessKey(projectID, *inventory.KeyID)
if err != nil {
return
}
}

if inventory.SSHKeyID != nil {
inventory.SSHKey, err = d.GetAccessKey(projectID, *inventory.SSHKeyID)
}

return
}

func (d *BoltDb) GetInventories(projectID int, params db.RetrieveQueryParams) (inventories []db.Inventory, err error) {
err = d.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("inventory_" + strconv.Itoa(projectID)))
err = d.db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(db.InventoryObject, projectID))
if b == nil {
return db.ErrNotFound
}
Expand All @@ -58,21 +56,22 @@ func (d *BoltDb) GetInventories(projectID int, params db.RetrieveQueryParams) (i
}

func (d *BoltDb) DeleteInventory(projectID int, inventoryID int) error {
return d.db.Update(func (tx *bolt.Tx) error {
b := tx.Bucket([]byte("inventory_" + strconv.Itoa(projectID)))
return d.db.Update(func (tx *bbolt.Tx) error {
b := tx.Bucket(makeBucketId(db.InventoryObject, projectID))
if b == nil {
return db.ErrNotFound
}
return b.Delete([]byte(strconv.Itoa(inventoryID)))
})

}

func (d *BoltDb) DeleteInventorySoft(projectID int, inventoryID int) error {
return d.DeleteInventory(projectID, inventoryID)
}

func (d *BoltDb) UpdateInventory(inventory db.Inventory) error {
err := d.db.Update(func(tx *bolt.Tx) error {
err := d.db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte("inventory_" + strconv.Itoa(inventory.ProjectID)))
if b == nil {
return db.ErrNotFound
Expand All @@ -95,8 +94,8 @@ func (d *BoltDb) UpdateInventory(inventory db.Inventory) error {
}

func (d *BoltDb) CreateInventory(inventory db.Inventory) (newInventory db.Inventory, err error) {
err = d.db.Update(func(tx *bolt.Tx) error {
b, err2 := tx.CreateBucketIfNotExists([]byte("inventory_" + strconv.Itoa(inventory.ProjectID)))
err = d.db.Update(func(tx *bbolt.Tx) error {
b, err2 := tx.CreateBucketIfNotExists(makeBucketId(db.InventoryObject, inventory.ProjectID))
if err2 != nil {
return err2
}
Expand Down

0 comments on commit 0cc640b

Please sign in to comment.