diff --git a/core/relation/relation.go b/core/relation/relation.go index c95cc7658..4508f4cd0 100644 --- a/core/relation/relation.go +++ b/core/relation/relation.go @@ -15,6 +15,7 @@ const ( ) type Repository interface { + Transactor Get(ctx context.Context, id string) (RelationV2, error) Create(ctx context.Context, relation RelationV2) (RelationV2, error) List(ctx context.Context) ([]RelationV2, error) @@ -23,6 +24,10 @@ type Repository interface { GetByFields(ctx context.Context, rel RelationV2) (RelationV2, error) } +type Transactor interface { + WithoutTx(ctx context.Context) context.Context +} + type AuthzRepository interface { Add(ctx context.Context, rel Relation) error Check(ctx context.Context, rel Relation, act action.Action) (bool, error) diff --git a/core/relation/service.go b/core/relation/service.go index 02ea377df..a259e902c 100644 --- a/core/relation/service.go +++ b/core/relation/service.go @@ -62,8 +62,10 @@ func (s Service) Create(ctx context.Context, rel RelationV2) (RelationV2, error) return RelationV2{}, fmt.Errorf("%w: %s", ErrCreatingRelationInAuthzEngine, err.Error()) } + ctx = s.repository.WithoutTx(ctx) + go func() { - ctx := context.TODO() + ctx = context.WithoutCancel(ctx) relationLogData := createdRelation.ToLogData() actor := activity.Actor{ID: currentUser.ID, Email: currentUser.Email} if err := s.activityService.Log(ctx, auditKeyRelationCreate, actor, relationLogData); err != nil { diff --git a/core/resource/resource.go b/core/resource/resource.go index 9f8b265ea..d5d75d246 100644 --- a/core/resource/resource.go +++ b/core/resource/resource.go @@ -15,6 +15,7 @@ const ( ) type Repository interface { + Transactor GetByID(ctx context.Context, id string) (Resource, error) GetByURN(ctx context.Context, urn string) (Resource, error) Upsert(ctx context.Context, resource Resource) (Resource, error) @@ -24,6 +25,10 @@ type Repository interface { GetByNamespace(ctx context.Context, name string, ns string) (Resource, error) } +type Transactor interface { + WithoutTx(ctx context.Context) context.Context +} + type ConfigRepository interface { GetAll(ctx context.Context) ([]YAML, error) } diff --git a/core/resource/service.go b/core/resource/service.go index 4231f430a..addbfb26a 100644 --- a/core/resource/service.go +++ b/core/resource/service.go @@ -188,8 +188,10 @@ func (s Service) Create(ctx context.Context, res Resource) (Resource, error) { return Resource{}, err } + ctx = s.repository.WithoutTx(ctx) + go func() { - ctx := context.TODO() + ctx = context.WithoutCancel(ctx) resourceLogData := newResource.ToLogData() actor := activity.Actor{ID: currentUser.ID, Email: currentUser.Email} if err := s.activityService.Log(ctx, auditKeyResourceCreate, actor, resourceLogData); err != nil { diff --git a/core/servicedata/mocks/servicedata_repository.go b/core/servicedata/mocks/servicedata_repository.go index c3597043f..60c8071c8 100644 --- a/core/servicedata/mocks/servicedata_repository.go +++ b/core/servicedata/mocks/servicedata_repository.go @@ -393,6 +393,54 @@ func (_c *Repository_WithTransaction_Call) RunAndReturn(run func(context.Context return _c } +// WithoutTx provides a mock function with given fields: ctx +func (_m *Repository) WithoutTx(ctx context.Context) context.Context { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for WithoutTx") + } + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// Repository_WithoutTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WithoutTx' +type Repository_WithoutTx_Call struct { + *mock.Call +} + +// WithoutTx is a helper method to define mock.On call +// - ctx context.Context +func (_e *Repository_Expecter) WithoutTx(ctx interface{}) *Repository_WithoutTx_Call { + return &Repository_WithoutTx_Call{Call: _e.mock.On("WithoutTx", ctx)} +} + +func (_c *Repository_WithoutTx_Call) Run(run func(ctx context.Context)) *Repository_WithoutTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Repository_WithoutTx_Call) Return(_a0 context.Context) *Repository_WithoutTx_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Repository_WithoutTx_Call) RunAndReturn(run func(context.Context) context.Context) *Repository_WithoutTx_Call { + _c.Call.Return(run) + return _c +} + // NewRepository creates a new instance of Repository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewRepository(t interface { diff --git a/core/servicedata/service.go b/core/servicedata/service.go index 2b30e76e3..1da6a8cfc 100644 --- a/core/servicedata/service.go +++ b/core/servicedata/service.go @@ -146,8 +146,10 @@ func (s Service) CreateKey(ctx context.Context, key Key) (Key, error) { return Key{}, err } + ctx = s.repository.WithoutTx(ctx) + go func() { - ctx := context.TODO() + ctx = context.WithoutCancel(ctx) actor := activity.Actor{ID: currentUser.ID, Email: currentUser.Email} if err := s.activityService.Log(ctx, auditKeyServiceDataKeyCreate, actor, key.ToKeyLogData()); err != nil { s.logger.Error(fmt.Sprintf("%s: %s", ErrLogActivity.Error(), err.Error())) diff --git a/core/servicedata/service_test.go b/core/servicedata/service_test.go index af1cecbe8..e28e91b84 100644 --- a/core/servicedata/service_test.go +++ b/core/servicedata/service_test.go @@ -107,6 +107,7 @@ func TestService_CreateKey(t *testing.T) { activityService := &mocks.ActivityService{} repository.On("WithTransaction", mock.Anything).Return(context.TODO()) repository.On("Commit", mock.Anything).Return(nil) + repository.On("WithoutTx", mock.Anything).Return(context.TODO()) userService.EXPECT().FetchCurrentUser(mock.Anything). Return(user.User{ ID: testUserID, diff --git a/core/servicedata/servicedata.go b/core/servicedata/servicedata.go index 3ccd322e8..e1c483736 100644 --- a/core/servicedata/servicedata.go +++ b/core/servicedata/servicedata.go @@ -19,6 +19,7 @@ type Transactor interface { WithTransaction(ctx context.Context) context.Context Rollback(ctx context.Context, err error) error Commit(ctx context.Context) error + WithoutTx(ctx context.Context) context.Context } type Key struct { diff --git a/internal/store/postgres/relation_repository.go b/internal/store/postgres/relation_repository.go index 0d771cc45..5a83188d8 100644 --- a/internal/store/postgres/relation_repository.go +++ b/internal/store/postgres/relation_repository.go @@ -279,3 +279,7 @@ func (r RelationRepository) GetByFields(ctx context.Context, rel relation.Relati return fetchedRelation.transformToRelationV2(), nil } + +func (r RelationRepository) WithoutTx(ctx context.Context) context.Context { + return r.dbc.WithoutTx(ctx) +} diff --git a/internal/store/postgres/resource_repository.go b/internal/store/postgres/resource_repository.go index fe786f0d9..d8f7940bd 100644 --- a/internal/store/postgres/resource_repository.go +++ b/internal/store/postgres/resource_repository.go @@ -431,3 +431,7 @@ func (r ResourceRepository) GetByNamespace(ctx context.Context, name string, ns return fetchedResource.transformToResource(), nil } + +func (r ResourceRepository) WithoutTx(ctx context.Context) context.Context { + return r.dbc.WithoutTx(ctx) +} diff --git a/internal/store/postgres/servicedata_repository.go b/internal/store/postgres/servicedata_repository.go index 9502cf395..436352f8f 100644 --- a/internal/store/postgres/servicedata_repository.go +++ b/internal/store/postgres/servicedata_repository.go @@ -258,3 +258,7 @@ func (r ServiceDataRepository) Rollback(ctx context.Context, err error) error { func (r ServiceDataRepository) Commit(ctx context.Context) error { return r.dbc.Commit(ctx) } + +func (r ServiceDataRepository) WithoutTx(ctx context.Context) context.Context { + return r.dbc.WithoutTx(ctx) +} diff --git a/pkg/db/db.go b/pkg/db/db.go index c4e3a0823..4a44855a5 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -126,6 +126,10 @@ func (c Client) Rollback(ctx context.Context) error { return errors.New("no transaction") } +func (c Client) WithoutTx(ctx context.Context) context.Context { + return context.WithValue(ctx, transactionContextKey, nil) +} + func extractTransaction(ctx context.Context) *sqlx.Tx { if tx, ok := ctx.Value(transactionContextKey).(*sqlx.Tx); !ok { return nil