Browse Source

feat(syncing-server): remove owned shared vaults upon account deletion (#838)

Karol Sójko 1 year ago
parent
commit
22a8cc90f1

+ 15 - 4
packages/syncing-server/src/Bootstrap/Container.ts

@@ -165,6 +165,7 @@ import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersiste
 import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
 import { SendEventToClient } from '../Domain/UseCase/Syncing/SendEventToClient/SendEventToClient'
 import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
+import { DeleteSharedVaults } from '../Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults'
 
 export class ContainerConfigLoader {
   private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -780,10 +781,19 @@ export class ContainerConfigLoader {
       .bind<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault)
       .toConstantValue(
         new DeleteSharedVault(
-          container.get(TYPES.Sync_SharedVaultRepository),
-          container.get(TYPES.Sync_SharedVaultUserRepository),
-          container.get(TYPES.Sync_SharedVaultInviteRepository),
-          container.get(TYPES.Sync_RemoveSharedVaultUser),
+          container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
+          container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
+          container.get<SharedVaultInviteRepositoryInterface>(TYPES.Sync_SharedVaultInviteRepository),
+          container.get<RemoveUserFromSharedVault>(TYPES.Sync_RemoveSharedVaultUser),
+          container.get<DeclineInviteToSharedVault>(TYPES.Sync_DeclineInviteToSharedVault),
+        ),
+      )
+    container
+      .bind<DeleteSharedVaults>(TYPES.Sync_DeleteSharedVaults)
+      .toConstantValue(
+        new DeleteSharedVaults(
+          container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
+          container.get<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault),
         ),
       )
     container
@@ -902,6 +912,7 @@ export class ContainerConfigLoader {
       .toConstantValue(
         new AccountDeletionRequestedEventHandler(
           container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
+          container.get<DeleteSharedVaults>(TYPES.Sync_DeleteSharedVaults),
           container.get<Logger>(TYPES.Sync_Logger),
         ),
       )

+ 1 - 0
packages/syncing-server/src/Bootstrap/Types.ts

@@ -54,6 +54,7 @@ const TYPES = {
   Sync_GetSharedVaults: Symbol.for('Sync_GetSharedVaults'),
   Sync_CreateSharedVault: Symbol.for('Sync_CreateSharedVault'),
   Sync_DeleteSharedVault: Symbol.for('Sync_DeleteSharedVault'),
+  Sync_DeleteSharedVaults: Symbol.for('Sync_DeleteSharedVaults'),
   Sync_CreateSharedVaultFileValetToken: Symbol.for('Sync_CreateSharedVaultFileValetToken'),
   Sync_GetSharedVaultUsers: Symbol.for('Sync_GetSharedVaultUsers'),
   Sync_AddUserToSharedVault: Symbol.for('Sync_AddUserToSharedVault'),

+ 0 - 71
packages/syncing-server/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts

@@ -1,71 +0,0 @@
-import 'reflect-metadata'
-
-import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
-import { Logger } from 'winston'
-import { Item } from '../Item/Item'
-import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
-import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
-import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
-import { ItemRepositoryResolverInterface } from '../Item/ItemRepositoryResolverInterface'
-
-describe('AccountDeletionRequestedEventHandler', () => {
-  let itemRepositoryResolver: ItemRepositoryResolverInterface
-  let itemRepository: ItemRepositoryInterface
-  let logger: Logger
-  let event: AccountDeletionRequestedEvent
-  let item: Item
-
-  const createHandler = () => new AccountDeletionRequestedEventHandler(itemRepositoryResolver, logger)
-
-  beforeEach(() => {
-    item = Item.create(
-      {
-        userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        updatedWithSession: null,
-        content: 'foobar',
-        contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
-        encItemKey: null,
-        authHash: null,
-        itemsKeyId: null,
-        duplicateOf: null,
-        deleted: false,
-        dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
-        timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
-      },
-      new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
-    ).getValue()
-
-    itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
-    itemRepository.findAll = jest.fn().mockReturnValue([item])
-    itemRepository.deleteByUserUuid = jest.fn()
-
-    itemRepositoryResolver = {} as jest.Mocked<ItemRepositoryResolverInterface>
-    itemRepositoryResolver.resolve = jest.fn().mockReturnValue(itemRepository)
-
-    logger = {} as jest.Mocked<Logger>
-    logger.info = jest.fn()
-
-    event = {} as jest.Mocked<AccountDeletionRequestedEvent>
-    event.createdAt = new Date(1)
-    event.payload = {
-      userUuid: '2-3-4',
-      userCreatedAtTimestamp: 1,
-      regularSubscriptionUuid: '1-2-3',
-      roleNames: ['CORE_USER'],
-    }
-  })
-
-  it('should remove all items for a user', async () => {
-    await createHandler().handle(event)
-
-    expect(itemRepository.deleteByUserUuid).toHaveBeenCalledWith('2-3-4')
-  })
-
-  it('should do nothing if role names are not valid', async () => {
-    event.payload.roleNames = ['INVALID_ROLE_NAME']
-
-    await createHandler().handle(event)
-
-    expect(itemRepository.deleteByUserUuid).not.toHaveBeenCalled()
-  })
-})

+ 11 - 0
packages/syncing-server/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts

@@ -3,10 +3,12 @@ import { RoleNameCollection } from '@standardnotes/domain-core'
 import { Logger } from 'winston'
 
 import { ItemRepositoryResolverInterface } from '../Item/ItemRepositoryResolverInterface'
+import { DeleteSharedVaults } from '../UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults'
 
 export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
   constructor(
     private itemRepositoryResolver: ItemRepositoryResolverInterface,
+    private deleteSharedVaults: DeleteSharedVaults,
     private logger: Logger,
   ) {}
 
@@ -21,6 +23,15 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
 
     await itemRepository.deleteByUserUuid(event.payload.userUuid)
 
+    const result = await this.deleteSharedVaults.execute({
+      ownerUuid: event.payload.userUuid,
+    })
+    if (result.isFailed()) {
+      this.logger.error(`Failed to delete shared vaults for user: ${event.payload.userUuid}: ${result.getError()}`)
+
+      return
+    }
+
     this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`)
   }
 }

+ 1 - 0
packages/syncing-server/src/Domain/SharedVault/SharedVaultRepositoryInterface.ts

@@ -3,6 +3,7 @@ import { SharedVault } from './SharedVault'
 
 export interface SharedVaultRepositoryInterface {
   findByUuid(uuid: Uuid): Promise<SharedVault | null>
+  findByUserUuid(userUuid: Uuid): Promise<SharedVault[]>
   countByUserUuid(userUuid: Uuid): Promise<number>
   findByUuids(uuids: Uuid[], lastSyncTime?: number): Promise<SharedVault[]>
   save(sharedVault: SharedVault): Promise<void>

+ 1 - 1
packages/syncing-server/src/Domain/SharedVault/User/Invite/SharedVaultInviteRepositoryInterface.ts

@@ -6,7 +6,7 @@ export interface SharedVaultInviteRepositoryInterface {
   findByUuid(sharedVaultInviteUuid: Uuid): Promise<SharedVaultInvite | null>
   save(sharedVaultInvite: SharedVaultInvite): Promise<void>
   remove(sharedVaultInvite: SharedVaultInvite): Promise<void>
-  removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void>
+  findBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultInvite[]>
   findByUserUuid(userUuid: Uuid): Promise<SharedVaultInvite[]>
   findByUserUuidUpdatedAfter(userUuid: Uuid, updatedAtTimestamp: number): Promise<SharedVaultInvite[]>
   findBySenderUuid(senderUuid: Uuid): Promise<SharedVaultInvite[]>

+ 38 - 7
packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.spec.ts

@@ -6,14 +6,18 @@ import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/Sh
 import { DeleteSharedVault } from './DeleteSharedVault'
 import { SharedVault } from '../../../SharedVault/SharedVault'
 import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
+import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
+import { SharedVaultInvite } from '../../../SharedVault/User/Invite/SharedVaultInvite'
 
 describe('DeleteSharedVault', () => {
   let sharedVaultRepository: SharedVaultRepositoryInterface
   let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
   let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
   let removeUserFromSharedVault: RemoveUserFromSharedVault
+  let declineInviteToSharedVault: DeclineInviteToSharedVault
   let sharedVault: SharedVault
   let sharedVaultUser: SharedVaultUser
+  let sharedVaultInvite: SharedVaultInvite
 
   const createUseCase = () =>
     new DeleteSharedVault(
@@ -21,6 +25,7 @@ describe('DeleteSharedVault', () => {
       sharedVaultUserRepository,
       sharedVaultInviteRepository,
       removeUserFromSharedVault,
+      declineInviteToSharedVault,
     )
 
   beforeEach(() => {
@@ -42,8 +47,19 @@ describe('DeleteSharedVault', () => {
     sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
     sharedVaultUserRepository.findBySharedVaultUuid = jest.fn().mockResolvedValue([sharedVaultUser])
 
+    sharedVaultInvite = SharedVaultInvite.create({
+      encryptedMessage: 'test',
+      userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
+      timestamps: Timestamps.create(123, 123).getValue(),
+    }).getValue()
     sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
-    sharedVaultInviteRepository.removeBySharedVaultUuid = jest.fn()
+    sharedVaultInviteRepository.findBySharedVaultUuid = jest.fn().mockReturnValue([sharedVaultInvite])
+
+    declineInviteToSharedVault = {} as jest.Mocked<DeclineInviteToSharedVault>
+    declineInviteToSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
 
     removeUserFromSharedVault = {} as jest.Mocked<RemoveUserFromSharedVault>
     removeUserFromSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
@@ -59,7 +75,7 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeFalsy()
     expect(sharedVaultRepository.remove).toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
   })
 
@@ -74,7 +90,7 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeTruthy()
     expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).not.toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
   })
 
@@ -88,7 +104,7 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeTruthy()
     expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).not.toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
   })
 
@@ -102,7 +118,7 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeTruthy()
     expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).not.toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
   })
 
@@ -122,7 +138,7 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeTruthy()
     expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).not.toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
   })
 
@@ -137,7 +153,22 @@ describe('DeleteSharedVault', () => {
 
     expect(result.isFailed()).toBeTruthy()
     expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
-    expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).not.toHaveBeenCalled()
+    expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
+  })
+
+  it('should return error if declining invite to shared vault fails', async () => {
+    declineInviteToSharedVault.execute = jest.fn().mockReturnValue(Result.fail('failed'))
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
+      originatorUuid: '00000000-0000-0000-0000-000000000000',
+    })
+
+    expect(result.isFailed()).toBeTruthy()
+    expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
+    expect(declineInviteToSharedVault.execute).toHaveBeenCalled()
     expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
   })
 })

+ 13 - 1
packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.ts

@@ -5,6 +5,7 @@ import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVault
 import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
 import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
 import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
+import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
 
 export class DeleteSharedVault implements UseCaseInterface<void> {
   constructor(
@@ -12,6 +13,7 @@ export class DeleteSharedVault implements UseCaseInterface<void> {
     private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
     private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
     private removeUserFromSharedVault: RemoveUserFromSharedVault,
+    private declineInviteToSharedVault: DeclineInviteToSharedVault,
   ) {}
 
   async execute(dto: DeleteSharedVaultDTO): Promise<Result<void>> {
@@ -50,7 +52,17 @@ export class DeleteSharedVault implements UseCaseInterface<void> {
       }
     }
 
-    await this.sharedVaultInviteRepository.removeBySharedVaultUuid(sharedVaultUuid)
+    const sharedVaultInvites = await this.sharedVaultInviteRepository.findBySharedVaultUuid(sharedVaultUuid)
+    for (const sharedVaultInvite of sharedVaultInvites) {
+      const result = await this.declineInviteToSharedVault.execute({
+        inviteUuid: sharedVaultInvite.id.toString(),
+        userUuid: sharedVaultInvite.props.userUuid.value,
+      })
+
+      if (result.isFailed()) {
+        return Result.fail(result.getError())
+      }
+    }
 
     await this.sharedVaultRepository.remove(sharedVault)
 

+ 72 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults.spec.ts

@@ -0,0 +1,72 @@
+import { Result, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
+import { SharedVault } from '../../../SharedVault/SharedVault'
+import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
+import { DeleteSharedVault } from '../DeleteSharedVault/DeleteSharedVault'
+import { DeleteSharedVaults } from './DeleteSharedVaults'
+
+describe('DeleteSharedVaults', () => {
+  let sharedVaultRepository: SharedVaultRepositoryInterface
+  let deleteSharedVaultUseCase: DeleteSharedVault
+  let sharedVault: SharedVault
+
+  const createUseCase = () => new DeleteSharedVaults(sharedVaultRepository, deleteSharedVaultUseCase)
+
+  beforeEach(() => {
+    sharedVault = SharedVault.create(
+      {
+        fileUploadBytesUsed: 2,
+        userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+        timestamps: Timestamps.create(123, 123).getValue(),
+      },
+      new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
+    ).getValue()
+    sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
+    sharedVaultRepository.findByUserUuid = jest.fn().mockResolvedValue([sharedVault])
+
+    deleteSharedVaultUseCase = {} as jest.Mocked<DeleteSharedVault>
+    deleteSharedVaultUseCase.execute = jest.fn().mockResolvedValue(Result.ok())
+  })
+
+  it('should delete all shared vaults for a user', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      ownerUuid: '00000000-0000-0000-0000-000000000000',
+    })
+
+    expect(result.isFailed()).toBe(false)
+    expect(sharedVaultRepository.findByUserUuid).toHaveBeenCalled()
+    expect(deleteSharedVaultUseCase.execute).toHaveBeenCalledWith({
+      originatorUuid: '00000000-0000-0000-0000-000000000000',
+      sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
+    })
+  })
+
+  it('should return error if delete shared vault fails', async () => {
+    deleteSharedVaultUseCase.execute = jest.fn().mockResolvedValue(Result.fail('error'))
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      ownerUuid: '00000000-0000-0000-0000-000000000000',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultRepository.findByUserUuid).toHaveBeenCalled()
+    expect(deleteSharedVaultUseCase.execute).toHaveBeenCalledWith({
+      originatorUuid: '00000000-0000-0000-0000-000000000000',
+      sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
+    })
+  })
+
+  it('should return error if owner uuid is invalid', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      ownerUuid: 'invalid',
+    })
+
+    expect(result.isFailed()).toBeTruthy()
+    expect(sharedVaultRepository.findByUserUuid).not.toHaveBeenCalled()
+    expect(deleteSharedVaultUseCase.execute).not.toHaveBeenCalled()
+  })
+})

+ 34 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults.ts

@@ -0,0 +1,34 @@
+import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
+
+import { DeleteSharedVaultsDTO } from './DeleteSharedVaultsDTO'
+import { DeleteSharedVault } from '../DeleteSharedVault/DeleteSharedVault'
+import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
+
+export class DeleteSharedVaults implements UseCaseInterface<void> {
+  constructor(
+    private sharedVaultRepository: SharedVaultRepositoryInterface,
+    private deleteSharedVaultUseCase: DeleteSharedVault,
+  ) {}
+
+  async execute(dto: DeleteSharedVaultsDTO): Promise<Result<void>> {
+    const ownerUuidOrError = Uuid.create(dto.ownerUuid)
+    if (ownerUuidOrError.isFailed()) {
+      return Result.fail(ownerUuidOrError.getError())
+    }
+    const ownerUuid = ownerUuidOrError.getValue()
+
+    const sharedVaults = await this.sharedVaultRepository.findByUserUuid(ownerUuid)
+
+    for (const sharedVault of sharedVaults) {
+      const result = await this.deleteSharedVaultUseCase.execute({
+        originatorUuid: ownerUuid.value,
+        sharedVaultUuid: sharedVault.id.toString(),
+      })
+      if (result.isFailed()) {
+        return Result.fail(result.getError())
+      }
+    }
+
+    return Result.ok()
+  }
+}

+ 3 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaultsDTO.ts

@@ -0,0 +1,3 @@
+export interface DeleteSharedVaultsDTO {
+  ownerUuid: string
+}

+ 6 - 6
packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultInviteRepository.ts

@@ -64,13 +64,13 @@ export class TypeORMSharedVaultInviteRepository implements SharedVaultInviteRepo
     return persistence.map((p) => this.mapper.toDomain(p))
   }
 
-  async removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void> {
-    await this.ormRepository
+  async findBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultInvite[]> {
+    const persistence = await this.ormRepository
       .createQueryBuilder('shared_vault_invite')
-      .delete()
-      .from('shared_vault_invites')
-      .where('shared_vault_uuid = :sharedVaultUuid', { sharedVaultUuid: sharedVaultUuid.value })
-      .execute()
+      .where('shared_vault_invite.shared_vault_uuid = :sharedVaultUuid', { sharedVaultUuid: sharedVaultUuid.value })
+      .getMany()
+
+    return persistence.map((p) => this.mapper.toDomain(p))
   }
 
   async findByUserUuidAndSharedVaultUuid(dto: {

+ 11 - 0
packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultRepository.ts

@@ -11,6 +11,17 @@ export class TypeORMSharedVaultRepository implements SharedVaultRepositoryInterf
     private mapper: MapperInterface<SharedVault, TypeORMSharedVault>,
   ) {}
 
+  async findByUserUuid(userUuid: Uuid): Promise<SharedVault[]> {
+    const persistence = await this.ormRepository
+      .createQueryBuilder('shared_vault')
+      .where('shared_vault.user_uuid = :userUuid', {
+        userUuid: userUuid.value,
+      })
+      .getMany()
+
+    return persistence.map((p) => this.mapper.toDomain(p))
+  }
+
   async countByUserUuid(userUuid: Uuid): Promise<number> {
     const count = await this.ormRepository
       .createQueryBuilder('shared_vault')