Compare commits

..

4 Commits

Author SHA1 Message Date
standardci c970b1ea68 chore(release): publish new version
- @standardnotes/home-server@1.11.38
 - @standardnotes/syncing-server@1.62.1
2023-07-11 15:22:11 +00:00
Karol Sójko 4d1e2dec26 fix: unify use case usage (#654) 2023-07-11 17:05:45 +02:00
standardci 108408a944 chore(release): publish new version
- @standardnotes/home-server@1.11.37
 - @standardnotes/syncing-server@1.62.0
2023-07-10 12:37:48 +00:00
Karol Sójko 18d07d431f feat: messages controller. (#653)
Co-authored-by: Mo <mo@standardnotes.com>
2023-07-10 14:21:59 +02:00
22 changed files with 426 additions and 194 deletions
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.38](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.37...@standardnotes/home-server@1.11.38) (2023-07-11)
**Note:** Version bump only for package @standardnotes/home-server
## [1.11.37](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.36...@standardnotes/home-server@1.11.37) (2023-07-10)
**Note:** Version bump only for package @standardnotes/home-server
## [1.11.36](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.35...@standardnotes/home-server@1.11.36) (2023-07-10)
**Note:** Version bump only for package @standardnotes/home-server
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.11.36",
"version": "1.11.38",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.62.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.62.0...@standardnotes/syncing-server@1.62.1) (2023-07-11)
### Bug Fixes
* unify use case usage ([#654](https://github.com/standardnotes/syncing-server-js/issues/654)) ([4d1e2de](https://github.com/standardnotes/syncing-server-js/commit/4d1e2dec264b156a4cfb4980ca3b486433ce64b7))
# [1.62.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.61.0...@standardnotes/syncing-server@1.62.0) (2023-07-10)
### Features
* messages controller. ([#653](https://github.com/standardnotes/syncing-server-js/issues/653)) ([18d07d4](https://github.com/standardnotes/syncing-server-js/commit/18d07d431f08dc17a276f84c0724935d9014a4fd))
# [1.61.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.60.0...@standardnotes/syncing-server@1.61.0) (2023-07-10)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.61.0",
"version": "1.62.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -52,7 +52,11 @@ const TYPES = {
Sync_DeleteSharedVaultInvitesSentByUser: Symbol.for('Sync_DeleteSharedVaultInvitesSentByUser'),
Sync_GetSharedVaultInvitesSentByUser: Symbol.for('Sync_GetSharedVaultInvitesSentByUser'),
Sync_GetSharedVaultInvitesSentToUser: Symbol.for('Sync_GetSharedVaultInvitesSentToUser'),
Sync_SharedVaultInviteHttpMapper: Symbol.for('Sync_SharedVaultInviteHttpMapper'),
Sync_GetMessagesSentToUser: Symbol.for('Sync_GetMessagesSentToUser'),
Sync_GetMessagesSentByUser: Symbol.for('Sync_GetMessagesSentByUser'),
Sync_SendMessageToUser: Symbol.for('Sync_SendMessageToUser'),
Sync_DeleteAllMessagesSentToUser: Symbol.for('Sync_DeleteAllMessagesSentToUser'),
Sync_DeleteMessage: Symbol.for('Sync_DeleteMessage'),
// Handlers
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
@@ -86,6 +90,8 @@ const TYPES = {
// Mapping
Sync_SharedVaultHttpMapper: Symbol.for('Sync_SharedVaultHttpMapper'),
Sync_SharedVaultUserHttpMapper: Symbol.for('Sync_SharedVaultUserHttpMapper'),
Sync_SharedVaultInviteHttpMapper: Symbol.for('Sync_SharedVaultInviteHttpMapper'),
Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'),
}
export default TYPES
@@ -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: [],
})
})
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,
},
],
})
})
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: [
{
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 () => {
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: 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 () => {
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: '5-6-7',
updated_at_timestamp: 5,
},
],
})
expect(result.getValue()).toEqual([
{
uuid: '3-4-5',
updated_at_timestamp: 3,
},
])
})
})
@@ -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)
}
}
@@ -1,5 +0,0 @@
import { IntegrityPayload } from '@standardnotes/responses'
export type CheckIntegrityResponse = {
mismatches: IntegrityPayload[]
}
@@ -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)
})
})
@@ -1,24 +1,19 @@
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
import { UseCaseInterface } from '../../UseCaseInterface'
import { GetItemDTO } from './GetItemDTO'
import { GetItemResponse } from './GetItemResponse'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
export class GetItem implements UseCaseInterface {
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
import { GetItemDTO } from './GetItemDTO'
import { Item } from '../../../Item/Item'
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)
}
}
@@ -1,11 +0,0 @@
import { Item } from '../../../Item/Item'
export type GetItemResponse =
| {
success: true
item: Item
}
| {
success: false
message: string
}
@@ -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,
@@ -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 {
@@ -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
}
@@ -1,3 +0,0 @@
export interface UseCaseInterface {
execute(...args: any[]): Promise<Record<string, unknown>>
}
@@ -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()) })
}
}
@@ -0,0 +1,138 @@
import { Request, Response } from 'express'
import { BaseHttpController, results } from 'inversify-express-utils'
import { HttpStatusCode } from '@standardnotes/responses'
import { ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
import { GetMessagesSentToUser } from '../../../Domain/UseCase/Messaging/GetMessagesSentToUser/GetMessagesSentToUser'
import { MessageHttpRepresentation } from '../../../Mapping/Http/MessageHttpRepresentation'
import { Message } from '../../../Domain/Message/Message'
import { SendMessageToUser } from '../../../Domain/UseCase/Messaging/SendMessageToUser/SendMessageToUser'
import { DeleteAllMessagesSentToUser } from '../../../Domain/UseCase/Messaging/DeleteAllMessagesSentToUser/DeleteAllMessagesSentToUser'
import { DeleteMessage } from '../../../Domain/UseCase/Messaging/DeleteMessage/DeleteMessage'
import { GetMessagesSentByUser } from '../../../Domain/UseCase/Messaging/GetMessagesSentByUser/GetMessagesSentByUser'
export class HomeServerMessagesController extends BaseHttpController {
constructor(
protected getMessageSentToUserUseCase: GetMessagesSentToUser,
protected getMessagesSentByUserUseCase: GetMessagesSentByUser,
protected sendMessageToUserUseCase: SendMessageToUser,
protected deleteMessagesSentToUserUseCase: DeleteAllMessagesSentToUser,
protected deleteMessageUseCase: DeleteMessage,
protected messageHttpMapper: MapperInterface<Message, MessageHttpRepresentation>,
private controllerContainer?: ControllerContainerInterface,
) {
super()
if (this.controllerContainer !== undefined) {
this.controllerContainer.register('sync.messages.get-received', this.getMessages.bind(this))
this.controllerContainer.register('sync.messages.get-sent', this.getMessagesSent.bind(this))
this.controllerContainer.register('sync.messages.send', this.sendMessage.bind(this))
this.controllerContainer.register('sync.messages.delete-all', this.deleteMessagesSentToUser.bind(this))
this.controllerContainer.register('sync.messages.delete', this.deleteMessage.bind(this))
}
}
async getMessages(_request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.getMessageSentToUserUseCase.execute({
recipientUuid: response.locals.user.uuid,
})
if (result.isFailed()) {
return this.json(
{
error: {
message: result.getError(),
},
},
HttpStatusCode.BadRequest,
)
}
return this.json({
messages: result.getValue().map((message) => this.messageHttpMapper.toProjection(message)),
})
}
async getMessagesSent(_request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.getMessagesSentByUserUseCase.execute({
senderUuid: response.locals.user.uuid,
})
if (result.isFailed()) {
return this.json(
{
error: {
message: result.getError(),
},
},
HttpStatusCode.BadRequest,
)
}
return this.json({
messages: result.getValue().map((message) => this.messageHttpMapper.toProjection(message)),
})
}
async sendMessage(request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.sendMessageToUserUseCase.execute({
senderUuid: response.locals.user.uuid,
recipientUuid: request.body.recipient_uuid,
encryptedMessage: request.body.encrypted_message,
replaceabilityIdentifier: request.body.replaceability_identifier,
})
if (result.isFailed()) {
return this.json(
{
error: {
message: result.getError(),
},
},
HttpStatusCode.BadRequest,
)
}
return this.json({
message: this.messageHttpMapper.toProjection(result.getValue()),
})
}
async deleteMessagesSentToUser(_request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.deleteMessagesSentToUserUseCase.execute({
recipientUuid: response.locals.user.uuid,
})
if (result.isFailed()) {
return this.json(
{
error: {
message: result.getError(),
},
},
HttpStatusCode.BadRequest,
)
}
return this.json({ success: true })
}
async deleteMessage(request: Request, response: Response): Promise<results.JsonResult> {
const result = await this.deleteMessageUseCase.execute({
messageUuid: request.params.messageUuid,
originatorUuid: response.locals.user.uuid,
})
if (result.isFailed()) {
return this.json(
{
error: {
message: result.getError(),
},
},
HttpStatusCode.BadRequest,
)
}
return this.json({ success: true })
}
}
@@ -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()
@@ -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)
}
}
@@ -0,0 +1,62 @@
import { controller, httpDelete, httpGet, httpPost, results } from 'inversify-express-utils'
import { inject } from 'inversify'
import { MapperInterface } from '@standardnotes/domain-core'
import { Request, Response } from 'express'
import TYPES from '../../Bootstrap/Types'
import { HomeServerMessagesController } from './HomeServer/HomeServerMessagesController'
import { GetMessagesSentToUser } from '../../Domain/UseCase/Messaging/GetMessagesSentToUser/GetMessagesSentToUser'
import { DeleteAllMessagesSentToUser } from '../../Domain/UseCase/Messaging/DeleteAllMessagesSentToUser/DeleteAllMessagesSentToUser'
import { DeleteMessage } from '../../Domain/UseCase/Messaging/DeleteMessage/DeleteMessage'
import { SendMessageToUser } from '../../Domain/UseCase/Messaging/SendMessageToUser/SendMessageToUser'
import { MessageHttpRepresentation } from '../../Mapping/Http/MessageHttpRepresentation'
import { Message } from '../../Domain/Message/Message'
import { GetMessagesSentByUser } from '../../Domain/UseCase/Messaging/GetMessagesSentByUser/GetMessagesSentByUser'
@controller('/messages', TYPES.Sync_AuthMiddleware)
export class InversifyExpressMessagesController extends HomeServerMessagesController {
constructor(
@inject(TYPES.Sync_GetMessagesSentToUser) override getMessageSentToUserUseCase: GetMessagesSentToUser,
@inject(TYPES.Sync_GetMessagesSentByUser) override getMessagesSentByUserUseCase: GetMessagesSentByUser,
@inject(TYPES.Sync_SendMessageToUser) override sendMessageToUserUseCase: SendMessageToUser,
@inject(TYPES.Sync_DeleteAllMessagesSentToUser)
override deleteMessagesSentToUserUseCase: DeleteAllMessagesSentToUser,
@inject(TYPES.Sync_DeleteMessage) override deleteMessageUseCase: DeleteMessage,
@inject(TYPES.Sync_MessageHttpMapper)
override messageHttpMapper: MapperInterface<Message, MessageHttpRepresentation>,
) {
super(
getMessageSentToUserUseCase,
getMessagesSentByUserUseCase,
sendMessageToUserUseCase,
deleteMessagesSentToUserUseCase,
deleteMessageUseCase,
messageHttpMapper,
)
}
@httpGet('/')
override async getMessages(_request: Request, response: Response): Promise<results.JsonResult> {
return super.getMessages(_request, response)
}
@httpGet('/outbound')
override async getMessagesSent(_request: Request, response: Response): Promise<results.JsonResult> {
return super.getMessagesSent(_request, response)
}
@httpPost('/')
override async sendMessage(request: Request, response: Response): Promise<results.JsonResult> {
return super.sendMessage(request, response)
}
@httpDelete('/inbound')
override async deleteMessagesSentToUser(_request: Request, response: Response): Promise<results.JsonResult> {
return super.deleteMessagesSentToUser(_request, response)
}
@httpDelete('/:messageUuid')
override async deleteMessage(request: Request, response: Response): Promise<results.JsonResult> {
return super.deleteMessage(request, response)
}
}
@@ -0,0 +1,21 @@
import { MapperInterface } from '@standardnotes/domain-core'
import { Message } from '../../Domain/Message/Message'
import { MessageHttpRepresentation } from './MessageHttpRepresentation'
export class MessageHttpMapper implements MapperInterface<Message, MessageHttpRepresentation> {
toDomain(_projection: MessageHttpRepresentation): Message {
throw new Error('Mapping from http representation to domain is not implemented.')
}
toProjection(domain: Message): MessageHttpRepresentation {
return {
uuid: domain.id.toString(),
recipient_uuid: domain.props.recipientUuid.value,
sender_uuid: domain.props.senderUuid.value,
encrypted_message: domain.props.encryptedMessage,
created_at_timestamp: domain.props.timestamps.createdAt,
updated_at_timestamp: domain.props.timestamps.updatedAt,
}
}
}
@@ -0,0 +1,8 @@
export interface MessageHttpRepresentation {
uuid: string
recipient_uuid: string
sender_uuid: string
encrypted_message: string
created_at_timestamp: number
updated_at_timestamp: number
}