Archive manual users on remove
Switch remove-user behavior for manually managed profiles from hard\ndelete to archival by deactivating the profile. This matches the\nrequested SCIM-like lifecycle while avoiding dependency errors for\nlinked records such as signatures and assets.\n\nThe remove flow now updates profile state to INACTIVE, updates\nmembership timestamps, and emits a user-updated webhook event instead of\ndelete events. E2E coverage now asserts that remove keeps the profile\nand marks it inactive. Signed-off-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
committed by
Bryan Frimin
parent
ffa3db3cd2
commit
b50bbc8d6a
@@ -21,7 +21,6 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"go.gearno.de/crypto/uuid"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/packages/emails"
|
||||
@@ -350,44 +349,30 @@ func (s *OrganizationService) RemoveUser(
|
||||
}
|
||||
}
|
||||
|
||||
if err := webhook.InsertData(ctx, tx, scope, organizationID, coredata.WebhookEventTypeUserDeleted, webhooktypes.NewUser(&profile, membership)); err != nil {
|
||||
now := time.Now()
|
||||
if profile.State != coredata.ProfileStateInactive {
|
||||
profile.State = coredata.ProfileStateInactive
|
||||
profile.UpdatedAt = now
|
||||
|
||||
if err := profile.Update(ctx, tx, scope); err != nil {
|
||||
return fmt.Errorf("cannot update profile state: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
membership.UpdatedAt = now
|
||||
if err := membership.Update(ctx, tx, scope); err != nil {
|
||||
return fmt.Errorf("cannot update membership: %w", err)
|
||||
}
|
||||
|
||||
if err := webhook.InsertData(ctx, tx, scope, organizationID, coredata.WebhookEventTypeUserUpdated, webhooktypes.NewUser(&profile, membership)); err != nil {
|
||||
return fmt.Errorf("cannot insert webhook event: %w", err)
|
||||
}
|
||||
|
||||
if err := profile.Delete(ctx, tx, scope, profileID); err != nil {
|
||||
if isUserRemovalDependencyError(err) {
|
||||
return NewUserReferencedByRecordsError(profileID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot delete profile: %w", err)
|
||||
}
|
||||
|
||||
if err := membership.Delete(ctx, tx, scope, membership.ID); err != nil {
|
||||
if isUserRemovalDependencyError(err) {
|
||||
return NewUserReferencedByRecordsError(profileID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot delete membership: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func isUserRemovalDependencyError(err error) bool {
|
||||
if errors.Is(err, coredata.ErrResourceInUse) {
|
||||
return true
|
||||
}
|
||||
|
||||
pgErr, ok := errors.AsType[*pgconn.PgError](err)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return pgErr.Code == "23503"
|
||||
}
|
||||
|
||||
func (s *OrganizationService) InviteUser(
|
||||
ctx context.Context,
|
||||
req *CreateInvitationRequest,
|
||||
|
||||
Reference in New Issue
Block a user