🐛 (collaboration) Fix a database rule preventing collaborators to edit a bot

Also check if new user has invitations when signup is disabled

Closes #265
This commit is contained in:
Baptiste Arnaud
2023-01-20 09:30:25 +01:00
parent 4435fb0d7e
commit fe2952d407
10 changed files with 209 additions and 108 deletions

View File

@@ -0,0 +1,52 @@
import { Invitation, PrismaClient, WorkspaceRole } from 'db'
export type InvitationWithWorkspaceId = Invitation & {
typebot: {
workspaceId: string | null
}
}
export const convertInvitationsToCollaborations = async (
p: PrismaClient,
{ id, email }: { id: string; email: string },
invitations: InvitationWithWorkspaceId[]
) => {
await p.collaboratorsOnTypebots.createMany({
data: invitations.map((invitation) => ({
typebotId: invitation.typebotId,
type: invitation.type,
userId: id,
})),
})
const workspaceInvitations = invitations.reduce<InvitationWithWorkspaceId[]>(
(acc, invitation) =>
acc.some(
(inv) => inv.typebot.workspaceId === invitation.typebot.workspaceId
)
? acc
: [...acc, invitation],
[]
)
for (const invitation of workspaceInvitations) {
if (!invitation.typebot.workspaceId) continue
await p.memberInWorkspace.upsert({
where: {
userId_workspaceId: {
userId: id,
workspaceId: invitation.typebot.workspaceId,
},
},
create: {
userId: id,
workspaceId: invitation.typebot.workspaceId,
role: WorkspaceRole.GUEST,
},
update: {},
})
}
return p.invitation.deleteMany({
where: {
email,
},
})
}

View File

@@ -0,0 +1,22 @@
import { PrismaClient, WorkspaceInvitation } from 'db'
import { InvitationWithWorkspaceId } from './convertInvitationsToCollaborations'
export const getNewUserInvitations = async (
p: PrismaClient,
email: string
): Promise<{
invitations: InvitationWithWorkspaceId[]
workspaceInvitations: WorkspaceInvitation[]
}> => {
const [invitations, workspaceInvitations] = await p.$transaction([
p.invitation.findMany({
where: { email },
include: { typebot: { select: { workspaceId: true } } },
}),
p.workspaceInvitation.findMany({
where: { email },
}),
])
return { invitations, workspaceInvitations }
}

View File

@@ -1 +1,4 @@
export * from './convertInvitationsToCollaborations'
export * from './getAuthenticatedUser'
export * from './getNewUserInvitations'
export * from './joinWorkspaces'

View File

@@ -0,0 +1,20 @@
import { PrismaClient, WorkspaceInvitation } from 'db'
export const joinWorkspaces = async (
p: PrismaClient,
{ id, email }: { id: string; email: string },
invitations: WorkspaceInvitation[]
) => {
await p.memberInWorkspace.createMany({
data: invitations.map((invitation) => ({
workspaceId: invitation.workspaceId,
role: invitation.type,
userId: id,
})),
})
return p.workspaceInvitation.deleteMany({
where: {
email,
},
})
}

View File

@@ -64,7 +64,7 @@ test.describe('Typebot owner', () => {
})
})
test.describe('Guest', () => {
test.describe('Guest with read access', () => {
test('should have shared typebots displayed', async ({ page }) => {
const typebotId = cuid()
const guestWorkspaceId = cuid()
@@ -121,3 +121,58 @@ test.describe('Guest', () => {
await expect(page.locator('text="See logs" >> nth=9')).toBeVisible()
})
})
test.describe('Guest with write access', () => {
test('should have shared typebots displayed', async ({ page }) => {
const typebotId = cuid()
const guestWorkspaceId = cuid()
await prisma.workspace.create({
data: {
id: guestWorkspaceId,
name: 'Guest Workspace #3',
plan: Plan.FREE,
members: {
createMany: {
data: [{ role: WorkspaceRole.GUEST, userId }],
},
},
},
})
await createTypebots([
{
id: typebotId,
name: 'Guest typebot',
workspaceId: guestWorkspaceId,
...parseDefaultGroupWithBlock({
type: InputBlockType.TEXT,
options: defaultTextInputOptions,
}),
},
{
name: 'Another typebot',
workspaceId: guestWorkspaceId,
},
])
await prisma.collaboratorsOnTypebots.create({
data: {
typebotId,
userId,
type: CollaborationType.WRITE,
},
})
await createFolder(guestWorkspaceId, 'Guest folder')
await page.goto(`/typebots`)
await page.click('text=Pro workspace')
await page.click('text=Guest workspace #3')
await expect(page.locator('text=Guest typebot')).toBeVisible()
await expect(page.locator('text=Another typebot')).toBeHidden()
await expect(page.locator('text=Guest folder')).toBeHidden()
await page.click('text=Guest typebot')
await page.click('button[aria-label="Show collaboration menu"]')
await page.click('text=Everyone at Guest workspace')
await expect(page.locator('text="Remove"')).toBeHidden()
await expect(page.locator('text=John Doe')).toBeVisible()
await page.click('text=Group #1', { force: true })
await expect(page.locator('input[value="Group #1"]')).toBeVisible()
})
})

View File

@@ -41,7 +41,7 @@ export const EditTypebotPage = () => {
>
{typebot ? (
<GraphDndProvider>
<BlocksSideBar />
{!isReadOnly && <BlocksSideBar />}
<GraphProvider isReadOnly={isReadOnly}>
<GroupsCoordinatesProvider groups={typebot.groups}>
<Graph flex="1" typebot={typebot} />