Browse Source

fix: unify use case usage (#654)

Karol Sójko 1 year ago
parent
commit
4d1e2dec26

+ 65 - 74
packages/syncing-server/src/Domain/UseCase/Syncing/CheckIntegrity/CheckIntegrity.spec.ts

@@ -42,95 +42,86 @@ describe('CheckIntegrity', () => {
   })
 
   it('should return an empty result if there are no integrity mismatches', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        freeUser: false,
-        integrityPayloads: [
-          {
-            uuid: '1-2-3',
-            updated_at_timestamp: 1,
-          },
-          {
-            uuid: '2-3-4',
-            updated_at_timestamp: 2,
-          },
-          {
-            uuid: '3-4-5',
-            updated_at_timestamp: 3,
-          },
-          {
-            uuid: '5-6-7',
-            updated_at_timestamp: 5,
-          },
-        ],
-      }),
-    ).toEqual({
-      mismatches: [],
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      freeUser: false,
+      integrityPayloads: [
+        {
+          uuid: '1-2-3',
+          updated_at_timestamp: 1,
+        },
+        {
+          uuid: '2-3-4',
+          updated_at_timestamp: 2,
+        },
+        {
+          uuid: '3-4-5',
+          updated_at_timestamp: 3,
+        },
+        {
+          uuid: '5-6-7',
+          updated_at_timestamp: 5,
+        },
+      ],
     })
+    expect(result.getValue()).toEqual([])
   })
 
   it('should return a mismatch item that has a different update at timemstap', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        freeUser: false,
-        integrityPayloads: [
-          {
-            uuid: '1-2-3',
-            updated_at_timestamp: 1,
-          },
-          {
-            uuid: '2-3-4',
-            updated_at_timestamp: 1,
-          },
-          {
-            uuid: '3-4-5',
-            updated_at_timestamp: 3,
-          },
-          {
-            uuid: '5-6-7',
-            updated_at_timestamp: 5,
-          },
-        ],
-      }),
-    ).toEqual({
-      mismatches: [
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      freeUser: false,
+      integrityPayloads: [
+        {
+          uuid: '1-2-3',
+          updated_at_timestamp: 1,
+        },
         {
           uuid: '2-3-4',
-          updated_at_timestamp: 2,
+          updated_at_timestamp: 1,
+        },
+        {
+          uuid: '3-4-5',
+          updated_at_timestamp: 3,
+        },
+        {
+          uuid: '5-6-7',
+          updated_at_timestamp: 5,
         },
       ],
     })
+    expect(result.getValue()).toEqual([
+      {
+        uuid: '2-3-4',
+        updated_at_timestamp: 2,
+      },
+    ])
   })
 
   it('should return a mismatch item that is missing on the client side', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        freeUser: false,
-        integrityPayloads: [
-          {
-            uuid: '1-2-3',
-            updated_at_timestamp: 1,
-          },
-          {
-            uuid: '2-3-4',
-            updated_at_timestamp: 2,
-          },
-          {
-            uuid: '5-6-7',
-            updated_at_timestamp: 5,
-          },
-        ],
-      }),
-    ).toEqual({
-      mismatches: [
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      freeUser: false,
+      integrityPayloads: [
         {
-          uuid: '3-4-5',
-          updated_at_timestamp: 3,
+          uuid: '1-2-3',
+          updated_at_timestamp: 1,
+        },
+        {
+          uuid: '2-3-4',
+          updated_at_timestamp: 2,
+        },
+        {
+          uuid: '5-6-7',
+          updated_at_timestamp: 5,
         },
       ],
     })
+    expect(result.getValue()).toEqual([
+      {
+        uuid: '3-4-5',
+        updated_at_timestamp: 3,
+      },
+    ])
   })
 })

+ 4 - 7
packages/syncing-server/src/Domain/UseCase/Syncing/CheckIntegrity/CheckIntegrity.ts

@@ -1,16 +1,15 @@
 import { IntegrityPayload } from '@standardnotes/responses'
+import { Result, UseCaseInterface } from '@standardnotes/domain-core'
 import { ContentType } from '@standardnotes/common'
 
 import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
-import { UseCaseInterface } from '../../UseCaseInterface'
 import { CheckIntegrityDTO } from './CheckIntegrityDTO'
-import { CheckIntegrityResponse } from './CheckIntegrityResponse'
 import { ExtendedIntegrityPayload } from '../../../Item/ExtendedIntegrityPayload'
 
