feat: remove shared vault files upon shared vault removal (#852)
* feat: remove shared vault files upon shared vault removal * fix: link files queue with syncing-server-js topic
This commit is contained in:
parent
a58262d584
commit
7b1eec21e5
13 changed files with 74 additions and 173 deletions
|
@ -139,6 +139,11 @@ LINKING_RESULT=$(link_queue_and_topic $AUTH_TOPIC_ARN $FILES_QUEUE_ARN)
|
|||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
echo "linking topic $SYNCING_SERVER_TOPIC_ARN to queue $FILES_QUEUE_ARN"
|
||||
LINKING_RESULT=$(link_queue_and_topic $SYNCING_SERVER_TOPIC_ARN $FILES_QUEUE_ARN)
|
||||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
QUEUE_NAME="syncing-server-local-queue"
|
||||
|
||||
echo "creating queue $QUEUE_NAME"
|
||||
|
|
|
@ -3,8 +3,6 @@ module.exports = {
|
|||
testEnvironment: 'node',
|
||||
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$',
|
||||
testTimeout: 20000,
|
||||
coverageReporters: ['text'],
|
||||
reporters: ['summary'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 100,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export interface SharedVaultRemovedEventPayload {
|
||||
sharedVaultUuid: string
|
||||
vaultOwnerUuid: string
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
AccountDeletionRequestedEvent,
|
||||
AccountDeletionRequestedEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: AccountDeletionRequestedEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new AccountDeletionRequestedEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
} as jest.Mocked<AccountDeletionRequestedEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if user has no regular subscription', async () => {
|
||||
event.payload.regularSubscriptionUuid = undefined
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
|
@ -22,15 +22,17 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
|||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.userUuid,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.regularSubscriptionUuid,
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
SharedSubscriptionInvitationCanceledEvent,
|
||||
SharedSubscriptionInvitationCanceledEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { SharedSubscriptionInvitationCanceledEventHandler } from './SharedSubscriptionInvitationCanceledEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('SharedSubscriptionInvitationCanceledEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: SharedSubscriptionInvitationCanceledEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new SharedSubscriptionInvitationCanceledEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<SharedSubscriptionInvitationCanceledEvent>
|
||||
event.payload = {
|
||||
inviteeIdentifier: '1-2-3',
|
||||
inviteeIdentifierType: 'uuid',
|
||||
} as jest.Mocked<SharedSubscriptionInvitationCanceledEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if identifier is not of uuid type', async () => {
|
||||
event.payload.inviteeIdentifierType = 'email'
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
|
@ -22,15 +22,17 @@ export class SharedSubscriptionInvitationCanceledEventHandler implements DomainE
|
|||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.inviteeIdentifier,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.inviterSubscriptionUuid,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
SharedVaultRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class SharedVaultRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SharedVaultRemovedEvent): Promise<void> {
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(
|
||||
`Could not mark files to be removed for shared vault: ${event.payload.sharedVaultUuid}: ${result.getError()}`,
|
||||
)
|
||||
}
|
||||
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSharedVaultFileRemovedEvent({
|
||||
fileByteSize: fileRemoved.fileByteSize,
|
||||
fileName: fileRemoved.fileName,
|
||||
filePath: fileRemoved.filePath,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
vaultOwnerUuid: event.payload.vaultOwnerUuid,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,9 @@ describe('MarkFilesToBeRemoved', () => {
|
|||
})
|
||||
|
||||
it('should mark files for being removed', async () => {
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({ success: true })
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(result.isFailed()).toEqual(false)
|
||||
|
||||
expect(fileRemover.markFilesToBeRemoved).toHaveBeenCalledWith('1-2-3')
|
||||
})
|
||||
|
@ -31,9 +33,7 @@ describe('MarkFilesToBeRemoved', () => {
|
|||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
})
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
expect(result.isFailed()).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,36 +1,30 @@
|
|||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { FileRemoverInterface } from '../../Services/FileRemoverInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { MarkFilesToBeRemovedDTO } from './MarkFilesToBeRemovedDTO'
|
||||
import { MarkFilesToBeRemovedResponse } from './MarkFilesToBeRemovedResponse'
|
||||
import { RemovedFileDescription } from '../../File/RemovedFileDescription'
|
||||
|
||||
@injectable()
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface {
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface<RemovedFileDescription[]> {
|
||||
constructor(
|
||||
@inject(TYPES.Files_FileRemover) private fileRemover: FileRemoverInterface,
|
||||
@inject(TYPES.Files_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<MarkFilesToBeRemovedResponse> {
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<Result<RemovedFileDescription[]>> {
|
||||
try {
|
||||
this.logger.debug(`Marking files for later removal for user: ${dto.ownerUuid}`)
|
||||
|
||||
const filesRemoved = await this.fileRemover.markFilesToBeRemoved(dto.ownerUuid)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
filesRemoved,
|
||||
}
|
||||
return Result.ok(filesRemoved)
|
||||
} catch (error) {
|
||||
this.logger.error(`Could not mark resources for removal: ${dto.ownerUuid} - ${(error as Error).message}`)
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
}
|
||||
return Result.fail('Could not mark resources for removal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
|||
}
|
||||
}
|
||||
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string }): SharedVaultRemovedEvent {
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string; vaultOwnerUuid: string }): SharedVaultRemovedEvent {
|
||||
return {
|
||||
type: 'SHARED_VAULT_REMOVED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
|
|
|
@ -102,7 +102,7 @@ export interface DomainEventFactoryInterface {
|
|||
itemUuid: string
|
||||
userUuid: string
|
||||
}): ItemRemovedFromSharedVaultEvent
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string }): SharedVaultRemovedEvent
|
||||
createSharedVaultRemovedEvent(dto: { sharedVaultUuid: string; vaultOwnerUuid: string }): SharedVaultRemovedEvent
|
||||
createUserDesignatedAsSurvivorInSharedVaultEvent(dto: {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
|
|
|
@ -101,6 +101,7 @@ export class DeleteSharedVault implements UseCaseInterface<void> {
|
|||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSharedVaultRemovedEvent({
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
vaultOwnerUuid: sharedVault.props.userUuid.value,
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue