From effdfebc193c66d830bb4d516d408a9c126f3d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Mon, 28 Aug 2023 12:28:48 +0200 Subject: [PATCH] feat(syncing-server): turn mysql items model into legacy (#711) * feat(syncing-server): turn mysql items model into legacy * fix: rename MySQL model to SQL model to include SQLite option * fix: rename mysqlitem to sqlitem --- .../syncing-server/src/Bootstrap/Container.ts | 36 ++--- .../src/Bootstrap/DataSource.ts | 4 +- .../syncing-server/src/Bootstrap/Types.ts | 6 +- .../src/Infra/TypeORM/SQLItem.ts | 29 ++++ .../src/Infra/TypeORM/SQLItemRepository.ts | 78 +++++++++ .../{TypeORMItem.ts => SQLLegacyItem.ts} | 2 +- ...pository.ts => SQLLegacyItemRepository.ts} | 12 +- .../TypeORM/TypeORMItemRepositoryResolver.ts | 6 +- .../Persistence/SQLItemPersistenceMapper.ts | 148 ++++++++++++++++++ ...r.ts => SQLLegacyItemPersistenceMapper.ts} | 10 +- 10 files changed, 293 insertions(+), 38 deletions(-) create mode 100644 packages/syncing-server/src/Infra/TypeORM/SQLItem.ts create mode 100644 packages/syncing-server/src/Infra/TypeORM/SQLItemRepository.ts rename packages/syncing-server/src/Infra/TypeORM/{TypeORMItem.ts => SQLLegacyItem.ts} (98%) rename packages/syncing-server/src/Infra/TypeORM/{TypeORMItemRepository.ts => SQLLegacyItemRepository.ts} (95%) create mode 100644 packages/syncing-server/src/Mapping/Persistence/SQLItemPersistenceMapper.ts rename packages/syncing-server/src/Mapping/Persistence/{ItemPersistenceMapper.ts => SQLLegacyItemPersistenceMapper.ts} (92%) diff --git a/packages/syncing-server/src/Bootstrap/Container.ts b/packages/syncing-server/src/Bootstrap/Container.ts index a5a8f2751..78027b069 100644 --- a/packages/syncing-server/src/Bootstrap/Container.ts +++ b/packages/syncing-server/src/Bootstrap/Container.ts @@ -6,7 +6,7 @@ import TYPES from './Types' import { AppDataSource } from './DataSource' import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns' import { ItemRepositoryInterface } from '../Domain/Item/ItemRepositoryInterface' -import { TypeORMItemRepository } from '../Infra/TypeORM/TypeORMItemRepository' +import { SQLLegacyItemRepository } from '../Infra/TypeORM/SQLLegacyItemRepository' import { MongoRepository, Repository } from 'typeorm' import { Item } from '../Domain/Item/Item' import { @@ -61,8 +61,8 @@ import { S3ItemBackupService } from '../Infra/S3/S3ItemBackupService' import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core' import { BaseItemsController } from '../Infra/InversifyExpressUtils/Base/BaseItemsController' import { Transform } from 'stream' -import { TypeORMItem } from '../Infra/TypeORM/TypeORMItem' -import { ItemPersistenceMapper } from '../Mapping/Persistence/ItemPersistenceMapper' +import { SQLLegacyItem } from '../Infra/TypeORM/SQLLegacyItem' +import { SQLLegacyItemPersistenceMapper } from '../Mapping/Persistence/SQLLegacyItemPersistenceMapper' import { ItemHttpRepresentation } from '../Mapping/Http/ItemHttpRepresentation' import { ItemHttpMapper } from '../Mapping/Http/ItemHttpMapper' import { SavedItemHttpRepresentation } from '../Mapping/Http/SavedItemHttpRepresentation' @@ -303,8 +303,8 @@ export class ContainerConfigLoader { // Mapping container - .bind>(TYPES.Sync_ItemPersistenceMapper) - .toConstantValue(new ItemPersistenceMapper()) + .bind>(TYPES.Sync_SQLLegacyItemPersistenceMapper) + .toConstantValue(new SQLLegacyItemPersistenceMapper()) container .bind>(TYPES.Sync_ItemHashHttpMapper) .toConstantValue(new ItemHashHttpMapper()) @@ -360,8 +360,8 @@ export class ContainerConfigLoader { // ORM container - .bind>(TYPES.Sync_ORMItemRepository) - .toDynamicValue(() => appDataSource.getRepository(TypeORMItem)) + .bind>(TYPES.Sync_ORMLegacyItemRepository) + .toDynamicValue(() => appDataSource.getRepository(SQLLegacyItem)) container .bind>(TYPES.Sync_ORMSharedVaultRepository) .toConstantValue(appDataSource.getRepository(TypeORMSharedVault)) @@ -401,11 +401,11 @@ export class ContainerConfigLoader { // Repositories container - .bind(TYPES.Sync_MySQLItemRepository) + .bind(TYPES.Sync_SQLLegacyItemRepository) .toConstantValue( - new TypeORMItemRepository( - container.get>(TYPES.Sync_ORMItemRepository), - container.get>(TYPES.Sync_ItemPersistenceMapper), + new SQLLegacyItemRepository( + container.get>(TYPES.Sync_ORMLegacyItemRepository), + container.get>(TYPES.Sync_SQLLegacyItemPersistenceMapper), container.get(TYPES.Sync_Logger), ), ) @@ -413,7 +413,7 @@ export class ContainerConfigLoader { .bind(TYPES.Sync_ItemRepositoryResolver) .toConstantValue( new TypeORMItemRepositoryResolver( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, ), ) @@ -777,7 +777,7 @@ export class ContainerConfigLoader { ) .toConstantValue( new TransitionItemsFromPrimaryToSecondaryDatabaseForUser( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, container.get(TYPES.Sync_Timer), container.get(TYPES.Sync_Logger), @@ -843,7 +843,7 @@ export class ContainerConfigLoader { .bind(TYPES.Sync_DuplicateItemSyncedEventHandler) .toConstantValue( new DuplicateItemSyncedEventHandler( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, container.get(TYPES.Sync_DomainEventFactory), container.get(TYPES.Sync_DomainEventPublisher), @@ -854,7 +854,7 @@ export class ContainerConfigLoader { .bind(TYPES.Sync_AccountDeletionRequestedEventHandler) .toConstantValue( new AccountDeletionRequestedEventHandler( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, container.get(TYPES.Sync_Logger), ), @@ -863,7 +863,7 @@ export class ContainerConfigLoader { .bind(TYPES.Sync_ItemRevisionCreationRequestedEventHandler) .toConstantValue( new ItemRevisionCreationRequestedEventHandler( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, container.get(TYPES.Sync_ItemBackupService), container.get(TYPES.Sync_DomainEventFactory), @@ -918,7 +918,7 @@ export class ContainerConfigLoader { .toConstantValue( new ExtensionsHttpService( container.get(TYPES.Sync_HTTPClient), - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, container.get(TYPES.Sync_ContentDecoder), container.get(TYPES.Sync_DomainEventPublisher), @@ -964,7 +964,7 @@ export class ContainerConfigLoader { .bind(TYPES.Sync_EmailBackupRequestedEventHandler) .toConstantValue( new EmailBackupRequestedEventHandler( - container.get(TYPES.Sync_MySQLItemRepository), + container.get(TYPES.Sync_SQLLegacyItemRepository), isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, diff --git a/packages/syncing-server/src/Bootstrap/DataSource.ts b/packages/syncing-server/src/Bootstrap/DataSource.ts index b6c796ede..1047ae514 100644 --- a/packages/syncing-server/src/Bootstrap/DataSource.ts +++ b/packages/syncing-server/src/Bootstrap/DataSource.ts @@ -2,7 +2,7 @@ import { DataSource, EntityTarget, LoggerOptions, MongoRepository, ObjectLiteral import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions' import { Env } from './Env' import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions' -import { TypeORMItem } from '../Infra/TypeORM/TypeORMItem' +import { SQLLegacyItem } from '../Infra/TypeORM/SQLLegacyItem' import { TypeORMNotification } from '../Infra/TypeORM/TypeORMNotification' import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault' import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser' @@ -75,7 +75,7 @@ export class AppDataSource { const commonDataSourceOptions = { maxQueryExecutionTime, entities: [ - TypeORMItem, + SQLLegacyItem, TypeORMNotification, TypeORMSharedVault, TypeORMSharedVaultUser, diff --git a/packages/syncing-server/src/Bootstrap/Types.ts b/packages/syncing-server/src/Bootstrap/Types.ts index da45fac07..b25dec30c 100644 --- a/packages/syncing-server/src/Bootstrap/Types.ts +++ b/packages/syncing-server/src/Bootstrap/Types.ts @@ -8,7 +8,7 @@ const TYPES = { Sync_Env: Symbol.for('Sync_Env'), // Repositories Sync_ItemRepositoryResolver: Symbol.for('Sync_ItemRepositoryResolver'), - Sync_MySQLItemRepository: Symbol.for('Sync_MySQLItemRepository'), + Sync_SQLLegacyItemRepository: Symbol.for('Sync_SQLLegacyItemRepository'), Sync_MongoDBItemRepository: Symbol.for('Sync_MongoDBItemRepository'), Sync_SharedVaultRepository: Symbol.for('Sync_SharedVaultRepository'), Sync_SharedVaultInviteRepository: Symbol.for('Sync_SharedVaultInviteRepository'), @@ -16,7 +16,7 @@ const TYPES = { Sync_NotificationRepository: Symbol.for('Sync_NotificationRepository'), Sync_MessageRepository: Symbol.for('Sync_MessageRepository'), // ORM - Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'), + Sync_ORMLegacyItemRepository: Symbol.for('Sync_ORMLegacyItemRepository'), Sync_ORMSharedVaultRepository: Symbol.for('Sync_ORMSharedVaultRepository'), Sync_ORMSharedVaultInviteRepository: Symbol.for('Sync_ORMSharedVaultInviteRepository'), Sync_ORMSharedVaultUserRepository: Symbol.for('Sync_ORMSharedVaultUserRepository'), @@ -131,7 +131,7 @@ const TYPES = { Sync_MessagePersistenceMapper: Symbol.for('Sync_MessagePersistenceMapper'), Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'), Sync_NotificationHttpMapper: Symbol.for('Sync_NotificationHttpMapper'), - Sync_ItemPersistenceMapper: Symbol.for('Sync_ItemPersistenceMapper'), + Sync_SQLLegacyItemPersistenceMapper: Symbol.for('Sync_SQLLegacyItemPersistenceMapper'), Sync_MongoDBItemPersistenceMapper: Symbol.for('Sync_MongoDBItemPersistenceMapper'), Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'), Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'), diff --git a/packages/syncing-server/src/Infra/TypeORM/SQLItem.ts b/packages/syncing-server/src/Infra/TypeORM/SQLItem.ts new file mode 100644 index 000000000..f526541ff --- /dev/null +++ b/packages/syncing-server/src/Infra/TypeORM/SQLItem.ts @@ -0,0 +1,29 @@ +import { Column, Entity } from 'typeorm' +import { SQLLegacyItem } from './SQLLegacyItem' + +@Entity({ name: 'items' }) +export class SQLItem extends SQLLegacyItem { + @Column({ + type: 'varchar', + name: 'last_edited_by', + length: 36, + nullable: true, + }) + declare lastEditedBy: string | null + + @Column({ + type: 'varchar', + name: 'shared_vault_uuid', + length: 36, + nullable: true, + }) + declare sharedVaultUuid: string | null + + @Column({ + type: 'varchar', + name: 'key_system_identifier', + length: 36, + nullable: true, + }) + declare keySystemIdentifier: string | null +} diff --git a/packages/syncing-server/src/Infra/TypeORM/SQLItemRepository.ts b/packages/syncing-server/src/Infra/TypeORM/SQLItemRepository.ts new file mode 100644 index 000000000..568d6110d --- /dev/null +++ b/packages/syncing-server/src/Infra/TypeORM/SQLItemRepository.ts @@ -0,0 +1,78 @@ +import { Repository, SelectQueryBuilder } from 'typeorm' +import { MapperInterface } from '@standardnotes/domain-core' +import { Logger } from 'winston' + +import { Item } from '../../Domain/Item/Item' +import { SQLLegacyItem } from './SQLLegacyItem' +import { SQLLegacyItemRepository } from './SQLLegacyItemRepository' +import { ItemQuery } from '../../Domain/Item/ItemQuery' + +export class SQLItemRepository extends SQLLegacyItemRepository { + constructor( + protected override ormRepository: Repository, + protected override mapper: MapperInterface, + protected override logger: Logger, + ) { + super(ormRepository, mapper, logger) + } + + protected override 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.includeSharedVaultUuids !== undefined && query.includeSharedVaultUuids.length > 0) { + if (query.userUuid) { + queryBuilder.where('(item.user_uuid = :userUuid OR item.shared_vault_uuid IN (:...includeSharedVaultUuids))', { + userUuid: query.userUuid, + includeSharedVaultUuids: query.includeSharedVaultUuids, + }) + } else { + queryBuilder.where('item.shared_vault_uuid IN (:...includeSharedVaultUuids)', { + includeSharedVaultUuids: query.includeSharedVaultUuids, + }) + } + } else if (query.exclusiveSharedVaultUuids !== undefined && query.exclusiveSharedVaultUuids.length > 0) { + queryBuilder.where('item.shared_vault_uuid IN (:...exclusiveSharedVaultUuids)', { + exclusiveSharedVaultUuids: query.exclusiveSharedVaultUuids, + }) + } else 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 >= :createdAfter AND item.created_at <= :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 + } +} diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMItem.ts b/packages/syncing-server/src/Infra/TypeORM/SQLLegacyItem.ts similarity index 98% rename from packages/syncing-server/src/Infra/TypeORM/TypeORMItem.ts rename to packages/syncing-server/src/Infra/TypeORM/SQLLegacyItem.ts index d3b1f5815..57ee023f2 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMItem.ts +++ b/packages/syncing-server/src/Infra/TypeORM/SQLLegacyItem.ts @@ -8,7 +8,7 @@ import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm' 'createdAtTimestamp', ]) @Index('user_uuid_and_deleted', ['userUuid', 'deleted']) -export class TypeORMItem { +export class SQLLegacyItem { @PrimaryGeneratedColumn('uuid') declare uuid: string diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts b/packages/syncing-server/src/Infra/TypeORM/SQLLegacyItemRepository.ts similarity index 95% rename from packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts rename to packages/syncing-server/src/Infra/TypeORM/SQLLegacyItemRepository.ts index 1e1cfd9e9..0a36ffa72 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts +++ b/packages/syncing-server/src/Infra/TypeORM/SQLLegacyItemRepository.ts @@ -6,14 +6,14 @@ import { Item } from '../../Domain/Item/Item' import { ItemQuery } from '../../Domain/Item/ItemQuery' import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface' import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload' -import { TypeORMItem } from './TypeORMItem' +import { SQLLegacyItem } from './SQLLegacyItem' import { ItemContentSizeDescriptor } from '../../Domain/Item/ItemContentSizeDescriptor' -export class TypeORMItemRepository implements ItemRepositoryInterface { +export class SQLLegacyItemRepository implements ItemRepositoryInterface { constructor( - private ormRepository: Repository, - private mapper: MapperInterface, - private logger: Logger, + protected ormRepository: Repository, + protected mapper: MapperInterface, + protected logger: Logger, ) {} async save(item: Item): Promise { @@ -179,7 +179,7 @@ export class TypeORMItemRepository implements ItemRepositoryInterface { .execute() } - private createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder { + protected createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder { const queryBuilder = this.ormRepository.createQueryBuilder('item') if (query.sortBy !== undefined && query.sortOrder !== undefined) { diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts index 545988f22..92f490a36 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts +++ b/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepositoryResolver.ts @@ -5,13 +5,13 @@ import { ItemRepositoryResolverInterface } from '../../Domain/Item/ItemRepositor export class TypeORMItemRepositoryResolver implements ItemRepositoryResolverInterface { constructor( - private mysqlItemRepository: ItemRepositoryInterface, + private SQLItemRepository: ItemRepositoryInterface, private mongoDbItemRepository: ItemRepositoryInterface | null, ) {} resolve(roleNames: RoleNameCollection): ItemRepositoryInterface { if (!this.mongoDbItemRepository) { - return this.mysqlItemRepository + return this.SQLItemRepository } const transitionRoleName = RoleName.create(RoleName.NAMES.TransitionUser).getValue() @@ -20,6 +20,6 @@ export class TypeORMItemRepositoryResolver implements ItemRepositoryResolverInte return this.mongoDbItemRepository } - return this.mysqlItemRepository + return this.SQLItemRepository } } diff --git a/packages/syncing-server/src/Mapping/Persistence/SQLItemPersistenceMapper.ts b/packages/syncing-server/src/Mapping/Persistence/SQLItemPersistenceMapper.ts new file mode 100644 index 000000000..289ff7ec5 --- /dev/null +++ b/packages/syncing-server/src/Mapping/Persistence/SQLItemPersistenceMapper.ts @@ -0,0 +1,148 @@ +import { Timestamps, MapperInterface, UniqueEntityId, Uuid, ContentType, Dates } from '@standardnotes/domain-core' + +import { Item } from '../../Domain/Item/Item' + +import { SQLItem } from '../../Infra/TypeORM/SQLItem' +import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation' +import { SharedVaultAssociation } from '../../Domain/SharedVault/SharedVaultAssociation' + +export class SQLItemPersistenceMapper implements MapperInterface { + toDomain(projection: SQLItem): Item { + const uuidOrError = Uuid.create(projection.uuid) + if (uuidOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${uuidOrError.getError()}`) + } + const uuid = uuidOrError.getValue() + + let duplicateOf = null + if (projection.duplicateOf) { + const duplicateOfOrError = Uuid.create(projection.duplicateOf) + if (duplicateOfOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${duplicateOfOrError.getError()}`) + } + duplicateOf = duplicateOfOrError.getValue() + } + + const contentTypeOrError = ContentType.create(projection.contentType) + if (contentTypeOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${contentTypeOrError.getError()}`) + } + const contentType = contentTypeOrError.getValue() + + const userUuidOrError = Uuid.create(projection.userUuid) + if (userUuidOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${userUuidOrError.getError()}`) + } + const userUuid = userUuidOrError.getValue() + + const datesOrError = Dates.create(projection.createdAt, projection.updatedAt) + if (datesOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${datesOrError.getError()}`) + } + const dates = datesOrError.getValue() + + const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp) + if (timestampsOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${timestampsOrError.getError()}`) + } + const timestamps = timestampsOrError.getValue() + + let updatedWithSession = null + if (projection.updatedWithSession) { + const updatedWithSessionOrError = Uuid.create(projection.updatedWithSession) + if (updatedWithSessionOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${updatedWithSessionOrError.getError()}`) + } + updatedWithSession = updatedWithSessionOrError.getValue() + } + + let sharedVaultAssociation: SharedVaultAssociation | undefined = undefined + if (projection.sharedVaultUuid && projection.lastEditedBy) { + const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid) + if (sharedVaultUuidOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${sharedVaultUuidOrError.getError()}`) + } + const sharedVaultUuid = sharedVaultUuidOrError.getValue() + + const lastEditedByOrError = Uuid.create(projection.lastEditedBy) + if (lastEditedByOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${lastEditedByOrError.getError()}`) + } + const lastEditedBy = lastEditedByOrError.getValue() + + const sharedVaultAssociationOrError = SharedVaultAssociation.create({ + sharedVaultUuid, + lastEditedBy, + }) + if (sharedVaultAssociationOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${sharedVaultAssociationOrError.getError()}`) + } + sharedVaultAssociation = sharedVaultAssociationOrError.getValue() + } + + let keySystemAssociation: KeySystemAssociation | undefined = undefined + if (projection.keySystemIdentifier) { + const keySystemAssociationOrError = KeySystemAssociation.create(projection.keySystemIdentifier) + if (keySystemAssociationOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${keySystemAssociationOrError.getError()}`) + } + keySystemAssociation = keySystemAssociationOrError.getValue() + } + + const itemOrError = Item.create( + { + duplicateOf, + itemsKeyId: projection.itemsKeyId, + content: projection.content, + contentType, + contentSize: projection.contentSize ?? undefined, + encItemKey: projection.encItemKey, + authHash: projection.authHash, + userUuid, + deleted: projection.deleted, + dates, + timestamps, + updatedWithSession, + sharedVaultAssociation, + keySystemAssociation, + }, + new UniqueEntityId(uuid.value), + ) + if (itemOrError.isFailed()) { + throw new Error(`Failed to create item from projection: ${itemOrError.getError()}`) + } + + return itemOrError.getValue() + } + + toProjection(domain: Item): SQLItem { + const typeorm = new SQLItem() + + typeorm.uuid = domain.id.toString() + typeorm.duplicateOf = domain.props.duplicateOf ? domain.props.duplicateOf.value : null + typeorm.itemsKeyId = domain.props.itemsKeyId + typeorm.content = domain.props.content + typeorm.contentType = domain.props.contentType.value + typeorm.contentSize = domain.props.contentSize ?? null + typeorm.encItemKey = domain.props.encItemKey + typeorm.authHash = domain.props.authHash + typeorm.userUuid = domain.props.userUuid.value + typeorm.deleted = domain.props.deleted + typeorm.createdAt = domain.props.dates.createdAt + typeorm.updatedAt = domain.props.dates.updatedAt + typeorm.createdAtTimestamp = domain.props.timestamps.createdAt + typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt + typeorm.updatedWithSession = domain.props.updatedWithSession ? domain.props.updatedWithSession.value : null + typeorm.lastEditedBy = domain.props.sharedVaultAssociation + ? domain.props.sharedVaultAssociation.props.lastEditedBy.value + : null + typeorm.sharedVaultUuid = domain.props.sharedVaultAssociation + ? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value + : null + typeorm.keySystemIdentifier = domain.props.keySystemAssociation + ? domain.props.keySystemAssociation.props.keySystemIdentifier + : null + + return typeorm + } +} diff --git a/packages/syncing-server/src/Mapping/Persistence/ItemPersistenceMapper.ts b/packages/syncing-server/src/Mapping/Persistence/SQLLegacyItemPersistenceMapper.ts similarity index 92% rename from packages/syncing-server/src/Mapping/Persistence/ItemPersistenceMapper.ts rename to packages/syncing-server/src/Mapping/Persistence/SQLLegacyItemPersistenceMapper.ts index 1ab7b93ef..9a8a62d8f 100644 --- a/packages/syncing-server/src/Mapping/Persistence/ItemPersistenceMapper.ts +++ b/packages/syncing-server/src/Mapping/Persistence/SQLLegacyItemPersistenceMapper.ts @@ -2,10 +2,10 @@ import { Timestamps, MapperInterface, UniqueEntityId, Uuid, ContentType, Dates } import { Item } from '../../Domain/Item/Item' -import { TypeORMItem } from '../../Infra/TypeORM/TypeORMItem' +import { SQLLegacyItem } from '../../Infra/TypeORM/SQLLegacyItem' -export class ItemPersistenceMapper implements MapperInterface { - toDomain(projection: TypeORMItem): Item { +export class SQLLegacyItemPersistenceMapper implements MapperInterface { + toDomain(projection: SQLLegacyItem): Item { const uuidOrError = Uuid.create(projection.uuid) if (uuidOrError.isFailed()) { throw new Error(`Failed to create item from projection: ${uuidOrError.getError()}`) @@ -78,8 +78,8 @@ export class ItemPersistenceMapper implements MapperInterface return itemOrError.getValue() } - toProjection(domain: Item): TypeORMItem { - const typeorm = new TypeORMItem() + toProjection(domain: Item): SQLLegacyItem { + const typeorm = new SQLLegacyItem() typeorm.uuid = domain.id.toString() typeorm.duplicateOf = domain.props.duplicateOf ? domain.props.duplicateOf.value : null