diff --git a/db/Store.go b/db/Store.go index 3c967b26..bcd58b0b 100644 --- a/db/Store.go +++ b/db/Store.go @@ -163,3 +163,8 @@ var RepositoryObject = ObjectProperties{ TemplateColumnName: "repository_id", } +var TemplateObject = ObjectProperties{ + TableName: "project__template", + SortableColumns: []string{"name"}, +} + diff --git a/db/bolt/BoltDb.go b/db/bolt/BoltDb.go index 669835bf..5b4d2f02 100644 --- a/db/bolt/BoltDb.go +++ b/db/bolt/BoltDb.go @@ -11,14 +11,21 @@ import ( "strconv" ) + +type enumerable interface { + First() (key []byte, value []byte) + Next() (key []byte, value []byte) +} + + type BoltDb struct { db *bbolt.DB } -func makeBucketId(obj db.ObjectProperties, ids ...int) []byte { +func makeBucketId(props db.ObjectProperties, ids ...int) []byte { n := len(ids) - id := obj.TableName + id := props.TableName for i := 0; i < n; i++ { id += fmt.Sprintf("_%010d", ids[i]) } @@ -78,15 +85,19 @@ func getFieldNameByTag(t reflect.Type, tag string, value string) (string, error) 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() { + valueI := objectsValue.Index(i).FieldByName(fieldName) + valueJ := objectsValue.Index(j).FieldByName(fieldName) + + less := false + + switch valueI.Kind() { case reflect.Int: case reflect.Int8: case reflect.Int16: @@ -97,65 +108,72 @@ func sortObjects(objects interface{}, sortBy string, sortInverted bool) error { case reflect.Uint16: case reflect.Uint32: case reflect.Uint64: - return fieldI.Int() < fieldJ.Int() + less = valueI.Int() < valueJ.Int() case reflect.Float32: case reflect.Float64: - return fieldI.Float() < fieldJ.Float() + less = valueI.Float() < valueJ.Float() case reflect.String: - return fieldI.String() < fieldJ.String() + less = valueI.String() < valueJ.String() } - return false + + if sortInverted { + less = !less + } + + return less }) return nil } -func (d *BoltDb) getObjects(projectID int, props db.ObjectProperties, params db.RetrieveQueryParams, objects interface{}) (err error) { +func unmarshalObjects(rawData enumerable, 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 { + i := 0 // current item index + n := 0 // number of added items - 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 - } + for k, v := rawData.First(); k != nil; k, v = rawData.Next() { + if i < params.Offset { + continue } - return nil - }) + obj := reflect.New(objType).Elem() + err = json.Unmarshal(v, &obj) + if err == nil { + return err + } + + objectsValue.Set(reflect.Append(objectsValue, obj)) + + n++ + + if n > params.Count { + break + } + } if err != nil { return } - - // Sort elements - err = sortObjects(objects, params.SortBy, params.SortInverted) + if params.SortBy != "" { + err = sortObjects(objects, params.SortBy, params.SortInverted) + } return } +func (d *BoltDb) getObjects(projectID int, props db.ObjectProperties, params db.RetrieveQueryParams, objects interface{}) error { + return d.db.View(func(tx *bbolt.Tx) error { + + b := tx.Bucket(makeBucketId(props, projectID)) + c := b.Cursor() + + return unmarshalObjects(c, params, objects) + }) +} + func (d *BoltDb) isObjectInUse(projectID int, props db.ObjectProperties, objectID int) (inUse bool, err error) { err = d.db.View(func(tx *bbolt.Tx) error { @@ -190,3 +208,53 @@ func (d *BoltDb) deleteObject(projectID int, props db.ObjectProperties, objectID func (d *BoltDb) deleteObjectSoft(projectID int, props db.ObjectProperties, objectID int) error { return d.deleteObject(projectID, props, objectID) } + +func (d *BoltDb) updateObject(projectID int, props db.ObjectProperties, object interface{}) error { + return d.db.Update(func(tx *bbolt.Tx) error { + b := tx.Bucket(makeBucketId(props, projectID)) + if b == nil { + return db.ErrNotFound + } + + idValue := reflect.ValueOf(object).FieldByName("ID") + + id := []byte(strconv.Itoa(int(idValue.Int()))) + if b.Get(id) == nil { + return db.ErrNotFound + } + + str, err := json.Marshal(object) + if err != nil { + return err + } + + return b.Put(id, str) + }) +} + +func (d *BoltDb) createObject(projectID int, props db.ObjectProperties, object interface{}) (interface{}, error) { + err := d.db.Update(func(tx *bbolt.Tx) error { + b, err2 := tx.CreateBucketIfNotExists(makeBucketId(props, projectID)) + if err2 != nil { + return err2 + } + + id, err2 := b.NextSequence() + if err2 != nil { + return err2 + } + + idValue := reflect.ValueOf(object).FieldByName("ID") + + idValue.SetInt(int64(id)) + + str, err2 := json.Marshal(object) + if err2 != nil { + return err2 + } + + return b.Put([]byte(strconv.Itoa(int(id))), str) + }) + + return object, err +} diff --git a/db/bolt/access_key.go b/db/bolt/access_key.go index 06900bae..39cf556d 100644 --- a/db/bolt/access_key.go +++ b/db/bolt/access_key.go @@ -1,6 +1,8 @@ package bolt -import "github.com/ansible-semaphore/semaphore/db" +import ( + "github.com/ansible-semaphore/semaphore/db" +) func (d *BoltDb) GetAccessKey(projectID int, accessKeyID int) (db.AccessKey, error) { var key db.AccessKey @@ -15,11 +17,12 @@ func (d *BoltDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([] } func (d *BoltDb) UpdateAccessKey(key db.AccessKey) error { - return nil + return d.updateObject(*key.ProjectID, db.AccessKeyObject, key) } -func (d *BoltDb) CreateAccessKey(key db.AccessKey) (newKey db.AccessKey, err error) { - return +func (d *BoltDb) CreateAccessKey(key db.AccessKey) (db.AccessKey, error) { + newKey, err := d.createObject(*key.ProjectID, db.GlobalAccessKeyObject, key) + return newKey.(db.AccessKey), err } func (d *BoltDb) DeleteAccessKey(projectID int, accessKeyID int) error { @@ -31,24 +34,23 @@ func (d *BoltDb) DeleteAccessKeySoft(projectID int, accessKeyID int) error { } -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) GetGlobalAccessKey(accessKeyID int) (key db.AccessKey, err error) { + err = d.getObject(0, db.GlobalAccessKeyObject, accessKeyID, &key) + return } -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) GetGlobalAccessKeys(params db.RetrieveQueryParams) (keys []db.AccessKey, err error) { + err = d.getObjects(0, db.GlobalAccessKeyObject, params, &keys) + return } func (d *BoltDb) UpdateGlobalAccessKey(key db.AccessKey) error { - return nil + return d.updateObject(0, db.AccessKeyObject, key) } -func (d *BoltDb) CreateGlobalAccessKey(key db.AccessKey) (newKey db.AccessKey, err error) { - return +func (d *BoltDb) CreateGlobalAccessKey(key db.AccessKey) (db.AccessKey, error) { + newKey, err := d.createObject(0, db.GlobalAccessKeyObject, key) + return newKey.(db.AccessKey), err } func (d *BoltDb) DeleteGlobalAccessKey(accessKeyID int) error { diff --git a/db/bolt/environment.go b/db/bolt/environment.go new file mode 100644 index 00000000..ad44a5cf --- /dev/null +++ b/db/bolt/environment.go @@ -0,0 +1,30 @@ +package bolt + +import "github.com/ansible-semaphore/semaphore/db" + +func (d *BoltDb) GetEnvironment(projectID int, environmentID int) (environment db.Environment, err error) { + err = d.getObject(projectID, db.EnvironmentObject, environmentID, &environment) + return +} + +func (d *BoltDb) GetEnvironments(projectID int, params db.RetrieveQueryParams) (environment []db.Environment, err error) { + err = d.getObjects(projectID, db.EnvironmentObject, params, &environment) + return +} + +func (d *BoltDb) UpdateEnvironment(env db.Environment) error { + return d.updateObject(env.ProjectID, db.EnvironmentObject, &env) +} + +func (d *BoltDb) CreateEnvironment(env db.Environment) (db.Environment, error) { + newEnv, err := d.createObject(env.ProjectID, db.EnvironmentObject, env) + return newEnv.(db.Environment), err +} + +func (d *BoltDb) DeleteEnvironment(projectID int, environmentID int) error { + return d.deleteObject(projectID, db.EnvironmentObject, environmentID) +} + +func (d *BoltDb) DeleteEnvironmentSoft(projectID int, environmentID int) error { + return d.deleteObjectSoft(projectID, db.EnvironmentObject, environmentID) +} diff --git a/db/bolt/inventory.go b/db/bolt/inventory.go index 74278022..1e42be4d 100644 --- a/db/bolt/inventory.go +++ b/db/bolt/inventory.go @@ -1,28 +1,12 @@ package bolt import ( - "encoding/json" "github.com/ansible-semaphore/semaphore/db" - "go.etcd.io/bbolt" - "strconv" ) func (d *BoltDb) GetInventory(projectID int, inventoryID int) (inventory db.Inventory, err error) { - err = d.db.View(func(tx *bbolt.Tx) error { - b := tx.Bucket(makeBucketId(db.InventoryObject, projectID)) - if b == nil { - return db.ErrNotFound - } - - id := []byte(strconv.Itoa(inventoryID)) - str := b.Get(id) - if str == nil { - return db.ErrNotFound - } - - return json.Unmarshal(str, &inventory) - }) + err = d.getObject(projectID, db.InventoryObject, inventoryID, &inventory) if err != nil { return @@ -43,76 +27,26 @@ func (d *BoltDb) GetInventory(projectID int, inventoryID int) (inventory db.Inve } func (d *BoltDb) GetInventories(projectID int, params db.RetrieveQueryParams) (inventories []db.Inventory, err error) { - err = d.db.Update(func(tx *bbolt.Tx) error { - b := tx.Bucket(makeBucketId(db.InventoryObject, projectID)) - if b == nil { - return db.ErrNotFound - } - - return nil - }) - - return inventories, err -} - -func (d *BoltDb) DeleteInventory(projectID int, inventoryID int) error { - 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 *bbolt.Tx) error { - b := tx.Bucket([]byte("inventory_" + strconv.Itoa(inventory.ProjectID))) - if b == nil { - return db.ErrNotFound - } - - id := []byte(strconv.Itoa(inventory.ID)) - if b.Get(id) == nil { - return db.ErrNotFound - } - - str, err2 := json.Marshal(inventory) - if err2 != nil { - return err2 - } - - return b.Put(id, str) - }) - - return err -} - -func (d *BoltDb) CreateInventory(inventory db.Inventory) (newInventory db.Inventory, err error) { - err = d.db.Update(func(tx *bbolt.Tx) error { - b, err2 := tx.CreateBucketIfNotExists(makeBucketId(db.InventoryObject, inventory.ProjectID)) - if err2 != nil { - return err2 - } - - id, _ := b.NextSequence() - newInventory = inventory - newInventory.ID = int(id) - str, err2 := json.Marshal(newInventory) - if err2 != nil { - return err2 - } - - return b.Put([]byte(strconv.Itoa(newInventory.ID)), str) - }) - + err = d.getObjects(projectID, db.AccessKeyObject, params, &inventories) return } +func (d *BoltDb) DeleteInventory(projectID int, inventoryID int) error { + return d.deleteObject(projectID, db.InventoryObject, inventoryID) +} + +func (d *BoltDb) DeleteInventorySoft(projectID int, inventoryID int) error { + return d.deleteObjectSoft(projectID, db.InventoryObject, inventoryID) +} + +func (d *BoltDb) UpdateInventory(inventory db.Inventory) error { + return d.updateObject(inventory.ProjectID, db.InventoryObject, inventory) +} + +func (d *BoltDb) CreateInventory(inventory db.Inventory) (db.Inventory, error) { + newInventory, err := d.createObject(inventory.ProjectID, db.InventoryObject, inventory) + return newInventory.(db.Inventory), err +} + diff --git a/db/bolt/repository.go b/db/bolt/repository.go new file mode 100644 index 00000000..b5d5af10 --- /dev/null +++ b/db/bolt/repository.go @@ -0,0 +1,33 @@ +package bolt + +import ( + "github.com/ansible-semaphore/semaphore/db" +) + +func (d *BoltDb) GetRepository(projectID int, repositoryID int) (repository db.Repository, err error) { + err = d.getObject(projectID, db.RepositoryObject, repositoryID, &repository) + return +} + +func (d *BoltDb) GetRepositories(projectID int, params db.RetrieveQueryParams) (repositories []db.Repository, err error) { + err = d.getObjects(projectID, db.RepositoryObject, params, &repositories) + return +} + +func (d *BoltDb) UpdateRepository(repository db.Repository) error { + return d.updateObject(repository.ProjectID, db.RepositoryObject, repository) +} + +func (d *BoltDb) CreateRepository(repository db.Repository) (db.Repository, error) { + newRepo, err := d.createObject(repository.ProjectID, db.RepositoryObject, repository) + return newRepo.(db.Repository), err +} + +func (d *BoltDb) DeleteRepository(projectID int, repositoryId int) error { + return d.deleteObject(projectID, db.RepositoryObject, repositoryId) +} + +func (d *BoltDb) DeleteRepositorySoft(projectID int, repositoryId int) error { + return d.deleteObjectSoft(projectID, db.RepositoryObject, repositoryId) +} + diff --git a/db/bolt/template.go b/db/bolt/template.go new file mode 100644 index 00000000..fcf221bb --- /dev/null +++ b/db/bolt/template.go @@ -0,0 +1,28 @@ +package bolt + +import ( + "github.com/ansible-semaphore/semaphore/db" +) + +func (d *BoltDb) CreateTemplate(template db.Template) (db.Template, error) { + newTemplate, err := d.createObject(template.ProjectID, db.TemplateObject, template) + return newTemplate.(db.Template), err +} + +func (d *BoltDb) UpdateTemplate(template db.Template) error { + return d.updateObject(template.ProjectID, db.TemplateObject, template) +} + +func (d *BoltDb) GetTemplates(projectID int, params db.RetrieveQueryParams) (templates []db.Template, err error) { + err = d.getObjects(projectID, db.TemplateObject, params, &templates) + return +} + +func (d *BoltDb) GetTemplate(projectID int, templateID int) (template db.Template, err error) { + err = d.getObject(projectID, db.TemplateObject, templateID, &template) + return +} + +func (d *BoltDb) DeleteTemplate(projectID int, templateID int) error { + return d.deleteObject(projectID, db.TemplateObject, templateID) +}