Compare commits

...

16 Commits

Author SHA1 Message Date
standardci
8d006ece30 chore(release): publish new version
- @standardnotes/analytics@2.26.7
 - @standardnotes/api-gateway@1.74.3
 - @standardnotes/auth-server@1.140.0
 - @standardnotes/domain-events-infra@1.12.24
 - @standardnotes/domain-events@2.125.0
 - @standardnotes/event-store@1.11.35
 - @standardnotes/files-server@1.22.14
 - @standardnotes/home-server@1.15.31
 - @standardnotes/revisions-server@1.33.3
 - @standardnotes/scheduler-server@1.20.39
 - @standardnotes/syncing-server@1.95.0
 - @standardnotes/websockets-server@1.10.36
2023-09-08 14:54:00 +00:00
Karol Sójko
037c994040 chore: add run name for e2e test suite 2023-09-08 15:21:26 +02:00
Karol Sójko
a164ba291d fix(auth): specs 2023-09-08 13:41:37 +02:00
Karol Sójko
610fba2601 fix(auth): transition users only when transition mode enabled 2023-09-08 13:34:54 +02:00
Karol Sójko
398338c8f8 feat: transition users after sign out 2023-09-08 12:51:44 +02:00
standardci
34be157d8e chore(release): publish new version
- @standardnotes/home-server@1.15.30
 - @standardnotes/revisions-server@1.33.2
2023-09-08 10:08:48 +00:00
Karol Sójko
76372fe357 fix(revisions): persistence mapper (#814) 2023-09-08 11:29:03 +02:00
standardci
46879c336b chore(release): publish new version
- @standardnotes/auth-server@1.139.0
 - @standardnotes/home-server@1.15.29
 - @standardnotes/syncing-server@1.94.0
2023-09-08 07:53:10 +00:00
Karol Sójko
aef9e936bd feat(auth): add vaults user role into database 2023-09-08 09:19:52 +02:00
Karol Sójko
8cb92d9678 feat(syncing-server): add procedure to trigger transition for specific user 2023-09-08 09:14:07 +02:00
Karol Sójko
52db89de81 chore: disable fail fast on e2e 2023-09-07 19:03:38 +02:00
standardci
c5af8dfc05 chore(release): publish new version
- @standardnotes/home-server@1.15.28
 - @standardnotes/revisions-server@1.33.1
 - @standardnotes/syncing-server@1.93.1
2023-09-07 16:23:44 +00:00
Karol Sójko
27ad8e6959 fix(revisions): waiting for syncing-server on self-hosted setup 2023-09-07 17:44:53 +02:00
Karol Sójko
c4a1502f70 fix(syncing-server): invalidating cache for user removed from shared vault (#812) 2023-09-07 15:41:34 +02:00
standardci
d1d6c753c4 chore(release): publish new version
- @standardnotes/analytics@2.26.6
 - @standardnotes/api-gateway@1.74.2
 - @standardnotes/auth-server@1.138.2
 - @standardnotes/domain-events-infra@1.12.23
 - @standardnotes/domain-events@2.124.0
 - @standardnotes/event-store@1.11.34
 - @standardnotes/files-server@1.22.13
 - @standardnotes/home-server@1.15.27
 - @standardnotes/revisions-server@1.33.0
 - @standardnotes/scheduler-server@1.20.38
 - @standardnotes/syncing-server@1.93.0
 - @standardnotes/websockets-server@1.10.35
2023-09-07 10:46:30 +00:00
Karol Sójko
3bd63f7674 feat: add removing revisions from shared vaults (#811) 2023-09-07 12:02:38 +02:00
61 changed files with 708 additions and 47 deletions

View File

@@ -21,7 +21,7 @@ jobs:
e2e:
name: (Self Hosting) E2E Test Suite
strategy:
fail-fast: true
fail-fast: false
matrix:
secondary_db_enabled: [true, false]
transition_mode_enabled: [true, false]
@@ -70,7 +70,7 @@ jobs:
e2e-home-server:
name: (Home Server) E2E Test Suite
strategy:
fail-fast: true
fail-fast: false
matrix:
db_type: [mysql, sqlite]
cache_type: [redis, memory]

View File

@@ -1,5 +1,7 @@
name: E2E Test Suite On Self Hosted Server
run-name: E2E Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
on:
schedule:
- cron: '0 */12 * * *'
@@ -9,6 +11,14 @@ on:
type: string
default: latest
description: The Docker image tag used for SNJS container
author:
type: string
default: unknown
description: The author that triggered the workflow
ref_name:
type: string
default: unknown
description: The ref name from which the workflow was triggered
jobs:
e2e:

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.26.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.6...@standardnotes/analytics@2.26.7) (2023-09-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.5...@standardnotes/analytics@2.26.6) (2023-09-07)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.4...@standardnotes/analytics@2.26.5) (2023-09-07)
**Note:** Version bump only for package @standardnotes/analytics

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.26.5",
"version": "2.26.7",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.74.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.2...@standardnotes/api-gateway@1.74.3) (2023-09-08)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.1...@standardnotes/api-gateway@1.74.2) (2023-09-07)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.0...@standardnotes/api-gateway@1.74.1) (2023-09-07)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.74.1",
"version": "1.74.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.140.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.139.0...@standardnotes/auth-server@1.140.0) (2023-09-08)
### Bug Fixes
* **auth:** specs ([a164ba2](https://github.com/standardnotes/server/commit/a164ba291d4893b5a0bb4b39a8795d654212a1a6))
* **auth:** transition users only when transition mode enabled ([610fba2](https://github.com/standardnotes/server/commit/610fba260150d0757020e01ac0c8967feeadd4d6))
### Features
* transition users after sign out ([398338c](https://github.com/standardnotes/server/commit/398338c8f81b12406c7ab3bf1654e60b94d7cfd0))
# [1.139.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.2...@standardnotes/auth-server@1.139.0) (2023-09-08)
### Features
* **auth:** add vaults user role into database ([aef9e93](https://github.com/standardnotes/server/commit/aef9e936bdbd1f4ccc32658d3d892e7675ec0e0e))
## [1.138.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.1...@standardnotes/auth-server@1.138.2) (2023-09-07)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.138.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.0...@standardnotes/auth-server@1.138.1) (2023-09-07)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddVaultsUser1694157482134 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'INSERT INTO `roles` (uuid, name, version) VALUES ("35669f45-a2d8-4172-bdab-b7b3d42044ce", "VAULTS_USER", 1)',
)
}
public async down(): Promise<void> {
return
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.138.1",
"version": "1.140.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -981,6 +981,7 @@ export class ContainerConfigLoader {
container.get(TYPES.Auth_GenerateRecoveryCodes),
container.get(TYPES.Auth_Logger),
container.get(TYPES.Auth_SessionService),
container.get(TYPES.Auth_TRANSITION_MODE_ENABLED),
),
)
container

View File

@@ -27,6 +27,7 @@ describe('AuthController', () => {
let doGenerateRecoveryCodes: GenerateRecoveryCodes
let logger: Logger
let sessionService: SessionServiceInterface
let transitionModeEnabled: boolean
const createController = () =>
new AuthController(
@@ -39,6 +40,7 @@ describe('AuthController', () => {
doGenerateRecoveryCodes,
logger,
sessionService,
transitionModeEnabled,
)
beforeEach(() => {
@@ -64,6 +66,8 @@ describe('AuthController', () => {
sessionService = {} as jest.Mocked<SessionServiceInterface>
sessionService.deleteSessionByToken = jest.fn().mockReturnValue('1-2-3')
transitionModeEnabled = false
})
it('should register a user', async () => {

View File

@@ -37,6 +37,7 @@ export class AuthController implements UserServerInterface {
private doGenerateRecoveryCodes: GenerateRecoveryCodes,
private logger: Logger,
private sessionService: SessionServiceInterface,
private transitionModeEnabled: boolean,
) {}
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
@@ -226,6 +227,14 @@ export class AuthController implements UserServerInterface {
let headers = undefined
if (userUuid !== null) {
headers = new Map([['x-invalidate-cache', userUuid]])
if (this.transitionModeEnabled) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionRequestedEvent({
userUuid,
}),
)
}
}
return {

View File

@@ -20,6 +20,7 @@ import {
StatisticPersistenceRequestedEvent,
SessionCreatedEvent,
SessionRefreshedEvent,
TransitionRequestedEvent,
} from '@standardnotes/domain-events'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
@@ -32,6 +33,21 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
createTransitionRequestedEvent(dto: { userUuid: string }): TransitionRequestedEvent {
return {
type: 'TRANSITION_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.Auth,
},
payload: dto,
}
}
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
return {
type: 'SESSION_CREATED',

View File

@@ -18,6 +18,7 @@ import {
StatisticPersistenceRequestedEvent,
SessionCreatedEvent,
SessionRefreshedEvent,
TransitionRequestedEvent,
} from '@standardnotes/domain-events'
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
@@ -89,4 +90,5 @@ export interface DomainEventFactoryInterface {
}): StatisticPersistenceRequestedEvent
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
createTransitionRequestedEvent(dto: { userUuid: string }): TransitionRequestedEvent
}

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.24](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.23...@standardnotes/domain-events-infra@1.12.24) (2023-09-08)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.12.23](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.22...@standardnotes/domain-events-infra@1.12.23) (2023-09-07)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.12.22](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.21...@standardnotes/domain-events-infra@1.12.22) (2023-09-06)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.12.22",
"version": "1.12.24",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.125.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.124.0...@standardnotes/domain-events@2.125.0) (2023-09-08)
### Features
* transition users after sign out ([398338c](https://github.com/standardnotes/server/commit/398338c8f81b12406c7ab3bf1654e60b94d7cfd0))
# [2.124.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.123.0...@standardnotes/domain-events@2.124.0) (2023-09-07)
### Features
* add removing revisions from shared vaults ([#811](https://github.com/standardnotes/server/issues/811)) ([3bd63f7](https://github.com/standardnotes/server/commit/3bd63f767464baf9b9f1ffa52eea9eed4a4e11b5))
# [2.123.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.122.2...@standardnotes/domain-events@2.123.0) (2023-09-06)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.123.0",
"version": "2.125.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { ItemRemovedFromSharedVaultEventPayload } from './ItemRemovedFromSharedVaultEventPayload'
export interface ItemRemovedFromSharedVaultEvent extends DomainEventInterface {
type: 'ITEM_REMOVED_FROM_SHARED_VAULT'
payload: ItemRemovedFromSharedVaultEventPayload
}

View File

@@ -0,0 +1,6 @@
export interface ItemRemovedFromSharedVaultEventPayload {
userUuid: string
itemUuid: string
sharedVaultUuid: string
roleNames: string[]
}

View File

@@ -0,0 +1,8 @@
import { DomainEventInterface } from './DomainEventInterface'
import { TransitionRequestedEventPayload } from './TransitionRequestedEventPayload'
export interface TransitionRequestedEvent extends DomainEventInterface {
type: 'TRANSITION_REQUESTED'
payload: TransitionRequestedEventPayload
}

View File

@@ -0,0 +1,3 @@
export interface TransitionRequestedEventPayload {
userUuid: string
}

View File

@@ -32,6 +32,8 @@ export * from './Event/FileUploadedEvent'
export * from './Event/FileUploadedEventPayload'
export * from './Event/ItemDumpedEvent'
export * from './Event/ItemDumpedEventPayload'
export * from './Event/ItemRemovedFromSharedVaultEvent'
export * from './Event/ItemRemovedFromSharedVaultEventPayload'
export * from './Event/ItemRevisionCreationRequestedEvent'
export * from './Event/ItemRevisionCreationRequestedEventPayload'
export * from './Event/ListedAccountCreatedEvent'
@@ -94,6 +96,8 @@ export * from './Event/SubscriptionRevertRequestedEvent'
export * from './Event/SubscriptionRevertRequestedEventPayload'
export * from './Event/SubscriptionSyncRequestedEvent'
export * from './Event/SubscriptionSyncRequestedEventPayload'
export * from './Event/TransitionRequestedEvent'
export * from './Event/TransitionRequestedEventPayload'
export * from './Event/TransitionStatusUpdatedEvent'
export * from './Event/TransitionStatusUpdatedEventPayload'
export * from './Event/UserAddedToSharedVaultEvent'

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.35](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.34...@standardnotes/event-store@1.11.35) (2023-09-08)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.34](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.33...@standardnotes/event-store@1.11.34) (2023-09-07)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.33](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.32...@standardnotes/event-store@1.11.33) (2023-09-07)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.11.33",
"version": "1.11.35",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.22.14](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.13...@standardnotes/files-server@1.22.14) (2023-09-08)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.13](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.12...@standardnotes/files-server@1.22.13) (2023-09-07)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.12](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.11...@standardnotes/files-server@1.22.12) (2023-09-07)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.22.12",
"version": "1.22.14",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.15.31](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.30...@standardnotes/home-server@1.15.31) (2023-09-08)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.30](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.29...@standardnotes/home-server@1.15.30) (2023-09-08)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.29](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.28...@standardnotes/home-server@1.15.29) (2023-09-08)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.28](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.27...@standardnotes/home-server@1.15.28) (2023-09-07)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.27](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.26...@standardnotes/home-server@1.15.27) (2023-09-07)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.26](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.25...@standardnotes/home-server@1.15.26) (2023-09-07)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.15.26",
"version": "1.15.31",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.2...@standardnotes/revisions-server@1.33.3) (2023-09-08)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.33.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.1...@standardnotes/revisions-server@1.33.2) (2023-09-08)
### Bug Fixes
* **revisions:** persistence mapper ([#814](https://github.com/standardnotes/server/issues/814)) ([76372fe](https://github.com/standardnotes/server/commit/76372fe3579a99bf44abfce9667fec134baafedf))
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.0...@standardnotes/revisions-server@1.33.1) (2023-09-07)
### Bug Fixes
* **revisions:** waiting for syncing-server on self-hosted setup ([27ad8e6](https://github.com/standardnotes/server/commit/27ad8e6959aeb9c2f11ee0fecba37514bf91dda8))
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.32.0...@standardnotes/revisions-server@1.33.0) (2023-09-07)
### Features
* add removing revisions from shared vaults ([#811](https://github.com/standardnotes/server/issues/811)) ([3bd63f7](https://github.com/standardnotes/server/commit/3bd63f767464baf9b9f1ffa52eea9eed4a4e11b5))
# [1.32.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.31.0...@standardnotes/revisions-server@1.32.0) (2023-09-07)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.32.0",
"version": "1.33.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -66,6 +66,8 @@ import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
import { SQLRevisionRepository } from '../Infra/TypeORM/SQL/SQLRevisionRepository'
import { SQLRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionMetadataPersistenceMapper'
import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionPersistenceMapper'
import { RemoveRevisionsFromSharedVault } from '../Domain/UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
import { ItemRemovedFromSharedVaultEventHandler } from '../Domain/Handler/ItemRemovedFromSharedVaultEventHandler'
export class ContainerConfigLoader {
async load(configuration?: {
@@ -358,6 +360,13 @@ export class ContainerConfigLoader {
container.get<DomainEventFactoryInterface>(TYPES.Revisions_DomainEventFactory),
),
)
container
.bind<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault)
.toConstantValue(
new RemoveRevisionsFromSharedVault(
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
),
)
// env vars
container.bind(TYPES.Revisions_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
@@ -437,12 +446,21 @@ export class ContainerConfigLoader {
container.get<winston.Logger>(TYPES.Revisions_Logger),
),
)
container
.bind<ItemRemovedFromSharedVaultEventHandler>(TYPES.Revisions_ItemRemovedFromSharedVaultEventHandler)
.toConstantValue(
new ItemRemovedFromSharedVaultEventHandler(
container.get<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault),
container.get<winston.Logger>(TYPES.Revisions_Logger),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['ITEM_DUMPED', container.get(TYPES.Revisions_ItemDumpedEventHandler)],
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Revisions_AccountDeletionRequestedEventHandler)],
['REVISIONS_COPY_REQUESTED', container.get(TYPES.Revisions_RevisionsCopyRequestedEventHandler)],
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Revisions_TransitionStatusUpdatedEventHandler)],
['ITEM_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Revisions_ItemRemovedFromSharedVaultEventHandler)],
])
if (isConfiguredForHomeServer) {

View File

@@ -49,6 +49,7 @@ const TYPES = {
Revisions_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser: Symbol.for(
'Revisions_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser',
),
Revisions_RemoveRevisionsFromSharedVault: Symbol.for('Revisions_RemoveRevisionsFromSharedVault'),
// Controller
Revisions_ControllerContainer: Symbol.for('Revisions_ControllerContainer'),
Revisions_RevisionsController: Symbol.for('Revisions_RevisionsController'),
@@ -58,6 +59,7 @@ const TYPES = {
Revisions_AccountDeletionRequestedEventHandler: Symbol.for('Revisions_AccountDeletionRequestedEventHandler'),
Revisions_RevisionsCopyRequestedEventHandler: Symbol.for('Revisions_RevisionsCopyRequestedEventHandler'),
Revisions_TransitionStatusUpdatedEventHandler: Symbol.for('Revisions_TransitionStatusUpdatedEventHandler'),
Revisions_ItemRemovedFromSharedVaultEventHandler: Symbol.for('Revisions_ItemRemovedFromSharedVaultEventHandler'),
// Services
Revisions_CrossServiceTokenDecoder: Symbol.for('Revisions_CrossServiceTokenDecoder'),
Revisions_DomainEventSubscriberFactory: Symbol.for('Revisions_DomainEventSubscriberFactory'),

View File

@@ -0,0 +1,22 @@
import { DomainEventHandlerInterface, ItemRemovedFromSharedVaultEvent } from '@standardnotes/domain-events'
import { RemoveRevisionsFromSharedVault } from '../UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
import { Logger } from 'winston'
export class ItemRemovedFromSharedVaultEventHandler implements DomainEventHandlerInterface {
constructor(
private removeRevisionsFromSharedVault: RemoveRevisionsFromSharedVault,
private logger: Logger,
) {}
async handle(event: ItemRemovedFromSharedVaultEvent): Promise<void> {
const result = await this.removeRevisionsFromSharedVault.execute({
sharedVaultUuid: event.payload.sharedVaultUuid,
itemUuid: event.payload.itemUuid,
roleNames: event.payload.roleNames,
})
if (result.isFailed()) {
this.logger.error(`Failed to remove revisions from shared vault: ${result.getError()}`)
}
}
}

View File

@@ -13,4 +13,5 @@ export interface RevisionRepositoryInterface {
updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void>
findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Array<Revision>>
insert(revision: Revision): Promise<boolean>
clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void>
}

View File

@@ -0,0 +1,66 @@
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { RemoveRevisionsFromSharedVault } from './RemoveRevisionsFromSharedVault'
describe('RemoveRevisionsFromSharedVault', () => {
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
let revisionRepository: RevisionRepositoryInterface
const createUseCase = () => new RemoveRevisionsFromSharedVault(revisionRepositoryResolver)
beforeEach(() => {
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
revisionRepository.clearSharedVaultAndKeySystemAssociations = jest.fn()
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
})
it('should clear shared vault and key system associations', async () => {
const useCase = createUseCase()
await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['CORE_USER'],
})
expect(revisionRepository.clearSharedVaultAndKeySystemAssociations).toHaveBeenCalled()
})
it('should return error when shared vault uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: 'invalid',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should return error when item uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: 'invalid',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should return error when role names are invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['invalid'],
})
expect(result.isFailed()).toBe(true)
})
})

View File

@@ -0,0 +1,33 @@
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { RemoveRevisionsFromSharedVaultDTO } from './RemoveRevisionsFromSharedVaultDTO'
export class RemoveRevisionsFromSharedVault implements UseCaseInterface<void> {
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
async execute(dto: RemoveRevisionsFromSharedVaultDTO): Promise<Result<void>> {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const itemUuidOrError = Uuid.create(dto.itemUuid)
if (itemUuidOrError.isFailed()) {
return Result.fail(itemUuidOrError.getError())
}
const itemUuid = itemUuidOrError.getValue()
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
if (roleNamesOrError.isFailed()) {
return Result.fail(roleNamesOrError.getError())
}
const roleNames = roleNamesOrError.getValue()
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
await revisionRepository.clearSharedVaultAndKeySystemAssociations(itemUuid, sharedVaultUuid)
return Result.ok()
}
}

View File

@@ -0,0 +1,5 @@
export interface RemoveRevisionsFromSharedVaultDTO {
itemUuid: string
sharedVaultUuid: string
roleNames: string[]
}

View File

@@ -16,6 +16,21 @@ export class MongoDBRevisionRepository implements RevisionRepositoryInterface {
private logger: Logger,
) {}
async clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void> {
await this.mongoRepository.updateMany(
{
itemUuid: { $eq: itemUuid.value },
sharedVaultUuid: { $eq: sharedVaultUuid.value },
},
{
$set: {
sharedVaultUuid: null,
keySystemIdentifier: null,
},
},
)
}
async countByUserUuid(userUuid: Uuid): Promise<number> {
return this.mongoRepository.count({ userUuid: { $eq: userUuid.value } })
}

