Browse Source

feat: update shared vault invite. (#644)

Co-authored-by: Mo <mo@standardnotes.com>
Karol Sójko 1 year ago
parent
commit
912a29d091

+ 141 - 0
packages/syncing-server/src/Domain/UseCase/UpdateSharedVaultInvite/UpdateSharedVaultInvite.spec.ts

@@ -0,0 +1,141 @@
+import { TimerInterface } from '@standardnotes/time'
+import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
+import { UpdateSharedVaultInvite } from './UpdateSharedVaultInvite'
+import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
+import { Timestamps, Uuid } from '@standardnotes/domain-core'
+import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
+
+describe('UpdateSharedVaultInvite', () => {
+  let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
+  let timer: TimerInterface
+  let invite: SharedVaultInvite
+
+  const createUseCase = () => new UpdateSharedVaultInvite(sharedVaultInviteRepository, timer)
+
+  beforeEach(() => {
+    invite = SharedVaultInvite.create({
+      sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
+      encryptedMessage: 'encrypted message',
+      permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
+      timestamps: Timestamps.create(123, 123).getValue(),
+    }).getValue()
+
+    sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
+    sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(invite)
+    sharedVaultInviteRepository.save = jest.fn().mockResolvedValue(null)
+
+    timer = {} as jest.Mocked<TimerInterface>
+    timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
+  })
+
+  it('should update the invite', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: 'new encrypted message',
+    })
+
+    expect(result.isFailed()).toBe(false)
+    expect(sharedVaultInviteRepository.save).toHaveBeenCalled()
+  })
+
+  it('should update the invite with new permission', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: 'new encrypted message',
+      permission: SharedVaultUserPermission.PERMISSIONS.Write,
+    })
+
+    expect(result.isFailed()).toBe(false)
+    expect(sharedVaultInviteRepository.save).toHaveBeenCalled()
+  })
+
+  it('should fail if invite is not found', async () => {
+    sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(null)
+
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: 'new encrypted message',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+
+  it('should fail if sender is not the same as the invite sender', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000001',
+      encryptedMessage: 'new encrypted message',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(result.getError()).toBe('Only the sender can update the invite')
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+
+  it('should fail if the invite uuid is not valid', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: 'invalid-uuid',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: 'new encrypted message',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+
+  it('should fail if the sender uuid is not valid', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: 'invalid-uuid',
+      encryptedMessage: 'new encrypted message',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+
+  it('should fail if the encrypted message is not valid', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: '',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+
+  it('should fail if the permission is not valid', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      inviteUuid: '00000000-0000-0000-0000-000000000000',
+      senderUuid: '00000000-0000-0000-0000-000000000000',
+      encryptedMessage: 'new encrypted message',
+      permission: 'invalid-permission',
+    })
+
+    expect(result.isFailed()).toBe(true)
+    expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
+  })
+})

+ 62 - 0
packages/syncing-server/src/Domain/UseCase/UpdateSharedVaultInvite/UpdateSharedVaultInvite.ts

@@ -0,0 +1,62 @@
+import { Result, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
+
+import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
+import { UpdateSharedVaultInviteDTO } from './UpdateSharedVaultInviteDTO'
+import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
+import { TimerInterface } from '@standardnotes/time'
+
+export class UpdateSharedVaultInvite implements UseCaseInterface<void> {
+  constructor(
+    private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
+    private timer: TimerInterface,
+  ) {}
+
+  async execute(dto: UpdateSharedVaultInviteDTO): Promise<Result<void>> {
+    const inviteUuidOrError = Uuid.create(dto.inviteUuid)
+    if (inviteUuidOrError.isFailed()) {
+      return Result.fail(inviteUuidOrError.getError())
+    }
+    const inviteUuid = inviteUuidOrError.getValue()
+
+    const senderUuidOrError = Uuid.create(dto.senderUuid)
+    if (senderUuidOrError.isFailed()) {
+      return Result.fail(senderUuidOrError.getError())
+    }
+    const senderUuid = senderUuidOrError.getValue()
+
+    const emptyMessageValidation = Validator.isNotEmpty(dto.encryptedMessage)
+    if (emptyMessageValidation.isFailed()) {
+      return Result.fail(emptyMessageValidation.getError())
+    }
+
+    const invite = await this.sharedVaultInviteRepository.findByUuid(inviteUuid)
+    if (!invite) {
+      return Result.fail('Invite not found')
+    }
+
+    if (!invite.props.senderUuid.equals(senderUuid)) {
+      return Result.fail('Only the sender can update the invite')
+    }
+
+    invite.props.encryptedMessage = dto.encryptedMessage
+
+    if (dto.permission !== undefined) {
+      const permissionOrError = SharedVaultUserPermission.create(dto.permission)
+      if (permissionOrError.isFailed()) {
+        return Result.fail(permissionOrError.getError())
+      }
+      const permission = permissionOrError.getValue()
+
+      invite.props.permission = permission
+    }
+
+    invite.props.timestamps = Timestamps.create(
+      invite.props.timestamps.createdAt,
+      this.timer.getTimestampInMicroseconds(),
+    ).getValue()
+
+    await this.sharedVaultInviteRepository.save(invite)
+
+    return Result.ok()
+  }
+}

+ 6 - 0
packages/syncing-server/src/Domain/UseCase/UpdateSharedVaultInvite/UpdateSharedVaultInviteDTO.ts

@@ -0,0 +1,6 @@
+export interface UpdateSharedVaultInviteDTO {
+  encryptedMessage: string
+  inviteUuid: string
+  senderUuid: string
+  permission?: string
+}