|
@@ -3,20 +3,20 @@ import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
|
import { Logger } from 'winston'
|
|
import { Logger } from 'winston'
|
|
|
|
|
|
import { Item } from '../../Domain/Item/Item'
|
|
import { Item } from '../../Domain/Item/Item'
|
|
-import { SQLItem } from './SQLItem'
|
|
|
|
-import { SQLLegacyItemRepository } from './SQLLegacyItemRepository'
|
|
|
|
import { ItemQuery } from '../../Domain/Item/ItemQuery'
|
|
import { ItemQuery } from '../../Domain/Item/ItemQuery'
|
|
|
|
+import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface'
|
|
|
|
+import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload'
|
|
|
|
+import { ItemContentSizeDescriptor } from '../../Domain/Item/ItemContentSizeDescriptor'
|
|
|
|
+import { SQLItem } from './SQLItem'
|
|
|
|
|
|
-export class SQLItemRepository extends SQLLegacyItemRepository {
|
|
|
|
|
|
+export class SQLItemRepository implements ItemRepositoryInterface {
|
|
constructor(
|
|
constructor(
|
|
- protected override ormRepository: Repository<SQLItem>,
|
|
|
|
- protected override mapper: MapperInterface<Item, SQLItem>,
|
|
|
|
- protected override logger: Logger,
|
|
|
|
- ) {
|
|
|
|
- super(ormRepository, mapper, logger)
|
|
|
|
- }
|
|
|
|
|
|
+ protected ormRepository: Repository<SQLItem>,
|
|
|
|
+ protected mapper: MapperInterface<Item, SQLItem>,
|
|
|
|
+ protected logger: Logger,
|
|
|
|
+ ) {}
|
|
|
|
|
|
- override async deleteByUserUuidInSharedVaults(userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<void> {
|
|
|
|
|
|
+ async deleteByUserUuidInSharedVaults(userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<void> {
|
|
await this.ormRepository
|
|
await this.ormRepository
|
|
.createQueryBuilder('item')
|
|
.createQueryBuilder('item')
|
|
.delete()
|
|
.delete()
|
|
@@ -28,7 +28,7 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|
.execute()
|
|
.execute()
|
|
}
|
|
}
|
|
|
|
|
|
- override async deleteByUserUuidAndNotInSharedVault(userUuid: Uuid): Promise<void> {
|
|
|
|
|
|
+ async deleteByUserUuidAndNotInSharedVault(userUuid: Uuid): Promise<void> {
|
|
await this.ormRepository
|
|
await this.ormRepository
|
|
.createQueryBuilder('item')
|
|
.createQueryBuilder('item')
|
|
.delete()
|
|
.delete()
|
|
@@ -38,25 +38,21 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|
.execute()
|
|
.execute()
|
|
}
|
|
}
|
|
|
|
|
|
- override async updateSharedVaultOwner(dto: {
|
|
|
|
- sharedVaultUuid: Uuid
|
|
|
|
- fromOwnerUuid: Uuid
|
|
|
|
- toOwnerUuid: Uuid
|
|
|
|
- }): Promise<void> {
|
|
|
|
|
|
+ async updateSharedVaultOwner(dto: { sharedVaultUuid: Uuid; fromOwnerUuid: Uuid; toOwnerUuid: Uuid }): Promise<void> {
|
|
await this.ormRepository
|
|
await this.ormRepository
|
|
.createQueryBuilder('item')
|
|
.createQueryBuilder('item')
|
|
.update()
|
|
.update()
|
|
.set({
|
|
.set({
|
|
userUuid: dto.toOwnerUuid.value,
|
|
userUuid: dto.toOwnerUuid.value,
|
|
})
|
|
})
|
|
- .where('shared_vault_uuid = :sharedVaultUuid AND user_uuid = :fromOwnerUuid', {
|
|
|
|
- sharedVaultUuid: dto.sharedVaultUuid.value,
|
|
|
|
|
|
+ .where('user_uuid = :fromOwnerUuid AND shared_vault_uuid = :sharedVaultUuid', {
|
|
fromOwnerUuid: dto.fromOwnerUuid.value,
|
|
fromOwnerUuid: dto.fromOwnerUuid.value,
|
|
|
|
+ sharedVaultUuid: dto.sharedVaultUuid.value,
|
|
})
|
|
})
|
|
.execute()
|
|
.execute()
|
|
}
|
|
}
|
|
|
|
|
|
- override async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
|
|
|
|
|
+ async unassignFromSharedVault(sharedVaultUuid: Uuid): Promise<void> {
|
|
await this.ormRepository
|
|
await this.ormRepository
|
|
.createQueryBuilder('item')
|
|
.createQueryBuilder('item')
|
|
.update()
|
|
.update()
|
|
@@ -69,7 +65,186 @@ export class SQLItemRepository extends SQLLegacyItemRepository {
|
|
.execute()
|
|
.execute()
|
|
}
|
|
}
|
|
|
|
|
|
- protected override createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<SQLItem> {
|
|
|
|
|
|
+ async removeByUuid(uuid: Uuid): Promise<void> {
|
|
|
|
+ await this.ormRepository
|
|
|
|
+ .createQueryBuilder('item')
|
|
|
|
+ .delete()
|
|
|
|
+ .from('items')
|
|
|
|
+ .where('uuid = :uuid', { uuid: uuid.value })
|
|
|
|
+ .execute()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async insert(item: Item): Promise<void> {
|
|
|
|
+ const projection = this.mapper.toProjection(item)
|
|
|
|
+
|
|
|
|
+ await this.ormRepository.insert(projection)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async update(item: Item): Promise<void> {
|
|
|
|
+ const projection = this.mapper.toProjection(item)
|
|
|
|
+
|
|
|
|
+ const { uuid, ...updateValues } = projection
|
|
|
|
+
|
|
|
|
+ await this.ormRepository.update({ uuid: uuid }, updateValues)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async remove(item: Item): Promise<void> {
|
|
|
|
+ await this.ormRepository.remove(this.mapper.toProjection(item))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async updateContentSize(itemUuid: string, contentSize: number): Promise<void> {
|
|
|
|
+ await this.ormRepository
|
|
|
|
+ .createQueryBuilder('item')
|
|
|
|
+ .update()
|
|
|
|
+ .set({
|
|
|
|
+ contentSize,
|
|
|
|
+ })
|
|
|
|
+ .where('uuid = :itemUuid', {
|
|
|
|
+ itemUuid,
|
|
|
|
+ })
|
|
|
|
+ .execute()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findContentSizeForComputingTransferLimit(query: ItemQuery): Promise<ItemContentSizeDescriptor[]> {
|
|
|
|
+ const queryBuilder = this.createFindAllQueryBuilder(query)
|
|
|
|
+ queryBuilder.select('item.uuid', 'uuid')
|
|
|
|
+ queryBuilder.addSelect('item.content_size', 'contentSize')
|
|
|
|
+
|
|
|
|
+ const items = await queryBuilder.getRawMany()
|
|
|
|
+
|
|
|
|
+ const itemContentSizeDescriptors: ItemContentSizeDescriptor[] = []
|
|
|
|
+ for (const item of items) {
|
|
|
|
+ const ItemContentSizeDescriptorOrError = ItemContentSizeDescriptor.create(item.uuid, item.contentSize)
|
|
|
|
+ if (ItemContentSizeDescriptorOrError.isFailed()) {
|
|
|
|
+ this.logger.error(
|
|
|
|
+ `Failed to create ItemContentSizeDescriptor for item ${
|
|
|
|
+ item.uuid
|
|
|
|
+ }: ${ItemContentSizeDescriptorOrError.getError()}`,
|
|
|
|
+ )
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ itemContentSizeDescriptors.push(ItemContentSizeDescriptorOrError.getValue())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return itemContentSizeDescriptors
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findByUuid(uuid: Uuid): Promise<Item | null> {
|
|
|
|
+ const persistence = await this.ormRepository
|
|
|
|
+ .createQueryBuilder('item')
|
|
|
|
+ .where('item.uuid = :uuid', {
|
|
|
|
+ uuid: uuid.value,
|
|
|
|
+ })
|
|
|
|
+ .getOne()
|
|
|
|
+
|
|
|
|
+ if (persistence === null) {
|
|
|
|
+ return null
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const item = this.mapper.toDomain(persistence)
|
|
|
|
+
|
|
|
|
+ return item
|
|
|
|
+ } catch (error) {
|
|
|
|
+ this.logger.error(
|
|
|
|
+ `Failed to map item ${uuid.value} for user ${persistence.userUuid} by uuid: ${(error as Error).message}`,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return null
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findDatesForComputingIntegrityHash(userUuid: string): Promise<Array<{ updated_at_timestamp: number }>> {
|
|
|
|
+ const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
|
|
|
+ queryBuilder.select('item.updated_at_timestamp')
|
|
|
|
+ queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
|
|
|
+ queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
|
|
|
+
|
|
|
|
+ const items = await queryBuilder.getRawMany()
|
|
|
|
+
|
|
|
|
+ return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findItemsForComputingIntegrityPayloads(userUuid: string): Promise<ExtendedIntegrityPayload[]> {
|
|
|
|
+ const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
|
|
|
+ queryBuilder.select('item.uuid', 'uuid')
|
|
|
|
+ queryBuilder.addSelect('item.updated_at_timestamp', 'updated_at_timestamp')
|
|
|
|
+ queryBuilder.addSelect('item.content_type', 'content_type')
|
|
|
|
+ queryBuilder.where('item.user_uuid = :userUuid', { userUuid: userUuid })
|
|
|
|
+ queryBuilder.andWhere('item.deleted = :deleted', { deleted: false })
|
|
|
|
+
|
|
|
|
+ const items = await queryBuilder.getRawMany()
|
|
|
|
+
|
|
|
|
+ return items.sort((itemA, itemB) => itemB.updated_at_timestamp - itemA.updated_at_timestamp)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Item | null> {
|
|
|
|
+ const persistence = await this.ormRepository
|
|
|
|
+ .createQueryBuilder('item')
|
|
|
|
+ .where('item.uuid = :uuid AND item.user_uuid = :userUuid', {
|
|
|
|
+ uuid,
|
|
|
|
+ userUuid,
|
|
|
|
+ })
|
|
|
|
+ .getOne()
|
|
|
|
+
|
|
|
|
+ if (persistence === null) {
|
|
|
|
+ return null
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const item = this.mapper.toDomain(persistence)
|
|
|
|
+
|
|
|
|
+ return item
|
|
|
|
+ } catch (error) {
|
|
|
|
+ this.logger.error(
|
|
|
|
+ `Failed to map item ${uuid} for user ${persistence.userUuid} by uuid and userUuid: ${(error as Error).message}`,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return null
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async findAll(query: ItemQuery): Promise<Item[]> {
|
|
|
|
+ const persistence = await this.createFindAllQueryBuilder(query).getMany()
|
|
|
|
+
|
|
|
|
+ const domainItems: Item[] = []
|
|
|
|
+ for (const persistencItem of persistence) {
|
|
|
|
+ try {
|
|
|
|
+ domainItems.push(this.mapper.toDomain(persistencItem))
|
|
|
|
+ } catch (error) {
|
|
|
|
+ this.logger.error(
|
|
|
|
+ `Failed to map item ${persistencItem.uuid} for user ${persistencItem.userUuid} to domain: ${
|
|
|
|
+ (error as Error).message
|
|
|
|
+ }`,
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return domainItems
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async countAll(query: ItemQuery): Promise<number> {
|
|
|
|
+ return this.createFindAllQueryBuilder(query).getCount()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async markItemsAsDeleted(itemUuids: Array<string>, updatedAtTimestamp: number): Promise<void> {
|
|
|
|
+ await this.ormRepository
|
|
|
|
+ .createQueryBuilder('item')
|
|
|
|
+ .update()
|
|
|
|
+ .set({
|
|
|
|
+ deleted: true,
|
|
|
|
+ content: null,
|
|
|
|
+ encItemKey: null,
|
|
|
|
+ authHash: null,
|
|
|
|
+ updatedAtTimestamp,
|
|
|
|
+ })
|
|
|
|
+ .where('uuid IN (:...uuids)', {
|
|
|
|
+ uuids: itemUuids,
|
|
|
|
+ })
|
|
|
|
+ .execute()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<SQLItem> {
|
|
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
|
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
|
|
|
|
|
if (query.sortBy !== undefined && query.sortOrder !== undefined) {
|
|
if (query.sortBy !== undefined && query.sortOrder !== undefined) {
|