ソースを参照

fix(syncing-server): refactor shared vault and key system associations (#698)

* feat(syncing-server): refactor persistence of shared vault and key system associations

* fix(syncing-server): refactor shared vault and key system associations
Karol Sójko 1 年間 前
コミット
31d1eef7f7
26 ファイル変更133 行追加426 行削除
  1. 21 0
      packages/syncing-server/migrations/mysql/1692264556858-remove_associations.ts
  2. 17 0
      packages/syncing-server/migrations/sqlite/1692264735730-remove_associations.ts
  3. 0 36
      packages/syncing-server/src/Bootstrap/Container.ts
  4. 0 4
      packages/syncing-server/src/Bootstrap/Types.ts
  5. 0 12
      packages/syncing-server/src/Domain/Item/Item.spec.ts
  6. 0 8
      packages/syncing-server/src/Domain/Item/SaveRule/SharedVaultFilter.spec.ts
  7. 0 2
      packages/syncing-server/src/Domain/Item/SaveRule/SharedVaultSnjsFilter.spec.ts
  8. 0 4
      packages/syncing-server/src/Domain/KeySystem/KeySystemAssocationProps.ts
  9. 0 4
      packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts
  10. 0 9
      packages/syncing-server/src/Domain/KeySystem/KeySystemAssociationRepositoryInterface.ts
  11. 1 3
      packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts
  12. 1 3
      packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociationProps.ts
  13. 0 9
      packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociationRepositoryInterface.ts
  14. 0 6
      packages/syncing-server/src/Domain/UseCase/SharedVaults/DetermineSharedVaultOperationOnItem/DetermineSharedVaultOperationOnItem.spec.ts
  15. 0 3
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.spec.ts
  16. 0 10
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts
  17. 0 4
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts
  18. 0 12
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts
  19. 9 0
      packages/syncing-server/src/Infra/TypeORM/MongoDBItem.ts
  20. 32 5
      packages/syncing-server/src/Infra/TypeORM/MongoDBItemRepository.ts
  21. 3 98
      packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts
  22. 0 36
      packages/syncing-server/src/Infra/TypeORM/TypeORMKeySystemAssociationRepository.ts
  23. 0 36
      packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultAssociationRepository.ts
  24. 0 56
      packages/syncing-server/src/Mapping/Persistence/KeySystemAssociationPersistenceMapper.ts
  25. 49 1
      packages/syncing-server/src/Mapping/Persistence/MongoDB/MongoDBItemPersistenceMapper.ts
  26. 0 65
      packages/syncing-server/src/Mapping/Persistence/SharedVaultAssociationPersistenceMapper.ts

+ 21 - 0
packages/syncing-server/migrations/mysql/1692264556858-remove_associations.ts

@@ -0,0 +1,21 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RemoveAssociations1692264556858 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(
+      'DROP INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations`',
+    )
+    await queryRunner.query('DROP INDEX `item_uuid_on_key_system_associations` ON `key_system_associations`')
+    await queryRunner.query('DROP TABLE `key_system_associations`')
+
+    await queryRunner.query('DROP INDEX `item_uuid_on_shared_vault_associations` ON `shared_vault_associations`')
+    await queryRunner.query(
+      'DROP INDEX `shared_vault_uuid_on_shared_vault_associations` ON `shared_vault_associations`',
+    )
+    await queryRunner.query('DROP TABLE `shared_vault_associations`')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 17 - 0
packages/syncing-server/migrations/sqlite/1692264735730-remove_associations.ts

@@ -0,0 +1,17 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class RemoveAssociations1692264735730 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('DROP INDEX "key_system_identifier_on_key_system_associations"')
+    await queryRunner.query('DROP INDEX "item_uuid_on_key_system_associations"')
+    await queryRunner.query('DROP TABLE "key_system_associations"')
+
+    await queryRunner.query('DROP INDEX "item_uuid_on_shared_vault_associations"')
+    await queryRunner.query('DROP INDEX "shared_vault_uuid_on_shared_vault_associations"')
+    await queryRunner.query('DROP TABLE "shared_vault_associations"')
+  }
+
+  public async down(): Promise<void> {
+    return
+  }
+}

+ 0 - 36
packages/syncing-server/src/Bootstrap/Container.ts

@@ -79,15 +79,7 @@ import { ItemHashHttpMapper } from '../Mapping/Http/ItemHashHttpMapper'
 import { ItemHash } from '../Domain/Item/ItemHash'
 import { ItemHashHttpRepresentation } from '../Mapping/Http/ItemHashHttpRepresentation'
 import { TypeORMKeySystemAssociation } from '../Infra/TypeORM/TypeORMKeySystemAssociation'
-import { SharedVaultAssociation } from '../Domain/SharedVault/SharedVaultAssociation'
 import { TypeORMSharedVaultAssociation } from '../Infra/TypeORM/TypeORMSharedVaultAssociation'
