Skip to content

Commit a3c5896

Browse files
jaaydenhEmyrk
authored andcommitted
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 ff1bdff commit a3c5896

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

@@ -3748,6 +3744,16 @@ func (q *querier) UpdateOrganization(ctx context.Context, arg database.UpdateOrg
37483744
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateOrganization)(ctx, arg)
37493745
}
37503746

3747+
func (q *querier) UpdateOrganizationDeletedByID(ctx context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
3748+
deleteF := func(ctx context.Context, id uuid.UUID) error {
3749+
return q.db.UpdateOrganizationDeletedByID(ctx, database.UpdateOrganizationDeletedByIDParams{
3750+
ID: id,
3751+
UpdatedAt: dbtime.Now(),
3752+
})
3753+
}
3754+
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, deleteF)(ctx, arg.ID)
3755+
}
3756+
37513757
func (q *querier) UpdateProvisionerDaemonLastSeenAt(ctx context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
37523758
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceProvisionerDaemon); err != nil {
37533759
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
@@ -2158,19 +2158,6 @@ func (q *FakeQuerier) DeleteOldWorkspaceAgentStats(_ context.Context) error {
21582158
return nil
21592159
}
21602160

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

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

36963683
for _, organization := range q.organizations {
3697-
if organization.Name == name {
3684+
if organization.Name == params.Name && organization.Deleted == params.Deleted {
36983685
return organization, nil
36993686
}
37003687
}
@@ -3741,17 +3728,17 @@ func (q *FakeQuerier) GetOrganizations(_ context.Context, args database.GetOrgan
37413728
return tmp, nil
37423729
}
37433730

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

37483735
organizations := make([]database.Organization, 0)
37493736
for _, organizationMember := range q.organizationMembers {
3750-
if organizationMember.UserID != userID {
3737+
if organizationMember.UserID != arg.UserID {
37513738
continue
37523739
}
37533740
for _, organization := range q.organizations {
3754-
if organization.ID != organizationMember.OrganizationID {
3741+
if organization.ID != organizationMember.OrganizationID || organization.Deleted != arg.Deleted {
37553742
continue
37563743
}
37573744
organizations = append(organizations, organization)
@@ -9837,6 +9824,26 @@ func (q *FakeQuerier) UpdateOrganization(_ context.Context, arg database.UpdateO
98379824
return database.Organization{}, sql.ErrNoRows
98389825
}
98399826

9827+
func (q *FakeQuerier) UpdateOrganizationDeletedByID(_ context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
9828+
if err := validateDatabaseType(arg); err != nil {
9829+
return err
9830+
}
9831+
9832+
q.mutex.Lock()
9833+
defer q.mutex.Unlock()
9834+
9835+
for index, organization := range q.organizations {
9836+
if organization.ID != arg.ID || organization.IsDefault {
9837+
continue
9838+
}
9839+
organization.Deleted = true
9840+
organization.UpdatedAt = arg.UpdatedAt
9841+
q.organizations[index] = organization
9842+
return nil
9843+
}
9844+
return sql.ErrNoRows
9845+
}
9846+
98409847
func (q *FakeQuerier) UpdateProvisionerDaemonLastSeenAt(_ context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
98419848
err := validateDatabaseType(arg)
98429849
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)