Skip to content

feat: enable soft delete for organizations #16584

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

Merged
merged 33 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
606a5fa
feat: orgs soft delete
jaaydenh Feb 11, 2025
609890f
chore: rename migration
jaaydenh Feb 15, 2025
31e5eb2
chore: add deleted to audit table
jaaydenh Feb 15, 2025
608661b
chore: update audit table
jaaydenh Feb 15, 2025
54039cf
chore: code generated from db migration
jaaydenh Feb 15, 2025
9516695
feat: updates for soft delete
jaaydenh Feb 15, 2025
9a26df6
chore: updates to sql.go
jaaydenh Feb 16, 2025
f1f25e1
fix: update indexes and unique constraint
jaaydenh Feb 16, 2025
b202254
fix: update migration number
jaaydenh Feb 18, 2025
1bf3742
fix: fix TestEnterpriseAuditLogs
jaaydenh Feb 18, 2025
41e943b
fix: revert test
jaaydenh Feb 18, 2025
9fe8da1
fix: updates for pr review
jaaydenh Feb 19, 2025
6929988
feat: add trigger to check for resources on soft delete
jaaydenh Feb 20, 2025
eb88091
chore: update trigger function
jaaydenh Feb 20, 2025
10d1564
fix: drop index add members check to trigger
jaaydenh Feb 21, 2025
56260b0
fix: fix frontend display of error messages
jaaydenh Feb 21, 2025
5968da1
fix: fix tests
jaaydenh Feb 21, 2025
70bf5e3
fix: fix test
jaaydenh Feb 21, 2025
50074d1
fix fix e2e test
jaaydenh Feb 21, 2025
eed03fe
fix: fix format
jaaydenh Feb 21, 2025
891cbae
fix: update trigger
jaaydenh Feb 21, 2025
22d27f8
chore: implement unit test to test trigger with >0 workspaces
Emyrk Feb 21, 2025
dc5fc46
use dbfake
Emyrk Feb 21, 2025
9466ff7
fix: trigger
jaaydenh Feb 21, 2025
ae2bf08
Merge branch 'jaaydenh/orgs-soft-delete' of https://github.com/coder/…
jaaydenh Feb 21, 2025
041630c
chore: update delete org trigger name
jaaydenh Feb 21, 2025
607311f
chore: add additional org delete trigger tests
jaaydenh Feb 21, 2025
7291c02
chore: change name
jaaydenh Feb 21, 2025
b9b7a14
fix: update trigger name
jaaydenh Feb 21, 2025
9af7cb5
fix: fix tests
jaaydenh Feb 21, 2025
aee6834
fix: fix test
jaaydenh Feb 21, 2025
74ef3de
fix: update e2e test
jaaydenh Feb 21, 2025
4c70aaa
Update coderd/database/querier_test.go
jaaydenh Feb 24, 2025
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
5 changes: 2 additions & 3 deletions coderd/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
// Query functions are generated using sqlc.
//
// To modify the database schema:
// 1. Add a new migration using "create_migration.sh" in database/migrations/
// 2. Run "make coderd/database/generate" in the root to generate models.
// 3. Add/Edit queries in "query.sql" and run "make coderd/database/generate" to create Go code.
// 1. Add a new migration using "create_migration.sh" in database/migrations/ and run "make gen" to generate models.
// 2. Add/Edit queries in "query.sql" and run "make gen" to create Go code.
package database

import (
Expand Down
18 changes: 12 additions & 6 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -1302,10 +1302,6 @@ func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
return q.db.DeleteOldWorkspaceAgentStats(ctx)
}

func (q *querier) DeleteOrganization(ctx context.Context, id uuid.UUID) error {
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, q.db.DeleteOrganization)(ctx, id)
}