View File

@@ -15,6 +15,10 @@ export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface
protected logger: Logger,
) {}
async clearSharedVaultAndKeySystemAssociations(_itemUuid: Uuid, _sharedVaultUuid: Uuid): Promise<void> {
this.logger.error('Method clearSharedVaultAndKeySystemAssociations not implemented.')
}
async countByUserUuid(userUuid: Uuid): Promise<number> {
return this.ormRepository
.createQueryBuilder()

View File

@@ -45,6 +45,21 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
return this.revisionMapper.toDomain(sqlRevision)
}
override async clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void> {
await this.ormRepository
.createQueryBuilder()
.update()
.set({
sharedVaultUuid: null,
keySystemIdentifier: null,
})
.where('item_uuid = :itemUuid AND shared_vault_uuid = :sharedVaultUuid', {
itemUuid: itemUuid.value,
sharedVaultUuid: sharedVaultUuid.value,
})
.execute()
}
override async findMetadataByItemId(
itemUuid: Uuid,
userUuid: Uuid,

View File

@@ -1,8 +1,10 @@
import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@standardnotes/domain-core'
import { BSON } from 'mongodb'
import { MongoDBRevision } from '../../../Infra/TypeORM/MongoDB/MongoDBRevision'
import { Revision } from '../../../Domain/Revision/Revision'
import { BSON } from 'mongodb'
import { SharedVaultAssociation } from '../../../Domain/SharedVault/SharedVaultAssociation'
import { KeySystemAssociation } from '../../../Domain/KeySystem/KeySystemAssociation'
export class MongoDBRevisionPersistenceMapper implements MapperInterface<Revision, MongoDBRevision> {
toDomain(projection: MongoDBRevision): Revision {
@@ -33,6 +35,39 @@ export class MongoDBRevisionPersistenceMapper implements MapperInterface<Revisio
userUuid = userUuidOrError.getValue()
}
let sharedVaultAssociation: SharedVaultAssociation | undefined = undefined
if (projection.sharedVaultUuid && projection.editedBy) {
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
throw new Error(`Failed to create revision from projection: ${sharedVaultUuidOrError.getError()}`)
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const lastEditedByOrError = Uuid.create(projection.editedBy)
if (lastEditedByOrError.isFailed()) {
throw new Error(`Failed to create revision from projection: ${lastEditedByOrError.getError()}`)
}
const lastEditedBy = lastEditedByOrError.getValue()
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
sharedVaultUuid,
editedBy: lastEditedBy,
})
if (sharedVaultAssociationOrError.isFailed()) {
throw new Error(`Failed to create revision from projection: ${sharedVaultAssociationOrError.getError()}`)
}
sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
}
let keySystemAssociation: KeySystemAssociation | undefined = undefined
if (projection.keySystemIdentifier) {
const keySystemAssociationOrError = KeySystemAssociation.create(projection.keySystemIdentifier)
if (keySystemAssociationOrError.isFailed()) {
throw new Error(`Failed to create revision from projection: ${keySystemAssociationOrError.getError()}`)
}
keySystemAssociation = keySystemAssociationOrError.getValue()
}
const revisionOrError = Revision.create(
{
authHash: projection.authHash,
@@ -44,6 +79,8 @@ export class MongoDBRevisionPersistenceMapper implements MapperInterface<Revisio
itemUuid,
userUuid,
dates,
sharedVaultAssociation,
keySystemAssociation,
},
new UniqueEntityId(projection._id.toHexString()),
)
@@ -67,6 +104,15 @@ export class MongoDBRevisionPersistenceMapper implements MapperInterface<Revisio
mongoDBRevision.itemUuid = domain.props.itemUuid.value
mongoDBRevision.itemsKeyId = domain.props.itemsKeyId
mongoDBRevision.userUuid = domain.props.userUuid ? domain.props.userUuid.value : null
mongoDBRevision.sharedVaultUuid = domain.props.sharedVaultAssociation
? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
: null
mongoDBRevision.editedBy = domain.props.sharedVaultAssociation
? domain.props.sharedVaultAssociation.props.editedBy.value
: null
mongoDBRevision.keySystemIdentifier = domain.props.keySystemAssociation
? domain.props.keySystemAssociation.props.keySystemIdentifier
: null
mongoDBRevision._id = BSON.UUID.createFromHexString(domain.id.toString())
return mongoDBRevision

View File

@@ -2,6 +2,5 @@
set -euo pipefail
sh supervisor/wait-for.sh $DB_HOST $DB_PORT
sh supervisor/wait-for.sh $REDIS_HOST $REDIS_PORT
sh supervisor/wait-for.sh localhost $SYNCING_SERVER_PORT
node docker/entrypoint-server.js

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.39](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.38...@standardnotes/scheduler-server@1.20.39) (2023-09-08)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.38](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.37...@standardnotes/scheduler-server@1.20.38) (2023-09-07)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.37](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.36...@standardnotes/scheduler-server@1.20.37) (2023-09-07)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.37",
"version": "1.20.39",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.95.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.94.0...@standardnotes/syncing-server@1.95.0) (2023-09-08)
### Features
* transition users after sign out ([398338c](https://github.com/standardnotes/syncing-server-js/commit/398338c8f81b12406c7ab3bf1654e60b94d7cfd0))
# [1.94.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.93.1...@standardnotes/syncing-server@1.94.0) (2023-09-08)
### Features
* **syncing-server:** add procedure to trigger transition for specific user ([8cb92d9](https://github.com/standardnotes/syncing-server-js/commit/8cb92d9678cffe9188fa2b038f1b5a9e0247551e))
## [1.93.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.93.0...@standardnotes/syncing-server@1.93.1) (2023-09-07)
### Bug Fixes
* **syncing-server:** invalidating cache for user removed from shared vault ([#812](https://github.com/standardnotes/syncing-server-js/issues/812)) ([c4a1502](https://github.com/standardnotes/syncing-server-js/commit/c4a1502f70e675da5c81c4005ee1eca578a7e3f4))
# [1.93.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.92.1...@standardnotes/syncing-server@1.93.0) (2023-09-07)
### Features
* add removing revisions from shared vaults ([#811](https://github.com/standardnotes/syncing-server-js/issues/811)) ([3bd63f7](https://github.com/standardnotes/syncing-server-js/commit/3bd63f767464baf9b9f1ffa52eea9eed4a4e11b5))
## [1.92.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.92.0...@standardnotes/syncing-server@1.92.1) (2023-09-07)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -0,0 +1,50 @@
import 'reflect-metadata'
import { Logger } from 'winston'
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env'
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../src/Domain/UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
const inputArgs = process.argv.slice(2)
const userUuid = inputArgs[0]
const requestTransition = async (
triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
logger: Logger,
): Promise<void> => {
const result = await triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
userUuid,
})
if (result.isFailed()) {
logger.error(`Could not trigger transition for user ${userUuid}: ${result.getError()}`)
}
return
}
const container = new ContainerConfigLoader()
void container.load().then((container) => {
const env: Env = new Env()
env.load()
const logger: Logger = container.get(TYPES.Sync_Logger)
logger.info(`Starting transitiong for user ${userUuid} ...`)
const triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser =
container.get(TYPES.Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser)
Promise.resolve(requestTransition(triggerTransitionFromPrimaryToSecondaryDatabaseForUser, logger))
.then(() => {
logger.info(`Transition triggered for user ${userUuid}`)
process.exit(0)
})
.catch((error) => {
logger.error(`Could not trigger transition for user ${userUuid}: ${error.message}`)
process.exit(1)
})
})

View File

@@ -0,0 +1,11 @@
'use strict'
const path = require('path')
const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/transition.js')))
Object.defineProperty(exports, '__esModule', { value: true })
exports.default = index

View File

@@ -14,6 +14,12 @@ case "$COMMAND" in
node docker/entrypoint-worker.js
;;
'transition' )
echo "[Docker] Starting transition Single User..."
USER_UUID=$1 && shift 1
node docker/entrypoint-transition.js $USER_UUID
;;
* )
echo "[Docker] Unknown command"
;;

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.92.1",
"version": "1.95.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -166,6 +166,7 @@ import { SQLItem } from '../Infra/TypeORM/SQLItem'
import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersistenceMapper'
import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
import { SendEventToClient } from '../Domain/UseCase/Syncing/SendEventToClient/SendEventToClient'
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -955,6 +956,16 @@ export class ContainerConfigLoader {
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<TransitionRequestedEventHandler>(TYPES.Sync_TransitionRequestedEventHandler)
.toConstantValue(
new TransitionRequestedEventHandler(
container.get<TriggerTransitionFromPrimaryToSecondaryDatabaseForUser>(
TYPES.Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
),
container.get<Logger>(TYPES.Sync_Logger),
),
)
// Services
container.bind<ContentDecoder>(TYPES.Sync_ContentDecoder).toDynamicValue(() => new ContentDecoder())
@@ -993,6 +1004,10 @@ export class ContainerConfigLoader {
'TRANSITION_STATUS_UPDATED',
container.get<TransitionStatusUpdatedEventHandler>(TYPES.Sync_TransitionStatusUpdatedEventHandler),
],
[
'TRANSITION_REQUESTED',
container.get<TransitionRequestedEventHandler>(TYPES.Sync_TransitionRequestedEventHandler),
],
])
if (!isConfiguredForHomeServer) {
container.bind(TYPES.Sync_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL'))

View File

@@ -97,6 +97,7 @@ const TYPES = {
Sync_SharedVaultFileUploadedEventHandler: Symbol.for('Sync_SharedVaultFileUploadedEventHandler'),
Sync_SharedVaultFileMovedEventHandler: Symbol.for('Sync_SharedVaultFileMovedEventHandler'),
Sync_TransitionStatusUpdatedEventHandler: Symbol.for('Sync_TransitionStatusUpdatedEventHandler'),
Sync_TransitionRequestedEventHandler: Symbol.for('Sync_TransitionRequestedEventHandler'),
// Services
Sync_ContentDecoder: Symbol.for('Sync_ContentDecoder'),
Sync_DomainEventPublisher: Symbol.for('Sync_DomainEventPublisher'),

View File

@@ -4,6 +4,7 @@ import {
DuplicateItemSyncedEvent,
EmailRequestedEvent,
ItemDumpedEvent,
ItemRemovedFromSharedVaultEvent,
ItemRevisionCreationRequestedEvent,
MessageSentToUserEvent,
NotificationAddedForUserEvent,
@@ -20,6 +21,26 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(private timer: TimerInterface) {}
createItemRemovedFromSharedVaultEvent(dto: {
sharedVaultUuid: string
itemUuid: string
userUuid: string
roleNames: string[]
}): ItemRemovedFromSharedVaultEvent {
return {
type: 'ITEM_REMOVED_FROM_SHARED_VAULT',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
},
payload: dto,
}
}
createUserRemovedFromSharedVaultEvent(dto: {
sharedVaultUuid: string
userUuid: string

View File

@@ -2,6 +2,7 @@ import {
DuplicateItemSyncedEvent,
EmailRequestedEvent,
ItemDumpedEvent,
ItemRemovedFromSharedVaultEvent,
ItemRevisionCreationRequestedEvent,
MessageSentToUserEvent,
NotificationAddedForUserEvent,
@@ -93,4 +94,10 @@ export interface DomainEventFactoryInterface {
sharedVaultUuid: string
userUuid: string
}): UserRemovedFromSharedVaultEvent
createItemRemovedFromSharedVaultEvent(dto: {
sharedVaultUuid: string
itemUuid: string
userUuid: string
roleNames: string[]
}): ItemRemovedFromSharedVaultEvent
}

View File

@@ -0,0 +1,23 @@
import { DomainEventHandlerInterface, TransitionRequestedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../UseCase/Transition/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser/TriggerTransitionFromPrimaryToSecondaryDatabaseForUser'
export class TransitionRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
private triggerTransitionFromPrimaryToSecondaryDatabaseForUser: TriggerTransitionFromPrimaryToSecondaryDatabaseForUser,
private logger: Logger,
) {}
async handle(event: TransitionRequestedEvent): Promise<void> {
this.logger.info(`Handling transition requested event for user ${event.payload.userUuid}`)
const result = await this.triggerTransitionFromPrimaryToSecondaryDatabaseForUser.execute({
userUuid: event.payload.userUuid,
})
if (result.isFailed()) {
this.logger.error(`Failed to trigger transition for user ${event.payload.userUuid}`)
}
}
}

View File

@@ -111,6 +111,9 @@ describe('UpdateExistingItem', () => {
domainEventFactory.createItemRevisionCreationRequested = jest
.fn()
.mockReturnValue({} as jest.Mocked<DomainEventInterface>)
domainEventFactory.createItemRemovedFromSharedVaultEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<DomainEventInterface>)
determineSharedVaultOperationOnItem = {} as jest.Mocked<DetermineSharedVaultOperationOnItem>
determineSharedVaultOperationOnItem.execute = jest.fn().mockResolvedValue(
@@ -400,6 +403,47 @@ describe('UpdateExistingItem', () => {
expect(item1.props.sharedVaultAssociation).not.toBeUndefined()
})
it('should remove a shared vault association and publish an event that item has been removed from shared vault', async () => {
const useCase = createUseCase()
item1.props.sharedVaultAssociation = SharedVaultAssociation.create({
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
}).getValue()
const itemHash = ItemHash.create({
...itemHash1.props,
shared_vault_uuid: null,
}).getValue()
determineSharedVaultOperationOnItem.execute = jest.fn().mockReturnValue(
Result.ok(
SharedVaultOperationOnItem.create({
existingItem: item1,
incomingItemHash: itemHash1,
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
type: SharedVaultOperationOnItem.TYPES.RemoveFromSharedVault,
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
}).getValue(),
),
)
const result = await useCase.execute({
existingItem: item1,
itemHash,
sessionUuid: '00000000-0000-0000-0000-000000000000',
performingUserUuid: '00000000-0000-0000-0000-000000000000',
roleNames: [RoleName.NAMES.CoreUser],
})
expect(result.isFailed()).toBeFalsy()
expect(item1.props.sharedVaultAssociation).toBeUndefined()
expect(domainEventFactory.createItemRemovedFromSharedVaultEvent).toHaveBeenCalled()
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should return error if shared vault association could not be created', async () => {
const useCase = createUseCase()

View File

@@ -37,16 +37,6 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
) {}
async execute(dto: UpdateExistingItemDTO): Promise<Result<Item>> {
let sessionUuid = null
if (dto.sessionUuid) {
const sessionUuidOrError = Uuid.create(dto.sessionUuid)
if (sessionUuidOrError.isFailed()) {
return Result.fail(sessionUuidOrError.getError())
}
sessionUuid = sessionUuidOrError.getValue()
}
dto.existingItem.props.updatedWithSession = sessionUuid
const userUuidOrError = Uuid.create(dto.performingUserUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
@@ -59,6 +49,29 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
}
const roleNames = roleNamesOrError.getValue()
let sharedVaultOperation: SharedVaultOperationOnItem | null = null
if (dto.itemHash.representsASharedVaultItem() || dto.existingItem.isAssociatedWithASharedVault()) {
const sharedVaultOperationOrError = await this.determineSharedVaultOperationOnItem.execute({
existingItem: dto.existingItem,
itemHash: dto.itemHash,
userUuid: userUuid.value,
})
if (sharedVaultOperationOrError.isFailed()) {
return Result.fail(sharedVaultOperationOrError.getError())
}
sharedVaultOperation = sharedVaultOperationOrError.getValue()
}
let sessionUuid = null
if (dto.sessionUuid) {
const sessionUuidOrError = Uuid.create(dto.sessionUuid)
if (sessionUuidOrError.isFailed()) {
return Result.fail(sessionUuidOrError.getError())
}
sessionUuid = sessionUuidOrError.getValue()
}
dto.existingItem.props.updatedWithSession = sessionUuid
if (dto.itemHash.props.content) {
dto.existingItem.props.content = dto.itemHash.props.content
}
@@ -128,7 +141,6 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
dto.existingItem.props.contentSize = Buffer.byteLength(JSON.stringify(dto.existingItem))
let sharedVaultOperation: SharedVaultOperationOnItem | null = null
if (dto.itemHash.representsASharedVaultItem()) {
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
lastEditedBy: userUuid,
@@ -140,16 +152,6 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
}
dto.existingItem.props.sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
const sharedVaultOperationOrError = await this.determineSharedVaultOperationOnItem.execute({
existingItem: dto.existingItem,
itemHash: dto.itemHash,
userUuid: userUuid.value,
})
if (sharedVaultOperationOrError.isFailed()) {
return Result.fail(sharedVaultOperationOrError.getError())
}
sharedVaultOperation = sharedVaultOperationOrError.getValue()
} else {
dto.existingItem.props.sharedVaultAssociation = undefined
}
@@ -209,7 +211,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
)
}
const notificationsResult = await this.addNotifications(dto.existingItem.uuid, userUuid, sharedVaultOperation)
const notificationsResult = await this.addNotificationsAndPublishEvents(userUuid, sharedVaultOperation, dto)
if (notificationsResult.isFailed()) {
return Result.fail(notificationsResult.getError())
}
@@ -217,10 +219,10 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
return Result.ok(dto.existingItem)
}
private async addNotifications(
itemUuid: Uuid,
private async addNotificationsAndPublishEvents(
userUuid: Uuid,
sharedVaultOperation: SharedVaultOperationOnItem | null,
dto: UpdateExistingItemDTO,
): Promise<Result<void>> {
if (
sharedVaultOperation &&
@@ -229,7 +231,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
const notificationPayloadOrError = NotificationPayload.create({
sharedVaultUuid: sharedVaultOperation.props.sharedVaultUuid,
type: NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue(),
itemUuid: itemUuid,
itemUuid: dto.existingItem.uuid,
version: '1.0',
})
if (notificationPayloadOrError.isFailed()) {
@@ -246,6 +248,15 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
if (result.isFailed()) {
return Result.fail(result.getError())
}
await this.domainEventPublisher.publish(
this.domainEventFactory.createItemRemovedFromSharedVaultEvent({
sharedVaultUuid: sharedVaultOperation.props.sharedVaultUuid.value,
itemUuid: dto.existingItem.uuid.value,
userUuid: userUuid.value,
roleNames: dto.roleNames,
}),
)
}
if (sharedVaultOperation && sharedVaultOperation.props.type === SharedVaultOperationOnItem.TYPES.AddToSharedVault) {

View File

@@ -65,7 +65,7 @@ export class BaseSharedVaultUsersController extends BaseHttpController {
)
}
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
response.setHeader('x-invalidate-cache', request.params.userUuid)
return this.json({
success: true,

View File

@@ -155,15 +155,23 @@ export class MongoDBItemRepository implements ItemRepositoryInterface {
async markItemsAsDeleted(itemUuids: string[], updatedAtTimestamp: number): Promise<void> {
await this.mongoRepository.updateMany(
{ where: { _id: { $in: itemUuids.map((uuid) => BSON.UUID.createFromHexString(uuid)) } } },
{ deleted: true, content: null, encItemKey: null, authHash: null, updatedAtTimestamp },
{ _id: { $in: itemUuids.map((uuid) => BSON.UUID.createFromHexString(uuid)) } },
{
$set: {
deleted: true,
content: null,
encItemKey: null,
authHash: null,
updatedAtTimestamp,
},
},
)
}
async updateContentSize(itemUuid: string, contentSize: number): Promise<void> {
await this.mongoRepository.updateOne(
{ where: { _id: { $eq: BSON.UUID.createFromHexString(itemUuid) } } },
{ contentSize },
{ _id: { $eq: BSON.UUID.createFromHexString(itemUuid) } },
{ $set: { contentSize } },
)
}

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.36](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.35...@standardnotes/websockets-server@1.10.36) (2023-09-08)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.10.35](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.34...@standardnotes/websockets-server@1.10.35) (2023-09-07)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.10.34](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.33...@standardnotes/websockets-server@1.10.34) (2023-09-07)
**Note:** Version bump only for package @standardnotes/websockets-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.10.34",
"version": "1.10.36",
"engines": {
"node": ">=18.0.0 <21.0.0"
},