Files
Daniel Gak Anagrov 399c998fd7 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>
2025-09-04 13:07:19 +02:00

126 lines
3.5 KiB
TypeScript

import { ProjectEvents } from '@/modules/core/domain/projects/events'
import { generateProjectName } from '@/modules/core/domain/projects/logic'
import type {
CreateProject,
DeleteProject,
DeleteProjectAndCommits,
QueryAllProjects,
StoreProject,
StoreProjectRole
} from '@/modules/core/domain/projects/operations'
import type {
Project,
StreamWithOptionalRole
} from '@/modules/core/domain/streams/types'
import { ProjectQueryError } from '@/modules/core/errors/projects'
import { ProjectVisibility } from '@/modules/core/graph/generated/graphql'
import { mapGqlToDbProjectVisibility } from '@/modules/core/helpers/project'
import type { EventBusEmit } from '@/modules/shared/services/eventBus'
import { Roles } from '@speckle/shared'
import cryptoRandomString from 'crypto-random-string'
import type { GetExplicitProjects } from '@/modules/core/domain/streams/operations'
import type { DeleteProjectCommits } from '@/modules/core/domain/commits/operations'
export const createNewProjectFactory =
({
storeProject,
storeProjectRole,
emitEvent
}: {
storeProject: StoreProject
storeProjectRole: StoreProjectRole
emitEvent: EventBusEmit
}): CreateProject =>
async ({ description, name, regionKey, visibility, workspaceId, ownerId }) => {
visibility =
visibility ||
(workspaceId ? ProjectVisibility.Workspace : ProjectVisibility.Private)
const project: Project = {
id: cryptoRandomString({ length: 10 }),
name: name || generateProjectName(),
description: description || '',
visibility: mapGqlToDbProjectVisibility(visibility),
createdAt: new Date(),
clonedFrom: null,
updatedAt: new Date(),
workspaceId: workspaceId || null,
regionKey: regionKey || null,
allowPublicComments: false
}
await storeProject({ project })
const projectId = project.id
await storeProjectRole({ projectId, userId: ownerId, role: Roles.Stream.Owner })
await emitEvent({
eventName: ProjectEvents.Created,
payload: {
project,
ownerId,
input: {
description: project.description,
name: project.name,
visibility
}
}
})
await emitEvent({
eventName: ProjectEvents.PermissionsAdded,
payload: {
project,
activityUserId: ownerId,
targetUserId: ownerId,
role: Roles.Stream.Owner,
previousRole: null
}
})
return project
}
export const queryAllProjectsFactory = ({
getExplicitProjects
}: {
getExplicitProjects: GetExplicitProjects
}): QueryAllProjects =>
async function* queryAllWorkspaceProjects({
userId,
workspaceId
}): AsyncGenerator<StreamWithOptionalRole[], void, unknown> {
let currentCursor: string | null = null
let iterationCount = 0
if (!userId && !workspaceId)
throw new ProjectQueryError('No user or workspace ID provided')
do {
if (iterationCount > 500) throw new ProjectQueryError('Too many iterations')
const { items, cursor } = await getExplicitProjects({
cursor: currentCursor,
limit: 100,
filter: {
workspaceId,
userId
}
})
yield items
currentCursor = cursor
iterationCount++
} while (!!currentCursor)
}
export const deleteProjectAndCommitsFactory =
(deps: {
deleteProject: DeleteProject
deleteProjectCommits: DeleteProjectCommits
}): DeleteProjectAndCommits =>
async (project) => {
await deps.deleteProjectCommits(project)
await deps.deleteProject(project)
}