func (q *querier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
return deleteQ[database.OrganizationMember](q.log, q.auth, func(ctx context.Context, arg database.DeleteOrganizationMemberParams) (database.OrganizationMember, error) {
member, err := database.ExpectOne(q.OrganizationMembers(ctx, database.OrganizationMembersParams(arg)))
Expand Down Expand Up @@ -1926,7 +1922,7 @@ func (q *querier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (databa
return fetch(q.log, q.auth, q.db.GetOrganizationByID)(ctx, id)
}

func (q *querier) GetOrganizationByName(ctx context.Context, name string) (database.Organization, error) {
func (q *querier) GetOrganizationByName(ctx context.Context, name database.GetOrganizationByNameParams) (database.Organization, error) {
return fetch(q.log, q.auth, q.db.GetOrganizationByName)(ctx, name)
}

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

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

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

func (q *querier) UpdateOrganizationDeletedByID(ctx context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
deleteF := func(ctx context.Context, id uuid.UUID) error {
return q.db.UpdateOrganizationDeletedByID(ctx, database.UpdateOrganizationDeletedByIDParams{
ID: id,
UpdatedAt: dbtime.Now(),
})
}
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, deleteF)(ctx, arg.ID)
}

func (q *querier) UpdateProvisionerDaemonLastSeenAt(ctx context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceProvisionerDaemon); err != nil {
return err
Expand Down
13 changes: 7 additions & 6 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ func (s *MethodTestSuite) TestOrganization() {
}))
s.Run("GetOrganizationByName", s.Subtest(func(db database.Store, check *expects) {
o := dbgen.Organization(s.T(), db, database.Organization{})
check.Args(o.Name).Asserts(o, policy.ActionRead).Returns(o)
check.Args(database.GetOrganizationByNameParams{Name: o.Name, Deleted: o.Deleted}).Asserts(o, policy.ActionRead).Returns(o)
}))
s.Run("GetOrganizationIDsByMemberIDs", s.Subtest(func(db database.Store, check *expects) {
oa := dbgen.Organization(s.T(), db, database.Organization{})
Expand All @@ -839,7 +839,7 @@ func (s *MethodTestSuite) TestOrganization() {
_ = dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{UserID: u.ID, OrganizationID: a.ID})
b := dbgen.Organization(s.T(), db, database.Organization{})
_ = dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{UserID: u.ID, OrganizationID: b.ID})
check.Args(u.ID).Asserts(a, policy.ActionRead, b, policy.ActionRead).Returns(slice.New(a, b))
check.Args(database.GetOrganizationsByUserIDParams{UserID: u.ID, Deleted: false}).Asserts(a, policy.ActionRead, b, policy.ActionRead).Returns(slice.New(a, b))
}))
s.Run("InsertOrganization", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertOrganizationParams{
Expand Down Expand Up @@ -960,13 +960,14 @@ func (s *MethodTestSuite) TestOrganization() {
Name: "something-different",
}).Asserts(o, policy.ActionUpdate)
}))
s.Run("DeleteOrganization", s.Subtest(func(db database.Store, check *expects) {
s.Run("UpdateOrganizationDeletedByID", s.Subtest(func(db database.Store, check *expects) {
o := dbgen.Organization(s.T(), db, database.Organization{
Name: "doomed",
})
check.Args(
o.ID,
).Asserts(o, policy.ActionDelete)
check.Args(database.UpdateOrganizationDeletedByIDParams{
ID: o.ID,
UpdatedAt: o.UpdatedAt,
}).Asserts(o, policy.ActionDelete).Returns()
}))
s.Run("OrganizationMembers", s.Subtest(func(db database.Store, check *expects) {
o := dbgen.Organization(s.T(), db, database.Organization{})
Expand Down
43 changes: 25 additions & 18 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -2157,19 +2157,6 @@ func (q *FakeQuerier) DeleteOldWorkspaceAgentStats(_ context.Context) error {
return nil
}

func (q *FakeQuerier) DeleteOrganization(_ context.Context, id uuid.UUID) error {
q.mutex.Lock()
defer q.mutex.Unlock()

for i, org := range q.organizations {
if org.ID == id && !org.IsDefault {
q.organizations = append(q.organizations[:i], q.organizations[i+1:]...)
return nil
}
}
return sql.ErrNoRows
}

func (q *FakeQuerier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
err := validateDatabaseType(arg)
if err != nil {
Expand Down Expand Up @@ -3688,12 +3675,12 @@ func (q *FakeQuerier) GetOrganizationByID(_ context.Context, id uuid.UUID) (data
return q.getOrganizationByIDNoLock(id)
}

func (q *FakeQuerier) GetOrganizationByName(_ context.Context, name string) (database.Organization, error) {
func (q *FakeQuerier) GetOrganizationByName(_ context.Context, params database.GetOrganizationByNameParams) (database.Organization, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

for _, organization := range q.organizations {
if organization.Name == name {
if organization.Name == params.Name && organization.Deleted == params.Deleted {
return organization, nil
}
}
Expand Down Expand Up @@ -3740,17 +3727,17 @@ func (q *FakeQuerier) GetOrganizations(_ context.Context, args database.GetOrgan
return tmp, nil
}

func (q *FakeQuerier) GetOrganizationsByUserID(_ context.Context, userID uuid.UUID) ([]database.Organization, error) {
func (q *FakeQuerier) GetOrganizationsByUserID(_ context.Context, arg database.GetOrganizationsByUserIDParams) ([]database.Organization, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

organizations := make([]database.Organization, 0)
for _, organizationMember := range q.organizationMembers {
if organizationMember.UserID != userID {
if organizationMember.UserID != arg.UserID {
continue
}
for _, organization := range q.organizations {
if organization.ID != organizationMember.OrganizationID {
if organization.ID != organizationMember.OrganizationID || organization.Deleted != arg.Deleted {
continue
}
organizations = append(organizations, organization)
Expand Down Expand Up @@ -9822,6 +9809,26 @@ func (q *FakeQuerier) UpdateOrganization(_ context.Context, arg database.UpdateO
return database.Organization{}, sql.ErrNoRows
}

func (q *FakeQuerier) UpdateOrganizationDeletedByID(_ context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
if err := validateDatabaseType(arg); err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for index, organization := range q.organizations {
if organization.ID != arg.ID || organization.IsDefault {
continue
}
organization.Deleted = true
organization.UpdatedAt = arg.UpdatedAt
q.organizations[index] = organization
return nil
}
return sql.ErrNoRows
}

func (q *FakeQuerier) UpdateProvisionerDaemonLastSeenAt(_ context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
err := validateDatabaseType(arg)
if err != nil {
Expand Down
28 changes: 19 additions & 9 deletions coderd/database/dbmetrics/querymetrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 22 additions & 22 deletions coderd/database/dbmock/dbmock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading