Просмотр исходного кода

feat(revisions): fetching single revision in shared vault (#810)

* feat(revisions): fetching single revision in shared vault

* fix(revisions): mapping to http representation
Karol Sójko 1 год назад
Родитель
Сommit
e100c52bbc

+ 1 - 1
packages/revisions/src/Domain/Revision/RevisionRepositoryInterface.ts

@@ -7,7 +7,7 @@ export interface RevisionRepositoryInterface {
   countByUserUuid(userUuid: Uuid): Promise<number>
   removeByUserUuid(userUuid: Uuid): Promise<void>
   removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void>
-  findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null>
+  findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null>
   findByItemUuid(itemUuid: Uuid): Promise<Array<Revision>>
   findMetadataByItemId(itemUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Array<RevisionMetadata>>
   updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void>

+ 16 - 0
packages/revisions/src/Domain/UseCase/GetRevision/GetRevision.spec.ts

@@ -22,17 +22,30 @@ describe('GetRevision', () => {
       revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       roleNames: ['CORE_USER'],
+      sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
     })
 
     expect(result.isFailed()).toBeFalsy()
     expect(result.getValue()).not.toBeNull()
   })
 
+  it('should do nothing if shared vault uuid is invalid', async () => {
+    const result = await createUseCase().execute({
+      revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
+      userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
+      roleNames: ['CORE_USER'],
+      sharedVaultUuids: ['INVALID_SHARED_VAULT_UUID'],
+    })
+
+    expect(result.isFailed()).toBeTruthy()
+  })
+
   it('should do nothing if role names are not valid', async () => {
     const result = await createUseCase().execute({
       revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       roleNames: ['INVALID_ROLE_NAME'],
+      sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
     })
 
     expect(result.isFailed()).toBeTruthy()
@@ -45,6 +58,7 @@ describe('GetRevision', () => {
       revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       roleNames: ['CORE_USER'],
+      sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
     })
 
     expect(result.isFailed()).toBeTruthy()
@@ -55,6 +69,7 @@ describe('GetRevision', () => {
       revisionUuid: '1-2-3',
       userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       roleNames: ['CORE_USER'],
+      sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
     })
 
     expect(result.isFailed()).toBeTruthy()
@@ -65,6 +80,7 @@ describe('GetRevision', () => {
       userUuid: '1-2-3',
       revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
       roleNames: ['CORE_USER'],
+      sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
     })
 
     expect(result.isFailed()).toBeTruthy()

+ 10 - 1
packages/revisions/src/Domain/UseCase/GetRevision/GetRevision.ts

@@ -26,9 +26,18 @@ export class GetRevision implements UseCaseInterface<Revision> {
     }
     const roleNames = roleNamesOrError.getValue()
 
+    const sharedVaultUuids = []
+    for (const sharedVaultUuid of dto.sharedVaultUuids) {
+      const sharedVaultUuidOrError = Uuid.create(sharedVaultUuid)
+      if (sharedVaultUuidOrError.isFailed()) {
+        return Result.fail(`Could not get revision: ${sharedVaultUuidOrError.getError()}`)
+      }
+      sharedVaultUuids.push(sharedVaultUuidOrError.getValue())
+    }
+
     const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
 
-    const revision = await revisionRepository.findOneByUuid(revisionUuid, userUuid)
+    const revision = await revisionRepository.findOneByUuid(revisionUuid, userUuid, sharedVaultUuids)
 
     if (revision === null) {
       return Result.fail<Revision>(`Could not find revision with uuid: ${revisionUuid.value}`)

+ 1 - 0
packages/revisions/src/Domain/UseCase/GetRevision/GetRevisionDTO.ts

@@ -2,4 +2,5 @@ export interface GetRevisionDTO {
   userUuid: string
   revisionUuid: string
   roleNames: string[]
+  sharedVaultUuids: string[]
 }

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

@@ -174,7 +174,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
 
           const revisionInSecondary = await (
             this.secondRevisionsRepository as RevisionRepositoryInterface
-          ).findOneByUuid(revisionUuid, userUuid)
+          ).findOneByUuid(revisionUuid, userUuid, [])
           if (!revisionInSecondary) {
             return Result.fail(`Revision ${revision.id.toString()} not found in secondary database`)
           }

+ 4 - 1
packages/revisions/src/Infra/InversifyExpress/Base/BaseRevisionsController.ts

@@ -65,13 +65,16 @@ export class BaseRevisionsController extends BaseHttpController {
       revisionUuid: request.params.uuid,
       userUuid: response.locals.user.uuid,
       roleNames: response.locals.roles.map((role: Role) => role.name),
+      sharedVaultUuids: response.locals.belongsToSharedVaults.map(
+        (association: { shared_vault_uuid: string; permission: string }) => association.shared_vault_uuid,
+      ),
     })
 
     if (revisionOrError.isFailed()) {
       return this.json(
         {
           error: {
-            message: 'Could not retrieve revision.',
+            message: revisionOrError.getError(),
           },
         },
         HttpStatusCode.BadRequest,

+ 26 - 9
packages/revisions/src/Infra/TypeORM/MongoDB/MongoDBRevisionRepository.ts

@@ -49,15 +49,32 @@ export class MongoDBRevisionRepository implements RevisionRepositoryInterface {
     })
   }
 
-  async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null> {
-    const persistence = await this.mongoRepository.findOne({
-      where: {
-        $and: [
-          { _id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) } },
-          { userUuid: { $eq: userUuid.value } },
-        ],
-      },
-    })
+  async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null> {
+    let persistence = null
+    if (sharedVaultUuids.length > 0) {
+      persistence = await this.mongoRepository.findOne({
+        where: {
+          $and: [
+            { _id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) } },
+            {
+              $or: [
+                { sharedVaultUuid: { $in: sharedVaultUuids.map((uuid) => uuid.value) } },
+                { userUuid: { $eq: userUuid.value } },
+              ],
+            },
+          ],
+        },
+      })
+    } else {
+      persistence = await this.mongoRepository.findOne({
+        where: {
+          $and: [
+            { _id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) } },
+            { userUuid: { $eq: userUuid.value } },
+          ],
+        },
+      })
+    }
 
     if (persistence === null) {
       return null

+ 1 - 1
packages/revisions/src/Infra/TypeORM/SQL/SQLLegacyRevisionRepository.ts

@@ -92,7 +92,7 @@ export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface
       .execute()
   }
 
