Compare commits

..

6 Commits

28 changed files with 140 additions and 55 deletions

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.141.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.2...@standardnotes/auth-server@1.141.3) (2023-09-12)
### Bug Fixes
* transition adjustments ([f20a947](https://github.com/standardnotes/server/commit/f20a947f8a555c074d8dc1543c7a8bf61d1d887e))
## [1.141.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.1...@standardnotes/auth-server@1.141.2) (2023-09-11)
### Bug Fixes
* **auth:** remove transitioning upon sign out ([#819](https://github.com/standardnotes/server/issues/819)) ([330bff0](https://github.com/standardnotes/server/commit/330bff0124f5f49c3441304d166ea43c21fea7bc))
## [1.141.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.0...@standardnotes/auth-server@1.141.1) (2023-09-11)
### Bug Fixes

View File

@@ -10,6 +10,7 @@ import { Env } from '../src/Bootstrap/Env'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
import { RoleName } from '@standardnotes/domain-core'
const inputArgs = process.argv.slice(2)
const startDateString = inputArgs[0]
@@ -28,11 +29,24 @@ const requestTransition = async (
logger.info(`Found ${users.length} users created between ${startDateString} and ${endDateString}`)
let usersTriggered = 0
for (const user of users) {
const roles = await user.roles
const userHasTransitionUserRole = roles.some((role) => role.name === RoleName.NAMES.TransitionUser) === true
if (userHasTransitionUserRole === true) {
continue
}
const transitionRequestedEvent = domainEventFactory.createTransitionRequestedEvent({ userUuid: user.uuid })
usersTriggered += 1
await domainEventPublisher.publish(transitionRequestedEvent)
}
logger.info(
`Triggered transition for ${usersTriggered} users created between ${startDateString} and ${endDateString}`,
)
}
const container = new ContainerConfigLoader('worker')

View File

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

View File

@@ -983,7 +983,6 @@ 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,7 +27,6 @@ describe('AuthController', () => {
let doGenerateRecoveryCodes: GenerateRecoveryCodes
let logger: Logger
let sessionService: SessionServiceInterface
let transitionModeEnabled: boolean
const createController = () =>
new AuthController(
@@ -40,7 +39,6 @@ describe('AuthController', () => {
doGenerateRecoveryCodes,
logger,
sessionService,
transitionModeEnabled,
)
beforeEach(() => {
@@ -66,8 +64,6 @@ 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,7 +37,6 @@ export class AuthController implements UserServerInterface {
private doGenerateRecoveryCodes: GenerateRecoveryCodes,
private logger: Logger,
private sessionService: SessionServiceInterface,
private transitionModeEnabled: boolean,
) {}
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
@@ -227,14 +226,6 @@ 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

@@ -1,5 +1,8 @@
export interface TransitionStatusRepositoryInterface {
updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: 'STARTED' | 'FAILED'): Promise<void>
removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void>
getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<'STARTED' | 'FAILED' | null>
getStatus(
userUuid: string,
transitionType: 'items' | 'revisions',
): Promise<'STARTED' | 'IN_PROGRESS' | 'FAILED' | null>
}

View File

@@ -119,7 +119,7 @@ describe('CreateCrossServiceToken', () => {
})
it('should create a cross service token for user that has an ongoing transaction', async () => {
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('STARTED')
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('IN_PROGRESS')
await createUseCase().execute({
user,

View File

@@ -59,7 +59,7 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
user: this.projectUser(user),
roles: this.projectRoles(roles),
shared_vault_owner_context: undefined,
ongoing_transition: transitionStatus === 'STARTED',
ongoing_transition: transitionStatus === 'IN_PROGRESS',
belongs_to_shared_vaults: sharedVaultAssociations.map((association) => ({
shared_vault_uuid: association.props.sharedVaultUuid.value,
permission: association.props.permission.value,

View File

@@ -4,13 +4,17 @@ import { GetTransitionStatusDTO } from './GetTransitionStatusDTO'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
export class GetTransitionStatus implements UseCaseInterface<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'> {
export class GetTransitionStatus
implements UseCaseInterface<'TO-DO' | 'STARTED' | 'IN_PROGRESS' | 'FINISHED' | 'FAILED'>
{
constructor(
private transitionStatusRepository: TransitionStatusRepositoryInterface,
private userRepository: UserRepositoryInterface,
) {}
async execute(dto: GetTransitionStatusDTO): Promise<Result<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'>> {
async execute(
dto: GetTransitionStatusDTO,
): Promise<Result<'TO-DO' | 'STARTED' | 'IN_PROGRESS' | 'FINISHED' | 'FAILED'>> {
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())

View File

@@ -19,9 +19,13 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
await this.redisClient.del(`${this.PREFIX}:${transitionType}:${userUuid}`)
}
async getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<'STARTED' | 'FAILED' | null> {
async getStatus(
userUuid: string,
transitionType: 'items' | 'revisions',
): Promise<'STARTED' | 'IN_PROGRESS' | 'FAILED' | null> {
const status = (await this.redisClient.get(`${this.PREFIX}:${transitionType}:${userUuid}`)) as
| 'STARTED'
| 'IN_PROGRESS'
| 'FAILED'
| null

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.15.37](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.36...@standardnotes/home-server@1.15.37) (2023-09-12)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.36](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.35...@standardnotes/home-server@1.15.36) (2023-09-11)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.35](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.34...@standardnotes/home-server@1.15.35) (2023-09-11)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.34](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.33...@standardnotes/home-server@1.15.34) (2023-09-11)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.15.34",
"version": "1.15.37",
"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.
## [1.33.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.7...@standardnotes/revisions-server@1.33.8) (2023-09-12)
### Bug Fixes
* transition adjustments ([f20a947](https://github.com/standardnotes/server/commit/f20a947f8a555c074d8dc1543c7a8bf61d1d887e))
## [1.33.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.6...@standardnotes/revisions-server@1.33.7) (2023-09-11)
### Bug Fixes
* **auth:** remove transitioning upon sign out ([#819](https://github.com/standardnotes/server/issues/819)) ([330bff0](https://github.com/standardnotes/server/commit/330bff0124f5f49c3441304d166ea43c21fea7bc))
## [1.33.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.33.5...@standardnotes/revisions-server@1.33.6) (2023-09-11)
### Bug Fixes

View File

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

View File

@@ -350,6 +350,7 @@ export class ContainerConfigLoader {
: null,
container.get<TimerInterface>(TYPES.Revisions_Timer),
container.get<winston.Logger>(TYPES.Revisions_Logger),
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
),
)
container

View File

@@ -4,6 +4,6 @@ export interface DomainEventFactoryInterface {
createTransitionStatusUpdatedEvent(dto: {
userUuid: string
transitionType: 'items' | 'revisions'
status: 'STARTED' | 'FAILED' | 'FINISHED'
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' | 'FINISHED'
}): TransitionStatusUpdatedEvent
}

View File

@@ -29,6 +29,14 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
}
if (event.payload.status === 'STARTED' && event.payload.transitionType === 'revisions') {
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
userUuid: event.payload.userUuid,
status: 'IN_PROGRESS',
transitionType: 'revisions',
}),
)
const result = await this.transitionRevisionsFromPrimaryToSecondaryDatabaseForUser.execute({
userUuid: event.payload.userUuid,
})

View File

@@ -22,6 +22,7 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
secondaryRevisionRepository,
timer,
logger,
1,
)
beforeEach(() => {
@@ -200,9 +201,6 @@ describe('TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser', () => {
})
expect(result.isFailed()).toBeTruthy()
expect(result.getError()).toEqual(
'Revision 00000000-0000-0000-0000-000000000001 is not identical in primary and secondary database',
)
expect((secondaryRevisionRepository as RevisionRepositoryInterface).removeByUserUuid).toHaveBeenCalledTimes(1)
expect(primaryRevisionRepository.removeByUserUuid).not.toHaveBeenCalled()

View File

@@ -11,6 +11,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
private secondRevisionsRepository: RevisionRepositoryInterface | null,
private timer: TimerInterface,
private logger: Logger,
private pageSize: number,
) {}
async execute(dto: TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
@@ -83,13 +84,12 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
try {
const totalRevisionsCountForUser = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
let totalRevisionsCountTransitionedToSecondary = 0
const pageSize = 1
const totalPages = Math.ceil(totalRevisionsCountForUser / pageSize)
const totalPages = Math.ceil(totalRevisionsCountForUser / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
const query = {
userUuid: userUuid,
offset: (currentPage - 1) * pageSize,
limit: pageSize,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
}
const revisions = await this.primaryRevisionsRepository.findByUserUuid(query)
@@ -153,13 +153,12 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
try {
const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
const pageSize = 1
const totalPages = Math.ceil(totalRevisionsCountForUserInPrimary / pageSize)
const totalPages = Math.ceil(totalRevisionsCountForUserInPrimary / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
const query = {
userUuid: userUuid,
offset: (currentPage - 1) * pageSize,
limit: pageSize,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
}
const revisions = await this.primaryRevisionsRepository.findByUserUuid(query)
@@ -180,7 +179,11 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
if (!revision.isIdenticalTo(revisionInSecondary)) {
return Result.fail(`Revision ${revision.id.toString()} is not identical in primary and secondary database`)
return Result.fail(
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in primary database: ${JSON.stringify(
revision,
)}, revision in secondary database: ${JSON.stringify(revisionInSecondary)}`,
)
}
}
}

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.95.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.3...@standardnotes/syncing-server@1.95.4) (2023-09-12)
### Bug Fixes
* transition adjustments ([f20a947](https://github.com/standardnotes/syncing-server-js/commit/f20a947f8a555c074d8dc1543c7a8bf61d1d887e))
## [1.95.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.2...@standardnotes/syncing-server@1.95.3) (2023-09-11)
### Bug Fixes
* debug sync block ([1d751c0](https://github.com/standardnotes/syncing-server-js/commit/1d751c0fbe434f661466dde804a37849f23d9b1b))
## [1.95.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.1...@standardnotes/syncing-server@1.95.2) (2023-09-11)
### Bug Fixes
* **auth:** remove transitioning upon sign out ([#819](https://github.com/standardnotes/syncing-server-js/issues/819)) ([330bff0](https://github.com/standardnotes/syncing-server-js/commit/330bff0124f5f49c3441304d166ea43c21fea7bc))
## [1.95.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.95.0...@standardnotes/syncing-server@1.95.1) (2023-09-11)
### Bug Fixes

View File

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

View File

@@ -833,6 +833,7 @@ export class ContainerConfigLoader {
isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
container.get<TimerInterface>(TYPES.Sync_Timer),
container.get<Logger>(TYPES.Sync_Logger),
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
),
)
container

View File

@@ -52,7 +52,7 @@ export interface DomainEventFactoryInterface {
createTransitionStatusUpdatedEvent(dto: {
userUuid: string
transitionType: 'items' | 'revisions'
status: 'STARTED' | 'FAILED' | 'FINISHED'
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' | 'FINISHED'
}): TransitionStatusUpdatedEvent
createEmailRequestedEvent(dto: {
userEmail: string

View File

@@ -17,6 +17,14 @@ export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerIn
async handle(event: TransitionStatusUpdatedEvent): Promise<void> {
if (event.payload.status === 'STARTED' && event.payload.transitionType === 'items') {
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
userUuid: event.payload.userUuid,
status: 'IN_PROGRESS',
transitionType: 'items',
}),
)
const result = await this.transitionItemsFromPrimaryToSecondaryDatabaseForUser.execute({
userUuid: event.payload.userUuid,
})

View File

@@ -22,6 +22,7 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
secondaryItemRepository,
timer,
logger,
1,
)
beforeEach(() => {
@@ -205,9 +206,6 @@ describe('TransitionItemsFromPrimaryToSecondaryDatabaseForUser', () => {
})
expect(result.isFailed()).toBeTruthy()
expect(result.getError()).toEqual(
'Item 00000000-0000-0000-0000-000000000001 is not identical in primary and secondary database',
)
expect((secondaryItemRepository as ItemRepositoryInterface).deleteByUserUuid).toHaveBeenCalledTimes(1)
expect(primaryItemRepository.deleteByUserUuid).not.toHaveBeenCalled()

View File

@@ -12,6 +12,7 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
private secondaryItemRepository: ItemRepositoryInterface | null,
private timer: TimerInterface,
private logger: Logger,
private pageSize: number,
) {}
async execute(dto: TransitionItemsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
@@ -92,14 +93,12 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
private async migrateItemsForUser(userUuid: Uuid): Promise<Result<void>> {
try {
const totalItemsCountForUser = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
const pageSize = 1
const totalPages = totalItemsCountForUser
let currentPage = 1
for (currentPage; currentPage <= totalPages; currentPage++) {
const totalPages = Math.ceil(totalItemsCountForUser / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
const query: ItemQuery = {
userUuid: userUuid.value,
offset: currentPage - 1,
limit: pageSize,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
}
const items = await this.primaryItemRepository.findAll(query)
@@ -140,14 +139,12 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
)
}
const pageSize = 1
const totalPages = totalItemsCountForUserInPrimary
let currentPage = 1
for (currentPage; currentPage <= totalPages; currentPage++) {
const totalPages = Math.ceil(totalItemsCountForUserInPrimary / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
const query: ItemQuery = {
userUuid: userUuid.value,
offset: currentPage - 1,
limit: pageSize,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
}
const items = await this.primaryItemRepository.findAll(query)
@@ -159,7 +156,13 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
}
if (!item.isIdenticalTo(itemInSecondary)) {
return Result.fail(`Item ${item.uuid.value} is not identical in primary and secondary database`)
return Result.fail(
`Item ${
item.uuid.value
} is not identical in primary and secondary database. Item in primary database: ${JSON.stringify(
item,
)}, item in secondary database: ${JSON.stringify(itemInSecondary)}`,
)
}
}
}

View File

@@ -36,7 +36,7 @@ export class BaseItemsController extends BaseHttpController {
async sync(request: Request, response: Response): Promise<results.JsonResult> {
if (response.locals.ongoingTransition === true) {
throw new Error('Cannot sync during transition')
throw new Error(`Cannot sync user ${response.locals.user.uuid} during transition`)
}
const itemHashes: ItemHash[] = []