-import { SharedVaultAssociationPersistenceMapper } from '../Mapping/Persistence/SharedVaultAssociationPersistenceMapper'
-import { TypeORMKeySystemAssociationRepository } from '../Infra/TypeORM/TypeORMKeySystemAssociationRepository'
-import { SharedVaultAssociationRepositoryInterface } from '../Domain/SharedVault/SharedVaultAssociationRepositoryInterface'
-import { TypeORMSharedVaultAssociationRepository } from '../Infra/TypeORM/TypeORMSharedVaultAssociationRepository'
-import { KeySystemAssociation } from '../Domain/KeySystem/KeySystemAssociation'
-import { KeySystemAssociationRepositoryInterface } from '../Domain/KeySystem/KeySystemAssociationRepositoryInterface'
-import { KeySystemAssociationPersistenceMapper } from '../Mapping/Persistence/KeySystemAssociationPersistenceMapper'
 import { BaseSharedVaultInvitesController } from '../Infra/InversifyExpressUtils/Base/BaseSharedVaultInvitesController'
 import { InviteUserToSharedVault } from '../Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault'
 import { TypeORMSharedVaultRepository } from '../Infra/TypeORM/TypeORMSharedVaultRepository'
@@ -316,16 +308,6 @@ export class ContainerConfigLoader {
     container
       .bind<MapperInterface<Item, ItemBackupRepresentation>>(TYPES.Sync_ItemBackupMapper)
       .toConstantValue(new ItemBackupMapper(container.get(TYPES.Sync_Timer)))
-    container
-      .bind<MapperInterface<KeySystemAssociation, TypeORMKeySystemAssociation>>(
-        TYPES.Sync_KeySystemAssociationPersistenceMapper,
-      )
-      .toConstantValue(new KeySystemAssociationPersistenceMapper())
-    container
-      .bind<MapperInterface<SharedVaultAssociation, TypeORMSharedVaultAssociation>>(
-        TYPES.Sync_SharedVaultAssociationPersistenceMapper,
-      )
-      .toConstantValue(new SharedVaultAssociationPersistenceMapper())
     container
       .bind<MapperInterface<SharedVault, TypeORMSharedVault>>(TYPES.Sync_SharedVaultPersistenceMapper)
       .toConstantValue(new SharedVaultPersistenceMapper())
@@ -397,22 +379,6 @@ export class ContainerConfigLoader {
     }
 
     // Repositories
-    container
-      .bind<KeySystemAssociationRepositoryInterface>(TYPES.Sync_KeySystemAssociationRepository)
-      .toConstantValue(
-        new TypeORMKeySystemAssociationRepository(
-          container.get(TYPES.Sync_ORMKeySystemAssociationRepository),
-          container.get(TYPES.Sync_KeySystemAssociationPersistenceMapper),
-        ),
-      )
-    container
-      .bind<SharedVaultAssociationRepositoryInterface>(TYPES.Sync_SharedVaultAssociationRepository)
-      .toConstantValue(
-        new TypeORMSharedVaultAssociationRepository(
-          container.get(TYPES.Sync_ORMSharedVaultAssociationRepository),
-          container.get(TYPES.Sync_SharedVaultAssociationPersistenceMapper),
-        ),
-      )
     container
       .bind<ItemRepositoryInterface>(TYPES.Sync_ItemRepository)
       .toConstantValue(
@@ -425,8 +391,6 @@ export class ContainerConfigLoader {
           : new TypeORMItemRepository(
               container.get(TYPES.Sync_ORMItemRepository),
               container.get(TYPES.Sync_ItemPersistenceMapper),
-              container.get(TYPES.Sync_KeySystemAssociationRepository),
-              container.get(TYPES.Sync_SharedVaultAssociationRepository),
               container.get(TYPES.Sync_Logger),
             ),
       )

+ 0 - 4
packages/syncing-server/src/Bootstrap/Types.ts

@@ -8,8 +8,6 @@ const TYPES = {
   Sync_Env: Symbol.for('Sync_Env'),
   // Repositories
   Sync_ItemRepository: Symbol.for('Sync_ItemRepository'),
-  Sync_KeySystemAssociationRepository: Symbol.for('Sync_KeySystemAssociationRepository'),
-  Sync_SharedVaultAssociationRepository: Symbol.for('Sync_SharedVaultAssociationRepository'),
   Sync_SharedVaultRepository: Symbol.for('Sync_SharedVaultRepository'),
   Sync_SharedVaultInviteRepository: Symbol.for('Sync_SharedVaultInviteRepository'),
   Sync_SharedVaultUserRepository: Symbol.for('Sync_SharedVaultUserRepository'),
@@ -132,8 +130,6 @@ const TYPES = {
   Sync_SavedItemHttpMapper: Symbol.for('Sync_SavedItemHttpMapper'),
   Sync_ItemConflictHttpMapper: Symbol.for('Sync_ItemConflictHttpMapper'),
   Sync_ItemBackupMapper: Symbol.for('Sync_ItemBackupMapper'),
-  Sync_KeySystemAssociationPersistenceMapper: Symbol.for('Sync_KeySystemAssociationPersistenceMapper'),
-  Sync_SharedVaultAssociationPersistenceMapper: Symbol.for('Sync_SharedVaultAssociationPersistenceMapper'),
   Sync_SharedVaultPersistenceMapper: Symbol.for('Sync_SharedVaultPersistenceMapper'),
   Sync_SharedVaultUserPersistenceMapper: Symbol.for('Sync_SharedVaultUserPersistenceMapper'),
   Sync_SharedVaultInvitePersistenceMapper: Symbol.for('Sync_SharedVaultInvitePersistenceMapper'),

+ 0 - 12
packages/syncing-server/src/Domain/Item/Item.spec.ts

@@ -61,10 +61,8 @@ describe('Item', () => {
       dates: Dates.create(new Date(123), new Date(123)).getValue(),
       timestamps: Timestamps.create(123, 123).getValue(),
       sharedVaultAssociation: SharedVaultAssociation.create({
-        itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     })
 
@@ -113,9 +111,7 @@ describe('Item', () => {
       dates: Dates.create(new Date(123), new Date(123)).getValue(),
       timestamps: Timestamps.create(123, 123).getValue(),
       keySystemAssociation: KeySystemAssociation.create({
-        itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         keySystemIdentifier: 'key-system-identifier',
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     })
 
@@ -144,10 +140,8 @@ describe('Item', () => {
 
   it('should set shared vault association', () => {
     const sharedVaultAssociation = SharedVaultAssociation.create({
-      itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-      timestamps: Timestamps.create(123, 123).getValue(),
     }).getValue()
 
     const entity = Item.create({
@@ -184,10 +178,8 @@ describe('Item', () => {
       dates: Dates.create(new Date(123), new Date(123)).getValue(),
       timestamps: Timestamps.create(123, 123).getValue(),
       sharedVaultAssociation: SharedVaultAssociation.create({
-        itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     }).getValue()
 
@@ -199,9 +191,7 @@ describe('Item', () => {
 
   it('should set key system association', () => {
     const keySystemAssociation = KeySystemAssociation.create({
-      itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       keySystemIdentifier: 'key-system-identifier',
-      timestamps: Timestamps.create(123, 123).getValue(),
     }).getValue()
 
     const entity = Item.create({
@@ -238,9 +228,7 @@ describe('Item', () => {
       dates: Dates.create(new Date(123), new Date(123)).getValue(),
       timestamps: Timestamps.create(123, 123).getValue(),
       keySystemAssociation: KeySystemAssociation.create({
-        itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         keySystemIdentifier: 'key-system-identifier',
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     }).getValue()
 

+ 0 - 8
packages/syncing-server/src/Domain/Item/SaveRule/SharedVaultFilter.spec.ts

@@ -40,10 +40,8 @@ describe('SharedVaultFilter', () => {
         dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
         timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
         sharedVaultAssociation: SharedVaultAssociation.create({
-          itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-          timestamps: Timestamps.create(123, 123).getValue(),
         }).getValue(),
       },
       new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
@@ -254,10 +252,8 @@ describe('SharedVaultFilter', () => {
           dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
           timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
           sharedVaultAssociation: SharedVaultAssociation.create({
-            itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-            timestamps: Timestamps.create(123, 123).getValue(),
           }).getValue(),
         },
         new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
@@ -427,10 +423,8 @@ describe('SharedVaultFilter', () => {
           dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
           timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
           sharedVaultAssociation: SharedVaultAssociation.create({
-            itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-            timestamps: Timestamps.create(123, 123).getValue(),
           }).getValue(),
         },
         new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
@@ -589,10 +583,8 @@ describe('SharedVaultFilter', () => {
           dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
           timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
           sharedVaultAssociation: SharedVaultAssociation.create({
-            itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
             sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-            timestamps: Timestamps.create(123, 123).getValue(),
           }).getValue(),
         },
         new UniqueEntityId('00000000-0000-0000-0000-000000000000'),

+ 0 - 2
packages/syncing-server/src/Domain/Item/SaveRule/SharedVaultSnjsFilter.spec.ts

@@ -26,10 +26,8 @@ describe('SharedVaultSnjsFilter', () => {
         dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
         timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
         sharedVaultAssociation: SharedVaultAssociation.create({
-          itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-          timestamps: Timestamps.create(123, 123).getValue(),
         }).getValue(),
       },
       new UniqueEntityId('00000000-0000-0000-0000-000000000000'),

+ 0 - 4
packages/syncing-server/src/Domain/KeySystem/KeySystemAssocationProps.ts

@@ -1,7 +1,3 @@
-import { Timestamps, Uuid } from '@standardnotes/domain-core'
-
 export interface KeySystemAssociationProps {
-  itemUuid: Uuid
   keySystemIdentifier: string
-  timestamps: Timestamps
 }

+ 0 - 4
packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts

@@ -1,12 +1,8 @@
-import { Timestamps, Uuid } from '@standardnotes/domain-core'
-
 import { KeySystemAssociation } from './KeySystemAssociation'
 
 describe('KeySystemAssociation', () => {
   it('should create an entity', () => {
     const entityOrError = KeySystemAssociation.create({
-      timestamps: Timestamps.create(123456789, 123456789).getValue(),
-      itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       keySystemIdentifier: '00000000-0000-0000-0000-000000000000',
     })
 

+ 0 - 9
packages/syncing-server/src/Domain/KeySystem/KeySystemAssociationRepositoryInterface.ts

@@ -1,9 +0,0 @@
-import { Uuid } from '@standardnotes/domain-core'
-
-import { KeySystemAssociation } from './KeySystemAssociation'
-
-export interface KeySystemAssociationRepositoryInterface {
-  save(keySystem: KeySystemAssociation): Promise<void>
-  remove(keySystem: KeySystemAssociation): Promise<void>
-  findByItemUuid(itemUuid: Uuid): Promise<KeySystemAssociation | null>
-}

+ 1 - 3
packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts

@@ -1,12 +1,10 @@
-import { Timestamps, Uuid } from '@standardnotes/domain-core'
+import { Uuid } from '@standardnotes/domain-core'
 
 import { SharedVaultAssociation } from './SharedVaultAssociation'
 
 describe('SharedVaultAssociation', () => {
   it('should create an entity', () => {
     const entityOrError = SharedVaultAssociation.create({
-      timestamps: Timestamps.create(123456789, 123456789).getValue(),
-      itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
     })

+ 1 - 3
packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociationProps.ts

@@ -1,8 +1,6 @@
-import { Timestamps, Uuid } from '@standardnotes/domain-core'
+import { Uuid } from '@standardnotes/domain-core'
 
 export interface SharedVaultAssociationProps {
   lastEditedBy: Uuid
   sharedVaultUuid: Uuid
-  itemUuid: Uuid
-  timestamps: Timestamps
 }

+ 0 - 9
packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociationRepositoryInterface.ts

@@ -1,9 +0,0 @@
-import { Uuid } from '@standardnotes/domain-core'
-
-import { SharedVaultAssociation } from './SharedVaultAssociation'
-
-export interface SharedVaultAssociationRepositoryInterface {
-  save(sharedVaultAssociation: SharedVaultAssociation): Promise<void>
-  remove(sharedVaultAssociation: SharedVaultAssociation): Promise<void>
-  findByItemUuid(itemUuid: Uuid): Promise<SharedVaultAssociation | null>
-}

+ 0 - 6
packages/syncing-server/src/Domain/UseCase/SharedVaults/DetermineSharedVaultOperationOnItem/DetermineSharedVaultOperationOnItem.spec.ts

@@ -60,10 +60,8 @@ describe('DetermineSharedVaultOperationOnItem', () => {
     existingItem = Item.create({
       ...existingItem.props,
       sharedVaultAssociation: SharedVaultAssociation.create({
-        itemUuid: existingItem.uuid,
         lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     }).getValue()
 
@@ -88,10 +86,8 @@ describe('DetermineSharedVaultOperationOnItem', () => {
     existingItem = Item.create({
       ...existingItem.props,
       sharedVaultAssociation: SharedVaultAssociation.create({
-        itemUuid: existingItem.uuid,
         lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     }).getValue()
 
@@ -132,10 +128,8 @@ describe('DetermineSharedVaultOperationOnItem', () => {
     existingItem = Item.create({
       ...existingItem.props,
       sharedVaultAssociation: SharedVaultAssociation.create({
-        itemUuid: existingItem.uuid,
         lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
         sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-        timestamps: Timestamps.create(123, 123).getValue(),
       }).getValue(),
     }).getValue()
 

+ 0 - 3
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.spec.ts

@@ -365,9 +365,6 @@ describe('SaveNewItem', () => {
       })
 
       expect(result.isFailed()).toBeFalsy()
-      expect(result.getValue().props.keySystemAssociation?.props.itemUuid.value).toEqual(
-        '00000000-0000-0000-0000-000000000000',
-      )
       expect(itemRepository.save).toHaveBeenCalled()
     })
 

+ 0 - 10
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts

@@ -112,11 +112,6 @@ export class SaveNewItem implements UseCaseInterface<Item> {
       const sharedVaultAssociationOrError = SharedVaultAssociation.create({
         lastEditedBy: userUuid,
         sharedVaultUuid: dto.itemHash.sharedVaultUuid as Uuid,
-        timestamps: Timestamps.create(
-          this.timer.getTimestampInMicroseconds(),
-          this.timer.getTimestampInMicroseconds(),
-        ).getValue(),
-        itemUuid: uuid,
       })
       if (sharedVaultAssociationOrError.isFailed()) {
         return Result.fail(sharedVaultAssociationOrError.getError())
@@ -132,11 +127,6 @@ export class SaveNewItem implements UseCaseInterface<Item> {
       const keySystemIdentifier = dto.itemHash.props.key_system_identifier as string
 
       const keySystemAssociationOrError = KeySystemAssociation.create({
-        itemUuid: uuid,
-        timestamps: Timestamps.create(
-          this.timer.getTimestampInMicroseconds(),
-          this.timer.getTimestampInMicroseconds(),
-        ).getValue(),
         keySystemIdentifier,
       })
       if (keySystemAssociationOrError.isFailed()) {

+ 0 - 4
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts

@@ -352,10 +352,8 @@ describe('UpdateExistingItem', () => {
 
       item1.setSharedVaultAssociation(
         SharedVaultAssociation.create({
-          itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
-          timestamps: Timestamps.create(123, 123).getValue(),
         }).getValue(),
       )
       const idBefore = item1.props.sharedVaultAssociation?.id.toString()
@@ -532,9 +530,7 @@ describe('UpdateExistingItem', () => {
 
       item1.setKeySystemAssociation(
         KeySystemAssociation.create({
-          itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
           keySystemIdentifier: '00000000-0000-0000-0000-000000000000',
-          timestamps: Timestamps.create(123, 123).getValue(),
         }).getValue(),
       )
       const idBefore = item1.props.keySystemAssociation?.id.toString()

+ 0 - 12
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts

@@ -128,13 +128,6 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
         {
           lastEditedBy: userUuid,
           sharedVaultUuid: dto.itemHash.sharedVaultUuid as Uuid,
-          timestamps: Timestamps.create(
-            dto.existingItem.props.sharedVaultAssociation
-              ? dto.existingItem.props.sharedVaultAssociation.props.timestamps.createdAt
-              : this.timer.getTimestampInMicroseconds(),
-            this.timer.getTimestampInMicroseconds(),
-          ).getValue(),
-          itemUuid: Uuid.create(dto.existingItem.id.toString()).getValue(),
         },
         new UniqueEntityId(
           dto.existingItem.props.sharedVaultAssociation
@@ -171,11 +164,6 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
 
       const keySystemAssociationOrError = KeySystemAssociation.create(
         {
-          itemUuid: Uuid.create(dto.existingItem.id.toString()).getValue(),
-          timestamps: Timestamps.create(
-            this.timer.getTimestampInMicroseconds(),
-            this.timer.getTimestampInMicroseconds(),
-          ).getValue(),
           keySystemIdentifier,
         },
         new UniqueEntityId(

+ 9 - 0
packages/syncing-server/src/Infra/TypeORM/MongoDBItem.ts

@@ -53,4 +53,13 @@ export class MongoDBItem {
 
   @Column()
   declare updatedWithSession: string | null
+
+  @Column()
+  declare lastEditedBy: string | null
+
+  @Column()
+  declare sharedVaultUuid: string | null
+
+  @Column()
+  declare keySystemIdentifier: string | null
 }

+ 32 - 5
packages/syncing-server/src/Infra/TypeORM/MongoDBItemRepository.ts

@@ -39,7 +39,9 @@ export class MongoDBItemRepository implements ItemRepositoryInterface {
   }
 
   async countAll(query: ItemQuery): Promise<number> {
-    return this.mongoRepository.count(this.createFindOptions(query))
+    const options = this.createFindOptions(query)
+
+    return this.mongoRepository.count((options as FindManyOptions<MongoDBItem>).where)
   }
 
   async findContentSizeForComputingTransferLimit(
@@ -168,10 +170,6 @@ export class MongoDBItemRepository implements ItemRepositoryInterface {
       options.order = { [query.sortBy]: query.sortOrder }
     }
 
-    if (query.userUuid !== undefined) {
-      options.where = { ...options.where, userUuid: { $eq: query.userUuid } }
-    }
-
     if (query.uuids && query.uuids.length > 0) {
       options.where = {
         ...options.where,
@@ -205,6 +203,35 @@ export class MongoDBItemRepository implements ItemRepositoryInterface {
       }
     }
 
+    if (query.includeSharedVaultUuids !== undefined && query.includeSharedVaultUuids.length > 0) {
+      if (query.userUuid) {
+        options.where = {
+          $and: [
+            { ...options.where },
+            {
+              $or: [{ sharedVaultUuid: { $in: query.includeSharedVaultUuids } }, { userUuid: { $eq: query.userUuid } }],
+            },
+          ],
+        }
+      } else {
+        options.where = {
+          $and: [
+            { ...options.where },
+            {
+              $or: [{ sharedVaultUuid: { $in: query.includeSharedVaultUuids } }],
+            },
+          ],
+        }
+      }
+    } else if (query.exclusiveSharedVaultUuids !== undefined && query.exclusiveSharedVaultUuids.length > 0) {
+      options.where = {
+        ...options.where,
+        sharedVaultUuid: { $in: query.exclusiveSharedVaultUuids },
+      }
+    } else if (query.userUuid !== undefined) {
+      options.where = { ...options.where, userUuid: { $eq: query.userUuid } }
+    }
+
     if (query.offset !== undefined) {
       options.skip = query.offset
     }

+ 3 - 98
packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts

@@ -1,5 +1,5 @@
-import { Repository, SelectQueryBuilder, Brackets } from 'typeorm'
-import { Change, MapperInterface, Uuid } from '@standardnotes/domain-core'
+import { Repository, SelectQueryBuilder } from 'typeorm'
+import { MapperInterface, Uuid } from '@standardnotes/domain-core'
 import { Logger } from 'winston'
 
 import { Item } from '../../Domain/Item/Item'
@@ -7,18 +7,11 @@ import { ItemQuery } from '../../Domain/Item/ItemQuery'
 import { ItemRepositoryInterface } from '../../Domain/Item/ItemRepositoryInterface'
 import { ExtendedIntegrityPayload } from '../../Domain/Item/ExtendedIntegrityPayload'
 import { TypeORMItem } from './TypeORMItem'
-import { KeySystemAssociationRepositoryInterface } from '../../Domain/KeySystem/KeySystemAssociationRepositoryInterface'
-import { SharedVaultAssociationRepositoryInterface } from '../../Domain/SharedVault/SharedVaultAssociationRepositoryInterface'
-import { TypeORMSharedVaultAssociation } from './TypeORMSharedVaultAssociation'
-import { SharedVaultAssociation } from '../../Domain/SharedVault/SharedVaultAssociation'
-import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation'
 
 export class TypeORMItemRepository implements ItemRepositoryInterface {
   constructor(
     private ormRepository: Repository<TypeORMItem>,
     private mapper: MapperInterface<Item, TypeORMItem>,
-    private keySystemAssociationRepository: KeySystemAssociationRepositoryInterface,
-    private sharedVaultAssociationRepository: SharedVaultAssociationRepositoryInterface,
     private logger: Logger,
   ) {}
 
@@ -26,19 +19,9 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
     const persistence = this.mapper.toProjection(item)
 
     await this.ormRepository.save(persistence)
-
-    await this.persistAssociationChanges(item)
   }
 
   async remove(item: Item): Promise<void> {
-    if (item.props.keySystemAssociation) {
-      await this.keySystemAssociationRepository.remove(item.props.keySystemAssociation)
-    }
-
-    if (item.props.sharedVaultAssociation) {
-      await this.sharedVaultAssociationRepository.remove(item.props.sharedVaultAssociation)
-    }
-
     await this.ormRepository.remove(this.mapper.toProjection(item))
   }
 
@@ -91,8 +74,6 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
     try {
       const item = this.mapper.toDomain(persistence)
 
-      await this.decorateItemWithAssociations(item)
-
       return item
     } catch (error) {
       this.logger.error(`Failed to find item ${uuid.value} by uuid: ${(error as Error).message}`)
@@ -141,8 +122,6 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
     try {
       const item = this.mapper.toDomain(persistence)
 
-      await this.decorateItemWithAssociations(item)
-
       return item
     } catch (error) {
       this.logger.error(`Failed to find item ${uuid} by uuid and userUuid: ${(error as Error).message}`)
@@ -163,8 +142,6 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
       }
     }
 
-    await Promise.all(domainItems.map((item) => this.decorateItemWithAssociations(item)))
-
     return domainItems
   }
 
@@ -196,35 +173,7 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
       queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder)
     }
 
-    if (query.includeSharedVaultUuids !== undefined && query.includeSharedVaultUuids.length > 0) {
-      queryBuilder
-        .leftJoin(
-          TypeORMSharedVaultAssociation,
-          'sharedVaultAssociation',
-          'sharedVaultAssociation.itemUuid = item.uuid',
-        )
-        .where(
-          new Brackets((qb) => {
-            qb.where('sharedVaultAssociation.sharedVaultUuid IN (:...sharedVaultUuids)', {
-              sharedVaultUuids: query.includeSharedVaultUuids,
-            })
-
-            if (query.userUuid) {
-              qb.orWhere('item.user_uuid = :userUuid', { userUuid: query.userUuid })
-            }
-          }),
-        )
-    } else if (query.exclusiveSharedVaultUuids !== undefined && query.exclusiveSharedVaultUuids.length > 0) {
-      queryBuilder
-        .innerJoin(
-          TypeORMSharedVaultAssociation,
-          'sharedVaultAssociation',
-          'sharedVaultAssociation.itemUuid = item.uuid',
-        )
-        .where('sharedVaultAssociation.sharedVaultUuid IN (:...sharedVaultUuids)', {
-          sharedVaultUuids: query.exclusiveSharedVaultUuids,
-        })
-    } else if (query.userUuid !== undefined) {
+    if (query.userUuid !== undefined) {
       queryBuilder.where('item.user_uuid = :userUuid', { userUuid: query.userUuid })
     }
 
@@ -261,48 +210,4 @@ export class TypeORMItemRepository implements ItemRepositoryInterface {
 
     return queryBuilder
   }
-
-  private async decorateItemWithAssociations(item: Item): Promise<void> {
-    await Promise.all([
-      this.decorateItemWithKeySystemAssociation(item),
-      this.decorateItemWithSharedVaultAssociation(item),
-    ])
-  }
-
-  private async decorateItemWithKeySystemAssociation(item: Item): Promise<void> {
-    const keySystemAssociation = await this.keySystemAssociationRepository.findByItemUuid(item.uuid)
-    if (keySystemAssociation) {
-      item.props.keySystemAssociation = keySystemAssociation
-    }
-  }
-
-  private async decorateItemWithSharedVaultAssociation(item: Item): Promise<void> {
-    const sharedVaultAssociation = await this.sharedVaultAssociationRepository.findByItemUuid(item.uuid)
-    if (sharedVaultAssociation) {
-      item.props.sharedVaultAssociation = sharedVaultAssociation
-    }
-  }
-
-  private async persistAssociationChanges(item: Item): Promise<void> {
-    for (const change of item.getChanges()) {
-      if (change.props.changeData instanceof SharedVaultAssociation) {
-        if ([Change.TYPES.Add, Change.TYPES.Modify].includes(change.props.changeType)) {
-          await this.sharedVaultAssociationRepository.save(change.props.changeData)
-        }
-        if (change.props.changeType === Change.TYPES.Remove) {
-          await this.sharedVaultAssociationRepository.remove(change.props.changeData)
-        }
-      }
-      if (change.props.changeData instanceof KeySystemAssociation) {
-        if ([Change.TYPES.Add, Change.TYPES.Modify].includes(change.props.changeType)) {
-          await this.keySystemAssociationRepository.save(change.props.changeData)
-        }
-        if (change.props.changeType === Change.TYPES.Remove) {
-          await this.keySystemAssociationRepository.remove(change.props.changeData)
-        }
-      }
-    }
-
-    item.flushChanges()
-  }
 }

+ 0 - 36
packages/syncing-server/src/Infra/TypeORM/TypeORMKeySystemAssociationRepository.ts

@@ -1,36 +0,0 @@
-import { Repository } from 'typeorm'
-import { MapperInterface, Uuid } from '@standardnotes/domain-core'
-
-import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation'
-import { KeySystemAssociationRepositoryInterface } from '../../Domain/KeySystem/KeySystemAssociationRepositoryInterface'
-import { TypeORMKeySystemAssociation } from './TypeORMKeySystemAssociation'
-
-export class TypeORMKeySystemAssociationRepository implements KeySystemAssociationRepositoryInterface {
-  constructor(
-    private ormRepository: Repository<TypeORMKeySystemAssociation>,
-    private mapper: MapperInterface<KeySystemAssociation, TypeORMKeySystemAssociation>,
-  ) {}
-
-  async findByItemUuid(itemUuid: Uuid): Promise<KeySystemAssociation | null> {
-    const persistence = await this.ormRepository
-      .createQueryBuilder('key_system_association')
-      .where('key_system_association.item_uuid = :itemUuid', {
-        itemUuid: itemUuid.value,
-      })
-      .getOne()
-
-    if (persistence === null) {
-      return null
-    }
-
-    return this.mapper.toDomain(persistence)
-  }
-
-  async save(keySystemAssociation: KeySystemAssociation): Promise<void> {
-    await this.ormRepository.save(this.mapper.toProjection(keySystemAssociation))
-  }
-
-  async remove(keySystemAssociation: KeySystemAssociation): Promise<void> {
-    await this.ormRepository.remove(this.mapper.toProjection(keySystemAssociation))
-  }
-}

+ 0 - 36
packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultAssociationRepository.ts

@@ -1,36 +0,0 @@
-import { Repository } from 'typeorm'
-import { MapperInterface, Uuid } from '@standardnotes/domain-core'
-
-import { SharedVaultAssociation } from '../../Domain/SharedVault/SharedVaultAssociation'
-import { SharedVaultAssociationRepositoryInterface } from '../../Domain/SharedVault/SharedVaultAssociationRepositoryInterface'
-import { TypeORMSharedVaultAssociation } from './TypeORMSharedVaultAssociation'
-
-export class TypeORMSharedVaultAssociationRepository implements SharedVaultAssociationRepositoryInterface {
-  constructor(
-    private ormRepository: Repository<TypeORMSharedVaultAssociation>,
-    private mapper: MapperInterface<SharedVaultAssociation, TypeORMSharedVaultAssociation>,
-  ) {}
-
-  async findByItemUuid(itemUuid: Uuid): Promise<SharedVaultAssociation | null> {
-    const persistence = await this.ormRepository
-      .createQueryBuilder('shared_vault_association')
-      .where('shared_vault_association.item_uuid = :itemUuid', {
-        itemUuid: itemUuid.value,
-      })
-      .getOne()
-
-    if (persistence === null) {
-      return null
-    }
-
-    return this.mapper.toDomain(persistence)
-  }
-
-  async save(sharedVaultAssociation: SharedVaultAssociation): Promise<void> {
-    await this.ormRepository.save(this.mapper.toProjection(sharedVaultAssociation))
-  }
-
-  async remove(sharedVaultAssociation: SharedVaultAssociation): Promise<void> {
-    await this.ormRepository.remove(this.mapper.toProjection(sharedVaultAssociation))
-  }
-}

+ 0 - 56
packages/syncing-server/src/Mapping/Persistence/KeySystemAssociationPersistenceMapper.ts

@@ -1,56 +0,0 @@
-import { MapperInterface, Timestamps, UniqueEntityId, Uuid, Validator } from '@standardnotes/domain-core'
-
-import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation'
-
-import { TypeORMKeySystemAssociation } from '../../Infra/TypeORM/TypeORMKeySystemAssociation'
-
-export class KeySystemAssociationPersistenceMapper
-  implements MapperInterface<KeySystemAssociation, TypeORMKeySystemAssociation>
-{
-  toDomain(projection: TypeORMKeySystemAssociation): KeySystemAssociation {
-    const itemUuidOrError = Uuid.create(projection.itemUuid)
-    if (itemUuidOrError.isFailed()) {
-      throw new Error(`Failed to create key system from projection: ${itemUuidOrError.getError()}`)
-    }
-    const itemUuid = itemUuidOrError.getValue()
-
-    const keySystemIdentifiedValidationResult = Validator.isNotEmptyString(projection.keySystemIdentifier)
-    if (keySystemIdentifiedValidationResult.isFailed()) {
-      throw new Error(`Failed to create key system from projection: ${keySystemIdentifiedValidationResult.getError()}`)
-    }
-
-    const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
-    if (timestampsOrError.isFailed()) {
-      throw new Error(`Failed to create key system from projection: ${timestampsOrError.getError()}`)
-    }
-    const timestamps = timestampsOrError.getValue()
-
-    const keySystemOrError = KeySystemAssociation.create(
-      {
-        itemUuid,
-        timestamps,
-        keySystemIdentifier: projection.keySystemIdentifier,
-      },
-      new UniqueEntityId(projection.uuid),
-    )
-    if (keySystemOrError.isFailed()) {
-      throw new Error(`Failed to create key system from projection: ${keySystemOrError.getError()}`)
-    }
-
-    const keySystem = keySystemOrError.getValue()
-
-    return keySystem
-  }
-
-  toProjection(domain: KeySystemAssociation): TypeORMKeySystemAssociation {
-    const typeorm = new TypeORMKeySystemAssociation()
-
-    typeorm.uuid = domain.id.toString()
-    typeorm.itemUuid = domain.props.itemUuid.value
-    typeorm.keySystemIdentifier = domain.props.keySystemIdentifier
-    typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
-    typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
-
-    return typeorm
-  }
-}

+ 49 - 1
packages/syncing-server/src/Mapping/Persistence/MongoDB/MongoDBItemPersistenceMapper.ts

@@ -1,8 +1,10 @@
 import { ContentType, Dates, MapperInterface, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
+import { BSON } from 'mongodb'
 
 import { MongoDBItem } from '../../../Infra/TypeORM/MongoDBItem'
 import { Item } from '../../../Domain/Item/Item'
-import { BSON } from 'mongodb'
+import { SharedVaultAssociation } from '../../../Domain/SharedVault/SharedVaultAssociation'
+import { KeySystemAssociation } from '../../../Domain/KeySystem/KeySystemAssociation'
 
 export class MongoDBItemPersistenceMapper implements MapperInterface<Item, MongoDBItem> {
   toDomain(projection: MongoDBItem): Item {
@@ -45,6 +47,41 @@ export class MongoDBItemPersistenceMapper implements MapperInterface<Item, Mongo
     }
     const timestamps = timestampsOrError.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({
+        keySystemIdentifier: projection.keySystemIdentifier,
+      })
+      if (keySystemAssociationOrError.isFailed()) {
+        throw new Error(`Failed to create item from projection: ${keySystemAssociationOrError.getError()}`)
+      }
+      keySystemAssociation = keySystemAssociationOrError.getValue()
+    }
+
     let updatedWithSession = null
     if (projection.updatedWithSession) {
       const updatedWithSessionOrError = Uuid.create(projection.updatedWithSession)
@@ -68,6 +105,8 @@ export class MongoDBItemPersistenceMapper implements MapperInterface<Item, Mongo
         dates,
         timestamps,
         updatedWithSession,
+        sharedVaultAssociation,
+        keySystemAssociation,
       },
       new UniqueEntityId(uuid.value),
     )
@@ -96,6 +135,15 @@ export class MongoDBItemPersistenceMapper implements MapperInterface<Item, Mongo
     mongoDbItem.createdAtTimestamp = domain.props.timestamps.createdAt
     mongoDbItem.updatedAtTimestamp = domain.props.timestamps.updatedAt
     mongoDbItem.updatedWithSession = domain.props.updatedWithSession ? domain.props.updatedWithSession.value : null
+    mongoDbItem.lastEditedBy = domain.props.sharedVaultAssociation
+      ? domain.props.sharedVaultAssociation.props.lastEditedBy.value
+      : null
+    mongoDbItem.sharedVaultUuid = domain.props.sharedVaultAssociation
+      ? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
+      : null
+    mongoDbItem.keySystemIdentifier = domain.props.keySystemAssociation
+      ? domain.props.keySystemAssociation.props.keySystemIdentifier
+      : null
 
     return mongoDbItem
   }

+ 0 - 65
packages/syncing-server/src/Mapping/Persistence/SharedVaultAssociationPersistenceMapper.ts

@@ -1,65 +0,0 @@
-import { MapperInterface, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
-
-import { TypeORMSharedVaultAssociation } from '../../Infra/TypeORM/TypeORMSharedVaultAssociation'
-import { SharedVaultAssociation } from '../../Domain/SharedVault/SharedVaultAssociation'
-
-export class SharedVaultAssociationPersistenceMapper
-  implements MapperInterface<SharedVaultAssociation, TypeORMSharedVaultAssociation>
-{
-  toDomain(projection: TypeORMSharedVaultAssociation): SharedVaultAssociation {
-    const itemUuidOrError = Uuid.create(projection.itemUuid)
-    if (itemUuidOrError.isFailed()) {
-      throw new Error(`Failed to create shared vault association from projection: ${itemUuidOrError.getError()}`)
-    }
-    const itemUuid = itemUuidOrError.getValue()
-
-    const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
-    if (sharedVaultUuidOrError.isFailed()) {
-      throw new Error(`Failed to create shared vault association from projection: ${sharedVaultUuidOrError.getError()}`)
-    }
-    const sharedVaultUuid = sharedVaultUuidOrError.getValue()
-
-    const lastEditedByOrError = Uuid.create(projection.lastEditedBy)
-    if (lastEditedByOrError.isFailed()) {
-      throw new Error(`Failed to create shared vault association from projection: ${lastEditedByOrError.getError()}`)
-    }
-    const lastEditedBy = lastEditedByOrError.getValue()
-
-    const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
-    if (timestampsOrError.isFailed()) {
-      throw new Error(`Failed to create shared vault association from projection: ${timestampsOrError.getError()}`)
-    }
-    const timestamps = timestampsOrError.getValue()
-
-    const sharedVaultAssociationOrError = SharedVaultAssociation.create(
-      {
-        itemUuid,
-        lastEditedBy,
-        sharedVaultUuid,
-        timestamps,
-      },
-      new UniqueEntityId(projection.uuid),
-    )
-    if (sharedVaultAssociationOrError.isFailed()) {
-      throw new Error(
-        `Failed to create shared vault association from projection: ${sharedVaultAssociationOrError.getError()}`,
-      )
-    }
-    const sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
-
-    return sharedVaultAssociation
-  }
-
-  toProjection(domain: SharedVaultAssociation): TypeORMSharedVaultAssociation {
-    const typeorm = new TypeORMSharedVaultAssociation()
-
-    typeorm.uuid = domain.id.toString()
-    typeorm.sharedVaultUuid = domain.props.sharedVaultUuid.value
-    typeorm.itemUuid = domain.props.itemUuid.value
-    typeorm.lastEditedBy = domain.props.lastEditedBy.value
-    typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
-    typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
-
-    return typeorm
-  }
-}