mirror of
https://github.com/standardnotes/server
synced 2026-01-22 20:01:10 -05:00
Compare commits
17 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15a914e25e | ||
|
|
912a29d091 | ||
|
|
b2c32ce70e | ||
|
|
ed1a708c40 | ||
|
|
e905128d45 | ||
|
|
fd598f372a | ||
|
|
7a3946a9e2 | ||
|
|
cbdd2584d0 | ||
|
|
f3161c2712 | ||
|
|
148542dd5a | ||
|
|
d2b2c339f2 | ||
|
|
d2578c48f0 | ||
|
|
fecfd54728 | ||
|
|
17e4162d3e | ||
|
|
742209d773 | ||
|
|
1fa4b7cf27 | ||
|
|
5dc5507039 |
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.24.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.6...@standardnotes/analytics@2.24.7) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.5...@standardnotes/analytics@2.24.6) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.4...@standardnotes/analytics@2.24.5) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.24.5",
|
||||
"version": "2.24.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.65.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.3...@standardnotes/api-gateway@1.65.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.2...@standardnotes/api-gateway@1.65.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.1...@standardnotes/api-gateway@1.65.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.65.2",
|
||||
"version": "1.65.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.122.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.0...@standardnotes/auth-server@1.122.1) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.122.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.121.0...@standardnotes/auth-server@1.122.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.121.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.2...@standardnotes/auth-server@1.121.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add notifications model ([#638](https://github.com/standardnotes/server/issues/638)) ([fecfd54](https://github.com/standardnotes/server/commit/fecfd5472824b5adae708db95d351e4ad65ee87b))
|
||||
|
||||
## [1.120.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.1...@standardnotes/auth-server@1.120.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
BIN
packages/auth/database.sqlite
Normal file
BIN
packages/auth/database.sqlite
Normal file
Binary file not shown.
@@ -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/auth-server",
|
||||
"version": "1.120.2",
|
||||
"version": "1.122.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -18,21 +18,26 @@ 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
|
||||
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'
|
||||
@@ -60,6 +65,7 @@ export class AppDataSource {
|
||||
TypeORMAuthenticatorChallenge,
|
||||
TypeORMEmergencyAccessInvitation,
|
||||
TypeORMCacheEntry,
|
||||
TypeORMNotification,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
@@ -104,7 +110,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,
|
||||
@@ -112,9 +118,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
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/auth/src/Bootstrap/MigrationsDataSource.ts
Normal file
7
packages/auth/src/Bootstrap/MigrationsDataSource.ts
Normal file
@@ -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
|
||||
17
packages/auth/src/Domain/Notifications/Notification.spec.ts
Normal file
17
packages/auth/src/Domain/Notifications/Notification.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { NotificationType, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Notification } from './Notification'
|
||||
|
||||
describe('Notification', () => {
|
||||
it('should create an entity', () => {
|
||||
const entityOrError = Notification.create({
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
payload: 'payload',
|
||||
type: NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue(),
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
expect(entityOrError.getValue().id).not.toBeNull()
|
||||
})
|
||||
})
|
||||
17
packages/auth/src/Domain/Notifications/Notification.ts
Normal file
17
packages/auth/src/Domain/Notifications/Notification.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { NotificationProps } from './NotificationProps'
|
||||
|
||||
export class Notification extends Entity<NotificationProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
private constructor(props: NotificationProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: NotificationProps, id?: UniqueEntityId): Result<Notification> {
|
||||
return Result.ok<Notification>(new Notification(props, id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NotificationType, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface NotificationProps {
|
||||
userUuid: Uuid
|
||||
type: NotificationType
|
||||
payload: string
|
||||
timestamps: Timestamps
|
||||
}
|
||||
38
packages/auth/src/Infra/TypeORM/TypeORMNotification.ts
Normal file
38
packages/auth/src/Infra/TypeORM/TypeORMNotification.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'notifications' })
|
||||
export class TypeORMNotification {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('index_notifications_on_user_uuid')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'type',
|
||||
length: 36,
|
||||
})
|
||||
declare type: string
|
||||
|
||||
@Column({
|
||||
name: 'payload',
|
||||
type: 'text',
|
||||
})
|
||||
declare payload: string
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare updatedAtTimestamp: number
|
||||
}
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.19.0",
|
||||
"version": "1.21.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
|
||||
return false
|
||||
}
|
||||
|
||||
equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
override equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
if (this.props.value.length !== roleNameCollection.value.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -13,4 +13,25 @@ describe('Uuid', () => {
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check equality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects of different types', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
|
||||
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,4 +7,12 @@ export abstract class ValueObject<T extends ValueObjectProps> {
|
||||
constructor(props: T) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
public equals(vo?: ValueObject<T>): boolean {
|
||||
if (vo === null || vo === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return JSON.stringify(this.props) === JSON.stringify(vo.props)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
describe('NotificationType', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('shared_vault_item_removed')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = NotificationType.create('TEST')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { NotificationTypeProps } from './NotificationTypeProps'
|
||||
|
||||
export class NotificationType extends ValueObject<NotificationTypeProps> {
|
||||
static readonly TYPES = {
|
||||
SharedVaultItemRemoved: 'shared_vault_item_removed',
|
||||
RemovedFromSharedVault: 'removed_from_shared_vault',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: NotificationTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(notificationType: string): Result<NotificationType> {
|
||||
const isValidPermission = Object.values(this.TYPES).includes(notificationType)
|
||||
if (!isValidPermission) {
|
||||
return Result.fail<NotificationType>(`Invalid shared vault user permission ${notificationType}`)
|
||||
} else {
|
||||
return Result.ok<NotificationType>(new NotificationType({ value: notificationType }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NotificationTypeProps {
|
||||
value: string
|
||||
}
|
||||
@@ -43,6 +43,9 @@ 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.8](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.7...@standardnotes/domain-events-infra@1.12.8) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.7](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.6...@standardnotes/domain-events-infra@1.12.7) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.8",
|
||||
"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.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.1...@standardnotes/domain-events@2.113.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
## [2.112.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.0...@standardnotes/domain-events@2.112.1) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.112.1",
|
||||
"version": "2.113.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { NotificationRequestedEventPayload } from './NotificationRequestedEventPayload'
|
||||
|
||||
export interface NotificationRequestedEvent extends DomainEventInterface {
|
||||
type: 'NOTIFICATION_REQUESTED'
|
||||
payload: NotificationRequestedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface NotificationRequestedEventPayload {
|
||||
userUuid: string
|
||||
type: string
|
||||
payload: string
|
||||
}
|
||||
@@ -42,6 +42,8 @@ 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,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.3...@standardnotes/event-store@1.11.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.2...@standardnotes/event-store@1.11.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.1...@standardnotes/event-store@1.11.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.2",
|
||||
"version": "1.11.4",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.19.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.3...@standardnotes/files-server@1.19.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.2...@standardnotes/files-server@1.19.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.1...@standardnotes/files-server@1.19.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.19.2",
|
||||
"version": "1.19.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.28](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.27...@standardnotes/home-server@1.11.28) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.27](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.26...@standardnotes/home-server@1.11.27) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.26](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.25...@standardnotes/home-server@1.11.26) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.25](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.24...@standardnotes/home-server@1.11.25) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.24](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.23...@standardnotes/home-server@1.11.24) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.23](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.22...@standardnotes/home-server@1.11.23) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.22](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.21...@standardnotes/home-server@1.11.22) (2023-07-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.21](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.20...@standardnotes/home-server@1.11.21) (2023-07-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.11.21",
|
||||
"version": "1.11.28",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.23.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.7...@standardnotes/revisions-server@1.23.8) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.6...@standardnotes/revisions-server@1.23.7) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.5...@standardnotes/revisions-server@1.23.6) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.23.6",
|
||||
"version": "1.23.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.6](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.5...@standardnotes/scheduler-server@1.20.6) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.4...@standardnotes/scheduler-server@1.20.5) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.3...@standardnotes/scheduler-server@1.20.4) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.20.4",
|
||||
"version": "1.20.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.11](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.10...@standardnotes/settings@1.21.11) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.10](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.9...@standardnotes/settings@1.21.10) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.9](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.8...@standardnotes/settings@1.21.9) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/settings",
|
||||
"version": "1.21.9",
|
||||
"version": "1.21.11",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,43 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.55.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.54.0...@standardnotes/syncing-server@1.55.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vault users controller. ([#643](https://github.com/standardnotes/syncing-server-js/issues/643)) ([b2c32ce](https://github.com/standardnotes/syncing-server-js/commit/b2c32ce70e9020b8d755a65432cb286b624a009c))
|
||||
* update shared vault invite. ([#644](https://github.com/standardnotes/syncing-server-js/issues/644)) ([912a29d](https://github.com/standardnotes/syncing-server-js/commit/912a29d091ed1ca0af1712cbd09986a1c173a960))
|
||||
|
||||
# [1.54.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.53.0...@standardnotes/syncing-server@1.54.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/syncing-server-js/issues/642)) ([e905128](https://github.com/standardnotes/syncing-server-js/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.53.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.52.0...@standardnotes/syncing-server@1.53.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* http controllers for shared vaults. ([#641](https://github.com/standardnotes/syncing-server-js/issues/641)) ([7a3946a](https://github.com/standardnotes/syncing-server-js/commit/7a3946a9e2d4168a1d286df321d9972588252b5d))
|
||||
|
||||
# [1.52.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.51.0...@standardnotes/syncing-server@1.52.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/syncing-server-js/issues/640)) ([f3161c2](https://github.com/standardnotes/syncing-server-js/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.50.0...@standardnotes/syncing-server@1.51.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add getting shared vaults for a user ([#639](https://github.com/standardnotes/syncing-server-js/issues/639)) ([d2b2c33](https://github.com/standardnotes/syncing-server-js/commit/d2b2c339f2089ea5d538ee40118af083983be5ef))
|
||||
|
||||
# [1.50.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.49.0...@standardnotes/syncing-server@1.50.0) (2023-07-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add invite users to a shared vault. ([#636](https://github.com/standardnotes/syncing-server-js/issues/636)) ([5dc5507](https://github.com/standardnotes/syncing-server-js/commit/5dc5507039c0dfb9df82a85377846651fef73c57))
|
||||
|
||||
# [1.49.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.48.0...@standardnotes/syncing-server@1.49.0) (2023-07-03)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.49.0",
|
||||
"version": "1.55.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -38,6 +38,12 @@ const TYPES = {
|
||||
Sync_SyncItems: Symbol.for('Sync_SyncItems'),
|
||||
Sync_CheckIntegrity: Symbol.for('Sync_CheckIntegrity'),
|
||||
Sync_GetItem: Symbol.for('Sync_GetItem'),
|
||||
Sync_GetSharedVaults: Symbol.for('Sync_GetSharedVaults'),
|
||||
Sync_CreateSharedVault: Symbol.for('Sync_CreateSharedVault'),
|
||||
Sync_DeleteSharedVault: Symbol.for('Sync_DeleteSharedVault'),
|
||||
Sync_CreateSharedVaultFileValetToken: Symbol.for('Sync_CreateSharedVaultFileValetToken'),
|
||||
Sync_GetSharedVaultUsers: Symbol.for('Sync_GetSharedVaultUsers'),
|
||||
Sync_RemoveSharedVaultUser: Symbol.for('Sync_RemoveSharedVaultUser'),
|
||||
// Handlers
|
||||
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
|
||||
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
|
||||
@@ -68,6 +74,9 @@ const TYPES = {
|
||||
Sync_ItemTransferCalculator: Symbol.for('Sync_ItemTransferCalculator'),
|
||||
Sync_ControllerContainer: Symbol.for('Sync_ControllerContainer'),
|
||||
Sync_HomeServerItemsController: Symbol.for('Sync_HomeServerItemsController'),
|
||||
// Mapping
|
||||
Sync_SharedVaultHttpMapper: Symbol.for('Sync_SharedVaultHttpMapper'),
|
||||
Sync_SharedVaultUserHttpMapper: Symbol.for('Sync_SharedVaultUserHttpMapper'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
EmailRequestedEvent,
|
||||
ItemDumpedEvent,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
NotificationRequestedEvent,
|
||||
RevisionsCopyRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
@@ -13,6 +14,25 @@ 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,10 +3,12 @@ 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
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SharedVault } from './SharedVault'
|
||||
|
||||
export interface SharedVaultRepositoryInterface {
|
||||
findByUuid(uuid: Uuid): Promise<SharedVault | null>
|
||||
findByUuids(uuids: Uuid[], lastSyncTime?: number): Promise<SharedVault[]>
|
||||
save(sharedVault: SharedVault): Promise<void>
|
||||
remove(sharedVault: SharedVault): Promise<void>
|
||||
}
|
||||
|
||||
@@ -6,4 +6,6 @@ export interface SharedVaultInviteRepositoryInterface {
|
||||
findByUuid(sharedVaultInviteUuid: Uuid): Promise<SharedVaultInvite | null>
|
||||
save(sharedVaultInvite: SharedVaultInvite): Promise<void>
|
||||
remove(sharedVaultInvite: SharedVaultInvite): Promise<void>
|
||||
removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void>
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultInvite | null>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { SharedVaultUser } from './SharedVaultUser'
|
||||
|
||||
export interface SharedVaultUserRepositoryInterface {
|
||||
findByUuid(sharedVaultUserUuid: Uuid): Promise<SharedVaultUser | null>
|
||||
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
findBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||
removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void>
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ export class CreateSharedVaultFileValetToken implements UseCaseInterface<string>
|
||||
}
|
||||
|
||||
if (
|
||||
sharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read &&
|
||||
sharedVaultUser.props.permission.equals(
|
||||
SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
) &&
|
||||
dto.operation !== ValetTokenOperation.Read
|
||||
) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
@@ -72,7 +74,11 @@ export class CreateSharedVaultFileValetToken implements UseCaseInterface<string>
|
||||
return Result.fail('Shared vault target user not found')
|
||||
}
|
||||
|
||||
if (toSharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read) {
|
||||
if (
|
||||
toSharedVaultUser.props.permission.equals(
|
||||
SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
)
|
||||
) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { Uuid, Timestamps, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { DeleteSharedVault } from './DeleteSharedVault'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
describe('DeleteSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let removeUserFromSharedVault: RemoveUserFromSharedVault
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new DeleteSharedVault(
|
||||
sharedVaultRepository,
|
||||
sharedVaultUserRepository,
|
||||
sharedVaultInviteRepository,
|
||||
removeUserFromSharedVault,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
sharedVaultRepository.remove = jest.fn()
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findBySharedVaultUuid = jest.fn().mockResolvedValue([sharedVaultUser])
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.removeBySharedVaultUuid = jest.fn()
|
||||
|
||||
removeUserFromSharedVault = {} as jest.Mocked<RemoveUserFromSharedVault>
|
||||
removeUserFromSharedVault.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
})
|
||||
|
||||
it('should remove shared vault', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(sharedVaultRepository.remove).toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault does not exist', async () => {
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when originator of the delete request is not the owner of the shared vault', async () => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error if removing user from shared vault fails', async () => {
|
||||
removeUserFromSharedVault.execute = jest.fn().mockReturnValue(Result.fail('failed'))
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(sharedVaultRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sharedVaultInviteRepository.removeBySharedVaultUuid).not.toHaveBeenCalled()
|
||||
expect(removeUserFromSharedVault.execute).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { DeleteSharedVaultDTO } from './DeleteSharedVaultDTO'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { RemoveUserFromSharedVault } from '../RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
export class DeleteSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private removeUserFromSharedVault: RemoveUserFromSharedVault,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteSharedVaultDTO): Promise<Result<void>> {
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Shared vault not found')
|
||||
}
|
||||
|
||||
if (sharedVault.props.userUuid.value !== originatorUuid.value) {
|
||||
return Result.fail('Shared vault does not belong to the user')
|
||||
}
|
||||
|
||||
const sharedVaultUsers = await this.sharedVaultUserRepository.findBySharedVaultUuid(sharedVaultUuid)
|
||||
for (const sharedVaultUser of sharedVaultUsers) {
|
||||
const result = await this.removeUserFromSharedVault.execute({
|
||||
originatorUuid: originatorUuid.value,
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
userUuid: sharedVaultUser.props.userUuid.value,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return Result.fail(result.getError())
|
||||
}
|
||||
}
|
||||
|
||||
await this.sharedVaultInviteRepository.removeBySharedVaultUuid(sharedVaultUuid)
|
||||
|
||||
await this.sharedVaultRepository.remove(sharedVault)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeleteSharedVaultDTO {
|
||||
originatorUuid: string
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
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 { GetSharedVaultUsers } from './GetSharedVaultUsers'
|
||||
|
||||
describe('GetSharedVaultUsers', () => {
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
let sharedVaultUsersRepository: SharedVaultUserRepositoryInterface
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
|
||||
const createUseCase = () => new GetSharedVaultUsers(sharedVaultUsersRepository, sharedVaultRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
sharedVaultUsersRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUsersRepository.findBySharedVaultUuid = jest.fn().mockResolvedValue([sharedVaultUser])
|
||||
})
|
||||
|
||||
it('returns shared vault users', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toEqual([sharedVaultUser])
|
||||
})
|
||||
|
||||
it('returns error when shared vault is not found', async () => {
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Shared vault not found')
|
||||
})
|
||||
|
||||
it('returns error when originator is not the owner of the shared vault', async () => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Only the owner can get shared vault users')
|
||||
})
|
||||
|
||||
it('returns error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: 'invalid',
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('returns error when originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
originatorUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { GetSharedVaultUsersDTO } from './GetSharedVaultUsersDTO'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
|
||||
export class GetSharedVaultUsers implements UseCaseInterface<SharedVaultUser[]> {
|
||||
constructor(
|
||||
private sharedVaultUsersRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: GetSharedVaultUsersDTO): Promise<Result<SharedVaultUser[]>> {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Shared vault not found')
|
||||
}
|
||||
|
||||
const isOriginatorTheOwnerOfTheSharedVault = sharedVault.props.userUuid.equals(originatorUuid)
|
||||
if (!isOriginatorTheOwnerOfTheSharedVault) {
|
||||
return Result.fail('Only the owner can get shared vault users')
|
||||
}
|
||||
|
||||
const sharedVaultUsers = await this.sharedVaultUsersRepository.findBySharedVaultUuid(sharedVaultUuid)
|
||||
|
||||
return Result.ok(sharedVaultUsers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetSharedVaultUsersDTO {
|
||||
sharedVaultUuid: string
|
||||
originatorUuid: string
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
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 { GetSharedVaults } from './GetSharedVaults'
|
||||
|
||||
describe('GetSharedVaults', () => {
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () => new GetSharedVaults(sharedVaultUserRepository, sharedVaultRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Admin).getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findByUserUuid = jest.fn().mockResolvedValue([sharedVaultUser])
|
||||
|
||||
sharedVault = SharedVault.create({
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
fileUploadBytesLimit: 123,
|
||||
fileUploadBytesUsed: 123,
|
||||
}).getValue()
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuids = jest.fn().mockResolvedValue([sharedVault])
|
||||
})
|
||||
|
||||
it('returns shared vaults', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([sharedVault])
|
||||
})
|
||||
|
||||
it('returns empty array if no shared vaults found', async () => {
|
||||
sharedVaultUserRepository.findByUserUuid = jest.fn().mockResolvedValue([])
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.getValue()).toEqual([])
|
||||
})
|
||||
|
||||
it('returns error if user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { GetSharedVaultsDTO } from './GetSharedVaultsDTO'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
|
||||
export class GetSharedVaults implements UseCaseInterface<SharedVault[]> {
|
||||
constructor(
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: GetSharedVaultsDTO): Promise<Result<SharedVault[]>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const ownedSharedVaultsAssociations = await this.sharedVaultUserRepository.findByUserUuid(userUuid)
|
||||
|
||||
const sharedVaultUuids = ownedSharedVaultsAssociations.map(
|
||||
(sharedVaultUser) => sharedVaultUser.props.sharedVaultUuid,
|
||||
)
|
||||
|
||||
if (sharedVaultUuids.length === 0) {
|
||||
return Result.ok([])
|
||||
}
|
||||
|
||||
const sharedVaults = await this.sharedVaultRepository.findByUuids(sharedVaultUuids, dto.lastSyncTime)
|
||||
|
||||
return Result.ok(sharedVaults)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetSharedVaultsDTO {
|
||||
userUuid: string
|
||||
lastSyncTime?: number
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { InviteUserToSharedVault } from './InviteUserToSharedVault'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { Uuid, Timestamps, Result } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('InviteUserToSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let sharedVault: SharedVault
|
||||
|
||||
const createUseCase = () => new InviteUserToSharedVault(sharedVaultRepository, sharedVaultInviteRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
sharedVaultInviteRepository = {} as jest.Mocked<SharedVaultInviteRepositoryInterface>
|
||||
sharedVaultInviteRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(null)
|
||||
sharedVaultInviteRepository.save = jest.fn()
|
||||
sharedVaultInviteRepository.remove = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
|
||||
})
|
||||
|
||||
it('should return a failure result if the shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: 'invalid',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return a failure result if the shared vault does not exist', async () => {
|
||||
const useCase = createUseCase()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(undefined)
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Attempting to invite a user to a non-existent shared vault')
|
||||
})
|
||||
|
||||
it('should return a failure result if the sender uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: 'invalid',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return a failure result if the recipient uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: 'invalid',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should remove an already existing invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
sharedVaultInviteRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockResolvedValue({} as jest.Mocked<SharedVaultInvite>)
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sharedVaultInviteRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should create a shared vault invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue().props.sharedVaultUuid.value).toBe('00000000-0000-0000-0000-000000000000')
|
||||
})
|
||||
|
||||
it('should return a failure if the permission is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'invalid',
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Invalid shared vault user permission invalid')
|
||||
})
|
||||
|
||||
it('should return a failure if the sender is not the owner of the shared vault', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('10000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000001',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Only the owner of a shared vault can invite users to it')
|
||||
})
|
||||
|
||||
it('should return a failure if the shared vault invite could not be created', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const mockSharedVaultInvite = jest.spyOn(SharedVaultInvite, 'create')
|
||||
mockSharedVaultInvite.mockImplementation(() => {
|
||||
return Result.fail('Oops')
|
||||
})
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
recipientUuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
encryptedMessage: 'encryptedMessage',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Oops')
|
||||
|
||||
mockSharedVaultInvite.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { InviteUserToSharedVaultDTO } from './InviteUserToSharedVaultDTO'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class InviteUserToSharedVault implements UseCaseInterface<SharedVaultInvite> {
|
||||
constructor(
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
async execute(dto: InviteUserToSharedVaultDTO): Promise<Result<SharedVaultInvite>> {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const senderUuidOrError = Uuid.create(dto.senderUuid)
|
||||
if (senderUuidOrError.isFailed()) {
|
||||
return Result.fail(senderUuidOrError.getError())
|
||||
}
|
||||
const senderUuid = senderUuidOrError.getValue()
|
||||
|
||||
const recipientUuidOrError = Uuid.create(dto.recipientUuid)
|
||||
if (recipientUuidOrError.isFailed()) {
|
||||
return Result.fail(recipientUuidOrError.getError())
|
||||
}
|
||||
const recipientUuid = recipientUuidOrError.getValue()
|
||||
|
||||
const permissionOrError = SharedVaultUserPermission.create(dto.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
return Result.fail(permissionOrError.getError())
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Attempting to invite a user to a non-existent shared vault')
|
||||
}
|
||||
|
||||
if (sharedVault.props.userUuid.value !== senderUuid.value) {
|
||||
return Result.fail('Only the owner of a shared vault can invite users to it')
|
||||
}
|
||||
|
||||
const existingInvite = await this.sharedVaultInviteRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid: recipientUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (existingInvite) {
|
||||
await this.sharedVaultInviteRepository.remove(existingInvite)
|
||||
}
|
||||
|
||||
const sharedVaultInviteOrError = SharedVaultInvite.create({
|
||||
encryptedMessage: dto.encryptedMessage,
|
||||
userUuid: recipientUuid,
|
||||
sharedVaultUuid,
|
||||
senderUuid,
|
||||
permission,
|
||||
timestamps: Timestamps.create(
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue(),
|
||||
})
|
||||
if (sharedVaultInviteOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultInviteOrError.getError())
|
||||
}
|
||||
const sharedVaultInvite = sharedVaultInviteOrError.getValue()
|
||||
|
||||
await this.sharedVaultInviteRepository.save(sharedVaultInvite)
|
||||
|
||||
return Result.ok(sharedVaultInvite)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface InviteUserToSharedVaultDTO {
|
||||
sharedVaultUuid: string
|
||||
senderUuid: string
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
permission: string
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
import { DomainEventPublisherInterface, NotificationRequestedEvent } from '@standardnotes/domain-events'
|
||||
|
||||
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'
|
||||
|
||||
describe('RemoveUserFromSharedVault', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new RemoveUserFromSharedVault(
|
||||
sharedVaultUserRepository,
|
||||
sharedVaultRepository,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
sharedVaultRepository.remove = jest.fn()
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.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>)
|
||||
})
|
||||
|
||||
it('should remove user from shared vault', 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(sharedVaultUserRepository.remove).toHaveBeenCalledWith(sharedVaultUser)
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when shared vault is not found', async () => {
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
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)
|
||||
expect(result.getError()).toBe('Shared vault not found')
|
||||
})
|
||||
|
||||
it('should return error when shared vault user is not found', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
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)
|
||||
expect(result.getError()).toBe('User is not a member of the shared vault')
|
||||
})
|
||||
|
||||
it('should return error when user is not owner of shared vault', async () => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000002').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
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)
|
||||
expect(result.getError()).toBe('Only owner can remove users from shared vault')
|
||||
})
|
||||
|
||||
it('should return error when user is owner of shared vault', async () => {
|
||||
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-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Owner cannot be removed from shared vault')
|
||||
})
|
||||
|
||||
it('should return error if shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid',
|
||||
userUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return error if user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
originatorUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: 'invalid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should return error if originator uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
originatorUuid: 'invalid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000001',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NotificationType, 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'
|
||||
|
||||
export class RemoveUserFromSharedVault implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultUsersRepository: SharedVaultUserRepositoryInterface,
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: RemoveUserFromSharedVaultDTO): Promise<Result<void>> {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const originatorUuidOrError = Uuid.create(dto.originatorUuid)
|
||||
if (originatorUuidOrError.isFailed()) {
|
||||
return Result.fail(originatorUuidOrError.getError())
|
||||
}
|
||||
const originatorUuid = originatorUuidOrError.getValue()
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Shared vault not found')
|
||||
}
|
||||
|
||||
const originatorIsOwner = sharedVault.props.userUuid.equals(originatorUuid)
|
||||
if (!originatorIsOwner) {
|
||||
return Result.fail('Only owner can remove users from shared vault')
|
||||
}
|
||||
|
||||
const removingOwner = sharedVault.props.userUuid.equals(userUuid)
|
||||
if (removingOwner) {
|
||||
return Result.fail('Owner cannot be removed from shared vault')
|
||||
}
|
||||
|
||||
const sharedVaultUser = await this.sharedVaultUsersRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (!sharedVaultUser) {
|
||||
return Result.fail('User is not a member of the shared vault')
|
||||
}
|
||||
|
||||
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',
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface RemoveUserFromSharedVaultDTO {
|
||||
sharedVaultUuid: string
|
||||
originatorUuid: string
|
||||
userUuid: string
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { UpdateSharedVaultInvite } from './UpdateSharedVaultInvite'
|
||||
import { SharedVaultInvite } from '../../SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
describe('UpdateSharedVaultInvite', () => {
|
||||
let sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let invite: SharedVaultInvite
|
||||
|
||||
const createUseCase = () => new UpdateSharedVaultInvite(sharedVaultInviteRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
invite = SharedVaultInvite.create({
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
senderUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: 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.save = jest.fn().mockResolvedValue(null)
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
|
||||
})
|
||||
|
||||
it('should update the invite', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sharedVaultInviteRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should update the invite with new permission', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
permission: SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sharedVaultInviteRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
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',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if sender is not the same as the invite sender', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000001',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Only the sender can update the invite')
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if the invite uuid is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: 'invalid-uuid',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if the sender uuid is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: 'invalid-uuid',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if the encrypted message is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: '',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if the permission is not valid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
inviteUuid: '00000000-0000-0000-0000-000000000000',
|
||||
senderUuid: '00000000-0000-0000-0000-000000000000',
|
||||
encryptedMessage: 'new encrypted message',
|
||||
permission: 'invalid-permission',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sharedVaultInviteRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Result, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultInviteRepositoryInterface } from '../../SharedVault/User/Invite/SharedVaultInviteRepositoryInterface'
|
||||
import { UpdateSharedVaultInviteDTO } from './UpdateSharedVaultInviteDTO'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
export class UpdateSharedVaultInvite implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateSharedVaultInviteDTO): Promise<Result<void>> {
|
||||
const inviteUuidOrError = Uuid.create(dto.inviteUuid)
|
||||
if (inviteUuidOrError.isFailed()) {
|
||||
return Result.fail(inviteUuidOrError.getError())
|
||||
}
|
||||
const inviteUuid = inviteUuidOrError.getValue()
|
||||
|
||||
const senderUuidOrError = Uuid.create(dto.senderUuid)
|
||||
if (senderUuidOrError.isFailed()) {
|
||||
return Result.fail(senderUuidOrError.getError())
|
||||
}
|
||||
const senderUuid = senderUuidOrError.getValue()
|
||||
|
||||
const emptyMessageValidation = Validator.isNotEmpty(dto.encryptedMessage)
|
||||
if (emptyMessageValidation.isFailed()) {
|
||||
return Result.fail(emptyMessageValidation.getError())
|
||||
}
|
||||
|
||||
const invite = await this.sharedVaultInviteRepository.findByUuid(inviteUuid)
|
||||
if (!invite) {
|
||||
return Result.fail('Invite not found')
|
||||
}
|
||||
|
||||
if (!invite.props.senderUuid.equals(senderUuid)) {
|
||||
return Result.fail('Only the sender can update the invite')
|
||||
}
|
||||
|
||||
invite.props.encryptedMessage = dto.encryptedMessage
|
||||
|
||||
if (dto.permission !== undefined) {
|
||||
const permissionOrError = SharedVaultUserPermission.create(dto.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
return Result.fail(permissionOrError.getError())
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
invite.props.permission = permission
|
||||
}
|
||||
|
||||
invite.props.timestamps = Timestamps.create(
|
||||
invite.props.timestamps.createdAt,
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue()
|
||||
|
||||
await this.sharedVaultInviteRepository.save(invite)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface UpdateSharedVaultInviteDTO {
|
||||
encryptedMessage: string
|
||||
inviteUuid: string
|
||||
senderUuid: string
|
||||
permission?: string
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
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 { SharedVaultUser } from '../../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserHttpRepresentation } from '../../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
||||
import { GetSharedVaultUsers } from '../../../Domain/UseCase/GetSharedVaultUsers/GetSharedVaultUsers'
|
||||
import { RemoveUserFromSharedVault } from '../../../Domain/UseCase/RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
export class HomeServerSharedVaultUsersController extends BaseHttpController {
|
||||
constructor(
|
||||
protected getSharedVaultUsersUseCase: GetSharedVaultUsers,
|
||||
protected removeUserFromSharedVaultUseCase: RemoveUserFromSharedVault,
|
||||
protected sharedVaultUserHttpMapper: MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation>,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
|
||||
if (this.controllerContainer !== undefined) {
|
||||
this.controllerContainer.register('sync.shared-vault-users.get-users', this.getSharedVaultUsers.bind(this))
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vault-users.remove-user',
|
||||
this.removeUserFromSharedVault.bind(this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async getSharedVaultUsers(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getSharedVaultUsersUseCase.execute({
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
users: result.getValue().map((sharedVault) => this.sharedVaultUserHttpMapper.toProjection(sharedVault)),
|
||||
})
|
||||
}
|
||||
|
||||
async removeUserFromSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.removeUserFromSharedVaultUseCase.execute({
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
userUuid: request.params.userUuid,
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
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 { GetSharedVaults } from '../../../Domain/UseCase/GetSharedVaults/GetSharedVaults'
|
||||
import { SharedVault } from '../../../Domain/SharedVault/SharedVault'
|
||||
import { SharedVaultHttpRepresentation } from '../../../Mapping/Http/SharedVaultHttpRepresentation'
|
||||
import { CreateSharedVault } from '../../../Domain/UseCase/CreateSharedVault/CreateSharedVault'
|
||||
import { SharedVaultUser } from '../../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserHttpRepresentation } from '../../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
||||
import { DeleteSharedVault } from '../../../Domain/UseCase/DeleteSharedVault/DeleteSharedVault'
|
||||
import { CreateSharedVaultFileValetToken } from '../../../Domain/UseCase/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken'
|
||||
|
||||
export class HomeServerSharedVaultsController extends BaseHttpController {
|
||||
constructor(
|
||||
protected getSharedVaultsUseCase: GetSharedVaults,
|
||||
protected createSharedVaultUseCase: CreateSharedVault,
|
||||
protected deleteSharedVaultUseCase: DeleteSharedVault,
|
||||
protected createSharedVaultFileValetTokenUseCase: CreateSharedVaultFileValetToken,
|
||||
protected sharedVaultHttpMapper: MapperInterface<SharedVault, SharedVaultHttpRepresentation>,
|
||||
protected sharedVaultUserHttpMapper: MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation>,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
|
||||
if (this.controllerContainer !== undefined) {
|
||||
this.controllerContainer.register('sync.shared-vaults.get-vaults', this.getSharedVaults.bind(this))
|
||||
this.controllerContainer.register('sync.shared-vaults.create-vault', this.createSharedVault.bind(this))
|
||||
this.controllerContainer.register('sync.shared-vaults.delete-vault', this.deleteSharedVault.bind(this))
|
||||
this.controllerContainer.register(
|
||||
'sync.shared-vaults.create-file-valet-token',
|
||||
this.createValetTokenForSharedVaultFile.bind(this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async getSharedVaults(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.getSharedVaultsUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
sharedVaults: result.getValue().map((sharedVault) => this.sharedVaultHttpMapper.toProjection(sharedVault)),
|
||||
})
|
||||
}
|
||||
|
||||
async createSharedVault(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.createSharedVaultUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
sharedVault: this.sharedVaultHttpMapper.toProjection(result.getValue().sharedVault),
|
||||
sharedVaultUser: this.sharedVaultUserHttpMapper.toProjection(result.getValue().sharedVaultUser),
|
||||
})
|
||||
}
|
||||
|
||||
async deleteSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.deleteSharedVaultUseCase.execute({
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({ success: true })
|
||||
}
|
||||
|
||||
async createValetTokenForSharedVaultFile(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.createSharedVaultFileValetTokenUseCase.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
fileUuid: request.body.file_uuid,
|
||||
remoteIdentifier: request.body.remote_identifier,
|
||||
operation: request.body.operation,
|
||||
unencryptedFileSize: request.body.unencrypted_file_size,
|
||||
moveOperationType: request.body.move_operation_type,
|
||||
sharedVaultToSharedVaultMoveTargetUuid: request.body.shared_vault_to_shared_vault_move_target_uuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: result.getError(),
|
||||
},
|
||||
},
|
||||
HttpStatusCode.BadRequest,
|
||||
)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
valetToken: result.getValue(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { controller, httpDelete, httpGet, results } from 'inversify-express-utils'
|
||||
import { inject } from 'inversify'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { HomeServerSharedVaultUsersController } from './HomeServer/HomeServerSharedVaultUsersController'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { SharedVaultUser } from '../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserHttpRepresentation } from '../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
||||
import { GetSharedVaultUsers } from '../../Domain/UseCase/GetSharedVaultUsers/GetSharedVaultUsers'
|
||||
import { RemoveUserFromSharedVault } from '../../Domain/UseCase/RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||
|
||||
@controller('/shared-vaults/:sharedVaultUuid/users', TYPES.Sync_AuthMiddleware)
|
||||
export class InversifyExpressSharedVaultUsersController extends HomeServerSharedVaultUsersController {
|
||||
constructor(
|
||||
@inject(TYPES.Sync_GetSharedVaultUsers) override getSharedVaultUsersUseCase: GetSharedVaultUsers,
|
||||
@inject(TYPES.Sync_RemoveSharedVaultUser) override removeUserFromSharedVaultUseCase: RemoveUserFromSharedVault,
|
||||
@inject(TYPES.Sync_SharedVaultUserHttpMapper)
|
||||
override sharedVaultUserHttpMapper: MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation>,
|
||||
) {
|
||||
super(getSharedVaultUsersUseCase, removeUserFromSharedVaultUseCase, sharedVaultUserHttpMapper)
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
override async getSharedVaultUsers(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getSharedVaultUsers(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/:userUuid')
|
||||
override async removeUserFromSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.removeUserFromSharedVault(request, response)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { controller, httpDelete, httpGet, httpPost, results } from 'inversify-express-utils'
|
||||
import { inject } from 'inversify'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { HomeServerSharedVaultsController } from './HomeServer/HomeServerSharedVaultsController'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { SharedVault } from '../../Domain/SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { CreateSharedVault } from '../../Domain/UseCase/CreateSharedVault/CreateSharedVault'
|
||||
import { CreateSharedVaultFileValetToken } from '../../Domain/UseCase/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken'
|
||||
import { DeleteSharedVault } from '../../Domain/UseCase/DeleteSharedVault/DeleteSharedVault'
|
||||
import { GetSharedVaults } from '../../Domain/UseCase/GetSharedVaults/GetSharedVaults'
|
||||
import { SharedVaultHttpRepresentation } from '../../Mapping/Http/SharedVaultHttpRepresentation'
|
||||
import { SharedVaultUserHttpRepresentation } from '../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
||||
|
||||
@controller('/shared-vaults', TYPES.Sync_AuthMiddleware)
|
||||
export class InversifyExpressSharedVaultsController extends HomeServerSharedVaultsController {
|
||||
constructor(
|
||||
@inject(TYPES.Sync_GetSharedVaults) override getSharedVaultsUseCase: GetSharedVaults,
|
||||
@inject(TYPES.Sync_CreateSharedVault) override createSharedVaultUseCase: CreateSharedVault,
|
||||
@inject(TYPES.Sync_DeleteSharedVault) override deleteSharedVaultUseCase: DeleteSharedVault,
|
||||
@inject(TYPES.Sync_CreateSharedVaultFileValetToken)
|
||||
override createSharedVaultFileValetTokenUseCase: CreateSharedVaultFileValetToken,
|
||||
@inject(TYPES.Sync_SharedVaultHttpMapper)
|
||||
override sharedVaultHttpMapper: MapperInterface<SharedVault, SharedVaultHttpRepresentation>,
|
||||
@inject(TYPES.Sync_SharedVaultUserHttpMapper)
|
||||
override sharedVaultUserHttpMapper: MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation>,
|
||||
) {
|
||||
super(
|
||||
getSharedVaultsUseCase,
|
||||
createSharedVaultUseCase,
|
||||
deleteSharedVaultUseCase,
|
||||
createSharedVaultFileValetTokenUseCase,
|
||||
sharedVaultHttpMapper,
|
||||
sharedVaultUserHttpMapper,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
override async getSharedVaults(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.getSharedVaults(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/')
|
||||
override async createSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.createSharedVault(request, response)
|
||||
}
|
||||
|
||||
@httpDelete('/:sharedVaultUuid')
|
||||
override async deleteSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.deleteSharedVault(request, response)
|
||||
}
|
||||
|
||||
@httpPost('/:sharedVaultUuid/valet-tokens')
|
||||
override async createValetTokenForSharedVaultFile(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
return super.createValetTokenForSharedVaultFile(request, response)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,36 @@ export class TypeORMSharedVaultInviteRepository implements SharedVaultInviteRepo
|
||||
private mapper: MapperInterface<SharedVaultInvite, TypeORMSharedVaultInvite>,
|
||||
) {}
|
||||
|
||||
async removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.delete()
|
||||
.from('shared_vault_invites')
|
||||
.where('shared_vault_uuid = :sharedVaultUuid', { sharedVaultUuid: sharedVaultUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findByUserUuidAndSharedVaultUuid(dto: {
|
||||
userUuid: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}): Promise<SharedVaultInvite | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.where('shared_vault_invite.user_uuid = :uuid', {
|
||||
uuid: dto.userUuid.value,
|
||||
})
|
||||
.andWhere('shared_vault_invite.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async save(sharedVaultInvite: SharedVaultInvite): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVaultInvite)
|
||||
|
||||
@@ -21,7 +51,7 @@ export class TypeORMSharedVaultInviteRepository implements SharedVaultInviteRepo
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_invite')
|
||||
.where('shared_vault_invite.uuid = :uuid', {
|
||||
uuid: uuid.toString(),
|
||||
uuid: uuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
|
||||
@@ -11,6 +11,20 @@ export class TypeORMSharedVaultRepository implements SharedVaultRepositoryInterf
|
||||
private mapper: MapperInterface<SharedVault, TypeORMSharedVault>,
|
||||
) {}
|
||||
|
||||
async findByUuids(uuids: Uuid[], lastSyncTime?: number | undefined): Promise<SharedVault[]> {
|
||||
const queryBuilder = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault')
|
||||
.where('shared_vault.uuid IN (:...sharedVaultUuids)', { sharedVaultUuids: uuids.map((uuid) => uuid.value) })
|
||||
|
||||
if (lastSyncTime !== undefined) {
|
||||
queryBuilder.andWhere('shared_vault.updated_at_timestamp > :lastSyncTime', { lastSyncTime })
|
||||
}
|
||||
|
||||
const persistence = await queryBuilder.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async save(sharedVault: SharedVault): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVault)
|
||||
|
||||
|
||||
@@ -11,6 +11,37 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
|
||||
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
|
||||
) {}
|
||||
|
||||
async removeBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.delete()
|
||||
.from('shared_vault_users')
|
||||
.where('shared_vault_uuid = :sharedVaultUuid', { sharedVaultUuid: sharedVaultUuid.value })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async findByUserUuidAndSharedVaultUuid(dto: {
|
||||
userUuid: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVault } from '../../Domain/SharedVault/SharedVault'
|
||||
import { SharedVaultHttpRepresentation } from './SharedVaultHttpRepresentation'
|
||||
|
||||
export class SharedVaultHttpMapper implements MapperInterface<SharedVault, SharedVaultHttpRepresentation> {
|
||||
toDomain(_projection: SharedVaultHttpRepresentation): SharedVault {
|
||||
throw new Error('Mapping from http representation to domain is not implemented.')
|
||||
}
|
||||
|
||||
toProjection(domain: SharedVault): SharedVaultHttpRepresentation {
|
||||
return {
|
||||
uuid: domain.id.toString(),
|
||||
user_uuid: domain.props.userUuid.value,
|
||||
file_upload_bytes_limit: domain.props.fileUploadBytesLimit,
|
||||
file_upload_bytes_used: domain.props.fileUploadBytesUsed,
|
||||
created_at_timestamp: domain.props.timestamps.createdAt,
|
||||
updated_at_timestamp: domain.props.timestamps.updatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface SharedVaultHttpRepresentation {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
file_upload_bytes_used: number
|
||||
file_upload_bytes_limit: number
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUser } from '../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserHttpRepresentation } from './SharedVaultUserHttpRepresentation'
|
||||
|
||||
export class SharedVaultUserHttpMapper implements MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation> {
|
||||
toDomain(_projection: SharedVaultUserHttpRepresentation): SharedVaultUser {
|
||||
throw new Error('Mapping from http representation to domain is not implemented.')
|
||||
}
|
||||
|
||||
toProjection(domain: SharedVaultUser): SharedVaultUserHttpRepresentation {
|
||||
return {
|
||||
uuid: domain.id.toString(),
|
||||
user_uuid: domain.props.userUuid.value,
|
||||
permission: domain.props.permission.value,
|
||||
shared_vault_uuid: domain.props.sharedVaultUuid.value,
|
||||
created_at_timestamp: domain.props.timestamps.createdAt,
|
||||
updated_at_timestamp: domain.props.timestamps.updatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface SharedVaultUserHttpRepresentation {
|
||||
uuid: string
|
||||
shared_vault_uuid: string
|
||||
user_uuid: string
|
||||
permission: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultInvite } from '../Domain/SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { TypeORMSharedVaultInvite } from '../Infra/TypeORM/TypeORMSharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../Domain/SharedVault/User/SharedVaultUserPermission'
|
||||
import { SharedVaultInvite } from '../../Domain/SharedVault/User/Invite/SharedVaultInvite'
|
||||
import { TypeORMSharedVaultInvite } from '../../Infra/TypeORM/TypeORMSharedVaultInvite'
|
||||
import { SharedVaultUserPermission } from '../../Domain/SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class SharedVaultInvitePersistenceMapper
|
||||
implements MapperInterface<SharedVaultInvite, TypeORMSharedVaultInvite>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVault } from '../Domain/SharedVault/SharedVault'
|
||||
import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
|
||||
import { SharedVault } from '../../Domain/SharedVault/SharedVault'
|
||||
import { TypeORMSharedVault } from '../../Infra/TypeORM/TypeORMSharedVault'
|
||||
|
||||
export class SharedVaultPersistenceMapper implements MapperInterface<SharedVault, TypeORMSharedVault> {
|
||||
toDomain(projection: TypeORMSharedVault): SharedVault {
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultUser } from '../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../Domain/SharedVault/User/SharedVaultUserPermission'
|
||||
import { SharedVaultUser } from '../../Domain/SharedVault/User/SharedVaultUser'
|
||||
import { TypeORMSharedVaultUser } from '../../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../Domain/SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedVaultUser, TypeORMSharedVaultUser> {
|
||||
toDomain(projection: TypeORMSharedVaultUser): SharedVaultUser {
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.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.9.6](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.5...@standardnotes/websockets-server@1.9.6) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
## [1.9.5](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.9.4...@standardnotes/websockets-server@1.9.5) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/websockets-server",
|
||||
"version": "1.9.5",
|
||||
"version": "1.9.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user