fix(syncing-server): cleanup unused events
This commit is contained in:
parent
79f5b54228
commit
f504a8288c
13 changed files with 0 additions and 548 deletions
|
@ -1,7 +0,0 @@
|
|||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { EmailArchiveExtensionSyncedEventPayload } from './EmailArchiveExtensionSyncedEventPayload'
|
||||
|
||||
export interface EmailArchiveExtensionSyncedEvent extends DomainEventInterface {
|
||||
type: 'EMAIL_ARCHIVE_EXTENSION_SYNCED'
|
||||
payload: EmailArchiveExtensionSyncedEventPayload
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export interface EmailArchiveExtensionSyncedEventPayload {
|
||||
userUuid: string
|
||||
extensionId: string
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { ItemsSyncedEventPayload } from './ItemsSyncedEventPayload'
|
||||
|
||||
export interface ItemsSyncedEvent extends DomainEventInterface {
|
||||
type: 'ITEMS_SYNCED'
|
||||
payload: ItemsSyncedEventPayload
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
export interface ItemsSyncedEventPayload {
|
||||
userUuid: string
|
||||
extensionUrl: string
|
||||
extensionId: string
|
||||
itemUuids: Array<string>
|
||||
forceMute: boolean
|
||||
skipFileBackup: boolean
|
||||
source: 'backup' | 'account-deletion' | 'realtime-extensions-sync' | 'daily-extensions-sync'
|
||||
}
|
|
@ -10,8 +10,6 @@ export * from './Event/DomainEventInterface'
|
|||
export * from './Event/DomainEventService'
|
||||
export * from './Event/DuplicateItemSyncedEvent'
|
||||
export * from './Event/DuplicateItemSyncedEventPayload'
|
||||
export * from './Event/EmailArchiveExtensionSyncedEvent'
|
||||
export * from './Event/EmailArchiveExtensionSyncedEventPayload'
|
||||
export * from './Event/EmailBackupRequestedEvent'
|
||||
export * from './Event/EmailBackupRequestedEventPayload'
|
||||
export * from './Event/EmailRequestedEvent'
|
||||
|
@ -34,8 +32,6 @@ export * from './Event/ItemDumpedEvent'
|
|||
export * from './Event/ItemDumpedEventPayload'
|
||||
export * from './Event/ItemRevisionCreationRequestedEvent'
|
||||
export * from './Event/ItemRevisionCreationRequestedEventPayload'
|
||||
export * from './Event/ItemsSyncedEvent'
|
||||
export * from './Event/ItemsSyncedEventPayload'
|
||||
export * from './Event/ListedAccountCreatedEvent'
|
||||
export * from './Event/ListedAccountCreatedEventPayload'
|
||||
export * from './Event/ListedAccountDeletedEvent'
|
||||
|
|
|
@ -32,8 +32,6 @@ import { ExtensionsHttpService } from '../Domain/Extension/ExtensionsHttpService
|
|||
import { ItemBackupServiceInterface } from '../Domain/Item/ItemBackupServiceInterface'
|
||||
import { S3ItemBackupService } from '../Infra/S3/S3ItemBackupService'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { ItemsSyncedEventHandler } from '../Domain/Handler/ItemsSyncedEventHandler'
|
||||
import { EmailArchiveExtensionSyncedEventHandler } from '../Domain/Handler/EmailArchiveExtensionSyncedEventHandler'
|
||||
import { RevisionServiceInterface } from '../Domain/Revision/RevisionServiceInterface'
|
||||
import { RevisionService } from '../Domain/Revision/RevisionService'
|
||||
import { DuplicateItemSyncedEventHandler } from '../Domain/Handler/DuplicateItemSyncedEventHandler'
|
||||
|
@ -233,10 +231,6 @@ export class ContainerConfigLoader {
|
|||
container.bind<GetItem>(TYPES.GetItem).to(GetItem)
|
||||
|
||||
// Handlers
|
||||
container.bind<ItemsSyncedEventHandler>(TYPES.ItemsSyncedEventHandler).to(ItemsSyncedEventHandler)
|
||||
container
|
||||
.bind<EmailArchiveExtensionSyncedEventHandler>(TYPES.EmailArchiveExtensionSyncedEventHandler)
|
||||
.to(EmailArchiveExtensionSyncedEventHandler)
|
||||
container
|
||||
.bind<DuplicateItemSyncedEventHandler>(TYPES.DuplicateItemSyncedEventHandler)
|
||||
.to(DuplicateItemSyncedEventHandler)
|
||||
|
@ -296,8 +290,6 @@ export class ContainerConfigLoader {
|
|||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['DUPLICATE_ITEM_SYNCED', container.get(TYPES.DuplicateItemSyncedEventHandler)],
|
||||
['ITEMS_SYNCED', container.get(TYPES.ItemsSyncedEventHandler)],
|
||||
['EMAIL_ARCHIVE_EXTENSION_SYNCED', container.get(TYPES.EmailArchiveExtensionSyncedEventHandler)],
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)],
|
||||
['EMAIL_BACKUP_REQUESTED', container.get(TYPES.EmailBackupRequestedEventHandler)],
|
||||
['CLOUD_BACKUP_REQUESTED', container.get(TYPES.CloudBackupRequestedEventHandler)],
|
||||
|
|
|
@ -45,8 +45,6 @@ const TYPES = {
|
|||
// Handlers
|
||||
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),
|
||||
DuplicateItemSyncedEventHandler: Symbol.for('DuplicateItemSyncedEventHandler'),
|
||||
ItemsSyncedEventHandler: Symbol.for('ItemsSyncedEventHandler'),
|
||||
EmailArchiveExtensionSyncedEventHandler: Symbol.for('EmailArchiveExtensionSyncedEventHandler'),
|
||||
EmailBackupRequestedEventHandler: Symbol.for('EmailBackupRequestedEventHandler'),
|
||||
CloudBackupRequestedEventHandler: Symbol.for('CloudBackupRequestedEventHandler'),
|
||||
UserContentSizeRecalculationRequestedEventHandler: Symbol.for('UserContentSizeRecalculationRequestedEventHandler'),
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
import {
|
||||
DomainEventService,
|
||||
DuplicateItemSyncedEvent,
|
||||
EmailArchiveExtensionSyncedEvent,
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
ItemsSyncedEvent,
|
||||
RevisionsCopyRequestedEvent,
|
||||
RevisionsOwnershipUpdateRequestedEvent,
|
||||
UserContentSizeRecalculationRequestedEvent,
|
||||
|
@ -155,45 +153,4 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
|||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createItemsSyncedEvent(dto: {
|
||||
userUuid: string
|
||||
extensionUrl: string
|
||||
extensionId: string
|
||||
itemUuids: Array<string>
|
||||
forceMute: boolean
|
||||
skipFileBackup: boolean
|
||||
source: 'account-deletion' | 'realtime-extensions-sync'
|
||||
}): ItemsSyncedEvent {
|
||||
return {
|
||||
type: 'ITEMS_SYNCED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createEmailArchiveExtensionSyncedEvent(userUuid: string, extensionId: string): EmailArchiveExtensionSyncedEvent {
|
||||
return {
|
||||
type: 'EMAIL_ARCHIVE_EXTENSION_SYNCED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: {
|
||||
userUuid,
|
||||
extensionId,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import {
|
||||
DuplicateItemSyncedEvent,
|
||||
EmailArchiveExtensionSyncedEvent,
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
ItemsSyncedEvent,
|
||||
RevisionsCopyRequestedEvent,
|
||||
RevisionsOwnershipUpdateRequestedEvent,
|
||||
UserContentSizeRecalculationRequestedEvent,
|
||||
|
@ -26,16 +24,6 @@ export interface DomainEventFactoryInterface {
|
|||
attachmentContentType: string
|
||||
}>
|
||||
}): EmailRequestedEvent
|
||||
createItemsSyncedEvent(dto: {
|
||||
userUuid: string
|
||||
extensionUrl: string
|
||||
extensionId: string
|
||||
itemUuids: Array<string>
|
||||
forceMute: boolean
|
||||
skipFileBackup: boolean
|
||||
source: 'account-deletion' | 'realtime-extensions-sync'
|
||||
}): ItemsSyncedEvent
|
||||
createEmailArchiveExtensionSyncedEvent(userUuid: string, extensionId: string): EmailArchiveExtensionSyncedEvent
|
||||
createDuplicateItemSyncedEvent(itemUuid: string, userUuid: string): DuplicateItemSyncedEvent
|
||||
createItemRevisionCreationRequested(itemUuid: string, userUuid: string): ItemRevisionCreationRequestedEvent
|
||||
createItemDumpedEvent(fileDumpPath: string, userUuid: string): ItemDumpedEvent
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
DomainEventPublisherInterface,
|
||||
EmailArchiveExtensionSyncedEvent,
|
||||
EmailRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { EmailArchiveExtensionSyncedEventHandler } from './EmailArchiveExtensionSyncedEventHandler'
|
||||
import { ItemTransferCalculatorInterface } from '../Item/ItemTransferCalculatorInterface'
|
||||
|
||||
describe('EmailArchiveExtensionSyncedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
let authHttpService: AuthHttpServiceInterface
|
||||
let itemBackupService: ItemBackupServiceInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
const emailAttachmentMaxByteSize = 100
|
||||
let itemTransferCalculator: ItemTransferCalculatorInterface
|
||||
let item: Item
|
||||
let event: EmailArchiveExtensionSyncedEvent
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new EmailArchiveExtensionSyncedEventHandler(
|
||||
itemRepository,
|
||||
authHttpService,
|
||||
itemBackupService,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
emailAttachmentMaxByteSize,
|
||||
itemTransferCalculator,
|
||||
's3-backup-bucket-name',
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
item = {} as jest.Mocked<Item>
|
||||
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findAll = jest.fn().mockReturnValue([item])
|
||||
|
||||
authHttpService = {} as jest.Mocked<AuthHttpServiceInterface>
|
||||
authHttpService.getUserKeyParams = jest.fn().mockReturnValue({ identifier: 'test@test.com' })
|
||||
authHttpService.getUserSetting = jest.fn().mockReturnValue({ uuid: '3-4-5', value: 'not_muted' })
|
||||
|
||||
event = {} as jest.Mocked<EmailArchiveExtensionSyncedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
extensionId: '2-3-4',
|
||||
}
|
||||
|
||||
itemBackupService = {} as jest.Mocked<ItemBackupServiceInterface>
|
||||
itemBackupService.backup = jest.fn().mockReturnValue('backup-file-name')
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createEmailRequestedEvent = jest.fn().mockReturnValue({} as jest.Mocked<EmailRequestedEvent>)
|
||||
|
||||
itemTransferCalculator = {} as jest.Mocked<ItemTransferCalculatorInterface>
|
||||
itemTransferCalculator.computeItemUuidBundlesToFetch = jest.fn().mockReturnValue([['1-2-3']])
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
})
|
||||
|
||||
it('should inform that backup attachment for email was created', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should inform that multipart backup attachment for email was created', async () => {
|
||||
itemBackupService.backup = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce('backup-file-name-1')
|
||||
.mockReturnValueOnce('backup-file-name-2')
|
||||
itemTransferCalculator.computeItemUuidBundlesToFetch = jest.fn().mockReturnValue([['1-2-3'], ['2-3-4']])
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(2)
|
||||
expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should not inform that backup attachment for email was created if user key params cannot be obtained', async () => {
|
||||
authHttpService.getUserKeyParams = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops!')
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailRequestedEvent).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not inform that backup attachment for email was created if backup file name is empty', async () => {
|
||||
itemBackupService.backup = jest.fn().mockReturnValue('')
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailRequestedEvent).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
|
@ -1,93 +0,0 @@
|
|||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
EmailArchiveExtensionSyncedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ItemQuery } from '../Item/ItemQuery'
|
||||
import { ItemTransferCalculatorInterface } from '../Item/ItemTransferCalculatorInterface'
|
||||
import { getBody, getSubject } from '../Email/EmailBackupAttachmentCreated'
|
||||
|
||||
@injectable()
|
||||
export class EmailArchiveExtensionSyncedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.ItemRepository) private itemRepository: ItemRepositoryInterface,
|
||||
@inject(TYPES.AuthHttpService) private authHttpService: AuthHttpServiceInterface,
|
||||
@inject(TYPES.ItemBackupService) private itemBackupService: ItemBackupServiceInterface,
|
||||
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
@inject(TYPES.EMAIL_ATTACHMENT_MAX_BYTE_SIZE) private emailAttachmentMaxByteSize: number,
|
||||
@inject(TYPES.ItemTransferCalculator) private itemTransferCalculator: ItemTransferCalculatorInterface,
|
||||
@inject(TYPES.S3_BACKUP_BUCKET_NAME) private s3BackupBucketName: string,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: EmailArchiveExtensionSyncedEvent): Promise<void> {
|
||||
let authParams: KeyParamsData
|
||||
try {
|
||||
authParams = await this.authHttpService.getUserKeyParams({
|
||||
uuid: event.payload.userUuid,
|
||||
authenticated: false,
|
||||
})
|
||||
} catch (error) {
|
||||
this.logger.warn(`Could not get user key params from auth service: ${(error as Error).message}`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const itemQuery: ItemQuery = {
|
||||
userUuid: event.payload.userUuid,
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
deleted: false,
|
||||
}
|
||||
const itemUuidBundles = await this.itemTransferCalculator.computeItemUuidBundlesToFetch(
|
||||
itemQuery,
|
||||
this.emailAttachmentMaxByteSize,
|
||||
)
|
||||
|
||||
let bundleIndex = 1
|
||||
for (const itemUuidBundle of itemUuidBundles) {
|
||||
const items = await this.itemRepository.findAll({
|
||||
uuids: itemUuidBundle,
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
|
||||
const backupFileName = await this.itemBackupService.backup(items, authParams)
|
||||
|
||||
this.logger.debug(`Data backed up into: ${backupFileName}`)
|
||||
|
||||
if (backupFileName.length !== 0) {
|
||||
const dateOnly = new Date().toISOString().substring(0, 10)
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailRequestedEvent({
|
||||
body: getBody(authParams.identifier as string),
|
||||
level: EmailLevel.LEVELS.System,
|
||||
messageIdentifier: 'DATA_BACKUP',
|
||||
subject: getSubject(bundleIndex++, itemUuidBundles.length, dateOnly),
|
||||
userEmail: authParams.identifier as string,
|
||||
sender: 'backups@standardnotes.org',
|
||||
attachments: [
|
||||
{
|
||||
fileName: backupFileName,
|
||||
filePath: this.s3BackupBucketName,
|
||||
attachmentFileName: `SN-Data-${dateOnly}.txt`,
|
||||
attachmentContentType: 'application/json',
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import { ItemsSyncedEvent } from '@standardnotes/domain-events'
|
||||
import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ItemsSyncedEventHandler } from './ItemsSyncedEventHandler'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { ExtensionsHttpServiceInterface } from '../Extension/ExtensionsHttpServiceInterface'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('ItemsSyncedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
let authHttpService: AuthHttpServiceInterface
|
||||
let extensionsHttpService: ExtensionsHttpServiceInterface
|
||||
let itemBackupService: ItemBackupServiceInterface
|
||||
let internalDNSRerouteEnabled = false
|
||||
const extensionsServerUrl = 'https://extensions-server'
|
||||
let event: ItemsSyncedEvent
|
||||
let item: Item
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new ItemsSyncedEventHandler(
|
||||
itemRepository,
|
||||
authHttpService,
|
||||
extensionsHttpService,
|
||||
itemBackupService,
|
||||
internalDNSRerouteEnabled,
|
||||
extensionsServerUrl,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
item = {} as jest.Mocked<Item>
|
||||
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findAll = jest.fn().mockReturnValue([item])
|
||||
|
||||
authHttpService = {} as jest.Mocked<AuthHttpServiceInterface>
|
||||
authHttpService.getUserKeyParams = jest.fn().mockReturnValue({ foo: 'bar' })
|
||||
|
||||
extensionsHttpService = {} as jest.Mocked<ExtensionsHttpServiceInterface>
|
||||
extensionsHttpService.sendItemsToExtensionsServer = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<ItemsSyncedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
extensionId: '2-3-4',
|
||||
extensionUrl: 'https://extensions-server/extension1',
|
||||
forceMute: false,
|
||||
itemUuids: ['4-5-6'],
|
||||
skipFileBackup: false,
|
||||
source: 'realtime-extensions-sync',
|
||||
}
|
||||
|
||||
itemBackupService = {} as jest.Mocked<ItemBackupServiceInterface>
|
||||
itemBackupService.backup = jest.fn().mockReturnValue('backup-file-name')
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
})
|
||||
|
||||
it('should send synced items to extensions server', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
userUuid: '1-2-3',
|
||||
uuids: ['4-5-6'],
|
||||
})
|
||||
|
||||
expect(extensionsHttpService.sendItemsToExtensionsServer).toHaveBeenCalledWith({
|
||||
authParams: {
|
||||
foo: 'bar',
|
||||
},
|
||||
backupFilename: 'backup-file-name',
|
||||
extensionId: '2-3-4',
|
||||
extensionsServerUrl: 'https://extensions-server/extension1',
|
||||
forceMute: false,
|
||||
userUuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should skip sending synced items to extensions server if user key params cannot be obtained', async () => {
|
||||
authHttpService.getUserKeyParams = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops!')
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(extensionsHttpService.sendItemsToExtensionsServer).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should send synced items to extensions server with skipped file backup', async () => {
|
||||
event.payload.skipFileBackup = true
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
userUuid: '1-2-3',
|
||||
uuids: ['4-5-6'],
|
||||
})
|
||||
|
||||
expect(extensionsHttpService.sendItemsToExtensionsServer).toHaveBeenCalledWith({
|
||||
authParams: {
|
||||
foo: 'bar',
|
||||
},
|
||||
backupFilename: '',
|
||||
extensionId: '2-3-4',
|
||||
extensionsServerUrl: 'https://extensions-server/extension1',
|
||||
forceMute: false,
|
||||
items: [item],
|
||||
userUuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should send all undeleted items to extensions server if none specified', async () => {
|
||||
event.payload.itemUuids = []
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
userUuid: '1-2-3',
|
||||
deleted: false,
|
||||
})
|
||||
|
||||
expect(extensionsHttpService.sendItemsToExtensionsServer).toHaveBeenCalledWith({
|
||||
authParams: {
|
||||
foo: 'bar',
|
||||
},
|
||||
backupFilename: 'backup-file-name',
|
||||
extensionId: '2-3-4',
|
||||
extensionsServerUrl: 'https://extensions-server/extension1',
|
||||
forceMute: false,
|
||||
userUuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should replace the Standard Notes extensions server url with internal URL if internal DNS reroute is enabled', async () => {
|
||||
internalDNSRerouteEnabled = true
|
||||
;(event.payload.extensionUrl = 'https://extensions.standardnotes.org/extension2'),
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(extensionsHttpService.sendItemsToExtensionsServer).toHaveBeenCalledWith({
|
||||
authParams: {
|
||||
foo: 'bar',
|
||||
},
|
||||
backupFilename: 'backup-file-name',
|
||||
extensionId: '2-3-4',
|
||||
extensionsServerUrl: 'https://extensions-server/extension2',
|
||||
forceMute: false,
|
||||
userUuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,82 +0,0 @@
|
|||
import { DomainEventHandlerInterface, ItemsSyncedEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ItemQuery } from '../Item/ItemQuery'
|
||||
import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ExtensionsHttpServiceInterface } from '../Extension/ExtensionsHttpServiceInterface'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { Logger } from 'winston'
|
||||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
|
||||
@injectable()
|
||||
export class ItemsSyncedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.ItemRepository) private itemRepository: ItemRepositoryInterface,
|
||||
@inject(TYPES.AuthHttpService) private authHttpService: AuthHttpServiceInterface,
|
||||
@inject(TYPES.ExtensionsHttpService) private extensionsHttpService: ExtensionsHttpServiceInterface,
|
||||
@inject(TYPES.ItemBackupService) private itemBackupService: ItemBackupServiceInterface,
|
||||
@inject(TYPES.INTERNAL_DNS_REROUTE_ENABLED) private internalDNSRerouteEnabled: boolean,
|
||||
@inject(TYPES.EXTENSIONS_SERVER_URL) private extensionsServerUrl: string,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: ItemsSyncedEvent): Promise<void> {
|
||||
const items = await this.getItemsForPostingToExtension(event)
|
||||
|
||||
let authParams: KeyParamsData
|
||||
try {
|
||||
authParams = await this.authHttpService.getUserKeyParams({
|
||||
uuid: event.payload.userUuid,
|
||||
authenticated: false,
|
||||
})
|
||||
} catch (error) {
|
||||
this.logger.warn(`Could not get user key params from auth service: ${(error as Error).message}`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let backupFilename = ''
|
||||
if (!event.payload.skipFileBackup) {
|
||||
backupFilename = await this.itemBackupService.backup(items, authParams)
|
||||
}
|
||||
const backingUpViaProxyFile = backupFilename !== ''
|
||||
|
||||
this.logger.debug(`Sending ${items.length} items to extensions server for user ${event.payload.userUuid}`)
|
||||
|
||||
await this.extensionsHttpService.sendItemsToExtensionsServer({
|
||||
items: backingUpViaProxyFile ? undefined : items,
|
||||
authParams,
|
||||
backupFilename,
|
||||
forceMute: event.payload.forceMute,
|
||||
extensionsServerUrl: this.getExtensionsServerUrl(event),
|
||||
userUuid: event.payload.userUuid,
|
||||
extensionId: event.payload.extensionId,
|
||||
})
|
||||
}
|
||||
|
||||
private getExtensionsServerUrl(event: ItemsSyncedEvent): string {
|
||||
if (this.internalDNSRerouteEnabled) {
|
||||
return event.payload.extensionUrl.replace('https://extensions.standardnotes.org', this.extensionsServerUrl)
|
||||
}
|
||||
|
||||
return event.payload.extensionUrl
|
||||
}
|
||||
|
||||
private async getItemsForPostingToExtension(event: ItemsSyncedEvent): Promise<Item[]> {
|
||||
const itemQuery: ItemQuery = {
|
||||
userUuid: event.payload.userUuid,
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
}
|
||||
if (event.payload.itemUuids.length) {
|
||||
itemQuery.uuids = event.payload.itemUuids
|
||||
} else {
|
||||
itemQuery.deleted = false
|
||||
}
|
||||
|
||||
return this.itemRepository.findAll(itemQuery)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue