Browse Source

fix: adjust transitions to not create revisions during ongoing revisions transition

Karol Sójko 1 năm trước cách đây
mục cha
commit
106d8f9192
23 tập tin đã thay đổi với 211 bổ sung26 xóa
  1. 5 1
      packages/auth/src/Domain/Transition/TransitionStatusRepositoryInterface.ts
  2. 5 0
      packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts
  3. 3 0
      packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts
  4. 6 2
      packages/auth/src/Infra/Redis/RedisTransitionStatusRepository.ts
  5. 24 4
      packages/revisions/src/Domain/UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser.spec.ts
  6. 14 0
      packages/revisions/src/Domain/UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser.ts
  7. 1 0
      packages/security/src/Domain/Token/CrossServiceTokenData.ts
  8. 13 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItems.spec.ts
  9. 2 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItems.ts
  10. 1 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItemsDTO.ts
  11. 19 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.spec.ts
  12. 9 7
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts
  13. 1 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItemDTO.ts
  14. 12 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.spec.ts
  15. 1 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.ts
  16. 1 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItemsDTO.ts
  17. 40 0
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts
  18. 9 7
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts
  19. 1 0
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItemDTO.ts
  20. 28 5
      packages/syncing-server/src/Domain/UseCase/Transition/TransitionItemsFromPrimaryToSecondaryDatabaseForUser/TransitionItemsFromPrimaryToSecondaryDatabaseForUser.spec.ts
  21. 14 0
      packages/syncing-server/src/Domain/UseCase/Transition/TransitionItemsFromPrimaryToSecondaryDatabaseForUser/TransitionItemsFromPrimaryToSecondaryDatabaseForUser.ts
  22. 1 0
      packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseItemsController.ts
  23. 1 0
      packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts

+ 5 - 1
packages/auth/src/Domain/Transition/TransitionStatusRepositoryInterface.ts

@@ -1,5 +1,9 @@
 export interface TransitionStatusRepositoryInterface {
-  updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: 'STARTED' | 'FAILED'): Promise<void>
+  updateStatus(
+    userUuid: string,
+    transitionType: 'items' | 'revisions',
+    status: 'STARTED' | 'IN_PROGRESS' | 'FAILED',
+  ): Promise<void>
   removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void>
   getStatus(
     userUuid: string,

+ 5 - 0
packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts

@@ -113,6 +113,7 @@ describe('CreateCrossServiceToken', () => {
           uuid: '00000000-0000-0000-0000-000000000000',
         },
         ongoing_transition: false,
+        ongoing_revisions_transition: false,
       },
       60,
     )
@@ -148,6 +149,7 @@ describe('CreateCrossServiceToken', () => {
           uuid: '00000000-0000-0000-0000-000000000000',
         },
         ongoing_transition: true,
+        ongoing_revisions_transition: true,
       },
       60,
     )
@@ -177,6 +179,7 @@ describe('CreateCrossServiceToken', () => {
           uuid: '00000000-0000-0000-0000-000000000000',
         },
         ongoing_transition: false,
+        ongoing_revisions_transition: false,
       },
       60,
     )
@@ -206,6 +209,7 @@ describe('CreateCrossServiceToken', () => {
           uuid: '00000000-0000-0000-0000-000000000000',
         },
         ongoing_transition: false,
+        ongoing_revisions_transition: false,
       },
       60,
     )
@@ -261,6 +265,7 @@ describe('CreateCrossServiceToken', () => {
             email: 'test@test.te',
             uuid: '00000000-0000-0000-0000-000000000000',
           },
+          ongoing_revisions_transition: false,
           ongoing_transition: false,
         },
         60,

+ 3 - 0
packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts

@@ -48,6 +48,7 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
     }
 
     const transitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'items')
+    const revisionsTransitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'revisions')
 
     const roles = await user.roles
 
@@ -60,6 +61,8 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
       roles: this.projectRoles(roles),
       shared_vault_owner_context: undefined,
       ongoing_transition: transitionStatus === 'IN_PROGRESS',
+      ongoing_revisions_transition:
+        revisionsTransitionStatus === 'STARTED' || revisionsTransitionStatus === 'IN_PROGRESS',
       belongs_to_shared_vaults: sharedVaultAssociations.map((association) => ({
         shared_vault_uuid: association.props.sharedVaultUuid.value,
         permission: association.props.permission.value,

+ 6 - 2
packages/auth/src/Infra/Redis/RedisTransitionStatusRepository.ts

@@ -10,9 +10,13 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
   async updateStatus(
     userUuid: string,
     transitionType: 'items' | 'revisions',
-    status: 'STARTED' | 'FAILED',
+    status: 'STARTED' | 'IN_PROGRESS' | 'FAILED',
   ): Promise<void> {
-    await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status)
+    if (status === 'IN_PROGRESS') {
+      await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, 7200, status)
+    } else {
+      await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status)
+    }
   }
 
   async removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void> {

+ 24 - 4
packages/revisions/src/Domain/UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser.spec.ts

@@ -99,7 +99,7 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
     secondaryRevisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
     secondaryRevisionRepository.insert = jest.fn().mockResolvedValue(true)
     secondaryRevisionRepository.removeByUserUuid = jest.fn().mockResolvedValue(undefined)
-    secondaryRevisionRepository.countByUserUuid = jest.fn().mockResolvedValue(2)
+    secondaryRevisionRepository.countByUserUuid = jest.fn().mockResolvedValueOnce(0).mockResolvedValueOnce(2)
     secondaryRevisionRepository.findOneByUuid = jest
       .fn()
       .mockResolvedValueOnce(secondaryRevision1)
@@ -329,7 +329,10 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
     })
 
     it('should fail integrity check if the Revision count is not the same in both databases', async () => {
-      ;(secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid = jest.fn().mockResolvedValue(1)
+      ;(secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid = jest
+        .fn()
+        .mockResolvedValueOnce(0)
+        .mockResolvedValueOnce(1)
 
       const useCase = createUseCase()
 
@@ -346,7 +349,7 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
       expect(primaryRevisionRepository.countByUserUuid).toHaveBeenCalledWith(
         Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       )
-      expect((secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid).toHaveBeenCalledTimes(1)
+      expect((secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid).toHaveBeenCalledTimes(2)
       expect(primaryRevisionRepository.removeByUserUuid).not.toHaveBeenCalled()
       expect((secondaryRevisionRepository as RevisionRepositoryInterface).removeByUserUuid).toHaveBeenCalledTimes(1)
     })
@@ -370,7 +373,6 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
       expect(primaryRevisionRepository.countByUserUuid).toHaveBeenCalledWith(
         Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
       )
-      expect((secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid).not.toHaveBeenCalled()
       expect(primaryRevisionRepository.removeByUserUuid).not.toHaveBeenCalled()
       expect((secondaryRevisionRepository as RevisionRepositoryInterface).removeByUserUuid).toHaveBeenCalledTimes(1)
     })
@@ -378,6 +380,7 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
     it('should fail if an error is thrown during integrity check between primary and secondary database', async () => {
       ;(secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid = jest
         .fn()
+        .mockReturnValueOnce(0)
         .mockRejectedValue(new Error('error'))
 
       const useCase = createUseCase()
@@ -432,4 +435,21 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
     expect(primaryRevisionRepository.removeByUserUuid).not.toHaveBeenCalled()
     expect((secondaryRevisionRepository as RevisionRepositoryInterface).removeByUserUuid).not.toHaveBeenCalled()
   })
+
+  it('should not migrate revisions if there are already revisions in the secondary database', async () => {
+    ;(secondaryRevisionRepository as RevisionRepositoryInterface).countByUserUuid = jest.fn().mockResolvedValueOnce(1)
+
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      userUuid: '00000000-0000-0000-0000-000000000000',
+    })
+
+    expect(result.isFailed()).toBeTruthy()
+
+    expect(primaryRevisionRepository.findByUserUuid).not.toHaveBeenCalled()
+    expect((secondaryRevisionRepository as RevisionRepositoryInterface).insert).not.toHaveBeenCalled()
+    expect(primaryRevisionRepository.removeByUserUuid).not.toHaveBeenCalled()
+    expect((secondaryRevisionRepository as RevisionRepositoryInterface).removeByUserUuid).not.toHaveBeenCalled()
+  })
 })

+ 14 - 0
packages/revisions/src/Domain/UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser.ts

@@ -15,6 +15,8 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
   ) {}
 
   async execute(dto: TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
+    this.logger.info(`Transitioning revisions for user ${dto.userUuid}`)
+
     if (this.secondRevisionsRepository === null) {
       return Result.fail('Secondary revision repository is not set')
     }
@@ -31,6 +33,10 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
       return Result.ok()
     }
 
+    if (await this.hasAlreadyDataInSecondaryDatabase(userUuid)) {
+      return Result.fail(`Revisions for user ${userUuid.value} already exist in secondary database`)
+    }
+
     const migrationTimeStart = this.timer.getTimestampInMicroseconds()
 
     this.logger.debug(`Transitioning revisions for user ${userUuid.value}`)
@@ -143,6 +149,14 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
     await this.timer.sleep(twoSecondsInMilliseconds)
   }
 
+  private async hasAlreadyDataInSecondaryDatabase(userUuid: Uuid): Promise<boolean> {
+    const totalRevisionsCountForUserInSecondary = await (
+      this.secondRevisionsRepository as RevisionRepositoryInterface
+    ).countByUserUuid(userUuid)
+
+    return totalRevisionsCountForUserInSecondary > 0
+  }
+
   private async isAlreadyMigrated(userUuid: Uuid): Promise<boolean> {
     const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
 

+ 1 - 0
packages/security/src/Domain/Token/CrossServiceTokenData.ts

@@ -25,4 +25,5 @@ export type CrossServiceTokenData = {
   }
   extensionKey?: string
   ongoing_transition?: boolean
+  ongoing_revisions_transition?: boolean
 }

+ 13 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItems.spec.ts

