Skip to content

Commit 546a549

Browse files
jaaydenhEmyrk
andauthored
feat: enable soft delete for organizations (#16584)
- Add deleted column to organizations table - Add trigger to check for existing workspaces, templates, groups and members in a org before allowing the soft delete --------- Co-authored-by: Steven Masley <stevenmasley@gmail.com> Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
1 parent dfa33b1 commit 546a549

28 files changed

+605
-215
lines changed

coderd/database/db.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
// Query functions are generated using sqlc.
44
//
55
// To modify the database schema:
6-
// 1. Add a new migration using "create_migration.sh" in database/migrations/
7-
// 2. Run "make coderd/database/generate" in the root to generate models.
8-
// 3. Add/Edit queries in "query.sql" and run "make coderd/database/generate" to create Go code.
6+
// 1. Add a new migration using "create_migration.sh" in database/migrations/ and run "make gen" to generate models.
7+
// 2. Add/Edit queries in "query.sql" and run "make gen" to create Go code.
98
package database
109

1110
import (

coderd/database/dbauthz/dbauthz.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,10 +1302,6 @@ func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
13021302
return q.db.DeleteOldWorkspaceAgentStats(ctx)
13031303
}
13041304

1305-
func (q *querier) DeleteOrganization(ctx context.Context, id uuid.UUID) error {
1306-
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, q.db.DeleteOrganization)(ctx, id)
1307-
}
1308-
13091305
func (q *querier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
13101306
return deleteQ[database.OrganizationMember](q.log, q.auth, func(ctx context.Context, arg database.DeleteOrganizationMemberParams) (database.OrganizationMember, error) {
13111307
member, err := database.ExpectOne(q.OrganizationMembers(ctx, database.OrganizationMembersParams(arg)))
@@ -1926,7 +1922,7 @@ func (q *querier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (databa
19261922
return fetch(q.log, q.auth, q.db.GetOrganizationByID)(ctx, id)
19271923
}
19281924

1929-
func (q *querier) GetOrganizationByName(ctx context.Context, name string) (database.Organization, error) {
1925+
func (q *querier) GetOrganizationByName(ctx context.Context, name database.GetOrganizationByNameParams) (database.Organization, error) {
19301926
return fetch(q.log, q.auth, q.db.GetOrganizationByName)(ctx, name)
19311927
}
19321928

@@ -1943,7 +1939,7 @@ func (q *querier) GetOrganizations(ctx context.Context, args database.GetOrganiz
19431939
return fetchWithPostFilter(q.auth, policy.ActionRead, fetch)(ctx, nil)
19441940
}
19451941

1946-
func (q *querier) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]database.Organization, error) {
1942+
func (q *querier) GetOrganizationsByUserID(ctx context.Context, userID database.GetOrganizationsByUserIDParams) ([]database.Organization, error) {
19471943
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetOrganizationsByUserID)(ctx, userID)
19481944
}
19491945

@@ -3737,6 +3733,16 @@ func (q *querier) UpdateOrganization(ctx context.Context, arg database.UpdateOrg
37373733
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateOrganization)(ctx, arg)
37383734
}
37393735

3736+
func (q *querier) UpdateOrganizationDeletedByID(ctx context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
3737+
deleteF := func(ctx context.Context, id uuid.UUID) error {
3738+
return q.db.UpdateOrganizationDeletedByID(ctx, database.UpdateOrganizationDeletedByIDParams{
3739+
ID: id,
3740+
UpdatedAt: dbtime.Now(),
3741+
})
3742+
}
3743+
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, deleteF)(ctx, arg.ID)
3744+
}
3745+
37403746
func (q *querier) UpdateProvisionerDaemonLastSeenAt(ctx context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
37413747
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceProvisionerDaemon); err != nil {
37423748
return err

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ func (s *MethodTestSuite) TestOrganization() {
815815
}))
816816
s.Run("GetOrganizationByName", s.Subtest(func(db database.Store, check *expects) {
817817
o := dbgen.Organization(s.T(), db, database.Organization{})
818-
check.Args(o.Name).Asserts(o, policy.ActionRead).Returns(o)
818+
check.Args(database.GetOrganizationByNameParams{Name: o.Name, Deleted: o.Deleted}).Asserts(o, policy.ActionRead).Returns(o)
819819
}))
820820
s.Run("GetOrganizationIDsByMemberIDs", s.Subtest(func(db database.Store, check *expects) {
821821
oa := dbgen.Organization(s.T(), db, database.Organization{})
@@ -839,7 +839,7 @@ func (s *MethodTestSuite) TestOrganization() {
839839
_ = dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{UserID: u.ID, OrganizationID: a.ID})
840840
b := dbgen.Organization(s.T(), db, database.Organization{})
841841
_ = dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{UserID: u.ID, OrganizationID: b.ID})
842-
check.Args(u.ID).Asserts(a, policy.ActionRead, b, policy.ActionRead).Returns(slice.New(a, b))
842+
check.Args(database.GetOrganizationsByUserIDParams{UserID: u.ID, Deleted: false}).Asserts(a, policy.ActionRead, b, policy.ActionRead).Returns(slice.New(a, b))
843843
}))
844844
s.Run("InsertOrganization", s.Subtest(func(db database.Store, check *expects) {
845845
check.Args(database.InsertOrganizationParams{
@@ -960,13 +960,14 @@ func (s *MethodTestSuite) TestOrganization() {
960960
Name: "something-different",
961961
}).Asserts(o, policy.ActionUpdate)
962962
}))
963-
s.Run("DeleteOrganization", s.Subtest(func(db database.Store, check *expects) {
963+
s.Run("UpdateOrganizationDeletedByID", s.Subtest(func(db database.Store, check *expects) {
964964
o := dbgen.Organization(s.T(), db, database.Organization{
965965
Name: "doomed",
966966
})
967-
check.Args(
968-
o.ID,
969-
).Asserts(o, policy.ActionDelete)
967+
check.Args(database.UpdateOrganizationDeletedByIDParams{
968+
ID: o.ID,
969+
UpdatedAt: o.UpdatedAt,
970+
}).Asserts(o, policy.ActionDelete).Returns()
970971
}))
971972
s.Run("OrganizationMembers", s.Subtest(func(db database.Store, check *expects) {
972973
o := dbgen.Organization(s.T(), db, database.Organization{})

coderd/database/dbmem/dbmem.go

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,19 +2157,6 @@ func (q *FakeQuerier) DeleteOldWorkspaceAgentStats(_ context.Context) error {
21572157
return nil
21582158
}
21592159

2160-
func (q *FakeQuerier) DeleteOrganization(_ context.Context, id uuid.UUID) error {
2161-
q.mutex.Lock()
2162-
defer q.mutex.Unlock()
2163-
2164-
for i, org := range q.organizations {
2165-
if org.ID == id && !org.IsDefault {
2166-
q.organizations = append(q.organizations[:i], q.organizations[i+1:]...)
2167-
return nil
2168-
}
2169-
}
2170-
return sql.ErrNoRows
2171-
}
2172-
21732160
func (q *FakeQuerier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
21742161
err := validateDatabaseType(arg)
21752162
if err != nil {
@@ -3688,12 +3675,12 @@ func (q *FakeQuerier) GetOrganizationByID(_ context.Context, id uuid.UUID) (data
36883675
return q.getOrganizationByIDNoLock(id)
36893676
}
36903677

3691-
func (q *FakeQuerier) GetOrganizationByName(_ context.Context, name string) (database.Organization, error) {
3678+
func (q *FakeQuerier) GetOrganizationByName(_ context.Context, params database.GetOrganizationByNameParams) (database.Organization, error) {
36923679
q.mutex.RLock()
36933680
defer q.mutex.RUnlock()
36943681

36953682
for _, organization := range q.organizations {
3696-
if organization.Name == name {
3683+
if organization.Name == params.Name && organization.Deleted == params.Deleted {
36973684
return organization, nil
36983685
}
36993686
}
@@ -3740,17 +3727,17 @@ func (q *FakeQuerier) GetOrganizations(_ context.Context, args database.GetOrgan
37403727
return tmp, nil
37413728
}
37423729

3743-
func (q *FakeQuerier) GetOrganizationsByUserID(_ context.Context, userID uuid.UUID) ([]database.Organization, error) {
3730+
func (q *FakeQuerier) GetOrganizationsByUserID(_ context.Context, arg database.GetOrganizationsByUserIDParams) ([]database.Organization, error) {
37443731
q.mutex.RLock()
37453732
defer q.mutex.RUnlock()
37463733

37473734
organizations := make([]database.Organization, 0)
37483735
for _, organizationMember := range q.organizationMembers {
3749-
if organizationMember.UserID != userID {
3736+
if organizationMember.UserID != arg.UserID {
37503737
continue
37513738
}
37523739
for _, organization := range q.organizations {
3753-
if organization.ID != organizationMember.OrganizationID {
3740+
if organization.ID != organizationMember.OrganizationID || organization.Deleted != arg.Deleted {
37543741
continue
37553742
}
37563743
organizations = append(organizations, organization)
@@ -9822,6 +9809,26 @@ func (q *FakeQuerier) UpdateOrganization(_ context.Context, arg database.UpdateO
98229809
return database.Organization{}, sql.ErrNoRows
98239810
}
98249811

9812+
func (q *FakeQuerier) UpdateOrganizationDeletedByID(_ context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
9813+
if err := validateDatabaseType(arg); err != nil {
9814+
return err
9815+
}
9816+
9817+
q.mutex.Lock()
9818+
defer q.mutex.Unlock()
9819+
9820+
for index, organization := range q.organizations {
9821+
if organization.ID != arg.ID || organization.IsDefault {
9822+
continue
9823+
}
9824+
organization.Deleted = true
9825+
organization.UpdatedAt = arg.UpdatedAt
9826+
q.organizations[index] = organization
9827+
return nil
9828+
}
9829+
return sql.ErrNoRows
9830+
}
9831+
98259832
func (q *FakeQuerier) UpdateProvisionerDaemonLastSeenAt(_ context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
98269833
err := validateDatabaseType(arg)
98279834
if err != nil {

coderd/database/dbmetrics/querymetrics.go

Lines changed: 19 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy