fix: remove unused axios dep in subservices
This commit is contained in:
parent
94e738532a
commit
45d4920e0f
13 changed files with 1 additions and 677 deletions
2
.pnp.cjs
generated
2
.pnp.cjs
generated
|
@ -6961,7 +6961,6 @@ const RAW_RUNTIME_STATE =
|
|||
["@types/uuid", "npm:9.0.3"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["axios", "npm:1.4.0"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["eslint", "npm:8.41.0"],\
|
||||
|
@ -7044,7 +7043,6 @@ const RAW_RUNTIME_STATE =
|
|||
["@types/jest", "npm:29.5.2"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["axios", "npm:1.4.0"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["eslint", "npm:8.41.0"],\
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"axios": "^1.1.3",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.2",
|
||||
|
|
|
@ -44,9 +44,6 @@ import {
|
|||
DomainEventPublisherInterface,
|
||||
DomainEventSubscriberInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { ExtensionsHttpService } from '../Domain/Extension/ExtensionsHttpService'
|
||||
import { ExtensionsHttpServiceInterface } from '../Domain/Extension/ExtensionsHttpServiceInterface'
|
||||
import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler'
|
||||
import { DuplicateItemSyncedEventHandler } from '../Domain/Handler/DuplicateItemSyncedEventHandler'
|
||||
import { EmailBackupRequestedEventHandler } from '../Domain/Handler/EmailBackupRequestedEventHandler'
|
||||
|
@ -949,20 +946,7 @@ export class ContainerConfigLoader {
|
|||
)
|
||||
|
||||
// Services
|
||||
container.bind<ContentDecoder>(TYPES.Sync_ContentDecoder).toDynamicValue(() => new ContentDecoder())
|
||||
container.bind<AxiosInstance>(TYPES.Sync_HTTPClient).toDynamicValue(() => axios.create())
|
||||
container
|
||||
.bind<ExtensionsHttpServiceInterface>(TYPES.Sync_ExtensionsHttpService)
|
||||
.toConstantValue(
|
||||
new ExtensionsHttpService(
|
||||
container.get<AxiosInstance>(TYPES.Sync_HTTPClient),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<ContentDecoderInterface>(TYPES.Sync_ContentDecoder),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container.bind<ContentDecoderInterface>(TYPES.Sync_ContentDecoder).toDynamicValue(() => new ContentDecoder())
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['DUPLICATE_ITEM_SYNCED', container.get(TYPES.Sync_DuplicateItemSyncedEventHandler)],
|
||||
|
|
|
@ -104,7 +104,6 @@ const TYPES = {
|
|||
Sync_SyncResponseFactory20161215: Symbol.for('Sync_SyncResponseFactory20161215'),
|
||||
Sync_SyncResponseFactory20200115: Symbol.for('Sync_SyncResponseFactory20200115'),
|
||||
Sync_SyncResponseFactoryResolver: Symbol.for('Sync_SyncResponseFactoryResolver'),
|
||||
Sync_ExtensionsHttpService: Symbol.for('Sync_ExtensionsHttpService'),
|
||||
Sync_ItemBackupService: Symbol.for('Sync_ItemBackupService'),
|
||||
Sync_ItemSaveValidator: Symbol.for('Sync_ItemSaveValidator'),
|
||||
Sync_OwnershipFilter: Symbol.for('Sync_OwnershipFilter'),
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
export enum ExtensionName {
|
||||
Dropbox = 'Dropbox',
|
||||
GoogleDrive = 'Google Drive',
|
||||
OneDrive = 'OneDrive',
|
||||
}
|
|
@ -1,438 +0,0 @@
|
|||
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 contentDecoder: ContentDecoderInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let item: Item
|
||||
let authParams: KeyParamsData
|
||||
let logger: Logger
|
||||
|
||||
const createService = () =>
|
||||
new ExtensionsHttpService(
|
||||
httpClient,
|
||||
primaryItemRepository,
|
||||
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 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()
|
||||
})
|
||||
})
|
|
@ -1,177 +0,0 @@
|
|||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
import { DomainEventInterface, DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
import { AxiosInstance } from 'axios'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { ContentDecoderInterface } from '../Item/ContentDecoderInterface'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ExtensionName } from './ExtensionName'
|
||||
import { ExtensionsHttpServiceInterface } from './ExtensionsHttpServiceInterface'
|
||||
import { SendItemsToExtensionsServerDTO } from './SendItemsToExtensionsServerDTO'
|
||||
import { getBody as googleDriveBody, getSubject as googleDriveSubject } from '../Email/GoogleDriveBackupFailed'
|
||||
import { getBody as dropboxBody, getSubject as dropboxSubject } from '../Email/DropboxBackupFailed'
|
||||
import { getBody as oneDriveBody, getSubject as oneDriveSubject } from '../Email/OneDriveBackupFailed'
|
||||
|
||||
export class ExtensionsHttpService implements ExtensionsHttpServiceInterface {
|
||||
constructor(
|
||||
private httpClient: AxiosInstance,
|
||||
private primaryItemRepository: ItemRepositoryInterface,
|
||||
private contentDecoder: ContentDecoderInterface,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async triggerCloudBackupOnExtensionsServer(dto: {
|
||||
cloudProvider: 'DROPBOX' | 'GOOGLE_DRIVE' | 'ONE_DRIVE'
|
||||
extensionsServerUrl: string
|
||||
backupFilename: string
|
||||
authParams: KeyParamsData
|
||||
forceMute: boolean
|
||||
userUuid: string
|
||||
}): Promise<void> {
|
||||
let sent = false
|
||||
try {
|
||||
const payload: Record<string, unknown> = {
|
||||
backup_filename: dto.backupFilename,
|
||||
auth_params: dto.authParams,
|
||||
silent: dto.forceMute,
|
||||
user_uuid: dto.userUuid,
|
||||
}
|
||||
|
||||
const response = await this.httpClient.request({
|
||||
method: 'POST',
|
||||
url: dto.extensionsServerUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: payload,
|
||||
validateStatus:
|
||||
/* istanbul ignore next */
|
||||
(status: number) => status >= 200 && status < 500,
|
||||
})
|
||||
|
||||
sent = response.status >= 200 && response.status < 300
|
||||
} catch (error) {
|
||||
this.logger.error(`[${dto.userUuid}] Failed to send a request to extensions server: ${(error as Error).message}`)
|
||||
}
|
||||
|
||||
if (!sent && !dto.forceMute) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.createCloudBackupFailedEventBasedOnProvider(dto.cloudProvider, dto.authParams.identifier as string),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async sendItemsToExtensionsServer(dto: SendItemsToExtensionsServerDTO): Promise<void> {
|
||||
let sent = false
|
||||
try {
|
||||
const payload: Record<string, unknown> = {
|
||||
backup_filename: dto.backupFilename,
|
||||
auth_params: dto.authParams,
|
||||
silent: dto.forceMute,
|
||||
user_uuid: dto.userUuid,
|
||||
}
|
||||
if (dto.items !== undefined) {
|
||||
payload.items = dto.items
|
||||
}
|
||||
|
||||
const response = await this.httpClient.request({
|
||||
method: 'POST',
|
||||
url: dto.extensionsServerUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: payload,
|
||||
validateStatus:
|
||||
/* istanbul ignore next */
|
||||
(status: number) => status >= 200 && status < 500,
|
||||
})
|
||||
|
||||
sent = response.status >= 200 && response.status < 300
|
||||
} catch (error) {
|
||||
this.logger.error(`[${dto.userUuid}] Failed to send a request to extensions server: ${(error as Error).message}`)
|
||||
}
|
||||
|
||||
if (!sent && !dto.forceMute) {
|
||||
await this.domainEventPublisher.publish(
|
||||
await this.getBackupFailedEvent(dto.extensionId, dto.userUuid, dto.authParams.identifier as string),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private createCloudBackupFailedEventBasedOnProvider(
|
||||
cloudProvider: 'DROPBOX' | 'GOOGLE_DRIVE' | 'ONE_DRIVE',
|
||||
email: string,
|
||||
): DomainEventInterface {
|
||||
switch (cloudProvider) {
|
||||
case 'DROPBOX':
|
||||
return this.domainEventFactory.createEmailRequestedEvent({
|
||||
userEmail: email,
|
||||
level: EmailLevel.LEVELS.FailedCloudBackup,
|
||||
body: dropboxBody(),
|
||||
messageIdentifier: 'FAILED_DROPBOX_BACKUP',
|
||||
subject: dropboxSubject(),
|
||||
})
|
||||
case 'GOOGLE_DRIVE':
|
||||
return this.domainEventFactory.createEmailRequestedEvent({
|
||||
userEmail: email,
|
||||
level: EmailLevel.LEVELS.FailedCloudBackup,
|
||||
body: googleDriveBody(),
|
||||
messageIdentifier: 'FAILED_GOOGLE_DRIVE_BACKUP',
|
||||
subject: googleDriveSubject(),
|
||||
})
|
||||
case 'ONE_DRIVE':
|
||||
return this.domainEventFactory.createEmailRequestedEvent({
|
||||
userEmail: email,
|
||||
level: EmailLevel.LEVELS.FailedCloudBackup,
|
||||
body: oneDriveBody(),
|
||||
messageIdentifier: 'FAILED_ONE_DRIVE_BACKUP',
|
||||
subject: oneDriveSubject(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private async getBackupFailedEvent(
|
||||
extensionId: string,
|
||||
userUuid: string,
|
||||
email: string,
|
||||
): Promise<DomainEventInterface> {
|
||||
const extension = await this.primaryItemRepository.findByUuidAndUserUuid(extensionId, userUuid)
|
||||
if (extension === null || !extension.props.content) {
|
||||
throw Error(`Could not find extensions with id ${extensionId}`)
|
||||
}
|
||||
|
||||
const content = this.contentDecoder.decode(extension.props.content)
|
||||
switch (this.getExtensionName(content)) {
|
||||
case ExtensionName.Dropbox:
|
||||
return this.createCloudBackupFailedEventBasedOnProvider('DROPBOX', email)
|
||||
case ExtensionName.GoogleDrive:
|
||||
return this.createCloudBackupFailedEventBasedOnProvider('GOOGLE_DRIVE', email)
|
||||
case ExtensionName.OneDrive:
|
||||
return this.createCloudBackupFailedEventBasedOnProvider('ONE_DRIVE', email)
|
||||
}
|
||||
}
|
||||
|
||||
private getExtensionName(content: Record<string, unknown>): ExtensionName {
|
||||
if ('name' in content) {
|
||||
return <ExtensionName>content.name
|
||||
}
|
||||
|
||||
const url = 'url' in content ? <string>content.url : undefined
|
||||
|
||||
if (url) {
|
||||
if (url.indexOf('dbt') !== -1) {
|
||||
return ExtensionName.Dropbox
|
||||
} else if (url.indexOf('gdrive') !== -1) {
|
||||
return ExtensionName.GoogleDrive
|
||||
} else if (url.indexOf('onedrive') !== -1) {
|
||||
return ExtensionName.OneDrive
|
||||
}
|
||||
}
|
||||
|
||||
throw Error('Could not deduce extension name from extension content')
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
import { SendItemsToExtensionsServerDTO } from './SendItemsToExtensionsServerDTO'
|
||||
|
||||
export interface ExtensionsHttpServiceInterface {
|
||||
triggerCloudBackupOnExtensionsServer(dto: {
|
||||
cloudProvider: 'DROPBOX' | 'GOOGLE_DRIVE' | 'ONE_DRIVE'
|
||||
extensionsServerUrl: string
|
||||
backupFilename: string
|
||||
authParams: KeyParamsData
|
||||
forceMute: boolean
|
||||
userUuid: string
|
||||
muteEmailsSettingUuid: string
|
||||
}): Promise<void>
|
||||
sendItemsToExtensionsServer(dto: SendItemsToExtensionsServerDTO): Promise<void>
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
|
||||
import { Item } from '../Item/Item'
|
||||
|
||||
export type SendItemsToExtensionsServerDTO = {
|
||||
extensionsServerUrl: string
|
||||
extensionId: string
|
||||
backupFilename: string
|
||||
authParams: KeyParamsData
|
||||
forceMute: boolean
|
||||
userUuid: string
|
||||
items?: Array<Item>
|
||||
}
|
|
@ -31,7 +31,6 @@
|
|||
"@standardnotes/domain-events-infra": "workspace:^",
|
||||
"@standardnotes/responses": "^1.13.27",
|
||||
"@standardnotes/security": "workspace:^",
|
||||
"axios": "^1.1.3",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.2",
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import * as winston from 'winston'
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const axios = require('axios')
|
||||
import { AxiosInstance } from 'axios'
|
||||
import Redis from 'ioredis'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { ApiGatewayManagementApiClient } from '@aws-sdk/client-apigatewaymanagementapi'
|
||||
|
@ -123,7 +120,6 @@ export class ContainerConfigLoader {
|
|||
.to(WebSocketMessageRequestedEventHandler)
|
||||
|
||||
// Services
|
||||
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
|
||||
container
|
||||
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
|
||||
.toConstantValue(new TokenDecoder<CrossServiceTokenData>(container.get(TYPES.AUTH_JWT_SECRET)))
|
||||
|
|
|
@ -29,7 +29,6 @@ const TYPES = {
|
|||
WebSocketConnectionTokenEncoder: Symbol.for('WebSocketConnectionTokenEncoder'),
|
||||
DomainEventSubscriber: Symbol.for('DomainEventSubscriber'),
|
||||
DomainEventMessageHandler: Symbol.for('DomainEventMessageHandler'),
|
||||
HTTPClient: Symbol.for('HTTPClient'),
|
||||
WebSocketsClientMessenger: Symbol.for('WebSocketsClientMessenger'),
|
||||
}
|
||||
|
||||
|
|
|
@ -5799,7 +5799,6 @@ __metadata:
|
|||
"@types/uuid": "npm:^9.0.3"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^6.5.0"
|
||||
"@typescript-eslint/parser": "npm:^6.5.0"
|
||||
axios: "npm:^1.1.3"
|
||||
cors: "npm:2.8.5"
|
||||
dotenv: "npm:^16.0.1"
|
||||
eslint: "npm:^8.39.0"
|
||||
|
@ -5877,7 +5876,6 @@ __metadata:
|
|||
"@types/jest": "npm:^29.5.1"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^6.5.0"
|
||||
"@typescript-eslint/parser": "npm:^6.5.0"
|
||||
axios: "npm:^1.1.3"
|
||||
cors: "npm:2.8.5"
|
||||
dotenv: "npm:^16.0.1"
|
||||
eslint: "npm:^8.39.0"
|
||||
|
|
Loading…
Reference in a new issue