@@ -84,6 +84,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -96,6 +97,7 @@ describe('SaveItems', () => {
     expect(saveNewItem.execute).toHaveBeenCalledWith({
       itemHash: itemHash1,
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       sessionUuid: 'session-uuid',
       roleNames: ['CORE_USER'],
     })
@@ -109,6 +111,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -133,6 +136,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -155,6 +159,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: true,
       sessionUuid: 'session-uuid',
@@ -178,6 +183,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -197,6 +203,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -215,6 +222,7 @@ describe('SaveItems', () => {
 
     const result = await useCase.execute({
       itemHashes: [itemHash1],
+      onGoingRevisionsTransition: false,
       userUuid: '00000000-0000-0000-0000-000000000000',
       apiVersion: '1',
       readOnlyAccess: false,
@@ -229,6 +237,7 @@ describe('SaveItems', () => {
       existingItem: savedItem,
       sessionUuid: 'session-uuid',
       performingUserUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: ['CORE_USER'],
     })
   })
@@ -242,6 +251,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -267,6 +277,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [ItemHash.create({ ...itemHash1.props, uuid: 'invalid-uuid' }).getValue()],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '1',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -313,6 +324,7 @@ describe('SaveItems', () => {
         ItemHash.create({ ...itemHash1.props, uuid: '00000000-0000-0000-0000-000000000003' }).getValue(),
       ],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '2',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',
@@ -330,6 +342,7 @@ describe('SaveItems', () => {
     const result = await useCase.execute({
       itemHashes: [itemHash1],
       userUuid: 'user-uuid',
+      onGoingRevisionsTransition: false,
       apiVersion: '2',
       readOnlyAccess: false,
       sessionUuid: 'session-uuid',

+ 2 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItems.ts

@@ -86,6 +86,7 @@ export class SaveItems implements UseCaseInterface<SaveItemsResult> {
           sessionUuid: dto.sessionUuid,
           performingUserUuid: dto.userUuid,
           roleNames: dto.roleNames,
+          onGoingRevisionsTransition: dto.onGoingRevisionsTransition,
         })
         if (udpatedItemOrError.isFailed()) {
           this.logger.error(
@@ -109,6 +110,7 @@ export class SaveItems implements UseCaseInterface<SaveItemsResult> {
             itemHash,
             sessionUuid: dto.sessionUuid,
             roleNames: dto.roleNames,
+            onGoingRevisionsTransition: dto.onGoingRevisionsTransition,
           })
           if (newItemOrError.isFailed()) {
             this.logger.error(

+ 1 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SaveItems/SaveItemsDTO.ts

@@ -8,4 +8,5 @@ export interface SaveItemsDTO {
   sessionUuid: string | null
   snjsVersion: string
   roleNames: string[]
+  onGoingRevisionsTransition: boolean
 }

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

@@ -90,6 +90,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -112,6 +113,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: true,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -132,6 +134,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -150,6 +153,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -170,6 +174,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -190,6 +195,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -208,6 +214,7 @@ describe('SaveNewItem', () => {
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
       roleNames: [RoleName.NAMES.CoreUser],
+      onGoingRevisionsTransition: false,
     })
 
     expect(result.isFailed()).toBeTruthy()
@@ -218,6 +225,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
       roleNames: ['invalid'],
@@ -231,6 +239,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-00000000000',
       itemHash: itemHash1,
@@ -249,6 +258,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -267,6 +277,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -285,6 +296,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -305,6 +317,7 @@ describe('SaveNewItem', () => {
 
     const result = await useCase.execute({
       userUuid: '00000000-0000-0000-0000-000000000000',
+      onGoingRevisionsTransition: false,
       roleNames: [RoleName.NAMES.CoreUser],
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
@@ -328,6 +341,7 @@ describe('SaveNewItem', () => {
       sessionUuid: '00000000-0000-0000-0000-000000000001',
       itemHash: itemHash1,
       roleNames: [RoleName.NAMES.CoreUser],
+      onGoingRevisionsTransition: false,
     })
 
     expect(result.isFailed()).toBeTruthy()
@@ -344,6 +358,7 @@ describe('SaveNewItem', () => {
 
       const result = await useCase.execute({
         userUuid: '00000000-0000-0000-0000-000000000000',
+        onGoingRevisionsTransition: false,
         roleNames: [RoleName.NAMES.CoreUser],
         sessionUuid: '00000000-0000-0000-0000-000000000001',
         itemHash: itemHash1,
@@ -371,6 +386,7 @@ describe('SaveNewItem', () => {
 
       const result = await useCase.execute({
         userUuid: '00000000-0000-0000-0000-000000000000',
+        onGoingRevisionsTransition: false,
         roleNames: [RoleName.NAMES.CoreUser],
         sessionUuid: '00000000-0000-0000-0000-000000000001',
         itemHash: itemHash1,
@@ -393,6 +409,7 @@ describe('SaveNewItem', () => {
 
       const result = await useCase.execute({
         userUuid: '00000000-0000-0000-0000-000000000000',
+        onGoingRevisionsTransition: false,
         roleNames: [RoleName.NAMES.CoreUser],
         sessionUuid: '00000000-0000-0000-0000-000000000001',
         itemHash: itemHash1,
@@ -412,6 +429,7 @@ describe('SaveNewItem', () => {
 
       const result = await useCase.execute({
         userUuid: '00000000-0000-0000-0000-000000000000',
+        onGoingRevisionsTransition: false,
         roleNames: [RoleName.NAMES.CoreUser],
         sessionUuid: '00000000-0000-0000-0000-000000000001',
         itemHash: itemHash1,
@@ -435,6 +453,7 @@ describe('SaveNewItem', () => {
 
       const result = await useCase.execute({
         userUuid: '00000000-0000-0000-0000-000000000000',
+        onGoingRevisionsTransition: false,
         roleNames: [RoleName.NAMES.CoreUser],
         sessionUuid: '00000000-0000-0000-0000-000000000001',
         itemHash: itemHash1,

+ 9 - 7
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts

@@ -145,13 +145,15 @@ export class SaveNewItem implements UseCaseInterface<Item> {
     await itemRepository.save(newItem)
 
     if (contentType.value !== null && [ContentType.TYPES.Note, ContentType.TYPES.File].includes(contentType.value)) {
-      await this.domainEventPublisher.publish(
-        this.domainEventFactory.createItemRevisionCreationRequested({
-          itemUuid: newItem.id.toString(),
-          userUuid: newItem.props.userUuid.value,
-          roleNames: dto.roleNames,
-        }),
-      )
+      if (!dto.onGoingRevisionsTransition) {
+        await this.domainEventPublisher.publish(
+          this.domainEventFactory.createItemRevisionCreationRequested({
+            itemUuid: newItem.id.toString(),
+            userUuid: newItem.props.userUuid.value,
+            roleNames: dto.roleNames,
+          }),
+        )
+      }
     }
 
     if (duplicateOf) {

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

@@ -5,4 +5,5 @@ export interface SaveNewItemDTO {
   roleNames: string[]
   itemHash: ItemHash
   sessionUuid: string | null
+  onGoingRevisionsTransition: boolean
 }

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

@@ -143,6 +143,7 @@ describe('SyncItems', () => {
   it('should sync items', async () => {
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -178,6 +179,7 @@ describe('SyncItems', () => {
     expect(saveItemsUseCase.execute).toHaveBeenCalledWith({
       itemHashes: [itemHash],
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       apiVersion: '20200115',
       snjsVersion: '1.2.3',
       roleNames: [RoleName.NAMES.CoreUser],
@@ -189,6 +191,7 @@ describe('SyncItems', () => {
   it('should sync items and return items keys on top for first sync that is not a shared vault exclusive sync', async () => {
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       limit: 10,
@@ -215,6 +218,7 @@ describe('SyncItems', () => {
   it('should sync items and not return items keys on top for first sync that is a shared vault exclusive sync', async () => {
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       limit: 10,
@@ -266,6 +270,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -306,6 +311,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -327,6 +333,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -346,6 +353,7 @@ describe('SyncItems', () => {
   it('should return error if role names are invalid', async () => {
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       limit: 10,
@@ -365,6 +373,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -386,6 +395,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -407,6 +417,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',
@@ -428,6 +439,7 @@ describe('SyncItems', () => {
 
     const result = await createUseCase().execute({
       userUuid: '1-2-3',
+      onGoingRevisionsTransition: false,
       itemHashes: [itemHash],
       computeIntegrityHash: false,
       syncToken: 'foo',

+ 1 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.ts

@@ -52,6 +52,7 @@ export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
       sessionUuid: dto.sessionUuid,
       snjsVersion: dto.snjsVersion,
       roleNames: dto.roleNames,
+      onGoingRevisionsTransition: dto.onGoingRevisionsTransition,
     })
     if (saveItemsResultOrError.isFailed()) {
       return Result.fail(saveItemsResultOrError.getError())

+ 1 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItemsDTO.ts

@@ -14,4 +14,5 @@ export type SyncItemsDTO = {
   snjsVersion: string
   readOnlyAccess: boolean
   sessionUuid: string | null
+  onGoingRevisionsTransition: boolean
 }

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

@@ -140,6 +140,23 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
+      itemHash: itemHash1,
+      sessionUuid: '00000000-0000-0000-0000-000000000000',
+      performingUserUuid: '00000000-0000-0000-0000-000000000000',
+      roleNames: [RoleName.NAMES.CoreUser],
+    })
+
+    expect(result.isFailed()).toBeFalsy()
+    expect(itemRepository.save).toHaveBeenCalled()
+  })
+
+  it('should not create a revision if user has an ongoin revisions transition', async () => {
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      existingItem: item1,
+      onGoingRevisionsTransition: true,
       itemHash: itemHash1,
       sessionUuid: '00000000-0000-0000-0000-000000000000',
       performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -155,6 +172,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: itemHash1,
       sessionUuid: 'invalid-uuid',
       performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -169,6 +187,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: itemHash1,
       sessionUuid: '00000000-0000-0000-0000-000000000000',
       performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -183,6 +202,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         content_type: 'invalid',
@@ -200,6 +220,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         deleted: true,
@@ -224,6 +245,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         duplicate_of: '00000000-0000-0000-0000-000000000001',
@@ -243,6 +265,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         duplicate_of: 'invalid-uuid',
@@ -260,6 +283,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         updated_at_timestamp: 123,
@@ -279,6 +303,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         created_at: undefined,
@@ -302,6 +327,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         created_at_timestamp: 123,
@@ -327,6 +353,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: ItemHash.create({
         ...itemHash1.props,
         created_at_timestamp: 123,
@@ -346,6 +373,7 @@ describe('UpdateExistingItem', () => {
 
     const result = await useCase.execute({
       existingItem: item1,
+      onGoingRevisionsTransition: false,
       itemHash: itemHash1,
       sessionUuid: '00000000-0000-0000-0000-000000000000',
       performingUserUuid: 'invalid-uuid',
@@ -365,6 +393,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -392,6 +421,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -430,6 +460,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -459,6 +490,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -480,6 +512,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -512,6 +545,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -547,6 +581,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -569,6 +604,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -589,6 +625,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -611,6 +648,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -632,6 +670,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',
@@ -655,6 +694,7 @@ describe('UpdateExistingItem', () => {
 
       const result = await useCase.execute({
         existingItem: item1,
+        onGoingRevisionsTransition: false,
         itemHash,
         sessionUuid: '00000000-0000-0000-0000-000000000000',
         performingUserUuid: '00000000-0000-0000-0000-000000000000',

+ 9 - 7
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts

@@ -191,13 +191,15 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
         dto.existingItem.props.contentType.value !== null &&
         [ContentType.TYPES.Note, ContentType.TYPES.File].includes(dto.existingItem.props.contentType.value)
       ) {
-        await this.domainEventPublisher.publish(
-          this.domainEventFactory.createItemRevisionCreationRequested({
-            itemUuid: dto.existingItem.id.toString(),
-            userUuid: dto.existingItem.props.userUuid.value,
-            roleNames: dto.roleNames,
-          }),
-        )
+        if (!dto.onGoingRevisionsTransition) {
+          await this.domainEventPublisher.publish(
+            this.domainEventFactory.createItemRevisionCreationRequested({
+              itemUuid: dto.existingItem.id.toString(),
+              userUuid: dto.existingItem.props.userUuid.value,
+              roleNames: dto.roleNames,
+            }),
+          )
+        }
       }
     }
 

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

@@ -7,4 +7,5 @@ export interface UpdateExistingItemDTO {
   sessionUuid: string | null
   performingUserUuid: string
   roleNames: string[]
+  onGoingRevisionsTransition: boolean
 }

+ 28 - 5
packages/syncing-server/src/Domain/UseCase/Transition/TransitionItemsFromPrimaryToSecondaryDatabaseForUser/TransitionItemsFromPrimaryToSecondaryDatabaseForUser.spec.ts

@@ -107,7 +107,7 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
     secondaryItemRepository = {} as jest.Mocked<ItemRepositoryInterface>
     secondaryItemRepository.save = jest.fn().mockResolvedValue(undefined)
     secondaryItemRepository.deleteByUserUuid = jest.fn().mockResolvedValue(undefined)
-    secondaryItemRepository.countAll = jest.fn().mockResolvedValue(2)
+    secondaryItemRepository.countAll = jest.fn().mockReturnValueOnce(0).mockReturnValueOnce(2)
     secondaryItemRepository.findByUuid = jest
       .fn()
       .mockResolvedValueOnce(secondaryItem1)
@@ -314,7 +314,10 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
     })
 
     it('should fail integrity check if the item count is not the same in both databases', async () => {
-      ;(secondaryItemRepository as ItemRepositoryInterface).countAll = jest.fn().mockResolvedValue(1)
+      ;(secondaryItemRepository as ItemRepositoryInterface).countAll = jest
+        .fn()
+        .mockResolvedValueOnce(0)
+        .mockResolvedValueOnce(1)
 
       const useCase = createUseCase()
 
@@ -329,7 +332,7 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
 
       expect(primaryItemRepository.countAll).toHaveBeenCalledTimes(3)
       expect(primaryItemRepository.countAll).toHaveBeenCalledWith({ userUuid: '00000000-0000-0000-0000-000000000000' })
-      expect((secondaryItemRepository as ItemRepositoryInterface).countAll).toHaveBeenCalledTimes(1)
+      expect((secondaryItemRepository as ItemRepositoryInterface).countAll).toHaveBeenCalledTimes(2)
       expect(primaryItemRepository.deleteByUserUuid).not.toHaveBeenCalled()
       expect((secondaryItemRepository as ItemRepositoryInterface).deleteByUserUuid).toHaveBeenCalledTimes(1)
     })
@@ -351,13 +354,16 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
 
       expect(primaryItemRepository.countAll).toHaveBeenCalledTimes(3)
       expect(primaryItemRepository.countAll).toHaveBeenCalledWith({ userUuid: '00000000-0000-0000-0000-000000000000' })
-      expect((secondaryItemRepository as ItemRepositoryInterface).countAll).toHaveBeenCalledTimes(1)
+      expect((secondaryItemRepository as ItemRepositoryInterface).countAll).toHaveBeenCalledTimes(2)
       expect(primaryItemRepository.deleteByUserUuid).not.toHaveBeenCalled()
       expect((secondaryItemRepository as ItemRepositoryInterface).deleteByUserUuid).toHaveBeenCalledTimes(1)
     })
 
     it('should fail if an error is thrown during integrity check between primary and secondary database', async () => {
-      ;(secondaryItemRepository as ItemRepositoryInterface).countAll = jest.fn().mockRejectedValue(new Error('error'))
+      ;(secondaryItemRepository as ItemRepositoryInterface).countAll = jest
+        .fn()
+        .mockReturnValueOnce(0)
+        .mockRejectedValue(new Error('error'))
 
       const useCase = createUseCase()
 
@@ -391,4 +397,21 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
     expect(primaryItemRepository.deleteByUserUuid).not.toHaveBeenCalled()
     expect((secondaryItemRepository as ItemRepositoryInterface).deleteByUserUuid).not.toHaveBeenCalled()
   })
+
+  it('should not migrate items if there are items in the secondary database', async () => {
+    ;(secondaryItemRepository as ItemRepositoryInterface).countAll = jest.fn().mockResolvedValue(1)
+
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      userUuid: '00000000-0000-0000-0000-000000000000',
+    })
+
+    expect(result.isFailed()).toBeTruthy()
+
+    expect(primaryItemRepository.findAll).not.toHaveBeenCalled()
+    expect((secondaryItemRepository as ItemRepositoryInterface).save).not.toHaveBeenCalled()
+    expect(primaryItemRepository.deleteByUserUuid).not.toHaveBeenCalled()
+    expect((secondaryItemRepository as ItemRepositoryInterface).deleteByUserUuid).not.toHaveBeenCalled()
+  })
 })

+ 14 - 0
packages/syncing-server/src/Domain/UseCase/Transition/TransitionItemsFromPrimaryToSecondaryDatabaseForUser/TransitionItemsFromPrimaryToSecondaryDatabaseForUser.ts

@@ -16,6 +16,8 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
   ) {}
 
   async execute(dto: TransitionItemsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
+    this.logger.info(`Transitioning items for user ${dto.userUuid}`)
+
     if (this.secondaryItemRepository === null) {
       return Result.fail('Secondary item repository is not set')
     }
@@ -32,6 +34,10 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
       return Result.ok()
     }
 
+    if (await this.hasAlreadyDataInSecondaryDatabase(userUuid)) {
+      return Result.fail(`Items for user ${userUuid.value} already exist in secondary database`)
+    }
+
     const migrationTimeStart = this.timer.getTimestampInMicroseconds()
 
     const migrationResult = await this.migrateItemsForUser(userUuid)
@@ -79,6 +85,14 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
     return Result.ok()
   }
 
+  private async hasAlreadyDataInSecondaryDatabase(userUuid: Uuid): Promise<boolean> {
+    const totalItemsCountForUser = await (this.secondaryItemRepository as ItemRepositoryInterface).countAll({
+      userUuid: userUuid.value,
+    })
+
+    return totalItemsCountForUser > 0
+  }
+
   private async isAlreadyMigrated(userUuid: Uuid): Promise<boolean> {
     const totalItemsCountForUser = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
 

+ 1 - 0
packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseItemsController.ts

@@ -79,6 +79,7 @@ export class BaseItemsController extends BaseHttpController {
       readOnlyAccess: response.locals.readOnlyAccess,
       sessionUuid: response.locals.session ? response.locals.session.uuid : null,
       sharedVaultUuids,
+      onGoingRevisionsTransition: response.locals.onGoingRevisionsTransition,
     })
     if (syncResult.isFailed()) {
       return this.json({ error: { message: syncResult.getError() } }, HttpStatusCode.BadRequest)

+ 1 - 0
packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts

@@ -30,6 +30,7 @@ export class InversifyExpressAuthMiddleware extends BaseMiddleware {
       response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false
       response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
       response.locals.ongoingTransition = decodedToken.ongoing_transition
+      response.locals.onGoingRevisionsTransition = decodedToken.ongoing_revisions_transition
 
       return next()
     } catch (error) {