f210d9b749
* feat(projects): add project regions, default to null * feat(multiregion): add projectRegion Db client lookup logic * feat(multiregion): add project region repositories and caching * feat(multiRegion): db initialization and get project db client * feat(docker-compose): add second db for regions testing * feat(multiRegion): initialize region with pubs and subs working * fix(multiRegion): get region client even if it was registered in another pod * feat(workspaces): create workspace resolver split * feat: update server region metadata * feat(projects): rewrite project creation * feat(multiRegion): getRegionDb * fix(workspaces): get projects now can retur null * feat(multiRegion): make local multi region DB-s work * feat: set d efault workspace region * CR changes * tests * feat(multiRegion): bind region properly * fe update * test fixes * feat(multiRegion): automatically create aiven extras plugin * ci(postgres): use published postgres with aiven extras * fix(multiRegion): roll back the aiven extras migration, there is a better way * tests fix * fix(billing): we do not need to add a seat, if the workspace is on a plan, but has no sub --------- Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { generateProjectName } from '@/modules/core/domain/projects/logic'
|
|
import {
|
|
CreateProject,
|
|
DeleteProject,
|
|
GetProject,
|
|
ProjectVisibility,
|
|
StoreModel,
|
|
StoreProject,
|
|
StoreProjectRole
|
|
} from '@/modules/core/domain/projects/operations'
|
|
import { Project } from '@/modules/core/domain/streams/types'
|
|
import { RegionalProjectCreationError } from '@/modules/core/errors/projects'
|
|
import { StreamNotFoundError } from '@/modules/core/errors/stream'
|
|
import {
|
|
ProjectEvents,
|
|
ProjectsEventsEmitter
|
|
} from '@/modules/core/events/projectsEmitter'
|
|
import { retry } from '@lifeomic/attempt'
|
|
import { Roles } from '@speckle/shared'
|
|
import cryptoRandomString from 'crypto-random-string'
|
|
|
|
export const createNewProjectFactory =
|
|
({
|
|
storeProject,
|
|
getProject,
|
|
deleteProject,
|
|
storeProjectRole,
|
|
storeModel,
|
|
projectsEventsEmitter
|
|
}: {
|
|
storeProject: StoreProject
|
|
getProject: GetProject
|
|
deleteProject: DeleteProject
|
|
storeProjectRole: StoreProjectRole
|
|
projectsEventsEmitter: ProjectsEventsEmitter
|
|
storeModel: StoreModel
|
|
}): CreateProject =>
|
|
async ({ description, name, regionKey, visibility, workspaceId, ownerId }) => {
|
|
const publicVisibilities: ProjectVisibility[] = ['PUBLIC', 'UNLISTED']
|
|
const isPublic = !visibility || publicVisibilities.includes(visibility)
|
|
const isDiscoverable = visibility === 'PUBLIC'
|
|
const project: Project = {
|
|
id: cryptoRandomString({ length: 10 }),
|
|
name: name || generateProjectName(),
|
|
description: description || '',
|
|
isPublic,
|
|
isDiscoverable,
|
|
createdAt: new Date(),
|
|
clonedFrom: null,
|
|
updatedAt: new Date(),
|
|
workspaceId: workspaceId || null,
|
|
regionKey: regionKey || null,
|
|
allowPublicComments: false
|
|
}
|
|
|
|
await storeProject({ project })
|
|
const projectId = project.id
|
|
// if regionKey, we need to make sure it is actually written and synced
|
|
if (regionKey) {
|
|
try {
|
|
await retry(
|
|
async () => {
|
|
await getProject({ projectId })
|
|
},
|
|
{ maxAttempts: 10 }
|
|
)
|
|
} catch (err) {
|
|
if (err instanceof StreamNotFoundError) {
|
|
// delete from region
|
|
await deleteProject({ projectId })
|
|
throw new RegionalProjectCreationError()
|
|
}
|
|
// else throw as is
|
|
throw err
|
|
}
|
|
}
|
|
await storeProjectRole({ projectId, userId: ownerId, role: Roles.Stream.Owner })
|
|
await storeModel({
|
|
name: 'main',
|
|
description: 'default model',
|
|
projectId,
|
|
authorId: ownerId
|
|
})
|
|
await projectsEventsEmitter(ProjectEvents.Created, { project, ownerId })
|
|
return project
|
|
}
|