Files
speckle-server/packages/server/modules/workspaces/graph/resolvers/regions.ts
T
Daniel Gak Anagrov 2c122a138d feat(workspaces): apply prepared transactions to workspaces (#5383)
* 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

* feat: workspace replciation

* fix: mr comments

* feat: removed test

* fix: worksapce test creation

* fix: mr issues

* updated mutations

* feat: drop workspace random defaults

---------

Co-authored-by: Charles Driesler <chuck@speckle.systems>
2025-09-11 10:08:26 +02:00

136 lines
4.3 KiB
TypeScript

import { db } from '@/db/knex'
import type { Resolvers } from '@/modules/core/graph/generated/graphql'
import { getWorkspacePlanFactory } from '@/modules/gatekeeper/repositories/billing'
import { canWorkspaceUseRegionsFactory } from '@/modules/gatekeeper/services/featureAuthorization'
import { getRegionsFactory } from '@/modules/multiregion/repositories'
import { authorizeResolver } from '@/modules/shared'
import {
getDefaultRegionFactory,
upsertRegionAssignmentFactory
} from '@/modules/workspaces/repositories/regions'
import { getWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces'
import {
assignWorkspaceRegionFactory,
getAvailableRegionsFactory
} from '@/modules/workspaces/services/regions'
import { Roles } from '@speckle/shared'
import { WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace'
import { scheduleJob } from '@/modules/multiregion/services/queue'
import { getExplicitProjects } from '@/modules/core/repositories/streams'
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
import { withOperationLogging } from '@/observability/domain/businessLogging'
import { queryAllProjectsFactory } from '@/modules/core/services/projects'
const { FF_MOVE_PROJECT_REGION_ENABLED } = getFeatureFlags()
export default {
Workspace: {
defaultRegion: async (parent) => {
const getDefaultRegion = getDefaultRegionFactory({ db })
return await getDefaultRegion({ workspaceId: parent.id })
}
},
WorkspaceMutations: {
setDefaultRegion: async (_parent, args, ctx) => {
const workspaceId = args.workspaceId
const regionKey = args.regionKey
await authorizeResolver(
ctx.userId,
workspaceId,
Roles.Workspace.Admin,
ctx.resourceAccessRules
)
const logger = ctx.log.child({
workspaceId,
regionKey
})
const assignRegion = assignWorkspaceRegionFactory({
getAvailableRegions: getAvailableRegionsFactory({
getRegions: getRegionsFactory({ db }),
canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({
getWorkspacePlan: getWorkspacePlanFactory({ db })
})
}),
upsertRegionAssignment: upsertRegionAssignmentFactory({ db }),
getDefaultRegion: getDefaultRegionFactory({ db }),
getWorkspace: getWorkspaceFactory({ db })
})
await withOperationLogging(
async () => await assignRegion({ workspaceId, regionKey }),
{
logger,
operationName: 'assignWorkspaceRegion',
operationDescription: 'Assign a region to a workspace'
}
)
// Move existing workspace projects to new target region
if (FF_MOVE_PROJECT_REGION_ENABLED) {
const queryAllProjects = queryAllProjectsFactory({
getExplicitProjects: getExplicitProjects({ db })
})
for await (const projects of queryAllProjects({
workspaceId
})) {
await Promise.all(
projects.map(async (project) => {
await scheduleJob({
type: 'move-project-region',
payload: {
projectId: project.id,
regionKey
}
})
})
)
}
}
return await ctx.loaders.workspaces!.getWorkspace.load(args.workspaceId)
}
},
WorkspaceProjectMutations: {
moveToRegion: async (_parent, args, context) => {
if (!FF_MOVE_PROJECT_REGION_ENABLED) {
throw new WorkspacesNotYetImplementedError()
}
const projectId = args.projectId
const regionKey = args.regionKey
await authorizeResolver(
context.userId,
projectId,
Roles.Stream.Owner,
context.resourceAccessRules
)
const logger = context.log.child({
projectId,
streamId: projectId, //legacy
regionKey
})
return await withOperationLogging(
async () => {
return await scheduleJob({
type: 'move-project-region',
payload: {
projectId,
regionKey
}
})
},
{
logger,
operationName: 'workspaceProjectMoveToRegion',
operationDescription: 'Move a workspace project to a different region'
}
)
}
}
} as Resolvers