-export class CheckIntegrity implements UseCaseInterface {
+export class CheckIntegrity implements UseCaseInterface<IntegrityPayload[]> {
   constructor(private itemRepository: ItemRepositoryInterface) {}
 
-  async execute(dto: CheckIntegrityDTO): Promise<CheckIntegrityResponse> {
+  async execute(dto: CheckIntegrityDTO): Promise<Result<IntegrityPayload[]>> {
     const serverItemIntegrityPayloads = await this.itemRepository.findItemsForComputingIntegrityPayloads(dto.userUuid)
 
     const serverItemIntegrityPayloadsMap = new Map<string, ExtendedIntegrityPayload>()
@@ -59,8 +58,6 @@ export class CheckIntegrity implements UseCaseInterface {
       }
     }
 
-    return {
-      mismatches,
-    }
+    return Result.ok(mismatches)
   }
 }

+ 0 - 5
packages/syncing-server/src/Domain/UseCase/Syncing/CheckIntegrity/CheckIntegrityResponse.ts

@@ -1,5 +0,0 @@
-import { IntegrityPayload } from '@standardnotes/responses'
-
-export type CheckIntegrityResponse = {
-  mismatches: IntegrityPayload[]
-}

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

@@ -15,23 +15,23 @@ describe('GetItem', () => {
   })
 
   it('should fail if item is not found', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        itemUuid: '2-3-4',
-      }),
-    ).toEqual({ success: false, message: 'Could not find item with uuid 2-3-4' })
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      itemUuid: '2-3-4',
+    })
+    expect(result.isFailed()).toBeTruthy()
+    expect(result.getError()).toEqual('Could not find item with uuid 2-3-4')
   })
 
   it('should succeed if item is found', async () => {
     const item = {} as jest.Mocked<Item>
     itemRepository.findByUuidAndUserUuid = jest.fn().mockReturnValue(item)
 
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        itemUuid: '2-3-4',
-      }),
-    ).toEqual({ success: true, item })
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      itemUuid: '2-3-4',
+    })
+
+    expect(result.getValue()).toEqual(item)
   })
 })

+ 7 - 12
packages/syncing-server/src/Domain/UseCase/Syncing/GetItem/GetItem.ts

@@ -1,24 +1,19 @@
+import { Result, UseCaseInterface } from '@standardnotes/domain-core'
+
 import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
-import { UseCaseInterface } from '../../UseCaseInterface'
 import { GetItemDTO } from './GetItemDTO'
-import { GetItemResponse } from './GetItemResponse'
+import { Item } from '../../../Item/Item'
 
