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

feat: unify pipeline trigger chart endpoint with credit chart #228

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
58 changes: 46 additions & 12 deletions pkg/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ type Repository interface {
UpdateOrganization(ctx context.Context, id string, user *datamodel.Owner) error
DeleteOrganization(ctx context.Context, id string) error

GetOwner(ctx context.Context, id string, includeAvatar bool) (*datamodel.Owner, error)
GetOwnerByUID(ctx context.Context, uid uuid.UUID) (*datamodel.Owner, error)

listOwners(ctx context.Context, ownerType string, pageSize int, pageToken string, filter filtering.Filter) ([]*datamodel.Owner, int64, string, error)
createOwner(ctx context.Context, ownerType string, user *datamodel.Owner) error
getOwner(ctx context.Context, ownerType string, id string, includeAvatar bool) (*datamodel.Owner, error)
getOwnerByUID(ctx context.Context, ownerType string, uid uuid.UUID) (*datamodel.Owner, error)
updateOwner(ctx context.Context, ownerType string, id string, user *datamodel.Owner) error
deleteOwner(ctx context.Context, ownerType string, id string) error
Comment on lines 67 to 70
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

💭 @donch1989 if owner ID is unique perhaps we don't need the type checks in these methods and we can delegate on the service layer to check the authenticated user permissions to perform these actions.


Expand Down Expand Up @@ -119,11 +120,32 @@ func (r *repository) ListUsers(ctx context.Context, pageSize int, pageToken stri
func (r *repository) CreateUser(ctx context.Context, user *datamodel.Owner) error {
return r.createOwner(ctx, "user", user)
}

// ownerWithType is used to wrap an owner fetch with a tyupe check.
func ownerWithType(o *datamodel.Owner, ownerType string) (*datamodel.Owner, error) {
if !o.OwnerType.Valid || o.OwnerType.String != ownerType {
return nil, gorm.ErrRecordNotFound
}

return o, nil
}

func (r *repository) GetUser(ctx context.Context, id string, includeAvatar bool) (*datamodel.Owner, error) {
return r.getOwner(ctx, "user", id, includeAvatar)
owner, err := r.GetOwner(ctx, id, includeAvatar)
if err != nil {
return nil, err
}

return ownerWithType(owner, "user")
}

func (r *repository) GetUserByUID(ctx context.Context, uid uuid.UUID) (*datamodel.Owner, error) {
return r.getOwnerByUID(ctx, "user", uid)
owner, err := r.GetOwnerByUID(ctx, uid)
if err != nil {
return nil, err
}

return ownerWithType(owner, "user")
}
func (r *repository) UpdateUser(ctx context.Context, id string, user *datamodel.Owner) error {
return r.updateOwner(ctx, "user", id, user)
Expand All @@ -138,12 +160,25 @@ func (r *repository) ListOrganizations(ctx context.Context, pageSize int, pageTo
func (r *repository) CreateOrganization(ctx context.Context, org *datamodel.Owner) error {
return r.createOwner(ctx, "organization", org)
}

func (r *repository) GetOrganization(ctx context.Context, id string, includeAvatar bool) (*datamodel.Owner, error) {
return r.getOwner(ctx, "organization", id, includeAvatar)
owner, err := r.GetOwner(ctx, id, includeAvatar)
if err != nil {
return nil, err
}

return ownerWithType(owner, "organization")
}

func (r *repository) GetOrganizationByUID(ctx context.Context, uid uuid.UUID) (*datamodel.Owner, error) {
return r.getOwnerByUID(ctx, "organization", uid)
owner, err := r.GetOwnerByUID(ctx, uid)
if err != nil {
return nil, err
}

return ownerWithType(owner, "organization")
}

func (r *repository) UpdateOrganization(ctx context.Context, id string, org *datamodel.Owner) error {
return r.updateOwner(ctx, "organization", id, org)
}
Expand Down Expand Up @@ -250,12 +285,11 @@ func (r *repository) createOwner(ctx context.Context, ownerType string, owner *d
return nil
}

func (r *repository) getOwner(ctx context.Context, ownerType string, id string, includeAvatar bool) (*datamodel.Owner, error) {

func (r *repository) GetOwner(ctx context.Context, id string, includeAvatar bool) (*datamodel.Owner, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@donch1989 I mentioned this change in your PR yesterday, what do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we can expose it.

db := r.CheckPinnedUser(ctx, r.db)

var owner datamodel.Owner
queryBuilder := db.Model(&datamodel.Owner{}).Where("owner_type = ?", ownerType).Where("id = ?", id)
queryBuilder := db.Model(&datamodel.Owner{}).Where("id = ?", id)
if !includeAvatar {
queryBuilder = queryBuilder.Omit("profile_avatar")
}
Expand All @@ -265,12 +299,12 @@ func (r *repository) getOwner(ctx context.Context, ownerType string, id string,
return &owner, nil
}

func (r *repository) getOwnerByUID(ctx context.Context, ownerType string, uid uuid.UUID) (*datamodel.Owner, error) {

func (r *repository) GetOwnerByUID(ctx context.Context, uid uuid.UUID) (*datamodel.Owner, error) {
db := r.CheckPinnedUser(ctx, r.db)

var owner datamodel.Owner
if result := db.Model(&datamodel.Owner{}).Omit("profile_avatar").Where("owner_type = ?", ownerType).Where("uid = ?", uid.String()).First(&owner); result.Error != nil {
result := db.Model(&datamodel.Owner{}).Omit("profile_avatar").Where("uid = ?", uid.String()).First(&owner)
if result.Error != nil {
return nil, result.Error
}
return &owner, nil
Expand Down
88 changes: 88 additions & 0 deletions pkg/repository/repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ package repository
import (
"context"
"database/sql"
"errors"
"os"
"testing"
"time"

qt "github.com/frankban/quicktest"
"github.com/go-redis/redismock/v9"
"github.com/gofrs/uuid"
"gorm.io/gorm"

"github.com/instill-ai/mgmt-backend/config"
Expand Down Expand Up @@ -62,3 +64,89 @@ func TestRepository_CreateUser(t *testing.T) {
c.Check(got.CreateTime.After(t0), qt.IsTrue)
c.Check(got.UpdateTime.After(t0), qt.IsTrue)
}

func TestRepository_GetOwner(t *testing.T) {
c := qt.New(t)
ctx := context.Background()

cache, _ := redismock.NewClientMock()
tx := db.Begin()
c.Cleanup(func() { tx.Rollback() })

repo := NewRepository(tx, cache)

user := &datamodel.Owner{
ID: "piano-wombat",
Email: "piano@wombats.com",
OwnerType: sql.NullString{
String: "user",
Valid: true,
},
}
org := &datamodel.Owner{
ID: "the-wombies",
Email: "org@wombats.com",
OwnerType: sql.NullString{
String: "organization",
Valid: true,
},
}

user.UID, org.UID = uuid.Must(uuid.NewV4()), uuid.Must(uuid.NewV4())

err := repo.CreateUser(ctx, user)
c.Check(err, qt.IsNil)

err = repo.CreateOrganization(ctx, org)
c.Check(err, qt.IsNil)

c.Run("nok - get with wrong type", func(c *qt.C) {
_, err := repo.GetUser(ctx, org.ID, false)
c.Check(errors.Is(err, gorm.ErrRecordNotFound), qt.IsTrue, qt.Commentf(err.Error()))

_, err = repo.GetUserByUID(ctx, org.UID)
c.Check(errors.Is(err, gorm.ErrRecordNotFound), qt.IsTrue, qt.Commentf(err.Error()))

_, err = repo.GetOrganization(ctx, user.ID, false)
c.Check(errors.Is(err, gorm.ErrRecordNotFound), qt.IsTrue, qt.Commentf(err.Error()))

_, err = repo.GetOrganizationByUID(ctx, user.UID)
c.Check(errors.Is(err, gorm.ErrRecordNotFound), qt.IsTrue, qt.Commentf(err.Error()))
})

c.Run("ok - get with type", func(c *qt.C) {
gotUser, err := repo.GetUser(ctx, user.ID, false)
c.Check(err, qt.IsNil)
c.Check(gotUser.UID, qt.Equals, user.UID)

gotUser, err = repo.GetUserByUID(ctx, user.UID)
c.Check(err, qt.IsNil)
c.Check(gotUser.ID, qt.Equals, user.ID)

gotOrg, err := repo.GetOrganization(ctx, org.ID, false)
c.Check(err, qt.IsNil)
c.Check(gotOrg.UID, qt.Equals, org.UID)

gotOrg, err = repo.GetOrganizationByUID(ctx, org.UID)
c.Check(err, qt.IsNil)
c.Check(gotOrg.ID, qt.Equals, org.ID)
})

c.Run("ok - get without type", func(c *qt.C) {
gotUser, err := repo.GetOwner(ctx, user.ID, false)
c.Check(err, qt.IsNil)
c.Check(gotUser.UID, qt.Equals, user.UID)

gotUser, err = repo.GetOwnerByUID(ctx, user.UID)
c.Check(err, qt.IsNil)
c.Check(gotUser.ID, qt.Equals, user.ID)

gotOrg, err := repo.GetOwner(ctx, org.ID, false)
c.Check(err, qt.IsNil)
c.Check(gotOrg.UID, qt.Equals, org.UID)

gotOrg, err = repo.GetOwnerByUID(ctx, org.UID)
c.Check(err, qt.IsNil)
c.Check(gotOrg.ID, qt.Equals, org.ID)
})
}
10 changes: 1 addition & 9 deletions pkg/service/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,7 @@ func (s *service) ListPipelineTriggerChartRecords(
// GrantedNamespaceUID returns the UID of a namespace, provided the
// authenticated user has access to it.
func (s *service) GrantedNamespaceUID(ctx context.Context, namespaceID string, authenticatedUserUID uuid.UUID) (uuid.UUID, error) {
owner, err := s.repository.GetUser(ctx, namespaceID, false)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
// TODO jvallesm: with the namespace refactor we should have a
// GetNamespace method that will allow us to fetch the owner UID
// without knowing the owner type. For convenience, we try user first
// and fall back to organization.
owner, err = s.repository.GetOrganization(ctx, namespaceID, false)
}

owner, err := s.repository.GetOwner(ctx, namespaceID, false)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = errdomain.ErrUnauthorized
Expand Down
Loading