feat: support media account unbind requests
This commit is contained in:
@@ -387,7 +387,7 @@ func (s *DesktopAccountService) Tombstone(ctx context.Context, client *repositor
|
||||
}
|
||||
|
||||
func (s *DesktopAccountService) RequestDelete(ctx context.Context, actor auth.Actor, desktopID uuid.UUID, undo bool) (*DesktopAccountView, error) {
|
||||
if actor.PrimaryWorkspaceID == 0 {
|
||||
if actor.PrimaryWorkspaceID == 0 || actor.UserID == 0 {
|
||||
return nil, response.ErrUnauthorized(40132, "workspace_scope_missing", "workspace context is required")
|
||||
}
|
||||
|
||||
@@ -397,9 +397,9 @@ func (s *DesktopAccountService) RequestDelete(ctx context.Context, actor auth.Ac
|
||||
)
|
||||
|
||||
if undo {
|
||||
account, err = s.repo.ClearDeleteRequested(ctx, desktopID, actor.PrimaryWorkspaceID)
|
||||
account, err = s.repo.ClearDeleteRequested(ctx, desktopID, actor.PrimaryWorkspaceID, actor.UserID)
|
||||
} else {
|
||||
account, err = s.repo.MarkDeleteRequested(ctx, desktopID, actor.PrimaryWorkspaceID)
|
||||
account, err = s.repo.MarkDeleteRequested(ctx, desktopID, actor.PrimaryWorkspaceID, actor.UserID)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
|
||||
@@ -1,15 +1,73 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/shared/auth"
|
||||
"github.com/geo-platform/tenant-api/internal/tenant/repository"
|
||||
)
|
||||
|
||||
type desktopAccountRepoSpy struct {
|
||||
markDesktopID uuid.UUID
|
||||
markWorkspaceID int64
|
||||
markUserID int64
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) ListByClient(context.Context, int64, uuid.UUID) ([]repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) ListByUser(context.Context, int64, int64) ([]repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) GetByDesktopID(context.Context, uuid.UUID, int64) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) GetByIdentity(context.Context, int64, uuid.UUID, string, string) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) Upsert(context.Context, repository.UpsertDesktopAccountParams) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) Patch(context.Context, repository.PatchDesktopAccountParams) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) Tombstone(context.Context, uuid.UUID, int64, uuid.UUID, int64) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) MarkDeleteRequested(_ context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*repository.DesktopAccount, error) {
|
||||
r.markDesktopID = desktopID
|
||||
r.markWorkspaceID = workspaceID
|
||||
r.markUserID = userID
|
||||
now := time.Date(2026, 6, 18, 9, 30, 0, 0, time.UTC)
|
||||
return &repository.DesktopAccount{
|
||||
DesktopID: desktopID,
|
||||
TenantID: 10,
|
||||
WorkspaceID: workspaceID,
|
||||
UserID: userID,
|
||||
Platform: "toutiaohao",
|
||||
PlatformUID: "122707592",
|
||||
DisplayName: "Media Account",
|
||||
Health: "live",
|
||||
DeleteRequestedAt: &now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepoSpy) ClearDeleteRequested(context.Context, uuid.UUID, int64, int64) (*repository.DesktopAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestResolveDesktopClientOnline_PresenceHitWins(t *testing.T) {
|
||||
now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC)
|
||||
|
||||
@@ -21,6 +79,26 @@ func TestResolveDesktopClientOnline_PresenceHitWins(t *testing.T) {
|
||||
assert.True(t, online)
|
||||
}
|
||||
|
||||
func TestRequestDeleteScopesToActorUser(t *testing.T) {
|
||||
repo := &desktopAccountRepoSpy{}
|
||||
service := &DesktopAccountService{repo: repo}
|
||||
accountID := uuid.New()
|
||||
|
||||
view, err := service.RequestDelete(
|
||||
context.Background(),
|
||||
auth.Actor{UserID: 42, TenantID: 10, PrimaryWorkspaceID: 7},
|
||||
accountID,
|
||||
false,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, accountID, repo.markDesktopID)
|
||||
assert.Equal(t, int64(7), repo.markWorkspaceID)
|
||||
assert.Equal(t, int64(42), repo.markUserID)
|
||||
assert.NotNil(t, view)
|
||||
assert.NotNil(t, view.DeleteRequestedAt)
|
||||
}
|
||||
|
||||
func TestResolveDesktopClientOnline_PresenceMissWithKnownPresenceIsOffline(t *testing.T) {
|
||||
now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC)
|
||||
lastSeen := now.Add(-desktopClientPresenceTTL / 2)
|
||||
|
||||
@@ -68,8 +68,8 @@ type DesktopAccountRepository interface {
|
||||
Upsert(ctx context.Context, params UpsertDesktopAccountParams) (*DesktopAccount, error)
|
||||
Patch(ctx context.Context, params PatchDesktopAccountParams) (*DesktopAccount, error)
|
||||
Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID int64, clientID uuid.UUID, ifSyncVersion int64) (*DesktopAccount, error)
|
||||
MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
||||
ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
||||
MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*DesktopAccount, error)
|
||||
ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*DesktopAccount, error)
|
||||
}
|
||||
|
||||
type desktopAccountRepository struct {
|
||||
@@ -222,10 +222,11 @@ func (r *desktopAccountRepository) Tombstone(ctx context.Context, desktopID uuid
|
||||
return desktopAccountFromTombstoneRow(row)
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error) {
|
||||
func (r *desktopAccountRepository) MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*DesktopAccount, error) {
|
||||
row, err := r.q.MarkDesktopAccountDeleteRequested(ctx, generated.MarkDesktopAccountDeleteRequestedParams{
|
||||
DesktopID: pgUUID(desktopID),
|
||||
WorkspaceID: workspaceID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -233,10 +234,11 @@ func (r *desktopAccountRepository) MarkDeleteRequested(ctx context.Context, desk
|
||||
return desktopAccountFromMarkDeleteRow(row)
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error) {
|
||||
func (r *desktopAccountRepository) ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*DesktopAccount, error) {
|
||||
row, err := r.q.ClearDesktopAccountDeleteRequested(ctx, generated.ClearDesktopAccountDeleteRequestedParams{
|
||||
DesktopID: pgUUID(desktopID),
|
||||
WorkspaceID: workspaceID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -18,6 +18,7 @@ SET delete_requested_at = NULL,
|
||||
updated_at = now()
|
||||
WHERE desktop_id = $1
|
||||
AND workspace_id = $2
|
||||
AND user_id = $3
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
@@ -43,6 +44,7 @@ RETURNING
|
||||
type ClearDesktopAccountDeleteRequestedParams struct {
|
||||
DesktopID pgtype.UUID `json:"desktop_id"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
type ClearDesktopAccountDeleteRequestedRow struct {
|
||||
@@ -67,7 +69,7 @@ type ClearDesktopAccountDeleteRequestedRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) ClearDesktopAccountDeleteRequested(ctx context.Context, arg ClearDesktopAccountDeleteRequestedParams) (ClearDesktopAccountDeleteRequestedRow, error) {
|
||||
row := q.db.QueryRow(ctx, clearDesktopAccountDeleteRequested, arg.DesktopID, arg.WorkspaceID)
|
||||
row := q.db.QueryRow(ctx, clearDesktopAccountDeleteRequested, arg.DesktopID, arg.WorkspaceID, arg.UserID)
|
||||
var i ClearDesktopAccountDeleteRequestedRow
|
||||
err := row.Scan(
|
||||
&i.DesktopID,
|
||||
@@ -449,6 +451,7 @@ SET delete_requested_at = now(),
|
||||
updated_at = now()
|
||||
WHERE desktop_id = $1
|
||||
AND workspace_id = $2
|
||||
AND user_id = $3
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
@@ -474,6 +477,7 @@ RETURNING
|
||||
type MarkDesktopAccountDeleteRequestedParams struct {
|
||||
DesktopID pgtype.UUID `json:"desktop_id"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
type MarkDesktopAccountDeleteRequestedRow struct {
|
||||
@@ -498,7 +502,7 @@ type MarkDesktopAccountDeleteRequestedRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) MarkDesktopAccountDeleteRequested(ctx context.Context, arg MarkDesktopAccountDeleteRequestedParams) (MarkDesktopAccountDeleteRequestedRow, error) {
|
||||
row := q.db.QueryRow(ctx, markDesktopAccountDeleteRequested, arg.DesktopID, arg.WorkspaceID)
|
||||
row := q.db.QueryRow(ctx, markDesktopAccountDeleteRequested, arg.DesktopID, arg.WorkspaceID, arg.UserID)
|
||||
var i MarkDesktopAccountDeleteRequestedRow
|
||||
err := row.Scan(
|
||||
&i.DesktopID,
|
||||
|
||||
@@ -260,6 +260,7 @@ SET delete_requested_at = now(),
|
||||
updated_at = now()
|
||||
WHERE desktop_id = sqlc.arg(desktop_id)
|
||||
AND workspace_id = sqlc.arg(workspace_id)
|
||||
AND user_id = sqlc.arg(user_id)
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
@@ -288,6 +289,7 @@ SET delete_requested_at = NULL,
|
||||
updated_at = now()
|
||||
WHERE desktop_id = sqlc.arg(desktop_id)
|
||||
AND workspace_id = sqlc.arg(workspace_id)
|
||||
AND user_id = sqlc.arg(user_id)
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
|
||||
Reference in New Issue
Block a user