-export class GetItem implements UseCaseInterface {
+export class GetItem implements UseCaseInterface<Item> {
   constructor(private itemRepository: ItemRepositoryInterface) {}
 
-  async execute(dto: GetItemDTO): Promise<GetItemResponse> {
+  async execute(dto: GetItemDTO): Promise<Result<Item>> {
     const item = await this.itemRepository.findByUuidAndUserUuid(dto.itemUuid, dto.userUuid)
 
     if (item === null) {
-      return {
-        success: false,
-        message: `Could not find item with uuid ${dto.itemUuid}`,
-      }
+      return Result.fail(`Could not find item with uuid ${dto.itemUuid}`)
     }
 
-    return {
-      success: true,
-      item,
-    }
+    return Result.ok(item)
   }
 }

+ 0 - 11
packages/syncing-server/src/Domain/UseCase/Syncing/GetItem/GetItemResponse.ts

@@ -1,11 +0,0 @@
-import { Item } from '../../../Item/Item'
-
-export type GetItemResponse =
-  | {
-      success: true
-      item: Item
-    }
-  | {
-      success: false
-      message: string
-    }

+ 41 - 40
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItems.spec.ts

@@ -54,20 +54,20 @@ describe('SyncItems', () => {
   })
 
   it('should sync items', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        itemHashes: [itemHash],
-        computeIntegrityHash: false,
-        syncToken: 'foo',
-        cursorToken: 'bar',
-        limit: 10,
-        readOnlyAccess: false,
-        contentType: 'Note',
-        apiVersion: ApiVersion.v20200115,
-        sessionUuid: null,
-      }),
-    ).toEqual({
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      itemHashes: [itemHash],
+      computeIntegrityHash: false,
+      syncToken: 'foo',
+      cursorToken: 'bar',
+      limit: 10,
+      readOnlyAccess: false,
+      contentType: 'Note',
+      apiVersion: ApiVersion.v20200115,
+      sessionUuid: null,
+      snjsVersion: '1.2.3',
+    })
+    expect(result.getValue()).toEqual({
       conflicts: [],
       cursorToken: 'asdzxc',
       retrievedItems: [item1],
@@ -93,18 +93,18 @@ describe('SyncItems', () => {
   })
 
   it('should sync items and return items keys on top for first sync', async () => {
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        itemHashes: [itemHash],
-        computeIntegrityHash: false,
-        limit: 10,
-        readOnlyAccess: false,
-        sessionUuid: '2-3-4',
-        contentType: 'Note',
-        apiVersion: ApiVersion.v20200115,
-      }),
-    ).toEqual({
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      itemHashes: [itemHash],
+      computeIntegrityHash: false,
+      limit: 10,
+      readOnlyAccess: false,
+      sessionUuid: '2-3-4',
+      contentType: 'Note',
+      apiVersion: ApiVersion.v20200115,
+      snjsVersion: '1.2.3',
+    })
+    expect(result.getValue()).toEqual({
       conflicts: [],
       cursorToken: 'asdzxc',
       retrievedItems: [item3, item1],
@@ -134,20 +134,21 @@ describe('SyncItems', () => {
       syncToken: 'qwerty',
     })
 
-    expect(
-      await createUseCase().execute({
-        userUuid: '1-2-3',
-        itemHashes: [itemHash],
-        computeIntegrityHash: false,
-        syncToken: 'foo',
-        readOnlyAccess: false,
-        sessionUuid: '2-3-4',
-        cursorToken: 'bar',
-        limit: 10,
-        contentType: 'Note',
-        apiVersion: ApiVersion.v20200115,
-      }),
-    ).toEqual({
+    const result = await createUseCase().execute({
+      userUuid: '1-2-3',
+      itemHashes: [itemHash],
+      computeIntegrityHash: false,
+      syncToken: 'foo',
+      readOnlyAccess: false,
+      sessionUuid: '2-3-4',
+      cursorToken: 'bar',
+      limit: 10,
+      contentType: 'Note',
+      apiVersion: ApiVersion.v20200115,
+      snjsVersion: '1.2.3',
+    })
+
+    expect(result.getValue()).toEqual({
       conflicts: [
         {
           serverItem: item2,

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

@@ -1,14 +1,14 @@
+import { Result, UseCaseInterface } from '@standardnotes/domain-core'
 import { Item } from '../../../Item/Item'
 import { ItemConflict } from '../../../Item/ItemConflict'
 import { ItemServiceInterface } from '../../../Item/ItemServiceInterface'
-import { UseCaseInterface } from '../../UseCaseInterface'
 import { SyncItemsDTO } from './SyncItemsDTO'
 import { SyncItemsResponse } from './SyncItemsResponse'
 
-export class SyncItems implements UseCaseInterface {
+export class SyncItems implements UseCaseInterface<SyncItemsResponse> {
   constructor(private itemService: ItemServiceInterface) {}
 
-  async execute(dto: SyncItemsDTO): Promise<SyncItemsResponse> {
+  async execute(dto: SyncItemsDTO): Promise<Result<SyncItemsResponse>> {
     const getItemsResult = await this.itemService.getItems({
       userUuid: dto.userUuid,
       syncToken: dto.syncToken,
@@ -38,7 +38,7 @@ export class SyncItems implements UseCaseInterface {
       cursorToken: getItemsResult.cursorToken,
     }
 
-    return syncResponse
+    return Result.ok(syncResponse)
   }
 
   private isFirstSync(dto: SyncItemsDTO): boolean {

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

@@ -5,10 +5,12 @@ export type SyncItemsDTO = {
   itemHashes: Array<ItemHash>
   computeIntegrityHash: boolean
   limit: number
+  sharedVaultUuids?: string[] | null
   syncToken?: string | null
   cursorToken?: string | null
   contentType?: string
   apiVersion: string
+  snjsVersion: string
   readOnlyAccess: boolean
   sessionUuid: string | null
 }

+ 0 - 3
packages/syncing-server/src/Domain/UseCase/UseCaseInterface.ts

@@ -1,3 +0,0 @@
-export interface UseCaseInterface {
-  execute(...args: any[]): Promise<Record<string, unknown>>
-}

+ 18 - 6
packages/syncing-server/src/Infra/InversifyExpressUtils/HomeServer/HomeServerItemsController.ts

@@ -10,6 +10,7 @@ import { ItemProjection } from '../../../Projection/ItemProjection'
 import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
 import { ApiVersion } from '../../../Domain/Api/ApiVersion'
 import { SyncItems } from '../../../Domain/UseCase/Syncing/SyncItems/SyncItems'
+import { HttpStatusCode } from '@standardnotes/responses'
 
 export class HomeServerItemsController extends BaseHttpController {
   constructor(
@@ -41,16 +42,21 @@ export class HomeServerItemsController extends BaseHttpController {
       computeIntegrityHash: request.body.compute_integrity === true,
       syncToken: request.body.sync_token,
       cursorToken: request.body.cursor_token,
+      sharedVaultUuids: request.body.shared_vault_uuids,
       limit: request.body.limit,
       contentType: request.body.content_type,
       apiVersion: request.body.api ?? ApiVersion.v20161215,
+      snjsVersion: <string>request.headers['x-snjs-version'],
       readOnlyAccess: response.locals.readOnlyAccess,
       sessionUuid: response.locals.session ? response.locals.session.uuid : null,
     })
+    if (syncResult.isFailed()) {
+      return this.json({ error: { message: syncResult.getError() } }, HttpStatusCode.BadRequest)
+    }
 
     const syncResponse = await this.syncResponseFactoryResolver
       .resolveSyncResponseFactoryVersion(request.body.api)
-      .createResponse(syncResult)
+      .createResponse(syncResult.getValue())
 
     return this.json(syncResponse)
   }
@@ -67,19 +73,25 @@ export class HomeServerItemsController extends BaseHttpController {
       freeUser: response.locals.freeUser,
     })
 
-    return this.json(result)
+    if (result.isFailed()) {
+      return this.json({ error: { message: result.getError() } }, HttpStatusCode.BadRequest)
+    }
+
+    return this.json({
+      mismatches: result.getValue(),
+    })
   }
 
-  async getSingleItem(request: Request, response: Response): Promise<results.NotFoundResult | results.JsonResult> {
+  async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
     const result = await this.getItem.execute({
       userUuid: response.locals.user.uuid,
       itemUuid: request.params.uuid,
     })
 
-    if (!result.success) {
-      return this.notFound()
+    if (result.isFailed()) {
+      return this.json({ error: { message: result.getError() } }, 404)
     }
 
-    return this.json({ item: await this.itemProjector.projectFull(result.item) })
+    return this.json({ item: await this.itemProjector.projectFull(result.getValue()) })
   }
 }

+ 6 - 5
packages/syncing-server/src/Infra/InversifyExpressUtils/InversifyExpressItemsController.spec.ts

@@ -2,9 +2,10 @@ import 'reflect-metadata'
 
 import * as express from 'express'
 import { ContentType } from '@standardnotes/common'
+import { Result } from '@standardnotes/domain-core'
+import { results } from 'inversify-express-utils'
 
 import { InversifyExpressItemsController } from './InversifyExpressItemsController'
-import { results } from 'inversify-express-utils'
 import { Item } from '../../Domain/Item/Item'
 import { ItemProjection } from '../../Projection/ItemProjection'
 import { ProjectorInterface } from '../../Projection/ProjectorInterface'
@@ -35,13 +36,13 @@ describe('InversifyExpressItemsController', () => {
     itemProjector.projectFull = jest.fn().mockReturnValue({ foo: 'bar' })
 
     syncItems = {} as jest.Mocked<SyncItems>
-    syncItems.execute = jest.fn().mockReturnValue({ foo: 'bar' })
+    syncItems.execute = jest.fn().mockReturnValue(Result.ok({ foo: 'bar' }))
 
     checkIntegrity = {} as jest.Mocked<CheckIntegrity>
-    checkIntegrity.execute = jest.fn().mockReturnValue({ mismatches: [{ uuid: '1-2-3', updated_at_timestamp: 2 }] })
+    checkIntegrity.execute = jest.fn().mockReturnValue(Result.ok([{ uuid: '1-2-3', updated_at_timestamp: 2 }]))
 
     getItem = {} as jest.Mocked<GetItem>
-    getItem.execute = jest.fn().mockReturnValue({ success: true, item: {} as jest.Mocked<Item> })
+    getItem.execute = jest.fn().mockReturnValue(Result.ok({} as jest.Mocked<Item>))
 
     request = {
       headers: {},
@@ -100,7 +101,7 @@ describe('InversifyExpressItemsController', () => {
 
   it('should return 404 on a missing single item', async () => {
     request.params.uuid = '1-2-3'
-    getItem.execute = jest.fn().mockReturnValue({ success: false })
+    getItem.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
 
     const httpResponse = <results.NotFoundResult>await createController().getSingleItem(request, response)
     const result = await httpResponse.executeAsync()

+ 1 - 4
packages/syncing-server/src/Infra/InversifyExpressUtils/InversifyExpressItemsController.ts

@@ -36,10 +36,7 @@ export class InversifyExpressItemsController extends HomeServerItemsController {
   }
 
   @httpGet('/:uuid')
-  override async getSingleItem(
-    request: Request,
-    response: Response,
-  ): Promise<results.NotFoundResult | results.JsonResult> {
+  override async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
     return super.getSingleItem(request, response)
   }
 }