feat(multiregion): apply prepared transactions to projects (#5322)
* feat(multiregion): replace user replication * chore(multiregion): optimise replication * maybe it's this * postgres is fun * once more * chore(multiregion): only replicate test user creation during multiregion tests * feat: improved replicate_query logic * fix: minor * fix: starting issue * feat: included user create and delete specs to multiregion * feat: removed console logs * fix: user defaults * fix: multiregion test helper * fix: update scenarios for users * refactor(multiregion): swap replicateQuery concept to asMultiregionOperation (#5301) feat(multiregion): introduced asMultregionOperator, refactor test to user builder classes * chore: renamings * fix: remove comments * feat: remove user replication * refactor: simplified spec usages * chore: comments * chore: branches and favs * chore: more tests * chore: more tests * fix linting * fix tests * feat: dropping replication * refactor: moved project delete to service * fix: comment * feat: updateStreamFactory and updateProjectFacotry * deleteProjectFactory + replicateFactory * deleteWorkspaceFactory * fix: selector * fix: tests * fix tests, finished createStreamFactory * feat: simplify changes * fix: remove comment * fix: minor strucutres * fix: moveProjectToRegion * fix: moved branch creation outside of multiregion scope * fix: branch creation * fix: tests * fix: ci tests * fix: removed log form test * fix: on specs, no random regionKeys * review fixes * fix: mr comments * feat: removed test --------- Co-authored-by: Charles Driesler <chuck@speckle.systems>
This commit is contained in:
committed by
GitHub
parent
6692fdf4aa
commit
399c998fd7
@@ -5,7 +5,6 @@ import { removePrivateFields } from '@/modules/core/helpers/userHelper'
|
||||
import {
|
||||
updateProjectFactory,
|
||||
getStreamFactory,
|
||||
deleteStreamFactory,
|
||||
revokeStreamPermissionsFactory,
|
||||
grantStreamPermissionsFactory,
|
||||
getStreamCollaboratorsFactory,
|
||||
@@ -151,9 +150,18 @@ import {
|
||||
import { updateStreamRoleAndNotifyFactory } from '@/modules/core/services/streams/management'
|
||||
import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { asOperation, commandFactory } from '@/modules/shared/command'
|
||||
import {
|
||||
asMultiregionalOperation,
|
||||
asOperation,
|
||||
commandFactory,
|
||||
replicateFactory
|
||||
} from '@/modules/shared/command'
|
||||
import { throwIfRateLimitedFactory } from '@/modules/core/utils/ratelimiter'
|
||||
import { getProjectDbClient, getRegionDb } from '@/modules/multiregion/utils/dbSelector'
|
||||
import {
|
||||
getAllRegisteredDbs,
|
||||
getProjectDbClient,
|
||||
getProjectReplicationDbs
|
||||
} from '@/modules/multiregion/utils/dbSelector'
|
||||
import {
|
||||
listUserExpiredSsoSessionsFactory,
|
||||
listWorkspaceSsoMembershipsByUserEmailFactory
|
||||
@@ -180,7 +188,6 @@ import {
|
||||
getWorkspaceWithPlanFactory,
|
||||
upsertWorkspacePlanFactory
|
||||
} from '@/modules/gatekeeper/repositories/billing'
|
||||
import type { Knex } from 'knex'
|
||||
import { getPaginatedItemsFactory } from '@/modules/shared/services/paginatedItems'
|
||||
import { BadRequestError, UnauthorizedError } from '@/modules/shared/errors'
|
||||
import {
|
||||
@@ -193,6 +200,7 @@ import {
|
||||
} from '@/modules/workspaces/repositories/workspaceJoinRequests'
|
||||
import { sendWorkspaceJoinRequestReceivedEmailFactory } from '@/modules/workspaces/services/workspaceJoinRequestEmails/received'
|
||||
import {
|
||||
deleteProjectFactory,
|
||||
getProjectFactory,
|
||||
getUserProjectRolesFactory
|
||||
} from '@/modules/core/repositories/projects'
|
||||
@@ -229,10 +237,13 @@ import {
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import { WorkspaceInvitesLimit } from '@/modules/workspaces/domain/constants'
|
||||
import { copyWorkspaceFactory } from '@/modules/workspaces/repositories/projectRegions'
|
||||
import { queryAllProjectsFactory } from '@/modules/core/services/projects'
|
||||
import {
|
||||
deleteProjectAndCommitsFactory,
|
||||
queryAllProjectsFactory
|
||||
} from '@/modules/core/services/projects'
|
||||
import { WorkspacePlanNotFoundError } from '@/modules/gatekeeper/errors/billing'
|
||||
import { deleteProjectCommitsFactory } from '@/modules/core/repositories/commits'
|
||||
|
||||
const eventBus = getEventBus()
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
@@ -805,38 +816,36 @@ export default FF_WORKSPACES_MODULE_ENABLED
|
||||
}
|
||||
}
|
||||
|
||||
const deleteWorkspaceFrom = (db: Knex) =>
|
||||
deleteWorkspaceFactory({
|
||||
deleteWorkspace: repoDeleteWorkspaceFactory({ db }),
|
||||
deleteProject: deleteStreamFactory({ db }),
|
||||
deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }),
|
||||
queryAllProjects: queryAllProjectsFactory({
|
||||
getExplicitProjects: getExplicitProjects({ db })
|
||||
}),
|
||||
deleteSsoProvider: deleteSsoProviderFactory({ db }),
|
||||
emitWorkspaceEvent: getEventBus().emit
|
||||
})
|
||||
// this is a bit of an overhead, we are issuing delete queries to all regions,
|
||||
// instead of being selective and clever about figuring out the project DB and only
|
||||
// deleting from main and the project db
|
||||
// while workspace must be deleted from all regions
|
||||
|
||||
// this should be turned into a get all regions and map over the regions...
|
||||
const region = await getDefaultRegionFactory({ db })({ workspaceId })
|
||||
if (region) {
|
||||
const regionDb = await getRegionDb({ regionKey: region.key })
|
||||
await withOperationLogging(
|
||||
async () => await deleteWorkspaceFrom(regionDb)({ workspaceId }),
|
||||
{
|
||||
logger: logger.child({ regionKey: region.key }),
|
||||
operationName: 'deleteWorkspaceFromRegion',
|
||||
operationDescription: 'Delete workspace from region'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
await withOperationLogging(
|
||||
async () => await deleteWorkspaceFrom(db)({ workspaceId }),
|
||||
await asMultiregionalOperation(
|
||||
async ({ mainDb, allDbs, emit }) =>
|
||||
deleteWorkspaceFactory({
|
||||
deleteWorkspace: replicateFactory(allDbs, repoDeleteWorkspaceFactory),
|
||||
deleteProjectAndCommits: deleteProjectAndCommitsFactory({
|
||||
deleteProject: replicateFactory(allDbs, deleteProjectFactory),
|
||||
deleteProjectCommits: replicateFactory(
|
||||
allDbs,
|
||||
deleteProjectCommitsFactory
|
||||
)
|
||||
}),
|
||||
deleteAllResourceInvites: deleteAllResourceInvitesFactory({
|
||||
db: mainDb
|
||||
}),
|
||||
queryAllProjects: queryAllProjectsFactory({
|
||||
getExplicitProjects: getExplicitProjects({ db: mainDb })
|
||||
}),
|
||||
deleteSsoProvider: deleteSsoProviderFactory({ db: mainDb }),
|
||||
emitWorkspaceEvent: emit
|
||||
})({ workspaceId }),
|
||||
{
|
||||
logger,
|
||||
operationName: 'deleteWorkspace',
|
||||
operationDescription: 'Delete workspace'
|
||||
name: 'delete workspace',
|
||||
description: 'Delete workspace',
|
||||
dbs: await getAllRegisteredDbs()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1606,63 +1615,76 @@ export default FF_WORKSPACES_MODULE_ENABLED
|
||||
throw mapAuthToServerError(canMoveToWorkspace.error)
|
||||
}
|
||||
|
||||
const moveProjectToWorkspace = commandFactory({
|
||||
db,
|
||||
eventBus,
|
||||
operationFactory: ({ db, emit }) =>
|
||||
const updatedProject = await asMultiregionalOperation(
|
||||
({ mainDb, allDbs, emit }) =>
|
||||
moveProjectToWorkspaceFactory({
|
||||
getProject: getProjectFactory({ db }),
|
||||
updateProject: updateProjectFactory({ db: projectDb }),
|
||||
updateProjectRole: updateStreamRoleAndNotify,
|
||||
getProjectCollaborators: getStreamCollaboratorsFactory({ db }),
|
||||
getProject: getProjectFactory({ db: mainDb }),
|
||||
updateProject: replicateFactory(allDbs, updateProjectFactory),
|
||||
updateProjectRole: updateStreamRoleAndNotifyFactory({
|
||||
isStreamCollaborator: isStreamCollaboratorFactory({
|
||||
getStream: getStreamFactory({ db: mainDb })
|
||||
}),
|
||||
addOrUpdateStreamCollaborator: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess,
|
||||
getUser: getUserFactory({ db: mainDb }),
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({
|
||||
db: mainDb
|
||||
}),
|
||||
getStreamRoles: getStreamRolesFactory({ db: mainDb }),
|
||||
emitEvent: emit
|
||||
}),
|
||||
removeStreamCollaborator
|
||||
}),
|
||||
getProjectCollaborators: getStreamCollaboratorsFactory({ db: mainDb }),
|
||||
copyWorkspace: copyWorkspaceFactory({
|
||||
// TODO: must be removed when workspace replication is implemented
|
||||
sourceDb: db,
|
||||
targetDb: projectDb
|
||||
}),
|
||||
getWorkspaceRolesAndSeats: getWorkspaceRolesAndSeatsFactory({ db }),
|
||||
getWorkspaceRolesAndSeats: getWorkspaceRolesAndSeatsFactory({
|
||||
db: mainDb
|
||||
}),
|
||||
updateWorkspaceRole: addOrUpdateWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
|
||||
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
|
||||
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
|
||||
db
|
||||
getWorkspaceRoles: getWorkspaceRolesFactory({ db: mainDb }),
|
||||
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({
|
||||
db: mainDb
|
||||
}),
|
||||
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
|
||||
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
|
||||
db: mainDb
|
||||
}),
|
||||
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: mainDb }),
|
||||
emitWorkspaceEvent: emit,
|
||||
ensureValidWorkspaceRoleSeat: ensureValidWorkspaceRoleSeatFactory({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db }),
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db: mainDb }),
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db: mainDb }),
|
||||
getWorkspaceDefaultSeatType: getWorkspaceDefaultSeatTypeFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db })
|
||||
getWorkspace: getWorkspaceFactory({ db: mainDb })
|
||||
}),
|
||||
eventEmit: emit
|
||||
}),
|
||||
assignWorkspaceSeat: assignWorkspaceSeatFactory({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db: mainDb }),
|
||||
getWorkspaceRoleForUser: getWorkspaceRoleForUserFactory({
|
||||
db
|
||||
db: mainDb
|
||||
}),
|
||||
eventEmit: emit,
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db })
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db: mainDb })
|
||||
})
|
||||
}),
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db }),
|
||||
getWorkspaceDomains: getWorkspaceDomainsFactory({ db }),
|
||||
getUserEmails: findEmailsByUserIdFactory({ db })
|
||||
})
|
||||
})
|
||||
|
||||
const updatedProject = await withOperationLogging(
|
||||
async () =>
|
||||
await moveProjectToWorkspace({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db: mainDb }),
|
||||
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db: mainDb }),
|
||||
getWorkspaceDomains: getWorkspaceDomainsFactory({ db: mainDb }),
|
||||
getUserEmails: findEmailsByUserIdFactory({ db: mainDb })
|
||||
})({
|
||||
projectId,
|
||||
workspaceId,
|
||||
movedByUserId: context.userId!
|
||||
}),
|
||||
{
|
||||
logger,
|
||||
operationName: 'moveProjectToWorkspace',
|
||||
operationDescription: 'Move project to workspace'
|
||||
name: 'moveProjectToWorkspace',
|
||||
description: 'Move project to workspace',
|
||||
dbs: await getProjectReplicationDbs({ projectId })
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user