123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- import 'reflect-metadata'
- import { KeyParamsData } from '@standardnotes/responses'
- import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
- import { Logger } from 'winston'
- import { ContentDecoderInterface } from '../Item/ContentDecoderInterface'
- import { Item } from '../Item/Item'
- import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
- import { ExtensionsHttpService } from './ExtensionsHttpService'
- import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
- import { AxiosInstance } from 'axios'
- import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
- describe('ExtensionsHttpService', () => {
- let httpClient: AxiosInstance
- let primaryItemRepository: ItemRepositoryInterface
- let secondaryItemRepository: ItemRepositoryInterface | null
- let contentDecoder: ContentDecoderInterface
- let domainEventPublisher: DomainEventPublisherInterface
- let domainEventFactory: DomainEventFactoryInterface
- let item: Item
- let authParams: KeyParamsData
- let logger: Logger
- const createService = () =>
- new ExtensionsHttpService(
- httpClient,
- primaryItemRepository,
- secondaryItemRepository,
- contentDecoder,
- domainEventPublisher,
- domainEventFactory,
- logger,
- )
- beforeEach(() => {
- httpClient = {} as jest.Mocked<AxiosInstance>
- httpClient.request = jest.fn().mockReturnValue({ status: 200, data: { foo: 'bar' } })
- 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()
- authParams = {} as jest.Mocked<KeyParamsData>
- primaryItemRepository = {} as jest.Mocked<ItemRepositoryInterface>
- primaryItemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(item)
- logger = {} as jest.Mocked<Logger>
- logger.error = jest.fn()
- domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
- domainEventPublisher.publish = jest.fn()
- domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
- domainEventFactory.createEmailRequestedEvent = jest.fn()
- contentDecoder = {} as jest.Mocked<ContentDecoderInterface>
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'Dropbox' })
- })
- it('should trigger cloud backup on extensions server', async () => {
- await createService().triggerCloudBackupOnExtensionsServer({
- userUuid: '1-2-3',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- backupFilename: 'test',
- authParams,
- cloudProvider: 'DROPBOX',
- })
- expect(httpClient.request).toHaveBeenCalledWith({
- data: {
- auth_params: authParams,
- backup_filename: 'test',
- silent: false,
- user_uuid: '1-2-3',
- },
- headers: {
- 'Content-Type': 'application/json',
- },
- method: 'POST',
- url: 'https://extensions-server/extension1',
- validateStatus: expect.any(Function),
- })
- })
- it('should publish a failed Dropbox backup event if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'Dropbox' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().triggerCloudBackupOnExtensionsServer({
- userUuid: '1-2-3',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- backupFilename: 'test',
- authParams,
- cloudProvider: 'DROPBOX',
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should send items to extensions server', async () => {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: '',
- authParams,
- })
- expect(httpClient.request).toHaveBeenCalledWith({
- data: {
- auth_params: authParams,
- backup_filename: '',
- items: [item],
- silent: false,
- user_uuid: '1-2-3',
- },
- headers: {
- 'Content-Type': 'application/json',
- },
- method: 'POST',
- url: 'https://extensions-server/extension1',
- validateStatus: expect.any(Function),
- })
- })
- it('should send items proxy backup file name only to extensions server', async () => {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- backupFilename: 'backup-file',
- authParams,
- })
- expect(httpClient.request).toHaveBeenCalledWith({
- data: {
- auth_params: authParams,
- backup_filename: 'backup-file',
- silent: false,
- user_uuid: '1-2-3',
- },
- headers: {
- 'Content-Type': 'application/json',
- },
- method: 'POST',
- url: 'https://extensions-server/extension1',
- validateStatus: expect.any(Function),
- })
- })
- it('should publish a failed Dropbox backup event if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'Dropbox' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should publish a failed backup event if the extension is in the secondary repository', async () => {
- primaryItemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(null)
- secondaryItemRepository = {} as jest.Mocked<ItemRepositoryInterface>
- secondaryItemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(item)
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: '',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- secondaryItemRepository = null
- })
- it('should publish a failed Dropbox backup event if request was sent and extensions server responded not ok', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'Dropbox' })
- httpClient.request = jest.fn().mockReturnValue({ status: 400, data: { error: 'foo-bar' } })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should publish a failed Google Drive backup event if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'Google Drive' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should publish a failed One Drive backup event if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'OneDrive' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should not publish a failed backup event if emailes are force muted', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ name: 'OneDrive' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: true,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).not.toHaveBeenCalled()
- })
- it('should throw an error if the extension to post to is not found', async () => {
- primaryItemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(null)
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- let error = null
- try {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- } catch (e) {
- error = e
- }
- expect(error).not.toBeNull()
- })
- it('should throw an error if the extension to post to has no content', async () => {
- item = {} as jest.Mocked<Item>
- primaryItemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(item)
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- let error = null
- try {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- } catch (e) {
- error = e
- }
- expect(error).not.toBeNull()
- })
- it('should publish a failed Dropbox backup event judging by extension url if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ url: 'https://dbt.com/...' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should publish a failed Google Drive backup event judging by extension url if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ url: 'https://gdrive.com/...' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should publish a failed One Drive backup event judging by extension url if request was not sent successfully', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ url: 'https://onedrive.com/...' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- expect(domainEventPublisher.publish).toHaveBeenCalled()
- expect(domainEventFactory.createEmailRequestedEvent).toHaveBeenCalled()
- })
- it('should throw an error if cannot deduce extension by judging from the url', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({ url: 'https://foobar.com/...' })
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- let error = null
- try {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- } catch (e) {
- error = e
- }
- expect(error).not.toBeNull()
- })
- it('should throw an error if there is no extension name or url', async () => {
- contentDecoder.decode = jest.fn().mockReturnValue({})
- httpClient.request = jest.fn().mockImplementation(() => {
- throw new Error('Could not reach the extensions server')
- })
- let error = null
- try {
- await createService().sendItemsToExtensionsServer({
- userUuid: '1-2-3',
- extensionId: '2-3-4',
- extensionsServerUrl: 'https://extensions-server/extension1',
- forceMute: false,
- items: [item],
- backupFilename: 'backup-file',
- authParams,
- })
- } catch (e) {
- error = e
- }
- expect(error).not.toBeNull()
- })
- })
|