Skip to content

Commit

Permalink
Recursive Deletes (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
gbdubs authored Jan 22, 2024
1 parent 514a93e commit c263946
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 20 deletions.
5 changes: 4 additions & 1 deletion cmd/server/pactasrv/initiative.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ func (s *Server) DeleteInitiative(ctx context.Context, request api.DeleteInitiat
if err := s.initiativeDoAuthzAndAuditLog(ctx, id, pacta.AuditLogAction_Delete); err != nil {
return nil, err
}
err := s.DB.DeleteInitiative(s.DB.NoTxn(ctx), id)
buris, err := s.DB.DeleteInitiative(s.DB.NoTxn(ctx), id)
if err != nil {
return nil, oapierr.Internal("failed to delete initiative", zap.Error(err))
}
if err := s.deleteBlobs(ctx, buris...); err != nil {
return nil, err
}
return api.DeleteInitiative204Response{}, nil
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/server/pactasrv/pactasrv.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type DB interface {
AllInitiatives(tx db.Tx) ([]*pacta.Initiative, error)
CreateInitiative(tx db.Tx, i *pacta.Initiative) error
UpdateInitiative(tx db.Tx, id pacta.InitiativeID, mutations ...db.UpdateInitiativeFn) error
DeleteInitiative(tx db.Tx, id pacta.InitiativeID) error
DeleteInitiative(tx db.Tx, id pacta.InitiativeID) ([]pacta.BlobURI, error)

PACTAVersion(tx db.Tx, id pacta.PACTAVersionID) (*pacta.PACTAVersion, error)
DefaultPACTAVersion(tx db.Tx) (*pacta.PACTAVersion, error)
Expand Down Expand Up @@ -108,7 +108,7 @@ type DB interface {
PortfolioGroups(tx db.Tx, ids []pacta.PortfolioGroupID) (map[pacta.PortfolioGroupID]*pacta.PortfolioGroup, error)
CreatePortfolioGroup(tx db.Tx, p *pacta.PortfolioGroup) (pacta.PortfolioGroupID, error)
UpdatePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID, mutations ...db.UpdatePortfolioGroupFn) error
DeletePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID) error
DeletePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID) ([]pacta.BlobURI, error)
CreatePortfolioGroupMembership(tx db.Tx, pgID pacta.PortfolioGroupID, pID pacta.PortfolioID) error
DeletePortfolioGroupMembership(tx db.Tx, pgID pacta.PortfolioGroupID, pID pacta.PortfolioID) error

Expand Down
5 changes: 4 additions & 1 deletion cmd/server/pactasrv/portfolio_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ func (s *Server) DeletePortfolioGroup(ctx context.Context, request api.DeletePor
if err := s.portfolioGroupAuthz(ctx, id, pacta.AuditLogAction_Delete); err != nil {
return nil, err
}
err := s.DB.DeletePortfolioGroup(s.DB.NoTxn(ctx), id)
buris, err := s.DB.DeletePortfolioGroup(s.DB.NoTxn(ctx), id)
if err != nil {
return nil, oapierr.Internal("failed to delete portfolio group", zap.String("portfolio_group_id", request.Id), zap.Error(err))
}
if err := s.deleteBlobs(ctx, buris...); err != nil {
return nil, err
}
return api.DeletePortfolioGroup204Response{}, nil
}

Expand Down
31 changes: 25 additions & 6 deletions db/sqldb/initiative.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,25 @@ func (d *DB) UpdateInitiative(tx db.Tx, id pacta.InitiativeID, mutations ...db.U
return nil
}

func (d *DB) DeleteInitiative(tx db.Tx, id pacta.InitiativeID) error {
func (d *DB) DeleteInitiative(tx db.Tx, id pacta.InitiativeID) ([]pacta.BlobURI, error) {
buris := []pacta.BlobURI{}
err := d.RunOrContinueTransaction(tx, func(db.Tx) error {
// TODO(grady) add owner deletions here - where the initiative is the owner of the asset/blob
// TODO(grady) do snapshot deletions here
err := d.exec(tx, `DELETE FROM initiative_invitation WHERE initiative_id = $1;`, id)
owner, err := d.GetOrCreateOwnerForInitiative(tx, id)
if err != nil {
return fmt.Errorf("getting owner for initiative: %w", err)
}
analysisIDs, err := d.AnalysesRunOnInitiative(tx, id)
if err != nil {
return fmt.Errorf("reading initative analyses: %w", err)
}
for _, aID := range analysisIDs {
aBuris, err := d.DeleteAnalysis(tx, aID)
if err != nil {
return fmt.Errorf("deleting analysis: %w", err)
}
buris = append(buris, aBuris...)
}
err = d.exec(tx, `DELETE FROM initiative_invitation WHERE initiative_id = $1;`, id)
if err != nil {
return fmt.Errorf("deleting initiative_invitations: %w", err)
}
Expand All @@ -130,16 +144,21 @@ func (d *DB) DeleteInitiative(tx db.Tx, id pacta.InitiativeID) error {
if err != nil {
return fmt.Errorf("deleting portfolio_initiative_memberships: %w", err)
}
oBuris, err := d.DeleteOwner(tx, owner)
if err != nil {
return fmt.Errorf("deleting owner: %w", err)
}
buris = append(buris, oBuris...)
err = d.exec(tx, `DELETE FROM initiative WHERE id = $1;`, id)
if err != nil {
return fmt.Errorf("deleting initiative: %w", err)
}
return nil
})
if err != nil {
return fmt.Errorf("performing initiative deletion: %w", err)
return nil, fmt.Errorf("performing initiative deletion: %w", err)
}
return nil
return buris, nil
}

func rowToInitiative(row rowScanner) (*pacta.Initiative, error) {
Expand Down
11 changes: 9 additions & 2 deletions db/sqldb/initiative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,13 @@ func TestInitiativeCRUD(t *testing.T) {
}
assert(i)

err = tdb.DeleteInitiative(tx, i.ID)
buris, err := tdb.DeleteInitiative(tx, i.ID)
if err != nil {
t.Fatalf("delete initiative: %v", err)
}
if len(buris) != 0 {
t.Fatalf("expected no deleted buris, got %d", len(buris))
}
}

func TestDeleteInitiative(t *testing.T) {
Expand All @@ -109,11 +112,15 @@ func TestDeleteInitiative(t *testing.T) {
err1 := tdb.PutInitiativeUserRelationship(tx, iur)
noErrDuringSetup(t, err0, err1)

err := tdb.DeleteInitiative(tx, i.ID)
buris, err := tdb.DeleteInitiative(tx, i.ID)
if err != nil {
t.Fatalf("delete initiative: %v", err)
}

if len(buris) != 0 {
t.Fatalf("expected no buris but got %+v", buris)
}

_, err = tdb.Initiative(tx, i.ID)
if err == nil {
t.Fatalf("expected error, got nil")
Expand Down
5 changes: 4 additions & 1 deletion db/sqldb/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,13 @@ func (d *DB) DeleteOwner(tx db.Tx, oID pacta.OwnerID) ([]pacta.BlobURI, error) {
return fmt.Errorf("getting portfolio groups for owner: %w", err)
}
for _, pgroup := range pgroups {
err := d.DeletePortfolioGroup(tx, pgroup.ID)
pgBuris, err := d.DeletePortfolioGroup(tx, pgroup.ID)
if err != nil {
return fmt.Errorf("deleting portfolio group: %w", err)
}
if pgBuris != nil {
buris = append(buris, pgBuris...)
}
}
incompleteUploads, err := d.IncompleteUploadsByOwner(tx, oID)
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions db/sqldb/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@ func (d *DB) DeletePortfolio(tx db.Tx, id pacta.PortfolioID) ([]pacta.BlobURI, e
if err != nil {
return fmt.Errorf("reading portfolio: %w", err)
}
analysisIDs, err := d.AnalysesRunOnPortfolio(tx, id)
if err != nil {
return fmt.Errorf("reading portfolio analyses: %w", err)
}
for _, aID := range analysisIDs {
sBuris, err := d.DeleteAnalysis(tx, aID)
if err != nil {
return fmt.Errorf("deleting analysis: %w", err)
}
buris = append(buris, sBuris...)
}
err = d.exec(tx, `DELETE FROM portfolio_group_membership WHERE portfolio_id = $1;`, id)
if err != nil {
return fmt.Errorf("deleting portfolio_group_memberships: %w", err)
Expand Down
22 changes: 19 additions & 3 deletions db/sqldb/portfolio_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,21 @@ func (d *DB) UpdatePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID, mutations
return nil
}

func (d *DB) DeletePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID) error {
return d.RunOrContinueTransaction(tx, func(tx db.Tx) error {
err := d.exec(tx, `DELETE FROM portfolio_group_membership WHERE portfolio_group_id = $1;`, id)
func (d *DB) DeletePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID) ([]pacta.BlobURI, error) {
buris := []pacta.BlobURI{}
err := d.RunOrContinueTransaction(tx, func(tx db.Tx) error {
analysisIDs, err := d.AnalysesRunOnPortfolioGroup(tx, id)
if err != nil {
return fmt.Errorf("reading portfolio_group analyses: %w", err)
}
for _, aID := range analysisIDs {
sBuris, err := d.DeleteAnalysis(tx, aID)
if err != nil {
return fmt.Errorf("deleting analysis: %w", err)
}
buris = append(buris, sBuris...)
}
err = d.exec(tx, `DELETE FROM portfolio_group_membership WHERE portfolio_group_id = $1;`, id)
if err != nil {
return fmt.Errorf("deleting portfolio_group_memberships: %w", err)
}
Expand All @@ -120,6 +132,10 @@ func (d *DB) DeletePortfolioGroup(tx db.Tx, id pacta.PortfolioGroupID) error {
}
return nil
})
if err != nil {
return nil, fmt.Errorf("deleting portfolio_group txn: %w", err)
}
return buris, nil
}

func rowToPortfolioGroup(row rowScanner) (*pacta.PortfolioGroup, error) {
Expand Down
5 changes: 4 additions & 1 deletion db/sqldb/portfolio_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestPortfolioGroupCRUD(t *testing.T) {
CreatedAt: time.Now(),
}}

err = tdb.DeletePortfolioGroup(tx, pg1.ID)
buris, err := tdb.DeletePortfolioGroup(tx, pg1.ID)
if err != nil {
t.Fatalf("delete portfolio group: %v", err)
}
Expand All @@ -128,6 +128,9 @@ func TestPortfolioGroupCRUD(t *testing.T) {
if diff := cmp.Diff(expecteds, actuals, portfolioGroupCmpOpts()); diff != "" {
t.Fatalf("portfolio group mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff([]pacta.BlobURI{}, buris); diff != "" {
t.Fatalf("blob uri mismatch (-want +got):\n%s", diff)
}
}

func TestPortfolioGroupMembership(t *testing.T) {
Expand Down
40 changes: 37 additions & 3 deletions db/sqldb/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ func (d *DB) PortfolioSnapshot(tx db.Tx, psID pacta.PortfolioSnapshotID) (*pacta
return exactlyOneFromMap("portfolio_snapshot", psID, pss)
}

const snapshotQuery = `
SELECT id, portfolio_id, portfolio_group_id, initiative_id, portfolio_ids
FROM portfolio_snapshot
`

func (d *DB) PortfolioSnapshots(tx db.Tx, psID []pacta.PortfolioSnapshotID) (map[pacta.PortfolioSnapshotID]*pacta.PortfolioSnapshot, error) {
rows, err := d.query(tx, `
SELECT id, portfolio_id, portfolio_group_id, initiative_id, portfolio_ids
FROM portfolio_snapshot
rows, err := d.query(tx, snapshotQuery+`
WHERE id IN `+createWhereInFmt(len(psID))+`;`, idsToInterface(psID)...)
if err != nil {
return nil, fmt.Errorf("reading portfolio snapshot: %w", err)
Expand All @@ -65,6 +68,37 @@ func (d *DB) PortfolioSnapshots(tx db.Tx, psID []pacta.PortfolioSnapshotID) (map
return result, nil
}

const analysisLookupQuery = `
SELECT analysis.id
FROM analysis
JOIN portfolio_snapshot
ON analysis.portfolio_snapshot_id = portfolio_snapshot.id
`

func (d *DB) AnalysesRunOnPortfolio(tx db.Tx, pID pacta.PortfolioID) ([]pacta.AnalysisID, error) {
rows, err := d.query(tx, analysisLookupQuery+`WHERE portfolio_snapshot.portfolio_id = $1;`, pID)
if err != nil {
return nil, fmt.Errorf("reading portfolio snapshots by portfolio_id: %w", err)
}
return mapRowsToIDs[pacta.AnalysisID]("analyses_from_snapshot_from_portfolio", rows)
}

func (d *DB) AnalysesRunOnPortfolioGroup(tx db.Tx, pgID pacta.PortfolioGroupID) ([]pacta.AnalysisID, error) {
rows, err := d.query(tx, analysisLookupQuery+`WHERE portfolio_snapshot.portfolio_group_id = $1;`, pgID)
if err != nil {
return nil, fmt.Errorf("reading portfolio snapshots by portfolio_group_id: %w", err)
}
return mapRowsToIDs[pacta.AnalysisID]("analyses_from_snapshot_from_portfolio_group", rows)
}

func (d *DB) AnalysesRunOnInitiative(tx db.Tx, iID pacta.InitiativeID) ([]pacta.AnalysisID, error) {
rows, err := d.query(tx, analysisLookupQuery+`WHERE portfolio_snapshot.initiative_id = $1;`, iID)
if err != nil {
return nil, fmt.Errorf("reading portfolio snapshots by initiative_id: %w", err)
}
return mapRowsToIDs[pacta.AnalysisID]("analyses_from_snapshot_from_initiative", rows)
}

func (d *DB) createSnapshot(tx db.Tx, pID pacta.PortfolioID, pgID pacta.PortfolioGroupID, iID pacta.InitiativeID, portfolioIDs []pacta.PortfolioID) (pacta.PortfolioSnapshotID, error) {
included := make(map[string]bool)
canonical := []string{}
Expand Down

0 comments on commit c263946

Please sign in to comment.