-  async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<Revision | null> {
+  async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, _sharedVaultUuids: Uuid[]): Promise<Revision | null> {
     const SQLLegacyRevision = await this.ormRepository
       .createQueryBuilder()
       .where('uuid = :revisionUuid', { revisionUuid: revisionUuid.value })

+ 28 - 0
packages/revisions/src/Infra/TypeORM/SQL/SQLRevisionRepository.ts

@@ -17,6 +17,34 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
     super(ormRepository, revisionMetadataMapper, revisionMapper, logger)
   }
 
+  override async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null> {
+    const queryBuilder = this.ormRepository.createQueryBuilder()
+
+    if (sharedVaultUuids.length > 0) {
+      queryBuilder.where(
+        'uuid = :revisionUuid AND (user_uuid = :userUuid OR shared_vault_uuid IN (:...sharedVaultUuids))',
+        {
+          revisionUuid: revisionUuid.value,
+          userUuid: userUuid.value,
+          sharedVaultUuids: sharedVaultUuids.map((uuid) => uuid.value),
+        },
+      )
+    } else {
+      queryBuilder.where('uuid = :revisionUuid AND user_uuid = :userUuid', {
+        revisionUuid: revisionUuid.value,
+        userUuid: userUuid.value,
+      })
+    }
+
+    const sqlRevision = await queryBuilder.getOne()
+
+    if (sqlRevision === null) {
+      return null
+    }
+
+    return this.revisionMapper.toDomain(sqlRevision)
+  }
+
   override async findMetadataByItemId(
     itemUuid: Uuid,
     userUuid: Uuid,

+ 10 - 0
packages/revisions/src/Mapping/Http/RevisionHttpMapper.ts

@@ -19,6 +19,16 @@ export class RevisionHttpMapper implements MapperInterface<Revision, RevisionHtt
       auth_hash: domain.props.authHash,
       created_at: domain.props.dates.createdAt.toISOString(),
       updated_at: domain.props.dates.updatedAt.toISOString(),
+      key_system_identifier: domain.props.keySystemAssociation
+        ? domain.props.keySystemAssociation.props.keySystemIdentifier
+        : null,
+      shared_vault_uuid: domain.props.sharedVaultAssociation
+        ? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
+        : null,
+      last_edited_by_uuid: domain.props.sharedVaultAssociation
+        ? domain.props.sharedVaultAssociation.props.editedBy.value
+        : null,
+      user_uuid: domain.props.userUuid ? domain.props.userUuid.value : null,
     }
   }
 }

+ 4 - 0
packages/revisions/src/Mapping/Http/RevisionHttpRepresentation.ts

@@ -8,4 +8,8 @@ export interface RevisionHttpRepresentation {
   auth_hash: string | null
   created_at: string
   updated_at: string
+  key_system_identifier: string | null
+  shared_vault_uuid: string | null
+  user_uuid: string | null
+  last_edited_by_uuid: string | null
 }