chore: display shared vault file usage (#2399)
* chore: display shared vault file usage * fix: specs * fix: reshape filtering result * fix: resolving invalid server items * fix: get revisions specs * fix: processing issue * fix: tests --------- Co-authored-by: Mo <mo@standardnotes.com>
This commit is contained in:
@@ -14,9 +14,11 @@ import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
|
||||
import { ContactPublicKeySetInterface, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { SyncLocalVaultsWithRemoteSharedVaults } from './UseCase/SyncLocalVaultsWithRemoteSharedVaults'
|
||||
|
||||
describe('SharedVaultService', () => {
|
||||
let service: SharedVaultService
|
||||
let syncLocalVaultsWithRemoteSharedVaults: SyncLocalVaultsWithRemoteSharedVaults
|
||||
|
||||
beforeEach(() => {
|
||||
const sync = {} as jest.Mocked<SyncServiceInterface>
|
||||
@@ -37,12 +39,16 @@ describe('SharedVaultService', () => {
|
||||
const deleteSharedVaultUseCase = {} as jest.Mocked<DeleteSharedVault>
|
||||
const discardItemsLocally = {} as jest.Mocked<DiscardItemsLocally>
|
||||
|
||||
syncLocalVaultsWithRemoteSharedVaults = {} as jest.Mocked<SyncLocalVaultsWithRemoteSharedVaults>
|
||||
syncLocalVaultsWithRemoteSharedVaults.execute = jest.fn()
|
||||
|
||||
const eventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
eventBus.addEventHandler = jest.fn()
|
||||
|
||||
service = new SharedVaultService(
|
||||
items,
|
||||
session,
|
||||
syncLocalVaultsWithRemoteSharedVaults,
|
||||
getVault,
|
||||
getOwnedVaults,
|
||||
createSharedVaultUseCase,
|
||||
|
||||
@@ -32,6 +32,7 @@ import { ContentType, NotificationType, Uuid } from '@standardnotes/domain-core'
|
||||
import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange'
|
||||
import { FindContact } from '../Contacts/UseCase/FindContact'
|
||||
import { GetOwnedSharedVaults } from './UseCase/GetOwnedSharedVaults'
|
||||
import { SyncLocalVaultsWithRemoteSharedVaults } from './UseCase/SyncLocalVaultsWithRemoteSharedVaults'
|
||||
|
||||
export class SharedVaultService
|
||||
extends AbstractService<SharedVaultServiceEvent, SharedVaultServiceEventPayload>
|
||||
@@ -40,6 +41,7 @@ export class SharedVaultService
|
||||
constructor(
|
||||
private items: ItemManagerInterface,
|
||||
private session: SessionsClientInterface,
|
||||
private _syncLocalVaultsWithRemoteSharedVaults: SyncLocalVaultsWithRemoteSharedVaults,
|
||||
private _getVault: GetVault,
|
||||
private _getOwnedSharedVaults: GetOwnedSharedVaults,
|
||||
private _createSharedVault: CreateSharedVault,
|
||||
@@ -67,6 +69,7 @@ export class SharedVaultService
|
||||
super.deinit()
|
||||
;(this.items as unknown) = undefined
|
||||
;(this.session as unknown) = undefined
|
||||
;(this._syncLocalVaultsWithRemoteSharedVaults as unknown) = undefined
|
||||
;(this._getVault as unknown) = undefined
|
||||
;(this._createSharedVault as unknown) = undefined
|
||||
;(this._handleKeyPairChange as unknown) = undefined
|
||||
@@ -88,7 +91,7 @@ export class SharedVaultService
|
||||
break
|
||||
}
|
||||
case NotificationServiceEvent.NotificationReceived:
|
||||
await this.handleUserEvent(event.payload as NotificationServiceEventPayload)
|
||||
await this.handleNotification(event.payload as NotificationServiceEventPayload)
|
||||
break
|
||||
case SyncEvent.ReceivedRemoteSharedVaults:
|
||||
void this.notifyEventSync(SharedVaultServiceEvent.SharedVaultStatusChanged)
|
||||
@@ -96,7 +99,7 @@ export class SharedVaultService
|
||||
}
|
||||
}
|
||||
|
||||
private async handleUserEvent(event: NotificationServiceEventPayload): Promise<void> {
|
||||
private async handleNotification(event: NotificationServiceEventPayload): Promise<void> {
|
||||
switch (event.eventPayload.props.type.value) {
|
||||
case NotificationType.TYPES.RemovedFromSharedVault: {
|
||||
const vault = this._getVault.execute<SharedVaultListingInterface>({
|
||||
@@ -114,6 +117,19 @@ export class SharedVaultService
|
||||
}
|
||||
break
|
||||
}
|
||||
case NotificationType.TYPES.SharedVaultFileRemoved:
|
||||
case NotificationType.TYPES.SharedVaultFileUploaded: {
|
||||
const vaultOrError = this._getVault.execute<SharedVaultListingInterface>({
|
||||
sharedVaultUuid: event.eventPayload.props.sharedVaultUuid.value,
|
||||
})
|
||||
if (!vaultOrError.isFailed()) {
|
||||
await this._syncLocalVaultsWithRemoteSharedVaults.execute([vaultOrError.getValue()])
|
||||
|
||||
void this.notifyEventSync(SharedVaultServiceEvent.SharedVaultStatusChanged)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { KeySystemIdentifier } from '@standardnotes/models'
|
||||
|
||||
export enum SharedVaultServiceEvent {
|
||||
SharedVaultStatusChanged = 'SharedVaultStatusChanged',
|
||||
SharedVaultFileStorageUsageChanged = 'SharedVaultFileStorageUsageChanged',
|
||||
}
|
||||
|
||||
export type SharedVaultServiceEventPayload = {
|
||||
|
||||
@@ -31,6 +31,7 @@ export class ConvertToSharedVault {
|
||||
mutator.sharing = {
|
||||
sharedVaultUuid: serverVaultHash.uuid,
|
||||
ownerUserUuid: serverVaultHash.user_uuid,
|
||||
fileBytesUsed: serverVaultHash.file_upload_bytes_used,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ export class CreateSharedVault {
|
||||
mutator.sharing = {
|
||||
sharedVaultUuid: serverVaultHash.uuid,
|
||||
ownerUserUuid: serverVaultHash.user_uuid,
|
||||
fileBytesUsed: serverVaultHash.file_upload_bytes_used,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { MutatorClientInterface, SharedVaultServerInterface, VaultListingInterface } from '@standardnotes/snjs'
|
||||
import { SyncLocalVaultsWithRemoteSharedVaults } from './SyncLocalVaultsWithRemoteSharedVaults'
|
||||
|
||||
describe('SyncLocalVaultsWithRemoteSharedVaults', () => {
|
||||
let sharedVaultServer: SharedVaultServerInterface
|
||||
let mutator: MutatorClientInterface
|
||||
|
||||
const createUseCase = () => new SyncLocalVaultsWithRemoteSharedVaults(sharedVaultServer, mutator)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVaultServer = {} as jest.Mocked<SharedVaultServerInterface>
|
||||
sharedVaultServer.getSharedVaults = jest.fn().mockResolvedValue({ data: { sharedVaults: [{
|
||||
uuid: '1-2-3',
|
||||
user_uuid: '2-3-4',
|
||||
file_upload_bytes_used: 123,
|
||||
file_upload_bytes_limit: 10000000,
|
||||
created_at_timestamp: 123,
|
||||
updated_at_timestamp: 123,
|
||||
}] } })
|
||||
|
||||
mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
mutator.changeItem = jest.fn()
|
||||
})
|
||||
|
||||
it('should sync local vaults with remote shared vaults to update file storage usage', async () => {
|
||||
const localVaults = [{
|
||||
uuid: '1-2-3',
|
||||
name: 'Vault',
|
||||
isSharedVaultListing: () => true,
|
||||
sharing: {
|
||||
sharedVaultUuid: '1-2-3',
|
||||
ownerUserUuid: '2-3-4',
|
||||
fileBytesUsed: 0,
|
||||
},
|
||||
} as jest.Mocked<VaultListingInterface>]
|
||||
|
||||
const useCase = createUseCase()
|
||||
await useCase.execute(localVaults)
|
||||
|
||||
expect(mutator.changeItem).toHaveBeenCalledWith(localVaults[0], expect.any(Function))
|
||||
})
|
||||
|
||||
it('should fail if shared vault server returns error', async () => {
|
||||
sharedVaultServer.getSharedVaults = jest.fn().mockResolvedValue({ data: { error: { message: 'test-error' } } })
|
||||
|
||||
const localVaults = [{
|
||||
uuid: '1-2-3',
|
||||
name: 'Vault',
|
||||
isSharedVaultListing: () => true,
|
||||
sharing: {
|
||||
sharedVaultUuid: '1-2-3',
|
||||
ownerUserUuid: '2-3-4',
|
||||
fileBytesUsed: 0,
|
||||
},
|
||||
} as jest.Mocked<VaultListingInterface>]
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute(localVaults)
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('should not sync local vaults with remote shared vaults if local vault is not shared', async () => {
|
||||
const localVaults = [{
|
||||
uuid: '1-2-3',
|
||||
name: 'Vault',
|
||||
isSharedVaultListing: () => false,
|
||||
sharing: {
|
||||
sharedVaultUuid: '1-2-3',
|
||||
ownerUserUuid: '2-3-4',
|
||||
fileBytesUsed: 0,
|
||||
},
|
||||
} as jest.Mocked<VaultListingInterface>]
|
||||
|
||||
const useCase = createUseCase()
|
||||
await useCase.execute(localVaults)
|
||||
|
||||
expect(mutator.changeItem).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { SharedVaultServerInterface } from '@standardnotes/api'
|
||||
import { HttpError, isErrorResponse } from '@standardnotes/responses'
|
||||
import { SharedVaultListingInterface, VaultListingInterface, VaultListingMutator } from '@standardnotes/models'
|
||||
|
||||
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
|
||||
|
||||
export class SyncLocalVaultsWithRemoteSharedVaults implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultServer: SharedVaultServerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
) {}
|
||||
|
||||
async execute(localVaults: VaultListingInterface[]): Promise<Result<void>> {
|
||||
const remoteVaultsResponse = await this.sharedVaultServer.getSharedVaults()
|
||||
if (isErrorResponse(remoteVaultsResponse)) {
|
||||
return Result.fail((remoteVaultsResponse.data.error as HttpError).message as string)
|
||||
}
|
||||
const remoteVaults = remoteVaultsResponse.data.sharedVaults
|
||||
|
||||
for (const localVault of localVaults) {
|
||||
if (!localVault.isSharedVaultListing()) {
|
||||
continue
|
||||
}
|
||||
const remoteVault = remoteVaults.find((vault) => vault.uuid === localVault.sharing.sharedVaultUuid)
|
||||
if (remoteVault) {
|
||||
await this.mutator.changeItem<VaultListingMutator, SharedVaultListingInterface>(localVault, (mutator) => {
|
||||
/* istanbul ignore next */
|
||||
mutator.sharing = {
|
||||
sharedVaultUuid: remoteVault.uuid,
|
||||
ownerUserUuid: remoteVault.user_uuid,
|
||||
fileBytesUsed: remoteVault.file_upload_bytes_used,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user