mirror of
https://github.com/standardnotes/server
synced 2026-01-20 23:04:28 -05:00
Compare commits
8 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2606f6d929 | ||
|
|
c288e5d8dc | ||
|
|
4b76d4b71e | ||
|
|
72310130d2 | ||
|
|
f9e51ef06e | ||
|
|
92a5eb0d98 | ||
|
|
77d2ea1a1f | ||
|
|
92f96ddb84 |
@@ -3,6 +3,10 @@
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.24.7",
|
||||
"version": "2.24.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.65.4",
|
||||
"version": "1.65.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
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
|
||||
|
||||
@@ -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.122.1",
|
||||
"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,12 @@
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.21.0",
|
||||
"version": "1.21.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -43,9 +43,6 @@ export * from './Env/AbstractEnv'
|
||||
|
||||
export * from './Mapping/MapperInterface'
|
||||
|
||||
export * from './Notification/NotificationType'
|
||||
export * from './Notification/NotificationTypeProps'
|
||||
|
||||
export * from './Service/ServiceConfiguration'
|
||||
export * from './Service/ServiceContainer'
|
||||
export * from './Service/ServiceContainerInterface'
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.113.0",
|
||||
"version": "2.113.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { NotificationRequestedEventPayload } from './NotificationRequestedEventPayload'
|
||||
|
||||
export interface NotificationRequestedEvent extends DomainEventInterface {
|
||||
type: 'NOTIFICATION_REQUESTED'
|
||||
payload: NotificationRequestedEventPayload
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface NotificationRequestedEventPayload {
|
||||
userUuid: string
|
||||
type: string
|
||||
payload: string
|
||||
}
|
||||
@@ -42,8 +42,6 @@ export * from './Event/ListedAccountRequestedEvent'
|
||||
export * from './Event/ListedAccountRequestedEventPayload'
|
||||
export * from './Event/MuteEmailsSettingChangedEvent'
|
||||
export * from './Event/MuteEmailsSettingChangedEventPayload'
|
||||
export * from './Event/NotificationRequestedEvent'
|
||||
export * from './Event/NotificationRequestedEventPayload'
|
||||
export * from './Event/PaymentFailedEvent'
|
||||
export * from './Event/PaymentFailedEventPayload'
|
||||
export * from './Event/PaymentSuccessEvent'
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.4",
|
||||
"version": "1.11.5",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.19.4",
|
||||
"version": "1.19.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.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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.11.28",
|
||||
"version": "1.11.32",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.23.8",
|
||||
"version": "1.23.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.20.6",
|
||||
"version": "1.20.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/settings",
|
||||
"version": "1.21.11",
|
||||
"version": "1.21.12",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
|
||||
@@ -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.55.0",
|
||||
"version": "1.58.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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
|
||||
@@ -44,6 +44,15 @@ const TYPES = {
|
||||
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_SharedVaultInviteHttpMapper: Symbol.for('Sync_SharedVaultInviteHttpMapper'),
|
||||
// Handlers
|
||||
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
|
||||
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
NotificationRequestedEvent,
|
||||
RevisionsCopyRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
@@ -14,25 +13,6 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
createNotificationRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
type: string
|
||||
payload: string
|
||||
}): NotificationRequestedEvent {
|
||||
return {
|
||||
type: 'NOTIFICATION_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.SyncingServer,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createRevisionsCopyRequestedEvent(
|
||||
userUuid: string,
|
||||
dto: {
|
||||
|
||||
@@ -3,12 +3,10 @@ import {
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
NotificationRequestedEvent,
|
||||
RevisionsCopyRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
|
||||
export interface DomainEventFactoryInterface {
|
||||
createNotificationRequestedEvent(dto: { userUuid: string; type: string; payload: string }): NotificationRequestedEvent
|
||||
createEmailRequestedEvent(dto: {
|
||||
userEmail: string
|
||||
messageIdentifier: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NotificationType, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Notification } from './Notification'
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
describe('Notification', () => {
|
||||
it('should create an entity', () => {
|
||||
@@ -1,4 +1,6 @@
|
||||
import { NotificationType, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
export interface NotificationProps {
|
||||
userUuid: Uuid
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Notification } from './Notification'
|
||||
|
||||
export interface NotificationRepositoryInterface {
|
||||
save(notification: Notification): Promise<void>
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { ValueObject, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationTypeProps } from './NotificationTypeProps'
|
||||
|
||||
@@ -7,5 +7,8 @@ export interface SharedVaultInviteRepositoryInterface {
|
||||
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[]>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,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,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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeleteSharedVaultInvitesSentByUserDTO {
|
||||
userUuid: string
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Result, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { DeleteSharedVaultInvitesToUser } from './DeleteSharedVaultInvitesToUser'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('DeleteSharedVaultInvitesToUser', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let declineInviteToSharedVault: DeclineInviteToSharedVault
|
||||
let sharedVaultInvite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () =>
|
||||
new DeleteSharedVaultInvitesToUser(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.findByUserUuid = jest.fn().mockReturnValue([sharedVaultInvite])
|
||||
|
||||
declineInviteToSharedVault = {} as jest.Mocked<DeclineInviteToSharedVault>
|
||||
declineInviteToSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should decline all invites to user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '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',
|
||||
})
|
||||
|
||||
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',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { DeleteSharedVaultInvitesToUserDTO } from './DeleteSharedVaultInvitesToUserDTO'
|
||||
import { DeclineInviteToSharedVault } from '../DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class DeleteSharedVaultInvitesToUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private declineInviteToSharedVault: DeclineInviteToSharedVault,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteSharedVaultInvitesToUserDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const inboundInvites = await this.sharedVaultInviteRepository.findByUserUuid(userUuid)
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface DeleteSharedVaultInvitesToUserDTO {
|
||||
userUuid: string
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { GetSharedVaultInvitesSentByUser } from './GetSharedVaultInvitesSentByUser'
|
||||
|
||||
describe('GetSharedVaultInvitesSentByUser', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let invite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () => new GetSharedVaultInvitesSentByUser(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.findBySenderUuid = jest.fn().mockResolvedValue([invite])
|
||||
})
|
||||
|
||||
it('should return invites sent by user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([invite])
|
||||
})
|
||||
|
||||
it('should return empty array if no invites found', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
sharedVaultInviteRepository.findBySenderUuid = jest.fn().mockResolvedValue([])
|
||||
|
||||
const result = await useCase.execute({
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([])
|
||||
})
|
||||
|
||||
it('should fail if sender uuid is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
senderUuid: 'invalid-uuid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should return invites sent by user for specific shared vault', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
sharedVaultInviteRepository.findBySenderUuidAndSharedVaultUuid = jest.fn().mockResolvedValue([invite])
|
||||
|
||||
const result = await useCase.execute({
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([invite])
|
||||
})
|
||||
|
||||
it('should fail if shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid-uuid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { GetSharedVaultInvitesSentByUserDTO } from './GetSharedVaultInvitesSentByUserDTO'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class GetSharedVaultInvitesSentByUser implements UseCaseInterface<SharedVaultInvite[]> {
|
||||
constructor(private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetSharedVaultInvitesSentByUserDTO): Promise<Result<SharedVaultInvite[]>> {
|
||||
const senderUuidOrError = Uuid.create(dto.senderUuid)
|
||||
if (senderUuidOrError.isFailed()) {
|
||||
return Result.fail(senderUuidOrError.getError())
|
||||
}
|
||||
const senderUuid = senderUuidOrError.getValue()
|
||||
|
||||
let sharedVaultUuid: Uuid | undefined
|
||||
if (dto.sharedVaultUuid) {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
}
|
||||
|
||||
if (sharedVaultUuid) {
|
||||
return Result.ok(
|
||||
await this.sharedVaultInviteRepository.findBySenderUuidAndSharedVaultUuid({
|
||||
senderUuid,
|
||||
sharedVaultUuid,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return Result.ok(await this.sharedVaultInviteRepository.findBySenderUuid(senderUuid))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetSharedVaultInvitesSentByUserDTO {
|
||||
senderUuid: string
|
||||
sharedVaultUuid?: string
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { GetSharedVaultInvitesSentToUser } from './GetSharedVaultInvitesSentToUser'
|
||||
|
||||
describe('GetSharedVaultInvitesSentToUser', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let invite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () => new GetSharedVaultInvitesSentToUser(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.findByUserUuid = jest.fn().mockResolvedValue([invite])
|
||||
})
|
||||
|
||||
it('should return invites sent to user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([invite])
|
||||
})
|
||||
|
||||
it('should return empty array if no invites found', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
sharedVaultInviteRepository.findByUserUuid = jest.fn().mockReturnValue([])
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([])
|
||||
})
|
||||
|
||||
it('should fail if sender uuid is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid-uuid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { GetSharedVaultInvitesSentToUserDTO } from './GetSharedVaultInvitesSentToUserDTO'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class GetSharedVaultInvitesSentToUser implements UseCaseInterface<SharedVaultInvite[]> {
|
||||
constructor(private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetSharedVaultInvitesSentToUserDTO): Promise<Result<SharedVaultInvite[]>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
return Result.ok(await this.sharedVaultInviteRepository.findByUserUuid(userUuid))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GetSharedVaultInvitesSentToUserDTO {
|
||||
userUuid: string
|
||||
}
|
||||
@@ -1,29 +1,22 @@
|
||||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
import { DomainEventPublisherInterface, NotificationRequestedEvent } from '@standardnotes/domain-events'
|
||||
import { Uuid, Timestamps, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { RemoveUserFromSharedVault } from './RemoveUserFromSharedVault'
|
||||
import { AddNotificationForUser } from '../AddNotificationForUser/AddNotificationForUser'
|
||||
|
||||
describe('RemoveUserFromSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let addNotificationForUser: AddNotificationForUser
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new RemoveUserFromSharedVault(
|
||||
sharedVaultUserRepository,
|
||||
sharedVaultRepository,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
)
|
||||
new RemoveUserFromSharedVault(sharedVaultUserRepository, sharedVaultRepository, addNotificationForUser)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
@@ -46,13 +39,8 @@ describe('RemoveUserFromSharedVault', () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(sharedVaultUser)
|
||||
sharedVaultUserRepository.remove = jest.fn()
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createNotificationRequestedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<NotificationRequestedEvent>)
|
||||
addNotificationForUser = {} as jest.Mocked<AddNotificationForUser>
|
||||
addNotificationForUser.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should remove user from shared vault', async () => {
|
||||
@@ -64,7 +52,6 @@ describe('RemoveUserFromSharedVault', () => {
|
||||
})
|
||||
|
||||
expect(sharedVaultUserRepository.remove).toHaveBeenCalledWith(sharedVaultUser)
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault is not found', async () => {
|
||||
@@ -162,4 +149,28 @@ describe('RemoveUserFromSharedVault', () => {
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should add notification for user', async () => {
|
||||
const useCase = createUseCase()
|
||||
await useCase.execute({
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(addNotificationForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error if notification could not be added', async () => {
|
||||
addNotificationForUser.execute = jest.fn().mockResolvedValue(Result.fail('Could not add notification'))
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { NotificationType, Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RemoveUserFromSharedVaultDTO } from './RemoveUserFromSharedVaultDTO'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { AddNotificationForUser } from '../AddNotificationForUser/AddNotificationForUser'
|
||||
import { NotificationType } from '../../Notifications/NotificationType'
|
||||
|
||||
export class RemoveUserFromSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultUsersRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private addNotificationForUser: AddNotificationForUser,
|
||||
) {}
|
||||
|
||||
async execute(dto: RemoveUserFromSharedVaultDTO): Promise<Result<void>> {
|
||||
@@ -58,16 +57,17 @@ export class RemoveUserFromSharedVault implements UseCaseInterface<void> {
|
||||
|
||||
await this.sharedVaultUsersRepository.remove(sharedVaultUser)
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createNotificationRequestedEvent({
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
userUuid: sharedVaultUser.props.userUuid.value,
|
||||
payload: JSON.stringify({
|
||||
sharedVaultUuid: sharedVault.id.toString(),
|
||||
version: '1.0',
|
||||
}),
|
||||
const result = await this.addNotificationForUser.execute({
|
||||
userUuid: sharedVaultUser.props.userUuid.value,
|
||||
type: NotificationType.TYPES.RemovedFromSharedVault,
|
||||
payload: JSON.stringify({
|
||||
sharedVaultUuid: sharedVault.id.toString(),
|
||||
}),
|
||||
)
|
||||
version: '1.0',
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Inv
|
||||
import { UpdateSharedVaultInviteDTO } from './UpdateSharedVaultInviteDTO'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
|
||||
export class UpdateSharedVaultInvite implements UseCaseInterface<void> {
|
||||
export class UpdateSharedVaultInvite implements UseCaseInterface<SharedVaultInvite> {
|
||||
constructor(
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateSharedVaultInviteDTO): Promise<Result<void>> {
|
||||
async execute(dto: UpdateSharedVaultInviteDTO): Promise<Result<SharedVaultInvite>> {
|
||||
const inviteUuidOrError = Uuid.create(dto.inviteUuid)
|
||||
if (inviteUuidOrError.isFailed()) {
|
||||
return Result.fail(inviteUuidOrError.getError())
|
||||
@@ -57,6 +58,6 @@ export class UpdateSharedVaultInvite implements UseCaseInterface<void> {
|
||||
|
||||
await this.sharedVaultInviteRepository.save(invite)
|
||||
|
||||
return Result.ok()
|
||||
return Result.ok(invite)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||
import { HttpStatusCode } from '@standardnotes/responses'
|
||||
import { ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { InviteUserToSharedVault } from '../../../Domain/UseCase/InviteUserToSharedVault/InviteUserToSharedVault'
|
||||
import { SharedVaultInvite } from '../../../Domain/SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultInviteHttpRepresentation } from '../../../Mapping/Http/SharedVaultInviteHttpRepresentation'
|
||||
import { UpdateSharedVaultInvite } from '../../../Domain/UseCase/UpdateSharedVaultInvite/UpdateSharedVaultInvite'
|
||||
import { AcceptInviteToSharedVault } from '../../../Domain/UseCase/AcceptInviteToSharedVault/AcceptInviteToSharedVault'
|
||||
import { DeclineInviteToSharedVault } from '../../../Domain/UseCase/DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { DeleteSharedVaultInvitesToUser } from '../../../Domain/UseCase/DeleteSharedVaultInvitesToUser/DeleteSharedVaultInvitesToUser'
|
||||
import { GetSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/GetSharedVaultInvitesSentByUser/GetSharedVaultInvitesSentByUser'
|
||||
import { DeleteSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/DeleteSharedVaultInvitesSentByUser/DeleteSharedVaultInvitesSentByUser'
|
||||
import { GetSharedVaultInvitesSentToUser } from '../../../Domain/UseCase/GetSharedVaultInvitesSentToUser/GetSharedVaultInvitesSentToUser'
|
||||
|
||||
export class HomeServerSharedVaultInvitesController extends BaseHttpController {
|
||||
constructor(
|
||||
protected inviteUserToSharedVaultUseCase: InviteUserToSharedVault,
|
||||
protected updateSharedVaultInviteUseCase: UpdateSharedVaultInvite,
|
||||
protected acceptSharedVaultInviteUseCase: AcceptInviteToSharedVault,
|
||||
protected declineSharedVaultInviteUseCase: DeclineInviteToSharedVault,
|
||||
protected deleteSharedVaultInvitesToUserUseCase: DeleteSharedVaultInvitesToUser,
|
||||
protected deleteSharedVaultInvitesSentByUserUseCase: DeleteSharedVaultInvitesSentByUser,
|
||||
protected getSharedVaultInvitesSentByUserUseCase: GetSharedVaultInvitesSentByUser,
|
||||
protected getSharedVaultInvitesSentToUserUseCase: GetSharedVaultInvitesSentToUser,
|
||||
protected sharedVaultInviteHttpMapper: MapperInterface<SharedVaultInvite, SharedVaultInviteHttpRepresentation>,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
|
||||
if (this.controllerContainer !== undefined) {
|
||||
this.controllerContainer.register('sync.shared-vault-invites.create', this.createSharedVaultInvite.bind(this))
|
||||
this.controllerContainer.register('sync.shared-vault-invites.update', this.updateSharedVaultInvite.bind(this))
|
||||
this.controllerContainer.register('sync.shared-vault-invites.accept', this.acceptSharedVaultInvite.bind(this))
|
||||
this.controllerContainer.register('sync.shared-vault-invites.decline', this.declineSharedVaultInvite.bind(this))
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vault-invites.delete-inbound',
|
||||
this.deleteInboundUserInvites.bind(this),
|
||||
)
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vault-invites.get-outbound',
|
||||
this.getOutboundUserInvites.bind(this),
|
||||
)
|
||||
this.controllerContainer.register('sync.shared-vault-invites.get-user-invites', this.getUserInvites.bind(this))
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vault-invites.delete-invite',
|
||||
this.deleteSharedVaultInvite.bind(this),
|
||||
)
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vault-invites.delete-all',
|
||||
this.deleteAllSharedVaultInvites.bind(this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async createSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.inviteUserToSharedVaultUseCase.execute({
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
senderUuid: response.locals.user.uuid,
|
||||
recipientUuid: request.body.recipient_uid,
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
permission: request.body.permission,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
invite: this.sharedVaultInviteHttpMapper.toProjection(result.getValue()),
|
||||
})
|
||||
}
|
||||
|
||||
async updateSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.updateSharedVaultInviteUseCase.execute({
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
senderUuid: response.locals.user.uuid,
|
||||
permission: request.body.permission,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
invite: this.sharedVaultInviteHttpMapper.toProjection(result.getValue()),
|
||||
})
|
||||
}
|
||||
|
||||
async acceptSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.acceptSharedVaultInviteUseCase.execute({
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
|
||||
async declineSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.declineSharedVaultInviteUseCase.execute({
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
|
||||
async deleteInboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.deleteSharedVaultInvitesToUserUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
|
||||
async getOutboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
||||
senderUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
invites: result.getValue().map((invite) => this.sharedVaultInviteHttpMapper.toProjection(invite)),
|
||||
})
|
||||
}
|
||||
|
||||
async getSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
||||
senderUuid: response.locals.user.uuid,
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
invites: result.getValue().map((invite) => this.sharedVaultInviteHttpMapper.toProjection(invite)),
|
||||
})
|
||||
}
|
||||
|
||||
async getUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getSharedVaultInvitesSentToUserUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
invites: result.getValue().map((invite) => this.sharedVaultInviteHttpMapper.toProjection(invite)),
|
||||
})
|
||||
}
|
||||
|
||||
async deleteSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.declineSharedVaultInviteUseCase.execute({
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
|
||||
async deleteAllSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.deleteSharedVaultInvitesSentByUserUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { controller, httpDelete, httpGet, httpPatch, httpPost, results } from 'inversify-express-utils'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { SharedVaultInvite } from '../../Domain/SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { AcceptInviteToSharedVault } from '../../Domain/UseCase/AcceptInviteToSharedVault/AcceptInviteToSharedVault'
|
||||
import { DeclineInviteToSharedVault } from '../../Domain/UseCase/DeclineInviteToSharedVault/DeclineInviteToSharedVault'
|
||||
import { DeleteSharedVaultInvitesSentByUser } from '../../Domain/UseCase/DeleteSharedVaultInvitesSentByUser/DeleteSharedVaultInvitesSentByUser'
|
||||
import { DeleteSharedVaultInvitesToUser } from '../../Domain/UseCase/DeleteSharedVaultInvitesToUser/DeleteSharedVaultInvitesToUser'
|
||||
import { GetSharedVaultInvitesSentByUser } from '../../Domain/UseCase/GetSharedVaultInvitesSentByUser/GetSharedVaultInvitesSentByUser'
|
||||
import { InviteUserToSharedVault } from '../../Domain/UseCase/InviteUserToSharedVault/InviteUserToSharedVault'
|
||||
import { UpdateSharedVaultInvite } from '../../Domain/UseCase/UpdateSharedVaultInvite/UpdateSharedVaultInvite'
|
||||
import { SharedVaultInviteHttpRepresentation } from '../../Mapping/Http/SharedVaultInviteHttpRepresentation'
|
||||
import { HomeServerSharedVaultInvitesController } from './HomeServer/HomeServerSharedVaultInvitesController'
|
||||
import { GetSharedVaultInvitesSentToUser } from '../../Domain/UseCase/GetSharedVaultInvitesSentToUser/GetSharedVaultInvitesSentToUser'
|
||||
import { inject } from 'inversify'
|
||||
|
||||
@controller('/shared-vaults', TYPES.Sync_AuthMiddleware)
|
||||
export class InversifyExpressSharedVaultInvitesController extends HomeServerSharedVaultInvitesController {
|
||||
constructor(
|
||||
@inject(TYPES.Sync_InviteUserToSharedVault) override inviteUserToSharedVaultUseCase: InviteUserToSharedVault,
|
||||
@inject(TYPES.Sync_UpdateSharedVaultInvite) override updateSharedVaultInviteUseCase: UpdateSharedVaultInvite,
|
||||
@inject(TYPES.Sync_AcceptInviteToSharedVault) override acceptSharedVaultInviteUseCase: AcceptInviteToSharedVault,
|
||||
@inject(TYPES.Sync_DeclineInviteToSharedVault) override declineSharedVaultInviteUseCase: DeclineInviteToSharedVault,
|
||||
@inject(TYPES.Sync_DeleteSharedVaultInvitesToUser)
|
||||
override deleteSharedVaultInvitesToUserUseCase: DeleteSharedVaultInvitesToUser,
|
||||
@inject(TYPES.Sync_DeleteSharedVaultInvitesSentByUser)
|
||||
override deleteSharedVaultInvitesSentByUserUseCase: DeleteSharedVaultInvitesSentByUser,
|
||||
@inject(TYPES.Sync_GetSharedVaultInvitesSentByUser)
|
||||
override getSharedVaultInvitesSentByUserUseCase: GetSharedVaultInvitesSentByUser,
|
||||
@inject(TYPES.Sync_GetSharedVaultInvitesSentToUser)
|
||||
override getSharedVaultInvitesSentToUserUseCase: GetSharedVaultInvitesSentToUser,
|
||||
@inject(TYPES.Sync_SharedVaultInviteHttpMapper)
|
||||
override sharedVaultInviteHttpMapper: MapperInterface<SharedVaultInvite, SharedVaultInviteHttpRepresentation>,
|
||||
) {
|
||||
super(
|
||||
inviteUserToSharedVaultUseCase,
|
||||
updateSharedVaultInviteUseCase,
|
||||
acceptSharedVaultInviteUseCase,
|
||||
declineSharedVaultInviteUseCase,
|
||||
deleteSharedVaultInvitesToUserUseCase,
|
||||
deleteSharedVaultInvitesSentByUserUseCase,
|
||||
getSharedVaultInvitesSentByUserUseCase,
|
||||
getSharedVaultInvitesSentToUserUseCase,
|
||||
sharedVaultInviteHttpMapper,
|
||||
)
|
||||
}
|
||||
|
||||
@httpPost('/:sharedVaultUuid/invites')
|
||||
override async createSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.createSharedVaultInvite(request, response)
|
||||
}
|
||||
|
||||
@httpPatch('/:sharedVaultUuid/invites/:inviteUuid')
|
||||
override async updateSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.updateSharedVaultInvite(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/:sharedVaultUuid/invites/:inviteUuid/accept')
|
||||
override async acceptSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.acceptSharedVaultInvite(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/:sharedVaultUuid/invites/:inviteUuid/decline')
|
||||
override async declineSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.declineSharedVaultInvite(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/invites/inbound')
|
||||
override async deleteInboundUserInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteInboundUserInvites(request, response)
|
||||
}
|
||||
|
||||
@httpGet('/invites/outbound')
|
||||
override async getOutboundUserInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getOutboundUserInvites(request, response)
|
||||
}
|
||||
|
||||
@httpGet('/invites')
|
||||
override async getUserInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getUserInvites(request, response)
|
||||
}
|
||||
|
||||
@httpGet('/:sharedVaultUuid/invites')
|
||||
override async getSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getSharedVaultInvites(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/:sharedVaultUuid/invites/:inviteUuid')
|
||||
override async deleteSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteSharedVaultInvite(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/:sharedVaultUuid/invites')
|
||||
override async deleteAllSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteAllSharedVaultInvites(request, response)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Repository } from 'typeorm'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationRepositoryInterface } from '../../Domain/Notifications/NotificationRepositoryInterface'
|
||||
import { TypeORMNotification } from './TypeORMNotification'
|
||||
import { Notification } from '../../Domain/Notifications/Notification'
|
||||
|
||||
export class TypeORMNotificationRepository implements NotificationRepositoryInterface {
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMNotification>,
|
||||
private mapper: MapperInterface<Notification, TypeORMNotification>,
|
||||
) {}
|
||||
|
||||
async save(sharedVault: Notification): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVault)
|
||||
|
||||
await this.ormRepository.save(persistence)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,45 @@ export class TypeORMSharedVaultInviteRepository implements SharedVaultInviteRepo
|
||||
private mapper: MapperInterface<SharedVaultInvite, TypeORMSharedVaultInvite>,
|
||||
) {}
|
||||
|
||||
async findBySenderUuidAndSharedVaultUuid(dto: {
|
||||
senderUuid: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}): Promise<SharedVaultInvite[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.where('shared_vault_invite.sender_uuid = :uuid', {
|
||||
senderUuid: dto.senderUuid.value,
|
||||
})
|
||||
.andWhere('shared_vault_invite.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async findBySenderUuid(senderUuid: Uuid): Promise<SharedVaultInvite[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.where('shared_vault_invite.sender_uuid = :senderUuid', {
|
||||
senderUuid: senderUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultInvite[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.where('shared_vault_invite.user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultInvite } from '../../Domain/SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultInviteHttpRepresentation } from './SharedVaultInviteHttpRepresentation'
|
||||
|
||||
export class SharedVaultInviteHttpMapper
|
||||
implements MapperInterface<SharedVaultInvite, SharedVaultInviteHttpRepresentation>
|
||||
{
|
||||
toDomain(_projection: SharedVaultInviteHttpRepresentation): SharedVaultInvite {
|
||||
throw new Error('Mapping from http representation to domain is not implemented.')
|
||||
}
|
||||
|
||||
toProjection(domain: SharedVaultInvite): SharedVaultInviteHttpRepresentation {
|
||||
return {
|
||||
uuid: domain.id.toString(),
|
||||
shared_vault_uuid: domain.props.sharedVaultUuid.value,
|
||||
user_uuid: domain.props.userUuid.value,
|
||||
sender_uuid: domain.props.senderUuid.value,
|
||||
encrypted_message: domain.props.encryptedMessage,
|
||||
permissions: domain.props.permission.value,
|
||||
created_at_timestamp: domain.props.timestamps.createdAt,
|
||||
updated_at_timestamp: domain.props.timestamps.updatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface SharedVaultInviteHttpRepresentation {
|
||||
uuid: string
|
||||
shared_vault_uuid: string
|
||||
user_uuid: string
|
||||
sender_uuid: string
|
||||
encrypted_message: string
|
||||
permissions: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Notification } from '../../Domain/Notifications/Notification'
|
||||
|
||||
import { TypeORMNotification } from '../../Infra/TypeORM/TypeORMNotification'
|
||||
import { NotificationType } from '../../Domain/Notifications/NotificationType'
|
||||
|
||||
export class NotificationPersistenceMapper implements MapperInterface<Notification, TypeORMNotification> {
|
||||
toDomain(projection: TypeORMNotification): Notification {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create notification from projection: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
throw new Error(`Failed to create notification from projection: ${timestampsOrError.getError()}`)
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
const typeOrError = NotificationType.create(projection.type)
|
||||
if (typeOrError.isFailed()) {
|
||||
throw new Error(`Failed to create notification from projection: ${typeOrError.getError()}`)
|
||||
}
|
||||
const type = typeOrError.getValue()
|
||||
|
||||
const notificationOrError = Notification.create(
|
||||
{
|
||||
userUuid,
|
||||
payload: projection.payload,
|
||||
type,
|
||||
timestamps,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (notificationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create notification from projection: ${notificationOrError.getError()}`)
|
||||
}
|
||||
const notification = notificationOrError.getValue()
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
toProjection(domain: Notification): TypeORMNotification {
|
||||
const typeorm = new TypeORMNotification()
|
||||
|
||||
typeorm.uuid = domain.id.toString()
|
||||
typeorm.userUuid = domain.props.userUuid.value
|
||||
typeorm.payload = domain.props.payload
|
||||
typeorm.type = domain.props.type.value
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
|
||||
return typeorm
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.8](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.7...@standardnotes/websockets-server@1.9.8) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
## [1.9.7](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.6...@standardnotes/websockets-server@1.9.7) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/websockets-server",
|
||||
"version": "1.9.7",
|
||||
"version": "1.9.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user