Feat: Create project via org slug instead of org ID

This commit is contained in:
Daniel Hougaard
2024-03-17 18:48:09 +01:00
parent ce057f44ac
commit 12a6fba645
3 changed files with 34 additions and 8 deletions
@@ -160,7 +160,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const project = await server.services.project.createProject({ const project = await server.services.project.createProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
orgId: req.body.organizationId, orgSlug: req.body.organizationSlug,
workspaceName: req.body.projectName, workspaceName: req.body.projectName,
slug: req.body.slug slug: req.body.slug
}); });
@@ -18,6 +18,7 @@ import { ActorType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityProjectDALFactory } from "../identity-project/identity-project-dal"; import { TIdentityProjectDALFactory } from "../identity-project/identity-project-dal";
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal"; import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";
import { TOrgDALFactory } from "../org/org-dal";
import { TOrgServiceFactory } from "../org/org-service"; import { TOrgServiceFactory } from "../org/org-service";
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal"; import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
@@ -63,6 +64,7 @@ type TProjectServiceFactoryDep = {
permissionService: TPermissionServiceFactory; permissionService: TPermissionServiceFactory;
orgService: Pick<TOrgServiceFactory, "addGhostUser">; orgService: Pick<TOrgServiceFactory, "addGhostUser">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgDAL: Pick<TOrgDALFactory, "findOne">;
}; };
export type TProjectServiceFactory = ReturnType<typeof projectServiceFactory>; export type TProjectServiceFactory = ReturnType<typeof projectServiceFactory>;
@@ -72,6 +74,7 @@ export const projectServiceFactory = ({
projectQueue, projectQueue,
projectKeyDAL, projectKeyDAL,
permissionService, permissionService,
orgDAL,
userDAL, userDAL,
folderDAL, folderDAL,
orgService, orgService,
@@ -88,11 +91,33 @@ export const projectServiceFactory = ({
/* /*
* Create workspace. Make user the admin * Create workspace. Make user the admin
* */ * */
const createProject = async ({ orgId, actor, actorId, actorOrgId, workspaceName, slug }: TCreateProjectDTO) => { const createProject = async ({
orgId,
orgSlug,
actor,
actorId,
actorOrgId,
workspaceName,
slug
}: TCreateProjectDTO) => {
if (orgSlug && orgId) {
throw new BadRequestError({
message: "Cannot provide both orgId and orgSlug"
});
}
if (!orgSlug && !orgId) {
throw new BadRequestError({
message: "Must provide either orgId or orgSlug"
});
}
const organization = orgSlug ? await orgDAL.findOne({ slug: orgSlug }) : await orgDAL.findOne({ id: orgId });
const { permission, membership: orgMembership } = await permissionService.getOrgPermission( const { permission, membership: orgMembership } = await permissionService.getOrgPermission(
actor, actor,
actorId, actorId,
orgId, organization.id,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
@@ -100,7 +125,7 @@ export const projectServiceFactory = ({
const appCfg = getConfig(); const appCfg = getConfig();
const blindIndex = createSecretBlindIndex(appCfg.ROOT_ENCRYPTION_KEY, appCfg.ENCRYPTION_KEY); const blindIndex = createSecretBlindIndex(appCfg.ROOT_ENCRYPTION_KEY, appCfg.ENCRYPTION_KEY);
const plan = await licenseService.getPlan(orgId); const plan = await licenseService.getPlan(organization.id);
if (plan.workspaceLimit !== null && plan.workspacesUsed >= plan.workspaceLimit) { if (plan.workspaceLimit !== null && plan.workspacesUsed >= plan.workspaceLimit) {
// case: limit imposed on number of workspaces allowed // case: limit imposed on number of workspaces allowed
// case: number of workspaces used exceeds the number of workspaces allowed // case: number of workspaces used exceeds the number of workspaces allowed
@@ -110,12 +135,12 @@ export const projectServiceFactory = ({
} }
const results = await projectDAL.transaction(async (tx) => { const results = await projectDAL.transaction(async (tx) => {
const ghostUser = await orgService.addGhostUser(orgId, tx); const ghostUser = await orgService.addGhostUser(organization.id, tx);
const project = await projectDAL.create( const project = await projectDAL.create(
{ {
name: workspaceName, name: workspaceName,
orgId, orgId: organization.id,
slug: slug || slugify(`${workspaceName}-${alphaNumericNanoId(4)}`), slug: slug || slugify(`${workspaceName}-${alphaNumericNanoId(4)}`),
version: ProjectVersion.V2 version: ProjectVersion.V2
}, },
@@ -272,7 +297,7 @@ export const projectServiceFactory = ({
// Get the role permission for the identity // Get the role permission for the identity
const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole( const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole(
ProjectMembershipRole.Admin, ProjectMembershipRole.Admin,
orgId organization.id
); );
const hasPrivilege = isAtLeastAsPrivileged(permission, rolePermission); const hasPrivilege = isAtLeastAsPrivileged(permission, rolePermission);
@@ -23,7 +23,8 @@ export type TCreateProjectDTO = {
actor: ActorType; actor: ActorType;
actorId: string; actorId: string;
actorOrgId?: string; actorOrgId?: string;
orgId: string; orgId?: string;
orgSlug?: string;
workspaceName: string; workspaceName: string;
slug?: string; slug?: string;
}; };