From d2b2c339f2089ea5d538ee40118af083983be5ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Wed, 5 Jul 2023 11:01:21 +0200 Subject: [PATCH] feat: add getting shared vaults for a user (#639) --- .../SharedVaultRepositoryInterface.ts | 1 + .../SharedVaultUserRepositoryInterface.ts | 1 + .../GetSharedVaults/GetSharedVaults.spec.ts | 68 +++++++++++++++++++ .../GetSharedVaults/GetSharedVaults.ts | 35 ++++++++++ .../GetSharedVaults/GetSharedVaultsDTO.ts | 4 ++ .../TypeORM/TypeORMSharedVaultRepository.ts | 14 ++++ .../TypeORMSharedVaultUserRepository.ts | 11 +++ 7 files changed, 134 insertions(+) create mode 100644 packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.spec.ts create mode 100644 packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.ts create mode 100644 packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaultsDTO.ts diff --git a/packages/syncing-server/src/Domain/SharedVault/SharedVaultRepositoryInterface.ts b/packages/syncing-server/src/Domain/SharedVault/SharedVaultRepositoryInterface.ts index 8abf4f270..273fb95ce 100644 --- a/packages/syncing-server/src/Domain/SharedVault/SharedVaultRepositoryInterface.ts +++ b/packages/syncing-server/src/Domain/SharedVault/SharedVaultRepositoryInterface.ts @@ -3,6 +3,7 @@ import { SharedVault } from './SharedVault' export interface SharedVaultRepositoryInterface { findByUuid(uuid: Uuid): Promise + findByUuids(uuids: Uuid[], lastSyncTime?: number): Promise save(sharedVault: SharedVault): Promise remove(sharedVault: SharedVault): Promise } diff --git a/packages/syncing-server/src/Domain/SharedVault/User/SharedVaultUserRepositoryInterface.ts b/packages/syncing-server/src/Domain/SharedVault/User/SharedVaultUserRepositoryInterface.ts index be85a8460..332069fcf 100644 --- a/packages/syncing-server/src/Domain/SharedVault/User/SharedVaultUserRepositoryInterface.ts +++ b/packages/syncing-server/src/Domain/SharedVault/User/SharedVaultUserRepositoryInterface.ts @@ -4,6 +4,7 @@ import { SharedVaultUser } from './SharedVaultUser' export interface SharedVaultUserRepositoryInterface { findByUuid(sharedVaultUserUuid: Uuid): Promise + findByUserUuid(userUuid: Uuid): Promise save(sharedVaultUser: SharedVaultUser): Promise remove(sharedVault: SharedVaultUser): Promise findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise diff --git a/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.spec.ts b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.spec.ts new file mode 100644 index 000000000..b07636eb1 --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.spec.ts @@ -0,0 +1,68 @@ +import { Timestamps, Uuid } from '@standardnotes/domain-core' +import { SharedVault } from '../../SharedVault/SharedVault' +import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface' +import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser' +import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission' +import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface' +import { GetSharedVaults } from './GetSharedVaults' + +describe('GetSharedVaults', () => { + let sharedVaultUserRepository: SharedVaultUserRepositoryInterface + let sharedVaultRepository: SharedVaultRepositoryInterface + let sharedVault: SharedVault + let sharedVaultUser: SharedVaultUser + + const createUseCase = () => new GetSharedVaults(sharedVaultUserRepository, sharedVaultRepository) + + beforeEach(() => { + sharedVaultUser = SharedVaultUser.create({ + userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), + sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), + permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Admin).getValue(), + timestamps: Timestamps.create(123, 123).getValue(), + }).getValue() + sharedVaultUserRepository = {} as jest.Mocked + sharedVaultUserRepository.findByUserUuid = jest.fn().mockResolvedValue([sharedVaultUser]) + + sharedVault = SharedVault.create({ + userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), + timestamps: Timestamps.create(123, 123).getValue(), + fileUploadBytesLimit: 123, + fileUploadBytesUsed: 123, + }).getValue() + sharedVaultRepository = {} as jest.Mocked + sharedVaultRepository.findByUuids = jest.fn().mockResolvedValue([sharedVault]) + }) + + it('returns shared vaults', async () => { + const useCase = createUseCase() + + const result = await useCase.execute({ + userUuid: '00000000-0000-0000-0000-000000000000', + }) + + expect(result.getValue()).toEqual([sharedVault]) + }) + + it('returns empty array if no shared vaults found', async () => { + sharedVaultUserRepository.findByUserUuid = jest.fn().mockResolvedValue([]) + + const useCase = createUseCase() + + const result = await useCase.execute({ + userUuid: '00000000-0000-0000-0000-000000000000', + }) + + expect(result.getValue()).toEqual([]) + }) + + it('returns error if user uuid is invalid', async () => { + const useCase = createUseCase() + + const result = await useCase.execute({ + userUuid: 'invalid', + }) + + expect(result.isFailed()).toBeTruthy() + }) +}) diff --git a/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.ts b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.ts new file mode 100644 index 000000000..118a9faf1 --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaults.ts @@ -0,0 +1,35 @@ +import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core' + +import { SharedVault } from '../../SharedVault/SharedVault' +import { GetSharedVaultsDTO } from './GetSharedVaultsDTO' +import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface' +import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface' + +export class GetSharedVaults implements UseCaseInterface { + constructor( + private sharedVaultUserRepository: SharedVaultUserRepositoryInterface, + private sharedVaultRepository: SharedVaultRepositoryInterface, + ) {} + + async execute(dto: GetSharedVaultsDTO): Promise> { + const userUuidOrError = Uuid.create(dto.userUuid) + if (userUuidOrError.isFailed()) { + return Result.fail(userUuidOrError.getError()) + } + const userUuid = userUuidOrError.getValue() + + const ownedSharedVaultsAssociations = await this.sharedVaultUserRepository.findByUserUuid(userUuid) + + const sharedVaultUuids = ownedSharedVaultsAssociations.map( + (sharedVaultUser) => sharedVaultUser.props.sharedVaultUuid, + ) + + if (sharedVaultUuids.length === 0) { + return Result.ok([]) + } + + const sharedVaults = await this.sharedVaultRepository.findByUuids(sharedVaultUuids, dto.lastSyncTime) + + return Result.ok(sharedVaults) + } +} diff --git a/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaultsDTO.ts b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaultsDTO.ts new file mode 100644 index 000000000..78c61f1ca --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/GetSharedVaults/GetSharedVaultsDTO.ts @@ -0,0 +1,4 @@ +export interface GetSharedVaultsDTO { + userUuid: string + lastSyncTime?: number +} diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultRepository.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultRepository.ts index 3e9602138..57fdee238 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultRepository.ts +++ b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultRepository.ts @@ -11,6 +11,20 @@ export class TypeORMSharedVaultRepository implements SharedVaultRepositoryInterf private mapper: MapperInterface, ) {} + async findByUuids(uuids: Uuid[], lastSyncTime?: number | undefined): Promise { + const queryBuilder = await this.ormRepository + .createQueryBuilder('shared_vault') + .where('shared_vault.uuid IN (:...sharedVaultUuids)', { sharedVaultUuids: uuids.map((uuid) => uuid.value) }) + + if (lastSyncTime !== undefined) { + queryBuilder.andWhere('shared_vault.updated_at_timestamp > :lastSyncTime', { lastSyncTime }) + } + + const persistence = await queryBuilder.getMany() + + return persistence.map((p) => this.mapper.toDomain(p)) + } + async save(sharedVault: SharedVault): Promise { const persistence = this.mapper.toProjection(sharedVault) diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultUserRepository.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultUserRepository.ts index 7f1c56c06..8a5eaacea 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultUserRepository.ts +++ b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultUserRepository.ts @@ -11,6 +11,17 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito private mapper: MapperInterface, ) {} + async findByUserUuid(userUuid: Uuid): Promise { + const persistence = await this.ormRepository + .createQueryBuilder('shared_vault_user') + .where('shared_vault_user.user_uuid = :userUuid', { + userUuid: userUuid.value, + }) + .getMany() + + return persistence.map((p) => this.mapper.toDomain(p)) + } + async findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid sharedVaultUuid: Uuid