Skip to content

Commit

Permalink
Juju 7122/add methods for role management (#1442)
Browse files Browse the repository at this point in the history
* add role to parse tag

* feat(db): update db with roles

Updates db with roles entries.

* test(roleentry): adds some basic tests

* feat(database): addRole db method

* feat(dbmodel): migration was missing comma

* fix(schema-version): missing schema version in migration caused infinite migration loop

* feat(database): adds database methods for roles according to the specification

* feat(database): use List and Count for roles

* chore(testname): fix test name for groups

* chore(pr comments): pr comments

* chore(modify test): tests codenotfound with no fields set

* chore(pr commebnt): pr comment

* fix(role.go): force ID or UUID to be set

* feat(roles): get roles only by names & refactor GetRole test to get by uuid or name

Update roles only by names now and GetRole is explicit to uuid or name.

* fix(roles.go): use errcode

---------

Co-authored-by: SimoneDutto <[email protected]>
  • Loading branch information
ale8k and SimoneDutto authored Nov 19, 2024
1 parent 6738458 commit 92b1465
Show file tree
Hide file tree
Showing 6 changed files with 368 additions and 8 deletions.
4 changes: 2 additions & 2 deletions internal/db/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ func (d *Database) ListGroups(ctx context.Context, limit, offset int, match stri
return groups, nil
}

// UpdateGroup updates the group identified by its ID.
func (d *Database) UpdateGroup(ctx context.Context, group *dbmodel.GroupEntry) (err error) {
// UpdateGroupName updates the group name identified by its ID or UUID.
func (d *Database) UpdateGroupName(ctx context.Context, group *dbmodel.GroupEntry) (err error) {
const op = errors.Op("db.UpdateGroup")

if group.ID == 0 {
Expand Down
10 changes: 5 additions & 5 deletions internal/db/group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ func (s *dbSuite) TestGetGroup(c *qt.C) {
c.Assert(group.UUID, qt.Equals, uuid2)
}

func (s *dbSuite) TestUpdateGroup(c *qt.C) {
err := s.Database.UpdateGroup(context.Background(), &dbmodel.GroupEntry{Name: "test-group"})
func (s *dbSuite) TestUpdateGroupName(c *qt.C) {
err := s.Database.UpdateGroupName(context.Background(), &dbmodel.GroupEntry{Name: "test-group"})
c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound)

err = s.Database.Migrate(context.Background(), false)
Expand All @@ -130,7 +130,7 @@ func (s *dbSuite) TestUpdateGroup(c *qt.C) {
Name: "test-group",
}

err = s.Database.UpdateGroup(context.Background(), ge)
err = s.Database.UpdateGroupName(context.Background(), ge)
c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound)

_, err = s.Database.AddGroup(context.Background(), "test-group")
Expand All @@ -143,7 +143,7 @@ func (s *dbSuite) TestUpdateGroup(c *qt.C) {
c.Assert(err, qt.IsNil)

ge1.Name = "renamed-group"
err = s.Database.UpdateGroup(context.Background(), ge1)
err = s.Database.UpdateGroupName(context.Background(), ge1)
c.Check(err, qt.IsNil)

ge2 := &dbmodel.GroupEntry{
Expand Down Expand Up @@ -184,7 +184,7 @@ func (s *dbSuite) TestRemoveGroup(c *qt.C) {
c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound)
}

func (s *dbSuite) TestForEachGroup(c *qt.C) {
func (s *dbSuite) TestListGroups(c *qt.C) {
err := s.Database.Migrate(context.Background(), false)
c.Assert(err, qt.IsNil)

Expand Down
156 changes: 156 additions & 0 deletions internal/db/role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 2024 Canonical.

package db

import (
"context"

"github.com/canonical/jimm/v3/internal/dbmodel"
"github.com/canonical/jimm/v3/internal/errors"
"github.com/canonical/jimm/v3/internal/servermon"
)

// AddRole adds a new role.
func (d *Database) AddRole(ctx context.Context, name string) (re *dbmodel.RoleEntry, err error) {
const op = errors.Op("db.AddRole")
if err := d.ready(); err != nil {
return nil, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

re = &dbmodel.RoleEntry{
Name: name,
UUID: newUUID(),
}

if err := d.DB.WithContext(ctx).Create(re).Error; err != nil {
return nil, errors.E(op, dbError(err))
}
return re, nil
}

// GetRole populates the provided *dbmodel.RoleEntry based on name or UUID.
func (d *Database) GetRole(ctx context.Context, role *dbmodel.RoleEntry) (err error) {
const op = errors.Op("db.GetRole")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

if role.UUID == "" && role.Name == "" {
return errors.E(op, "must specify uuid or name")
}

db := d.DB.WithContext(ctx)
if role.ID != 0 {
db = db.Where("id = ?", role.ID)
}
if role.UUID != "" {
db = db.Where("uuid = ?", role.UUID)
}
if role.Name != "" {
db = db.Where("name = ?", role.Name)
}
if err := db.First(&role).Error; err != nil {
return errors.E(op, dbError(err))
}
return nil
}

// UpdateRoleName updates the name of a role identified by UUID.
func (d *Database) UpdateRoleName(ctx context.Context, uuid, name string) (err error) {
const op = errors.Op("db.UpdateRole")

if uuid == "" {
return errors.E(op, "uuid must be specified")
}

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

model := d.DB.WithContext(ctx).Model(&dbmodel.RoleEntry{})
model.Where("uuid = ?", uuid)
if model.Update("name", name).RowsAffected == 0 {
return errors.E(op, errors.CodeNotFound, "role not found")
}

return nil
}

// RemoveRole removes the role identified by its ID or UUID.
func (d *Database) RemoveRole(ctx context.Context, role *dbmodel.RoleEntry) (err error) {
const op = errors.Op("db.RemoveRole")

if role.ID == 0 && role.UUID == "" {
return errors.E("neither role UUID or ID specified", errors.CodeNotFound)
}

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

if err := d.DB.WithContext(ctx).Delete(role).Error; err != nil {
return errors.E(op, dbError(err))
}
return nil
}

// ListRoles returns a paginated list of Roles defined by limit and offset.
// match is used to fuzzy find based on entries' name or uuid using the LIKE operator (ex. LIKE %<match>%).
func (d *Database) ListRoles(ctx context.Context, limit, offset int, match string) (_ []dbmodel.RoleEntry, err error) {
const op = errors.Op("db.ListRoles")
if err := d.ready(); err != nil {
return nil, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if match != "" {
db = db.Where("name LIKE ? OR uuid LIKE ?", "%"+match+"%", "%"+match+"%")
}
db = db.Order("name asc")
db = db.Limit(limit)
db = db.Offset(offset)
var Roles []dbmodel.RoleEntry
if err := db.Find(&Roles).Error; err != nil {
return nil, errors.E(op, dbError(err))
}
return Roles, nil
}

// CountRoles returns a count of the number of Roles that exist.
func (d *Database) CountRoles(ctx context.Context) (count int, err error) {
const op = errors.Op("db.CountRoles")
if err := d.ready(); err != nil {
return 0, errors.E(op, err)
}
durationObserver := servermon.DurationObserver(servermon.DBQueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.DBQueryErrorCount, &err, string(op))

var c int64
var g dbmodel.RoleEntry
if err := d.DB.WithContext(ctx).Model(g).Count(&c).Error; err != nil {
return 0, errors.E(op, dbError(err))
}
count = int(c)
return count, nil
}
Loading

0 comments on commit 92b1465

Please sign in to comment.