Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
62 changes: 59 additions & 3 deletions core/deleter/deleter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package deleter

import "strings"
import (
"fmt"
"slices"
"strings"

"github.com/raystack/frontier/core/organization"
)

// Reasons a delete can be blocked. The API error carries
// them as violation types, so a client can tell the reasons apart
Expand All @@ -10,6 +16,7 @@ const (
BlockerUnpaidInvoice = "UNPAID_INVOICE"
BlockerNegativeTokenBalance = "NEGATIVE_TOKEN_BALANCE"
BlockerResourcesExist = "RESOURCES_EXIST"
BlockerSoleOwnerWithMembers = "SOLE_OWNER_WITH_MEMBERS"
)

// Kinds of entity a Blocker's Subject id refers to.
Expand All @@ -18,9 +25,10 @@ const (
SubjectInvoice = "billing_invoice"
SubjectBillingAccount = "billing_account"
SubjectResourceNamespace = "resource_namespace"
SubjectOrganization = "organization"
)

// Blocker is one reason a project or an organization cannot be deleted right now.
// Blocker is one reason a project, an organization or a user cannot be deleted right now.
type Blocker struct {
// Type is one of the Blocker* constants.
Type string
Expand All @@ -33,16 +41,61 @@ type Blocker struct {
// Count is the number of blocking resources, for BlockerResourcesExist.
Count int
// Message says what blocks the delete and how to clear it. Empty for
// BlockerResourcesExist.
// BlockerResourcesExist, except in a refused user delete.
Message string
}

type UserDeleteCheck struct {
Blockers []Blocker
Comment thread
rohilsurana marked this conversation as resolved.
OrganizationsToDelete []OrganizationToDelete
OrganizationsToLeave []OrganizationToLeave
PATCount int
}

type OrganizationToDelete struct {
Organization organization.Organization
Blockers []Blocker
}

type OrganizationToLeave struct {
Organization organization.Organization
RoleTitle string
Blockers []Blocker
}

func (c UserDeleteCheck) CanDelete() bool {
return len(c.allBlockers()) == 0
}

func (c UserDeleteCheck) allBlockers() []Blocker {
blockers := slices.Clone(c.Blockers)
for _, o := range c.OrganizationsToDelete {
for _, b := range o.Blockers {
blockers = append(blockers, labelWithOrg(o.Organization.Name, b))
}
}
for _, o := range c.OrganizationsToLeave {
blockers = append(blockers, o.Blockers...)
}
return blockers
}

func labelWithOrg(orgName string, b Blocker) Blocker {
text := b.Message
if text == "" {
text = b.Type
}
b.Message = fmt.Sprintf("organization[%s]: %s", orgName, text)
return b
}

// BlockedError carries every blocker the up-front check found, so the
// caller gets one checklist instead of discovering blockers one retry at
// a time.
type BlockedError struct {
OrgID string
ProjectID string
UserID string
Blockers []Blocker
}

Expand All @@ -51,6 +104,9 @@ func (e *BlockedError) Error() string {
if e.ProjectID != "" {
target = "project"
}
if e.UserID != "" {
target = "user"
}
var msgs []string
for _, b := range e.Blockers {
if b.Message != "" {
Expand Down
57 changes: 57 additions & 0 deletions core/deleter/mocks/user_pat_service.go

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

59 changes: 59 additions & 0 deletions core/deleter/mocks/user_service.go

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

Loading
Loading