mirror of
https://github.com/standardnotes/server
synced 2026-02-03 11:01:13 -05:00
Compare commits
29 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c970b1ea68 | ||
|
|
4d1e2dec26 | ||
|
|
108408a944 | ||
|
|
18d07d431f | ||
|
|
cbc024f67a | ||
|
|
55ec5970da | ||
|
|
58bdca6659 | ||
|
|
ef49b0d3f8 | ||
|
|
9cb691e5ad | ||
|
|
04d09582d4 | ||
|
|
8f90dc172b | ||
|
|
f759261919 | ||
|
|
2606f6d929 | ||
|
|
c288e5d8dc | ||
|
|
4b76d4b71e | ||
|
|
72310130d2 | ||
|
|
f9e51ef06e | ||
|
|
92a5eb0d98 | ||
|
|
77d2ea1a1f | ||
|
|
92f96ddb84 | ||
|
|
15a914e25e | ||
|
|
912a29d091 | ||
|
|
b2c32ce70e | ||
|
|
ed1a708c40 | ||
|
|
e905128d45 | ||
|
|
fd598f372a | ||
|
|
7a3946a9e2 | ||
|
|
cbdd2584d0 | ||
|
|
f3161c2712 |
@@ -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.
|
||||
|
||||
## [2.24.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.7...@standardnotes/analytics@2.24.8) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.6...@standardnotes/analytics@2.24.7) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.5...@standardnotes/analytics@2.24.6) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.4...@standardnotes/analytics@2.24.5) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.24.5",
|
||||
"version": "2.24.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.65.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.4...@standardnotes/api-gateway@1.65.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.3...@standardnotes/api-gateway@1.65.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.2...@standardnotes/api-gateway@1.65.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.1...@standardnotes/api-gateway@1.65.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.65.2",
|
||||
"version": "1.65.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.122.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.1...@standardnotes/auth-server@1.122.2) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
## [1.122.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.0...@standardnotes/auth-server@1.122.1) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.122.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.121.0...@standardnotes/auth-server@1.122.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.121.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.2...@standardnotes/auth-server@1.121.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540448428 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540448428'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540623273 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540623273'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.121.0",
|
||||
"version": "1.122.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -18,7 +18,6 @@ import { TypeORMEmergencyAccessInvitation } from '../Infra/TypeORM/TypeORMEmerge
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { TypeORMNotification } from '../Infra/TypeORM/TypeORMNotification'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
@@ -65,7 +64,6 @@ export class AppDataSource {
|
||||
TypeORMAuthenticatorChallenge,
|
||||
TypeORMEmergencyAccessInvitation,
|
||||
TypeORMCacheEntry,
|
||||
TypeORMNotification,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.0...@standardnotes/domain-core@1.21.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.19.0",
|
||||
"version": "1.21.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
|
||||
return false
|
||||
}
|
||||
|
||||
equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
override equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
if (this.props.value.length !== roleNameCollection.value.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -13,4 +13,25 @@ describe('Uuid', () => {
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check equality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects of different types', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
|
||||
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,4 +7,12 @@ export abstract class ValueObject<T extends ValueObjectProps> {
|
||||
constructor(props: T) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
public equals(vo?: ValueObject<T>): boolean {
|
||||
if (vo === null || vo === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return JSON.stringify(this.props) === JSON.stringify(vo.props)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.12.9](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.8...@standardnotes/domain-events-infra@1.12.9) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.8](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.7...@standardnotes/domain-events-infra@1.12.8) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.7](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.6...@standardnotes/domain-events-infra@1.12.7) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [2.113.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.113.0...@standardnotes/domain-events@2.113.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [2.113.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.1...@standardnotes/domain-events@2.113.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
## [2.112.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.0...@standardnotes/domain-events@2.112.1) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.112.1",
|
||||
"version": "2.113.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.11.5](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.4...@standardnotes/event-store@1.11.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.3...@standardnotes/event-store@1.11.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.2...@standardnotes/event-store@1.11.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.1...@standardnotes/event-store@1.11.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.2",
|
||||
"version": "1.11.5",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -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.19.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.4...@standardnotes/files-server@1.19.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.3...@standardnotes/files-server@1.19.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.2...@standardnotes/files-server@1.19.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.1...@standardnotes/files-server@1.19.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.19.2",
|
||||
"version": "1.19.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,62 @@
|
||||
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.11.35](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.34...@standardnotes/home-server@1.11.35) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.34](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.33...@standardnotes/home-server@1.11.34) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.33](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.32...@standardnotes/home-server@1.11.33) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.32](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.31...@standardnotes/home-server@1.11.32) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.31](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.30...@standardnotes/home-server@1.11.31) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.30](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.29...@standardnotes/home-server@1.11.30) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.29](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.28...@standardnotes/home-server@1.11.29) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.28](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.27...@standardnotes/home-server@1.11.28) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.27](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.26...@standardnotes/home-server@1.11.27) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.26](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.25...@standardnotes/home-server@1.11.26) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.25](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.24...@standardnotes/home-server@1.11.25) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.24](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.23...@standardnotes/home-server@1.11.24) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.11.24",
|
||||
"version": "1.11.38",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.23.9](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.8...@standardnotes/revisions-server@1.23.9) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.7...@standardnotes/revisions-server@1.23.8) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.6...@standardnotes/revisions-server@1.23.7) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.5...@standardnotes/revisions-server@1.23.6) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.23.6",
|
||||
"version": "1.23.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.20.7](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.6...@standardnotes/scheduler-server@1.20.7) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.6](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.5...@standardnotes/scheduler-server@1.20.6) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.4...@standardnotes/scheduler-server@1.20.5) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.3...@standardnotes/scheduler-server@1.20.4) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.20.4",
|
||||
"version": "1.20.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.21.12](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.11...@standardnotes/settings@1.21.12) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.11](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.10...@standardnotes/settings@1.21.11) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.10](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.9...@standardnotes/settings@1.21.10) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.9](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.8...@standardnotes/settings@1.21.9) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/settings",
|
||||
"version": "1.21.9",
|
||||
"version": "1.21.12",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,91 @@
|
||||
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
|
||||
|
||||
* message operations use cases. ([#652](https://github.com/standardnotes/syncing-server-js/issues/652)) ([55ec597](https://github.com/standardnotes/syncing-server-js/commit/55ec5970daff9ef51f59e23eca17b312d392542a))
|
||||
|
||||
# [1.60.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.59.1...@standardnotes/syncing-server@1.60.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* sending messages. ([#651](https://github.com/standardnotes/syncing-server-js/issues/651)) ([ef49b0d](https://github.com/standardnotes/syncing-server-js/commit/ef49b0d3f8ab76dfa63a4c691feb9f35ad65c46f))
|
||||
|
||||
## [1.59.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.59.0...@standardnotes/syncing-server@1.59.1) (2023-07-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* restructure use cases ([#650](https://github.com/standardnotes/syncing-server-js/issues/650)) ([04d0958](https://github.com/standardnotes/syncing-server-js/commit/04d09582d4c90706a7c7a4601ce011edf6cbc9c2))
|
||||
|
||||
# [1.59.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.58.1...@standardnotes/syncing-server@1.59.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* user to user message model. ([#649](https://github.com/standardnotes/syncing-server-js/issues/649)) ([f759261](https://github.com/standardnotes/syncing-server-js/commit/f7592619199596f7bef5787dde25efee017c8e60))
|
||||
|
||||
## [1.58.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.58.0...@standardnotes/syncing-server@1.58.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/syncing-server-js/issues/648)) ([c288e5d](https://github.com/standardnotes/syncing-server-js/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.58.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.57.0...@standardnotes/syncing-server@1.58.0) (2023-07-07)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vault invites controller and use cases ([#647](https://github.com/standardnotes/syncing-server-js/issues/647)) ([7231013](https://github.com/standardnotes/syncing-server-js/commit/72310130d215047a8097a0c42a7b7dddeb4e3827))
|
||||
|
||||
# [1.57.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.56.0...@standardnotes/syncing-server@1.57.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* remove inbound shared vault invites. ([#646](https://github.com/standardnotes/syncing-server-js/issues/646)) ([92a5eb0](https://github.com/standardnotes/syncing-server-js/commit/92a5eb0d98486f25b761f37bc5710c45bd95d965))
|
||||
|
||||
# [1.56.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.55.0...@standardnotes/syncing-server@1.56.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* accept and decline shared vault invites ([#645](https://github.com/standardnotes/syncing-server-js/issues/645)) ([92f96dd](https://github.com/standardnotes/syncing-server-js/commit/92f96ddb84b9b7662899ef187ac38ad2a5769640))
|
||||
|
||||
# [1.55.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.54.0...@standardnotes/syncing-server@1.55.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vault users controller. ([#643](https://github.com/standardnotes/syncing-server-js/issues/643)) ([b2c32ce](https://github.com/standardnotes/syncing-server-js/commit/b2c32ce70e9020b8d755a65432cb286b624a009c))
|
||||
* update shared vault invite. ([#644](https://github.com/standardnotes/syncing-server-js/issues/644)) ([912a29d](https://github.com/standardnotes/syncing-server-js/commit/912a29d091ed1ca0af1712cbd09986a1c173a960))
|
||||
|
||||
# [1.54.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.53.0...@standardnotes/syncing-server@1.54.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/syncing-server-js/issues/642)) ([e905128](https://github.com/standardnotes/syncing-server-js/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.53.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.52.0...@standardnotes/syncing-server@1.53.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* http controllers for shared vaults. ([#641](https://github.com/standardnotes/syncing-server-js/issues/641)) ([7a3946a](https://github.com/standardnotes/syncing-server-js/commit/7a3946a9e2d4168a1d286df321d9972588252b5d))
|
||||
|
||||
# [1.52.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.51.0...@standardnotes/syncing-server@1.52.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/syncing-server-js/issues/640)) ([f3161c2](https://github.com/standardnotes/syncing-server-js/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.50.0...@standardnotes/syncing-server@1.51.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540448427 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540448427'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540623272 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540623272'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.51.0",
|
||||
"version": "1.62.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -41,9 +41,9 @@ import { SyncResponseFactory20161215 } from '../Domain/Item/SyncResponse/SyncRes
|
||||
import { SyncResponseFactory20200115 } from '../Domain/Item/SyncResponse/SyncResponseFactory20200115'
|
||||
import { SyncResponseFactoryResolver } from '../Domain/Item/SyncResponse/SyncResponseFactoryResolver'
|
||||
import { SyncResponseFactoryResolverInterface } from '../Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
|
||||
import { CheckIntegrity } from '../Domain/UseCase/CheckIntegrity/CheckIntegrity'
|
||||
import { GetItem } from '../Domain/UseCase/GetItem/GetItem'
|
||||
import { SyncItems } from '../Domain/UseCase/SyncItems'
|
||||
import { CheckIntegrity } from '../Domain/UseCase/Syncing/CheckIntegrity/CheckIntegrity'
|
||||
import { GetItem } from '../Domain/UseCase/Syncing/GetItem/GetItem'
|
||||
import { SyncItems } from '../Domain/UseCase/Syncing/SyncItems/SyncItems'
|
||||
import { InversifyExpressAuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware'
|
||||
import { ItemConflictProjection } from '../Projection/ItemConflictProjection'
|
||||
import { ItemConflictProjector } from '../Projection/ItemConflictProjector'
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
import { Item } from '../Domain/Item/Item'
|
||||
import { Notification } from '../Domain/Notifications/Notification'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
|
||||
export class AppDataSource {
|
||||
private dataSource: DataSource | undefined
|
||||
private _dataSource: DataSource | undefined
|
||||
|
||||
constructor(private env: Env) {}
|
||||
|
||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||
if (!this.dataSource) {
|
||||
if (!this._dataSource) {
|
||||
throw new Error('DataSource not initialized')
|
||||
}
|
||||
|
||||
return this.dataSource.getRepository(target)
|
||||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
this.env.load()
|
||||
|
||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||
@@ -28,7 +33,7 @@ export class AppDataSource {
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [Item],
|
||||
entities: [Item, Notification],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
@@ -72,7 +77,7 @@ export class AppDataSource {
|
||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
@@ -80,9 +85,9 @@ export class AppDataSource {
|
||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
await this.dataSource.initialize()
|
||||
return this._dataSource
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
||||
@@ -38,6 +38,25 @@ const TYPES = {
|
||||
Sync_SyncItems: Symbol.for('Sync_SyncItems'),
|
||||
Sync_CheckIntegrity: Symbol.for('Sync_CheckIntegrity'),
|
||||
Sync_GetItem: Symbol.for('Sync_GetItem'),
|
||||
Sync_GetSharedVaults: Symbol.for('Sync_GetSharedVaults'),
|
||||
Sync_CreateSharedVault: Symbol.for('Sync_CreateSharedVault'),
|
||||
Sync_DeleteSharedVault: Symbol.for('Sync_DeleteSharedVault'),
|
||||
Sync_CreateSharedVaultFileValetToken: Symbol.for('Sync_CreateSharedVaultFileValetToken'),
|
||||
Sync_GetSharedVaultUsers: Symbol.for('Sync_GetSharedVaultUsers'),
|
||||
Sync_RemoveSharedVaultUser: Symbol.for('Sync_RemoveSharedVaultUser'),
|
||||
Sync_InviteUserToSharedVault: Symbol.for('Sync_InviteUserToSharedVault'),
|
||||
Sync_UpdateSharedVaultInvite: Symbol.for('Sync_UpdateSharedVaultInvite'),
|
||||
Sync_AcceptInviteToSharedVault: Symbol.for('Sync_AcceptInviteToSharedVault'),
|
||||
Sync_DeclineInviteToSharedVault: Symbol.for('Sync_DeclineInviteToSharedVault'),
|
||||
Sync_DeleteSharedVaultInvitesToUser: Symbol.for('Sync_DeleteSharedVaultInvitesToUser'),
|
||||
Sync_DeleteSharedVaultInvitesSentByUser: Symbol.for('Sync_DeleteSharedVaultInvitesSentByUser'),
|
||||
Sync_GetSharedVaultInvitesSentByUser: Symbol.for('Sync_GetSharedVaultInvitesSentByUser'),
|
||||
Sync_GetSharedVaultInvitesSentToUser: Symbol.for('Sync_GetSharedVaultInvitesSentToUser'),
|
||||
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'),
|
||||
@@ -68,6 +87,11 @@ const TYPES = {
|
||||
Sync_ItemTransferCalculator: Symbol.for('Sync_ItemTransferCalculator'),
|
||||
Sync_ControllerContainer: Symbol.for('Sync_ControllerContainer'),
|
||||
Sync_HomeServerItemsController: Symbol.for('Sync_HomeServerItemsController'),
|
||||
// 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
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
|
||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||
import { SyncItemsResponse } from '../../UseCase/SyncItemsResponse'
|
||||
import { Item } from '../Item'
|
||||
import { ItemConflict } from '../ItemConflict'
|
||||
import { ItemHash } from '../ItemHash'
|
||||
import { ItemProjection } from '../../../Projection/ItemProjection'
|
||||
import { SyncResponse20161215 } from './SyncResponse20161215'
|
||||
import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface'
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
import { SyncItemsResponse } from '../../UseCase/Syncing/SyncItems/SyncItemsResponse'
|
||||
|
||||
export class SyncResponseFactory20161215 implements SyncResponseFactoryInterface {
|
||||
private readonly LEGACY_MIN_CONFLICT_INTERVAL = 20_000_000
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||
import { SyncItemsResponse } from '../../UseCase/SyncItemsResponse'
|
||||
import { Item } from '../Item'
|
||||
import { ItemConflict } from '../ItemConflict'
|
||||
import { ItemConflictProjection } from '../../../Projection/ItemConflictProjection'
|
||||
@@ -7,6 +6,7 @@ import { ItemProjection } from '../../../Projection/ItemProjection'
|
||||
import { SyncResponse20200115 } from './SyncResponse20200115'
|
||||
import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface'
|
||||
import { SavedItemProjection } from '../../../Projection/SavedItemProjection'
|
||||
import { SyncItemsResponse } from '../../UseCase/Syncing/SyncItems/SyncItemsResponse'
|
||||
|
||||
export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SyncItemsResponse } from '../../UseCase/SyncItemsResponse'
|
||||
import { SyncItemsResponse } from '../../UseCase/Syncing/SyncItems/SyncItemsResponse'
|
||||
import { SyncResponse20161215 } from './SyncResponse20161215'
|
||||
import { SyncResponse20200115 } from './SyncResponse20200115'
|
||||
|
||||
|
||||
18
packages/syncing-server/src/Domain/Message/Message.spec.ts
Normal file
18
packages/syncing-server/src/Domain/Message/Message.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Message } from './Message'
|
||||
|
||||
describe('Message', () => {
|
||||
it('should create an entity', () => {
|
||||
const entityOrError = Message.create({
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
recipientUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
replaceabilityIdentifier: 'replaceabilityIdentifier',
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
expect(entityOrError.getValue().id).not.toBeNull()
|
||||
})
|
||||
})
|
||||
17
packages/syncing-server/src/Domain/Message/Message.ts
Normal file
17
packages/syncing-server/src/Domain/Message/Message.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { MessageProps } from './MessageProps'
|
||||
|
||||
export class Message extends Entity<MessageProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
private constructor(props: MessageProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: MessageProps, id?: UniqueEntityId): Result<Message> {
|
||||
return Result.ok<Message>(new Message(props, id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface MessageProps {
|
||||
recipientUuid: Uuid
|
||||
senderUuid: Uuid
|
||||
encryptedMessage: string
|
||||
replaceabilityIdentifier: string | null
|
||||
timestamps: Timestamps
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Message } from './Message'
|
||||
|
||||
export interface MessageRepositoryInterface {
|
||||
findByUuid: (uuid: Uuid) => Promise<Message | null>
|
||||
findByRecipientUuid: (uuid: Uuid) => Promise<Message[]>
|
||||
findBySenderUuid: (uuid: Uuid) => Promise<Message[]>
|
||||
findByRecipientUuidAndReplaceabilityIdentifier: (dto: {
|
||||
recipientUuid: Uuid
|
||||
replaceabilityIdentifier: string
|
||||
}) => Promise<Message | null>
|
||||
save(message: Message): Promise<void>
|
||||
remove(message: Message): Promise<void>
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
export interface NotificationProps {
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Notification } from './Notification'
|
||||
|
||||
export interface NotificationRepositoryInterface {
|
||||
save(notification: Notification): Promise<void>
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Result, ValueObject } from '@standardnotes/domain-core'
|
||||
import { ValueObject, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationTypeProps } from './NotificationTypeProps'
|
||||
|
||||
@@ -6,5 +6,9 @@ export interface SharedVaultInviteRepositoryInterface {
|
||||
findByUuid(sharedVaultInviteUuid: Uuid): Promise<SharedVaultInvite | null>
|
||||
save(sharedVaultInvite: SharedVaultInvite): Promise<void>
|
||||
remove(sharedVaultInvite: SharedVaultInvite): Promise<void>
|
||||
removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void>
|
||||
findByUserUuid(userUuid: Uuid): Promise<SharedVaultInvite[]>
|
||||
findBySenderUuid(senderUuid: Uuid): Promise<SharedVaultInvite[]>
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultInvite | null>
|
||||
findBySenderUuidAndSharedVaultUuid(dto: { senderUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultInvite[]>
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import { SharedVaultUser } from './SharedVaultUser'
|
||||
export interface SharedVaultUserRepositoryInterface {
|
||||
findByUuid(sharedVaultUserUuid: Uuid): Promise<SharedVaultUser | null>
|
||||
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
findBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||
removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void>
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||
}
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ItemRepositoryInterface } from '../../Item/ItemRepositoryInterface'
|
||||
|
||||
import { CheckIntegrity } from './CheckIntegrity'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
describe('CheckIntegrity', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
|
||||
const createUseCase = () => new CheckIntegrity(itemRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findItemsForComputingIntegrityPayloads = jest.fn().mockReturnValue([
|
||||
{
|
||||
uuid: '1-2-3',
|
||||
updated_at_timestamp: 1,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
{
|
||||
uuid: '2-3-4',
|
||||
updated_at_timestamp: 2,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
{
|
||||
uuid: '3-4-5',
|
||||
updated_at_timestamp: 3,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
{
|
||||
uuid: '4-5-6',
|
||||
updated_at_timestamp: 4,
|
||||
content_type: ContentType.ItemsKey,
|
||||
},
|
||||
{
|
||||
uuid: '5-6-7',
|
||||
updated_at_timestamp: 5,
|
||||
content_type: ContentType.File,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
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,
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
import { IntegrityPayload } from '@standardnotes/responses'
|
||||
|
||||
export type CheckIntegrityResponse = {
|
||||
mismatches: IntegrityPayload[]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
|
||||
export interface CreateSharedVaultResult {
|
||||
sharedVaultUser: SharedVaultUser
|
||||
sharedVault: SharedVault
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { ItemRepositoryInterface } from '../../Item/ItemRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { GetItemDTO } from './GetItemDTO'
|
||||
import { GetItemResponse } from './GetItemResponse'
|
||||
|
||||
export class GetItem implements UseCaseInterface {
|
||||
constructor(private itemRepository: ItemRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetItemDTO): Promise<GetItemResponse> {
|
||||
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 {
|
||||
success: true,
|
||||
item,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Item } from '../../Item/Item'
|
||||
|
||||
export type GetItemResponse =
|
||||
| {
|
||||
success: true
|
||||
item: Item
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
message: string
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationRepositoryInterface } from '../../../Notifications/NotificationRepositoryInterface'
|
||||
import { Notification } from '../../../Notifications/Notification'
|
||||
import { AddNotificationForUser } from './AddNotificationForUser'
|
||||
import { NotificationType } from '../../../Notifications/NotificationType'
|
||||
|
||||
describe('AddNotificationForUser', () => {
|
||||
let notificationRepository: NotificationRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
|
||||
const createUseCase = () => new AddNotificationForUser(notificationRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
notificationRepository = {} as jest.Mocked<NotificationRepositoryInterface>
|
||||
notificationRepository.save = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123456789)
|
||||
})
|
||||
|
||||
it('should save notification', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '0e8c3c7e-3f1a-4f7a-9b5a-5b2b0a7d4b1e',
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
payload: 'payload',
|
||||
version: '1.0',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error if user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid',
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
payload: 'payload',
|
||||
version: '1.0',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error if notification type is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '0e8c3c7e-3f1a-4f7a-9b5a-5b2b0a7d4b1e',
|
||||
type: 'invalid',
|
||||
payload: 'payload',
|
||||
version: '1.0',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error if notification payload is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '0e8c3c7e-3f1a-4f7a-9b5a-5b2b0a7d4b1e',
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
payload: '',
|
||||
version: '1.0',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error if notification could not be created', async () => {
|
||||
const mock = jest.spyOn(Notification, 'create')
|
||||
mock.mockImplementation(() => {
|
||||
return Result.fail('Oops')
|
||||
})
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '0e8c3c7e-3f1a-4f7a-9b5a-5b2b0a7d4b1e',
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
payload: 'payload',
|
||||
version: '1.0',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Result, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { AddNotificationForUserDTO } from './AddNotificationForUserDTO'
|
||||
import { NotificationRepositoryInterface } from '../../../Notifications/NotificationRepositoryInterface'
|
||||
import { Notification } from '../../../Notifications/Notification'
|
||||
import { NotificationType } from '../../../Notifications/NotificationType'
|
||||
|
||||
export class AddNotificationForUser implements UseCaseInterface<Notification> {
|
||||
constructor(private notificationRepository: NotificationRepositoryInterface, private timer: TimerInterface) {}
|
||||
|
||||
async execute(dto: AddNotificationForUserDTO): Promise<Result<Notification>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const typeOrError = NotificationType.create(dto.type)
|
||||
if (typeOrError.isFailed()) {
|
||||
return Result.fail(typeOrError.getError())
|
||||
}
|
||||
const type = typeOrError.getValue()
|
||||
|
||||
const paylodNotEmptyValidationResult = Validator.isNotEmpty(dto.payload)
|
||||
if (paylodNotEmptyValidationResult.isFailed()) {
|
||||
return Result.fail(paylodNotEmptyValidationResult.getError())
|
||||
}
|
||||
|
||||
const notificationOrError = Notification.create({
|
||||
userUuid,
|
||||
type,
|
||||
payload: dto.payload,
|
||||
timestamps: Timestamps.create(
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue(),
|
||||
})
|
||||
if (notificationOrError.isFailed()) {
|
||||
return Result.fail(notificationOrError.getError())
|
||||
}
|
||||
const notification = notificationOrError.getValue()
|
||||
|
||||
await this.notificationRepository.save(notification)
|
||||
|
||||
return Result.ok(notification)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface AddNotificationForUserDTO {
|
||||
version: string
|
||||
type: string
|
||||
userUuid: string
|
||||
payload: string
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Result, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { Message } from '../../../Message/Message'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { DeleteMessage } from '../DeleteMessage/DeleteMessage'
|
||||
import { DeleteAllMessagesSentToUser } from './DeleteAllMessagesSentToUser'
|
||||
|
||||
describe('DeleteAllMessagesSentToUser', () => {
|
||||
let messageRepository: MessageRepositoryInterface
|
||||
let deleteMessageUseCase: DeleteMessage
|
||||
let message: Message
|
||||
|
||||
const createUseCase = () => new DeleteAllMessagesSentToUser(messageRepository, deleteMessageUseCase)
|
||||
|
||||
beforeEach(() => {
|
||||
message = Message.create({
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
recipientUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
replaceabilityIdentifier: 'replaceabilityIdentifier',
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
messageRepository = {} as jest.Mocked<MessageRepositoryInterface>
|
||||
messageRepository.findByRecipientUuid = jest.fn().mockReturnValue([message])
|
||||
|
||||
deleteMessageUseCase = {} as jest.Mocked<DeleteMessage>
|
||||
deleteMessageUseCase.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should delete all messages sent to user', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error when recipient uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return error when delete message use case fails', async () => {
|
||||
const useCase = createUseCase()
|
||||
deleteMessageUseCase.execute = jest.fn().mockReturnValue(Result.fail('error'))
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('error')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { DeleteAllMessagesSentToUserDTO } from './DeleteAllMessagesSentToUserDTO'
|
||||
import { DeleteMessage } from '../DeleteMessage/DeleteMessage'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
|
||||
export class DeleteAllMessagesSentToUser implements UseCaseInterface<void> {
|
||||
constructor(private messageRepository: MessageRepositoryInterface, private deleteMessageUseCase: DeleteMessage) {}
|
||||
|
||||
async execute(dto: DeleteAllMessagesSentToUserDTO): Promise<Result<void>> {
|
||||
const recipientUuidOrError = Uuid.create(dto.recipientUuid)
|
||||
if (recipientUuidOrError.isFailed()) {
|
||||
return Result.fail(recipientUuidOrError.getError())
|
||||
}
|
||||
const recipientUuid = recipientUuidOrError.getValue()
|
||||
|
||||
const messages = await this.messageRepository.findByRecipientUuid(recipientUuid)
|
||||
|
||||
for (const message of messages) {
|
||||
const result = await this.deleteMessageUseCase.execute({
|
||||
originatorUuid: recipientUuid.value,
|
||||
messageUuid: message.id.toString(),
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface DeleteAllMessagesSentToUserDTO {
|
||||
recipientUuid: string
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { Message } from '../../../Message/Message'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { DeleteMessage } from './DeleteMessage'
|
||||
|
||||
describe('DeleteMessage', () => {
|
||||
let messageRepository: MessageRepositoryInterface
|
||||
let message: Message
|
||||
|
||||
const createUseCase = () => new DeleteMessage(messageRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
message = Message.create({
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
recipientUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
replaceabilityIdentifier: 'replaceabilityIdentifier',
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
messageRepository = {} as jest.Mocked<MessageRepositoryInterface>
|
||||
messageRepository.remove = jest.fn()
|
||||
messageRepository.findByUuid = jest.fn().mockReturnValue(message)
|
||||
})
|
||||
|
||||
it('should remove message', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
messageUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should return error when message is not found', async () => {
|
||||
messageRepository.findByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
messageUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error if originator is neither the sender nor the recipient', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
messageUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '11111111-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error when message uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
messageUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error when originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
messageUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { DeleteMessageDTO } from './DeleteMessageDTO'
|
||||
|
||||
export class DeleteMessage implements UseCaseInterface<void> {
|
||||
constructor(private messageRepository: MessageRepositoryInterface) {}
|
||||
|
||||
async execute(dto: DeleteMessageDTO): Promise<Result<void>> {
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const messageUuidOrError = Uuid.create(dto.messageUuid)
|
||||
if (messageUuidOrError.isFailed()) {
|
||||
return Result.fail(messageUuidOrError.getError())
|
||||
}
|
||||
const messageUuid = messageUuidOrError.getValue()
|
||||
|
||||
const message = await this.messageRepository.findByUuid(messageUuid)
|
||||
if (!message) {
|
||||
return Result.fail('Message not found')
|
||||
}
|
||||
|
||||
const isSentByOriginator = message.props.senderUuid.equals(originatorUuid)
|
||||
const isSentToOriginator = message.props.recipientUuid.equals(originatorUuid)
|
||||
|
||||
if (!isSentByOriginator && !isSentToOriginator) {
|
||||
return Result.fail('Not authorized to delete this message')
|
||||
}
|
||||
|
||||
await this.messageRepository.remove(message)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeleteMessageDTO {
|
||||
originatorUuid: string
|
||||
messageUuid: string
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { GetMessagesSentByUser } from './GetMessagesSentByUser'
|
||||
|
||||
describe('GetMessagesSentByUser', () => {
|
||||
let messageRepository: MessageRepositoryInterface
|
||||
|
||||
const createUseCase = () => new GetMessagesSentByUser(messageRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
messageRepository = {} as jest.Mocked<MessageRepositoryInterface>
|
||||
messageRepository.findBySenderUuid = jest.fn().mockReturnValue([])
|
||||
})
|
||||
|
||||
it('should return messages sent by user', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([])
|
||||
})
|
||||
|
||||
it('should return error when sender uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
senderUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Message } from '../../../Message/Message'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { GetMessagesSentByUserDTO } from './GetMessagesSentByUserDTO'
|
||||
|
||||
export class GetMessagesSentByUser implements UseCaseInterface<Message[]> {
|
||||
constructor(private messageRepository: MessageRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetMessagesSentByUserDTO): Promise<Result<Message[]>> {
|
||||
const senderUuidOrError = Uuid.create(dto.senderUuid)
|
||||
if (senderUuidOrError.isFailed()) {
|
||||
return Result.fail(senderUuidOrError.getError())
|
||||
}
|
||||
const senderUuid = senderUuidOrError.getValue()
|
||||
|
||||
const messages = await this.messageRepository.findBySenderUuid(senderUuid)
|
||||
|
||||
return Result.ok(messages)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GetMessagesSentByUserDTO {
|
||||
senderUuid: string
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { GetMessagesSentToUser } from './GetMessagesSentToUser'
|
||||
|
||||
describe('GetMessagesSentToUser', () => {
|
||||
let messageRepository: MessageRepositoryInterface
|
||||
|
||||
const createUseCase = () => new GetMessagesSentToUser(messageRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
messageRepository = {} as jest.Mocked<MessageRepositoryInterface>
|
||||
messageRepository.findByRecipientUuid = jest.fn().mockReturnValue([])
|
||||
})
|
||||
|
||||
it('should return messages sent to user', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([])
|
||||
})
|
||||
|
||||
it('should return error when recipient uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Message } from '../../../Message/Message'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { GetMessagesSentToUserDTO } from './GetMessagesSentToUserDTO'
|
||||
|
||||
export class GetMessagesSentToUser implements UseCaseInterface<Message[]> {
|
||||
constructor(private messageRepository: MessageRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetMessagesSentToUserDTO): Promise<Result<Message[]>> {
|
||||
const recipientUuidOrError = Uuid.create(dto.recipientUuid)
|
||||
if (recipientUuidOrError.isFailed()) {
|
||||
return Result.fail(recipientUuidOrError.getError())
|
||||
}
|
||||
const recipientUuid = recipientUuidOrError.getValue()
|
||||
|
||||
const messages = await this.messageRepository.findByRecipientUuid(recipientUuid)
|
||||
|
||||
return Result.ok(messages)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GetMessagesSentToUserDTO {
|
||||
recipientUuid: string
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { SendMessageToUser } from './SendMessageToUser'
|
||||
import { Message } from '../../../Message/Message'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
describe('SendMessageToUser', () => {
|
||||
let messageRepository: MessageRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let existingMessage: Message
|
||||
|
||||
const createUseCase = () => new SendMessageToUser(messageRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
existingMessage = {} as jest.Mocked<Message>
|
||||
|
||||
messageRepository = {} as jest.Mocked<MessageRepositoryInterface>
|
||||
messageRepository.findByRecipientUuidAndReplaceabilityIdentifier = jest.fn().mockReturnValue(null)
|
||||
messageRepository.remove = jest.fn()
|
||||
messageRepository.save = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123456789)
|
||||
})
|
||||
|
||||
it('saves a new message', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'encrypted-message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('removes existing message with the same replaceability identifier', async () => {
|
||||
messageRepository.findByRecipientUuidAndReplaceabilityIdentifier = jest.fn().mockReturnValue(existingMessage)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'encrypted-message',
|
||||
replaceabilityIdentifier: 'replaceability-identifier',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(messageRepository.remove).toHaveBeenCalledWith(existingMessage)
|
||||
})
|
||||
|
||||
it('returns error when recipient uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: 'invalid-uuid',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'encrypted-message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('returns error when sender uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: 'invalid-uuid',
|
||||
encryptedMessage: 'encrypted-message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('returns error when message is empty', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: '',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('returns error when message fails to create', async () => {
|
||||
const mock = jest.spyOn(Message, 'create')
|
||||
mock.mockImplementation(() => {
|
||||
return Result.fail('Oops')
|
||||
})
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'encrypted-message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Result, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { SendMessageToUserDTO } from './SendMessageToUserDTO'
|
||||
import { MessageRepositoryInterface } from '../../../Message/MessageRepositoryInterface'
|
||||
import { Message } from '../../../Message/Message'
|
||||
|
||||
export class SendMessageToUser implements UseCaseInterface<Message> {
|
||||
constructor(private messageRepository: MessageRepositoryInterface, private timer: TimerInterface) {}
|
||||
|
||||
async execute(dto: SendMessageToUserDTO): Promise<Result<Message>> {
|
||||
const recipientUuidOrError = Uuid.create(dto.recipientUuid)
|
||||
if (recipientUuidOrError.isFailed()) {
|
||||
return Result.fail(recipientUuidOrError.getError())
|
||||
}
|
||||
const recipientUuid = recipientUuidOrError.getValue()
|
||||
|
||||
const senderUuidOrError = Uuid.create(dto.senderUuid)
|
||||
if (senderUuidOrError.isFailed()) {
|
||||
return Result.fail(senderUuidOrError.getError())
|
||||
}
|
||||
const senderUuid = senderUuidOrError.getValue()
|
||||
|
||||
const validateNotEmptyMessage = Validator.isNotEmpty(dto.encryptedMessage)
|
||||
if (validateNotEmptyMessage.isFailed()) {
|
||||
return Result.fail(validateNotEmptyMessage.getError())
|
||||
}
|
||||
|
||||
if (dto.replaceabilityIdentifier) {
|
||||
const existingMessage = await this.messageRepository.findByRecipientUuidAndReplaceabilityIdentifier({
|
||||
recipientUuid,
|
||||
replaceabilityIdentifier: dto.replaceabilityIdentifier,
|
||||
})
|
||||
|
||||
if (existingMessage) {
|
||||
await this.messageRepository.remove(existingMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const messageOrError = Message.create({
|
||||
recipientUuid,
|
||||
senderUuid,
|
||||
encryptedMessage: dto.encryptedMessage,
|
||||
timestamps: Timestamps.create(
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue(),
|
||||
replaceabilityIdentifier: dto.replaceabilityIdentifier ?? null,
|
||||
})
|
||||
if (messageOrError.isFailed()) {
|
||||
return Result.fail(messageOrError.getError())
|
||||
}
|
||||
const message = messageOrError.getValue()
|
||||
|
||||
await this.messageRepository.save(message)
|
||||
|
||||
return Result.ok(message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface SendMessageToUserDTO {
|
||||
recipientUuid: string
|
||||
senderUuid: string
|
||||
encryptedMessage: string
|
||||
replaceabilityIdentifier?: string
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { RemoveUserEventsDTO } from './RemoveUserEventsDTO'
|
||||
|
||||
export class RemoveUserEvents implements UseCaseInterface<void> {
|
||||
async execute(_dto: RemoveUserEventsDTO): Promise<Result<void>> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface RemoveUserEventsDTO {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Result, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { AddUserToSharedVault } from '../AddUserToSharedVault/AddUserToSharedVault'
|
||||
import { AcceptInviteToSharedVault } from './AcceptInviteToSharedVault'
|
||||
import { SharedVaultInvite } from '../../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('AcceptInviteToSharedVault', () => {
|
||||
let addUserToSharedVault: AddUserToSharedVault
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let invite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () => new AcceptInviteToSharedVault(addUserToSharedVault, sharedVaultInviteRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
invite = SharedVaultInvite.create({
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encrypted-message',
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
addUserToSharedVault = {} as jest.Mocked<AddUserToSharedVault>
|
||||
addUserToSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(invite)
|
||||
sharedVaultInviteRepository.remove = jest.fn()
|
||||
})
|
||||
|
||||
it('should fail if invite uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should fail if originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should fail if invite is not found', async () => {
|
||||
sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Invite not found')
|
||||
})
|
||||
|
||||
it('should fail if originator is not the recipient of the invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Only the recipient of the invite can accept it')
|
||||
})
|
||||
|
||||
it('should fail if adding user to shared vault fails', async () => {
|
||||
addUserToSharedVault.execute = jest.fn().mockReturnValue(Result.fail('Failed to add user to shared vault'))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Failed to add user to shared vault')
|
||||
})
|
||||
|
||||
it('should delete invite after adding user to shared vault', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(sharedVaultInviteRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { AcceptInviteToSharedVaultDTO } from './AcceptInviteToSharedVaultDTO'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { AddUserToSharedVault } from '../AddUserToSharedVault/AddUserToSharedVault'
|
||||
|
||||
export class AcceptInviteToSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private addUserToSharedVault: AddUserToSharedVault,
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: AcceptInviteToSharedVaultDTO): Promise<Result<void>> {
|
||||
const inviteUuidOrError = Uuid.create(dto.inviteUuid)
|
||||
if (inviteUuidOrError.isFailed()) {
|
||||
return Result.fail(inviteUuidOrError.getError())
|
||||
}
|
||||
const inviteUuid = inviteUuidOrError.getValue()
|
||||
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const invite = await this.sharedVaultInviteRepository.findByUuid(inviteUuid)
|
||||
if (!invite) {
|
||||
return Result.fail('Invite not found')
|
||||
}
|
||||
|
||||
if (!invite.props.userUuid.equals(originatorUuid)) {
|
||||
return Result.fail('Only the recipient of the invite can accept it')
|
||||
}
|
||||
|
||||
const result = await this.addUserToSharedVault.execute({
|
||||
sharedVaultUuid: invite.props.sharedVaultUuid.value,
|
||||
userUuid: invite.props.userUuid.value,
|
||||
permission: invite.props.permission.value,
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
|
||||
await this.sharedVaultInviteRepository.remove(invite)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface AcceptInviteToSharedVaultDTO {
|
||||
inviteUuid: string
|
||||
originatorUuid: string
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { RemoveUserEvents } from '../RemoveUserEvents/RemoveUserEvents'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { AddUserToSharedVault } from './AddUserToSharedVault'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../../SharedVault/User/SharedVaultUser'
|
||||
|
||||
describe('AddUserToSharedVault', () => {
|
||||
let removeUserEvents: RemoveUserEvents
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
@@ -16,13 +14,9 @@ describe('AddUserToSharedVault', () => {
|
||||
|
||||
const validUuid = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
const createUseCase = () =>
|
||||
new AddUserToSharedVault(removeUserEvents, sharedVaultRepository, sharedVaultUserRepository, timer)
|
||||
const createUseCase = () => new AddUserToSharedVault(sharedVaultRepository, sharedVaultUserRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
removeUserEvents = {} as jest.Mocked<RemoveUserEvents>
|
||||
removeUserEvents.execute = jest.fn().mockResolvedValue(Result.ok())
|
||||
|
||||
sharedVault = {} as jest.Mocked<SharedVault>
|
||||
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
@@ -89,21 +83,6 @@ describe('AddUserToSharedVault', () => {
|
||||
expect(result.getError()).toBe('Attempting to add a shared vault user to a non-existent shared vault')
|
||||
})
|
||||
|
||||
it('should return a failure result if removing user events fails', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
removeUserEvents.execute = jest.fn().mockResolvedValueOnce(Result.fail('test'))
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: validUuid,
|
||||
userUuid: validUuid,
|
||||
permission: 'read',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('test')
|
||||
})
|
||||
|
||||
it('should return a failure result if creating the shared vault user fails', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
@@ -2,15 +2,13 @@ import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domai
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { AddUserToSharedVaultDTO } from './AddUserToSharedVaultDTO'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { RemoveUserEvents } from '../RemoveUserEvents/RemoveUserEvents'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUser } from '../../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class AddUserToSharedVault implements UseCaseInterface<SharedVaultUser> {
|
||||
constructor(
|
||||
private removeUserEvents: RemoveUserEvents,
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
@@ -40,14 +38,6 @@ export class AddUserToSharedVault implements UseCaseInterface<SharedVaultUser> {
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const removingEventsResult = await this.removeUserEvents.execute({
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
if (removingEventsResult.isFailed()) {
|
||||
return Result.fail(removingEventsResult.getError())
|
||||
}
|
||||
|
||||
const timestamps = Timestamps.create(
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { AddUserToSharedVault } from '../AddUserToSharedVault/AddUserToSharedVault'
|
||||
import { CreateSharedVault } from './CreateSharedVault'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
|
||||
describe('CreateSharedVault', () => {
|
||||
let addUserToSharedVault: AddUserToSharedVault
|
||||
@@ -2,9 +2,9 @@ import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domai
|
||||
import { CreateSharedVaultResult } from './CreateSharedVaultResult'
|
||||
import { CreateSharedVaultDTO } from './CreateSharedVaultDTO'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { AddUserToSharedVault } from '../AddUserToSharedVault/AddUserToSharedVault'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
|
||||
export class CreateSharedVault implements UseCaseInterface<CreateSharedVaultResult> {
|
||||
private readonly FILE_UPLOAD_BYTES_LIMIT = 1_000_000
|
||||
@@ -0,0 +1,7 @@
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../../SharedVault/User/SharedVaultUser'
|
||||
|
||||
export interface CreateSharedVaultResult {
|
||||
sharedVaultUser: SharedVaultUser
|
||||
sharedVault: SharedVault
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SharedVaultValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { CreateSharedVaultFileValetToken } from './CreateSharedVaultFileValetToken'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
describe('CreateSharedVaultFileValetToken', () => {
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SharedVaultValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { CreateSharedVaultFileValetTokenDTO } from './CreateSharedVaultFileValetTokenDTO'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class CreateSharedVaultFileValetToken implements UseCaseInterface<string> {
|
||||
constructor(
|
||||
@@ -41,7 +41,9 @@ export class CreateSharedVaultFileValetToken implements UseCaseInterface<string>
|
||||
}
|
||||
|
||||
if (
|
||||
sharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read &&
|
||||
sharedVaultUser.props.permission.equals(
|
||||
SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
) &&
|
||||
dto.operation !== ValetTokenOperation.Read
|
||||
) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
@@ -72,7 +74,11 @@ export class CreateSharedVaultFileValetToken implements UseCaseInterface<string>
|
||||
return Result.fail('Shared vault target user not found')
|
||||
}
|
||||
|
||||
if (toSharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read) {
|
||||
if (
|
||||
toSharedVaultUser.props.permission.equals(
|
||||
SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
)
|
||||
) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { DeclineInviteToSharedVault } from './DeclineInviteToSharedVault'
|
||||
import { SharedVaultInvite } from '../../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('DeclineInviteToSharedVault', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let invite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () => new DeclineInviteToSharedVault(sharedVaultInviteRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
invite = SharedVaultInvite.create({
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encrypted-message',
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(invite)
|
||||
sharedVaultInviteRepository.remove = jest.fn()
|
||||
})
|
||||
|
||||
it('should fail if invite uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should fail if originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should fail if invite is not found', async () => {
|
||||
sharedVaultInviteRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Invite not found')
|
||||
})
|
||||
|
||||
it('should fail if originator is not the recipient of the invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Only the recipient of the invite can decline it')
|
||||
})
|
||||
|
||||
it('should delete invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(sharedVaultInviteRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { DeclineInviteToSharedVaultDTO } from './DeclineInviteToSharedVaultDTO'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class DeclineInviteToSharedVault implements UseCaseInterface<void> {
|
||||
constructor(private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface) {}
|
||||
|
||||
async execute(dto: DeclineInviteToSharedVaultDTO): Promise<Result<void>> {
|
||||
const inviteUuidOrError = Uuid.create(dto.inviteUuid)
|
||||
if (inviteUuidOrError.isFailed()) {
|
||||
return Result.fail(inviteUuidOrError.getError())
|
||||
}
|
||||
const inviteUuid = inviteUuidOrError.getValue()
|
||||
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const invite = await this.sharedVaultInviteRepository.findByUuid(inviteUuid)
|
||||
if (!invite) {
|
||||
return Result.fail('Invite not found')
|
||||
}
|
||||
|
||||
if (!invite.props.userUuid.equals(originatorUuid)) {
|
||||
return Result.fail('Only the recipient of the invite can decline it')
|
||||
}
|
||||
|
||||
await this.sharedVaultInviteRepository.remove(invite)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeclineInviteToSharedVaultDTO {
|
||||
inviteUuid: string
|
||||
originatorUuid: string
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import { Uuid, Timestamps, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { DeleteSharedVault } from './DeleteSharedVault'
|
||||
import { SharedVault } from '../../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
describe('DeleteSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let removeUserFromSharedVault: RemoveUserFromSharedVault
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new DeleteSharedVault(
|
||||
sharedVaultRepository,
|
||||
sharedVaultUserRepository,
|
||||
sharedVaultInviteRepository,
|
||||
removeUserFromSharedVault,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
sharedVaultRepository.remove = jest.fn()
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findBySharedVaultUuid = jest.fn().mockResolvedValue([sharedVaultUser])
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.removeBySharedVaultUuid = jest.fn()
|
||||
|
||||
removeUserFromSharedVault = {} as jest.Mocked<RemoveUserFromSharedVault>
|
||||
removeUserFromSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should remove shared vault', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(sharedVaultRepository.remove).toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault does not exist', async () => {
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when originator of the delete request is not the owner of the shared vault', async () => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error if removing user from shared vault fails', async () => {
|
||||
removeUserFromSharedVault.execute = jest.fn().mockReturnValue(Result.fail('failed'))
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { DeleteSharedVaultDTO } from './DeleteSharedVaultDTO'
|
||||
import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
export class DeleteSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private removeUserFromSharedVault: RemoveUserFromSharedVault,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteSharedVaultDTO): Promise<Result<void>> {
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Shared vault not found')
|
||||
}
|
||||
|
||||
if (sharedVault.props.userUuid.value !== originatorUuid.value) {
|
||||
return Result.fail('Shared vault does not belong to the user')
|
||||
}
|
||||
|
||||
const sharedVaultUsers = await this.sharedVaultUserRepository.findBySharedVaultUuid(sharedVaultUuid)
|
||||
for (const sharedVaultUser of sharedVaultUsers) {
|
||||
const result = await this.removeUserFromSharedVault.execute({
|
||||
originatorUuid: originatorUuid.value,
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
userUuid: sharedVaultUser.props.userUuid.value,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
}
|
||||
|
||||
await this.sharedVaultInviteRepository.removeBySharedVaultUuid(sharedVaultUuid)
|
||||
|
||||
await this.sharedVaultRepository.remove(sharedVault)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeleteSharedVaultDTO {
|
||||
originatorUuid: string
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Result, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { DeleteSharedVaultInvitesSentByUser } from './DeleteSharedVaultInvitesSentByUser'
|
||||
import { SharedVaultInvite } from '../../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('DeleteSharedVaultInvitesSentByUser', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let declineInviteToSharedVault: DeclineInviteToSharedVault
|
||||
let sharedVaultInvite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () =>
|
||||
new DeleteSharedVaultInvitesSentByUser(sharedVaultInviteRepository, declineInviteToSharedVault)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVaultInvite = SharedVaultInvite.create({
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
encryptedMessage: 'encrypted-message',
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.findBySenderUuidAndSharedVaultUuid = jest.fn().mockReturnValue([sharedVaultInvite])
|
||||
|
||||
declineInviteToSharedVault = {} as jest.Mocked<DeclineInviteToSharedVault>
|
||||
declineInviteToSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should decline all invites by user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(declineInviteToSharedVault.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid-uuid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid-uuid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return error when declineInviteToSharedVault fails', async () => {
|
||||
declineInviteToSharedVault.execute = jest.fn().mockReturnValue(Result.fail('error'))
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { DeleteSharedVaultInvitesSentByUserDTO } from './DeleteSharedVaultInvitesSentByUserDTO'
|
||||
import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class DeleteSharedVaultInvitesSentByUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private declineInviteToSharedVault: DeclineInviteToSharedVault,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteSharedVaultInvitesSentByUserDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const inboundInvites = await this.sharedVaultInviteRepository.findBySenderUuidAndSharedVaultUuid({
|
||||
senderUuid: userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
for (const invite of inboundInvites) {
|
||||
const result = await this.declineInviteToSharedVault.execute({
|
||||
inviteUuid: invite.id.toString(),
|
||||
originatorUuid: userUuid.value,
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user