import { inject, injectable } from 'inversify' import { Repository, SelectQueryBuilder } from 'typeorm' import { Item } from '../../Domain/Item/Item' import { ItemQuery } from '../../Domain/Item/ItemQuery' import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface' import { ReadStream } from 'fs' import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload' import TYPES from '../../Bootstrap/Types' @injectable() export class MySQLItemRepository implements ItemRepositoryInterface { constructor( @inject(TYPES.ORMItemRepository) private ormRepository: Repository, ) {} async save(item: Item): Promise { return this.ormRepository.save(item) } async remove(item: Item): Promise { return this.ormRepository.remove(item) } async updateContentSize(itemUuid: string, contentSize: number): Promise { await this.ormRepository .createQueryBuilder('item') .update() .set({ contentSize, }) .where('uuid = :itemUuid', { itemUuid, }) .execute() } async findContentSizeForComputingTransferLimit( query: ItemQuery, ): Promise<{ uuid: string; contentSize: number | null }[]> { const queryBuilder = this.createFindAllQueryBuilder(query) queryBuilder.select('item.uuid', 'uuid') queryBuilder.addSelect('item.content_size', 'contentSize') const items = await queryBuilder.getRawMany() return items } async deleteByUserUuid(userUuid: string): Promise { await this.ormRepository .createQueryBuilder('item') .delete() .from('items') .where('user_uuid = :userUuid', { userUuid }) .execute() } async findByUuid(uuid: string): Promise { return this.ormRepository .createQueryBuilder('item') .where('item.uuid = :uuid', { uuid, }) .getOne() } async findDatesForComputingIntegrityHash(userUuid: string): Promise> { 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 { 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 { return this.ormRepository .createQueryBuilder('item') .where('item.uuid = :uuid AND item.user_uuid = :userUuid', { uuid, userUuid, }) .getOne() } async findAll(query: ItemQuery): Promise { return this.createFindAllQueryBuilder(query).getMany() } async streamAll(query: ItemQuery): Promise { return this.createFindAllQueryBuilder(query).stream() } async countAll(query: ItemQuery): Promise { return this.createFindAllQueryBuilder(query).getCount() } async markItemsAsDeleted(itemUuids: Array, updatedAtTimestamp: number): Promise { await this.ormRepository .createQueryBuilder('item') .update() .set({ deleted: true, content: null, encItemKey: null, authHash: null, updatedAtTimestamp, }) .where('uuid IN (:...uuids)', { uuids: itemUuids, }) .execute() } private createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder { const queryBuilder = this.ormRepository.createQueryBuilder('item') if (query.sortBy !== undefined && query.sortOrder !== undefined) { queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder) } if (query.selectFields !== undefined) { queryBuilder.select(query.selectFields) } if (query.userUuid !== undefined) { queryBuilder.where('item.user_uuid = :userUuid', { userUuid: query.userUuid }) } if (query.uuids && query.uuids.length > 0) { queryBuilder.andWhere('item.uuid IN (:...uuids)', { uuids: query.uuids }) } if (query.deleted !== undefined) { queryBuilder.andWhere('item.deleted = :deleted', { deleted: query.deleted }) } if (query.contentType) { if (Array.isArray(query.contentType)) { queryBuilder.andWhere('item.content_type IN (:...contentTypes)', { contentTypes: query.contentType }) } else { queryBuilder.andWhere('item.content_type = :contentType', { contentType: query.contentType }) } } if (query.lastSyncTime && query.syncTimeComparison) { queryBuilder.andWhere(`item.updated_at_timestamp ${query.syncTimeComparison} :lastSyncTime`, { lastSyncTime: query.lastSyncTime, }) } if (query.createdBetween !== undefined) { queryBuilder.andWhere('item.created_at BETWEEN :createdAfter AND :createdBefore', { createdAfter: query.createdBetween[0].toISOString(), createdBefore: query.createdBetween[1].toISOString(), }) } if (query.offset !== undefined) { queryBuilder.skip(query.offset) } if (query.limit !== undefined) { queryBuilder.take(query.limit) } return queryBuilder } }