diff --git a/pkg/services/org/model.go b/pkg/services/org/model.go index d2fae4766f9..6ffecfa6cc2 100644 --- a/pkg/services/org/model.go +++ b/pkg/services/org/model.go @@ -97,6 +97,24 @@ type GetOrgByNameQuery struct { Name string } +type UpdateOrgAddressCommand struct { + OrgID int64 `xorm:"org_id"` + Address +} + +type Address struct { + Address1 string `json:"address1"` + Address2 string `json:"address2"` + City string `json:"city"` + ZipCode string `json:"zipCode"` + State string `json:"state"` + Country string `json:"country"` +} + +type DeleteOrgCommand struct { + ID int64 `xorm:"id"` +} + func (r RoleType) IsValid() bool { return r == RoleViewer || r == RoleAdmin || r == RoleEditor } diff --git a/pkg/services/org/org.go b/pkg/services/org/org.go index e447ea127ed..f899b588d5f 100644 --- a/pkg/services/org/org.go +++ b/pkg/services/org/org.go @@ -14,6 +14,9 @@ type Service interface { GetByID(context.Context, *GetOrgByIdQuery) (*Org, error) GetByNameHandler(context.Context, *GetOrgByNameQuery) (*Org, error) GetByName(string) (*Org, error) - CreateWithMember(name string, userID int64) (*Org, error) - Create(ctx context.Context, cmd *CreateOrgCommand) (*Org, error) + CreateWithMember(string, int64) (*Org, error) + Create(context.Context, *CreateOrgCommand) (*Org, error) + UpdateAddress(context.Context, *UpdateOrgAddressCommand) error + Delete(context.Context, *DeleteOrgCommand) error + GetOrCreate(context.Context, string) (int64, error) } diff --git a/pkg/services/org/orgimpl/org.go b/pkg/services/org/orgimpl/org.go index b27a598b700..2b6baa06803 100644 --- a/pkg/services/org/orgimpl/org.go +++ b/pkg/services/org/orgimpl/org.go @@ -243,3 +243,45 @@ func (s *Service) Create(ctx context.Context, cmd *org.CreateOrgCommand) (*org.O Updated: q.Result.Updated, }, nil } + +// TODO: refactor service to call store CRUD method +func (s *Service) UpdateAddress(ctx context.Context, cmd *org.UpdateOrgAddressCommand) error { + return s.store.UpdateAddress(ctx, cmd) +} + +// TODO: refactor service to call store CRUD method +func (s *Service) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error { + return s.store.Delete(ctx, cmd) +} + +func (s *Service) GetOrCreate(ctx context.Context, orgName string) (int64, error) { + var orga *org.Org + var err error + if s.cfg.AutoAssignOrg { + orga, err = s.store.Get(ctx, int64(s.cfg.AutoAssignOrgId)) + if err != nil { + return 0, err + } + + if s.cfg.AutoAssignOrgId != 1 { + s.log.Error("Could not create user: organization ID does not exist", "orgID", + s.cfg.AutoAssignOrgId) + return 0, fmt.Errorf("could not create user: organization ID %d does not exist", + s.cfg.AutoAssignOrgId) + } + + orga.Name = MainOrgName + orga.ID = int64(s.cfg.AutoAssignOrgId) + } else { + orga.Name = orgName + } + + orga.Created = time.Now() + orga.Updated = time.Now() + + _, err = s.store.Insert(ctx, orga) + if err != nil { + return 0, err + } + return orga.ID, nil +} diff --git a/pkg/services/org/orgimpl/org_test.go b/pkg/services/org/orgimpl/org_test.go index 7d0c9132ac5..e7add921e96 100644 --- a/pkg/services/org/orgimpl/org_test.go +++ b/pkg/services/org/orgimpl/org_test.go @@ -83,3 +83,11 @@ func (f *FakeOrgStore) DeleteUserFromAll(ctx context.Context, userID int64) erro func (f *FakeOrgStore) Update(ctx context.Context, cmd *org.UpdateOrgCommand) error { return f.ExpectedError } + +func (f *FakeOrgStore) UpdateAddress(ctx context.Context, cmd *org.UpdateOrgAddressCommand) error { + return f.ExpectedError +} + +func (f *FakeOrgStore) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error { + return f.ExpectedError +} diff --git a/pkg/services/org/orgimpl/store.go b/pkg/services/org/orgimpl/store.go index c93077b7a85..ae6f8c0936d 100644 --- a/pkg/services/org/orgimpl/store.go +++ b/pkg/services/org/orgimpl/store.go @@ -20,6 +20,10 @@ type store interface { InsertOrgUser(context.Context, *org.OrgUser) (int64, error) DeleteUserFromAll(context.Context, int64) error Update(ctx context.Context, cmd *org.UpdateOrgCommand) error + + // TO BE REFACTORED - move logic to service methods and leave CRUD methods for store + UpdateAddress(context.Context, *org.UpdateOrgAddressCommand) error + Delete(context.Context, *org.DeleteOrgCommand) error } type sqlStore struct { @@ -143,3 +147,73 @@ func isOrgNameTaken(name string, existingId int64, sess *sqlstore.DBSession) (bo return false, nil } + +// TODO: refactor move logic to service method +func (ss *sqlStore) UpdateAddress(ctx context.Context, cmd *org.UpdateOrgAddressCommand) error { + return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + org := models.Org{ + Address1: cmd.Address1, + Address2: cmd.Address2, + City: cmd.City, + ZipCode: cmd.ZipCode, + State: cmd.State, + Country: cmd.Country, + + Updated: time.Now(), + } + + if _, err := sess.ID(cmd.OrgID).Update(&org); err != nil { + return err + } + + sess.PublishAfterCommit(&events.OrgUpdated{ + Timestamp: org.Updated, + Id: org.Id, + Name: org.Name, + }) + + return nil + }) +} + +// TODO: refactor move logic to service method +func (ss *sqlStore) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error { + return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + if res, err := sess.Query("SELECT 1 from org WHERE id=?", cmd.ID); err != nil { + return err + } else if len(res) != 1 { + return models.ErrOrgNotFound + } + + deletes := []string{ + "DELETE FROM star WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ? AND star.dashboard_id = dashboard.id)", + "DELETE FROM dashboard_tag WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ? AND dashboard_tag.dashboard_id = dashboard.id)", + "DELETE FROM dashboard WHERE org_id = ?", + "DELETE FROM api_key WHERE org_id = ?", + "DELETE FROM data_source WHERE org_id = ?", + "DELETE FROM org_user WHERE org_id = ?", + "DELETE FROM org WHERE id = ?", + "DELETE FROM temp_user WHERE org_id = ?", + "DELETE FROM ngalert_configuration WHERE org_id = ?", + "DELETE FROM alert_configuration WHERE org_id = ?", + "DELETE FROM alert_instance WHERE rule_org_id = ?", + "DELETE FROM alert_notification WHERE org_id = ?", + "DELETE FROM alert_notification_state WHERE org_id = ?", + "DELETE FROM alert_rule WHERE org_id = ?", + "DELETE FROM alert_rule_tag WHERE EXISTS (SELECT 1 FROM alert WHERE alert.org_id = ? AND alert.id = alert_rule_tag.alert_id)", + "DELETE FROM alert_rule_version WHERE rule_org_id = ?", + "DELETE FROM alert WHERE org_id = ?", + "DELETE FROM annotation WHERE org_id = ?", + "DELETE FROM kv_store WHERE org_id = ?", + } + + for _, sql := range deletes { + _, err := sess.Exec(sql, cmd.ID) + if err != nil { + return err + } + } + + return nil + }) +} diff --git a/pkg/services/org/orgimpl/store_test.go b/pkg/services/org/orgimpl/store_test.go index 04b2c1ffe6e..6842824f2b7 100644 --- a/pkg/services/org/orgimpl/store_test.go +++ b/pkg/services/org/orgimpl/store_test.go @@ -53,6 +53,47 @@ func TestIntegrationOrgDataAccess(t *testing.T) { err := orgStore.DeleteUserFromAll(context.Background(), 1) require.NoError(t, err) }) + + t.Run("Update org address", func(t *testing.T) { + // make sure ac2 has no org + ac2 := &org.Org{ID: 21, Name: "name", Version: 1, Created: time.Now(), Updated: time.Now()} + _, err := orgStore.Insert(context.Background(), ac2) + require.NoError(t, err) + err = orgStore.UpdateAddress(context.Background(), &org.UpdateOrgAddressCommand{ + OrgID: ac2.ID, + Address: org.Address{ + Address1: "address1", + Address2: "address2", + City: "city", + ZipCode: "zip", + State: "state", + Country: "country"}, + }) + require.NoError(t, err) + orga, err := orgStore.Get(context.Background(), ac2.ID) + require.NoError(t, err) + require.Equal(t, "address1", orga.Address1) + }) + + t.Run("Removing org", func(t *testing.T) { + // make sure ac2 has no org + ac2 := &org.Org{ID: 22, Name: "ac2", Version: 1, Created: time.Now(), Updated: time.Now()} + _, err := orgStore.Insert(context.Background(), ac2) + require.NoError(t, err) + err = orgStore.Delete(context.Background(), &org.DeleteOrgCommand{ID: ac2.ID}) + require.NoError(t, err) + + // TODO: this part of the test will be added when we move RemoveOrgUser to org store + // "Removing user from org should delete user completely if in no other org" + // // remove ac2 user from ac1 org + // remCmd := models.RemoveOrgUserCommand{OrgId: ac1.OrgID, UserId: ac2.ID, ShouldDeleteOrphanedUser: true} + // err = orgStore.RemoveOrgUser(context.Background(), &remCmd) + // require.NoError(t, err) + // require.True(t, remCmd.UserWasDeleted) + + // err = orgStore.GetSignedInUser(context.Background(), &models.GetSignedInUserQuery{UserId: ac2.ID}) + // require.Equal(t, err, user.ErrUserNotFound) + }) } func TestIntegrationOrgUserDataAccess(t *testing.T) { diff --git a/pkg/services/org/orgtest/fake.go b/pkg/services/org/orgtest/fake.go index a9f9d9c8231..08f61420e0f 100644 --- a/pkg/services/org/orgtest/fake.go +++ b/pkg/services/org/orgtest/fake.go @@ -64,3 +64,15 @@ func (f *FakeOrgService) CreateWithMember(name string, userID int64) (*org.Org, func (f *FakeOrgService) Create(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) { return f.ExpectedOrg, f.ExpectedError } + +func (f *FakeOrgService) UpdateAddress(ctx context.Context, cmd *org.UpdateOrgAddressCommand) error { + return f.ExpectedError +} + +func (f *FakeOrgService) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error { + return f.ExpectedError +} + +func (f *FakeOrgService) GetOrCreate(ctx context.Context, orgName string) (int64, error) { + return 0, f.ExpectedError +}