Skip to content

Commit

Permalink
fix: team invite signup not working (#13717)
Browse files Browse the repository at this point in the history
* Fix team invite signup not working

* Add unit test

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
  • Loading branch information
2 people authored and keithwillcode committed Feb 16, 2024
1 parent 4360c36 commit 2f1316a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 6 deletions.
92 changes: 92 additions & 0 deletions packages/lib/server/username.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import prismaMock from "../../../tests/libs/__mocks__/prismaMock";

import { describe, expect, it, beforeEach } from "vitest";

import { usernameCheckForSignup } from "./username";

describe("usernameCheckForSignup ", async () => {
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
prismaMock.user.findUnique.mockImplementation(() => {
return null;
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
prismaMock.user.findMany.mockImplementation(() => {
return [];
});
});

it("should return available true for an email that doesn't exist", async () => {
const res = await usernameCheckForSignup({ username: "johnny", email: "johnny@example.com" });
expect(res).toEqual({
available: true,
premium: false,
suggestedUsername: "",
});
});

it("should return available false for an email that exists and a different username is provided", async () => {
mockUserInDB({
id: 1,
email: "john@example.com",
username: "john",
});
const res = await usernameCheckForSignup({ username: "johnny", email: "john@example.com" });
expect(res).toEqual({
available: false,
premium: false,
suggestedUsername: "johnny001",
});
});

it("should return available true for an email that exists but the user is signing up for an organization", async () => {
const userId = 1;
mockUserInDB({
id: userId,
email: "john@example.com",
username: "john",
});
mockMembership({ userId });
const res = await usernameCheckForSignup({ username: "john", email: "john@example.com" });
expect(res).toEqual({
available: true,
// An organization can't have premium username
premium: false,
suggestedUsername: "",
});
});
});

function mockUserInDB({ id, email, username }: { id: number; email: string; username: string }) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
prismaMock.user.findUnique.mockImplementation((arg) => {
if (arg.where.email === email) {
return {
id,
email,
username,
};
}
return null;
});
}

function mockMembership({ userId }: { userId: number }) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
prismaMock.membership.findFirst.mockImplementation((arg) => {
const isOrganizationWhereClause =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
arg?.where?.team?.metadata?.path[0] === "isOrganization" && arg?.where?.team?.metadata?.equals === true;
if (arg?.where?.userId === userId && isOrganizationWhereClause) {
return {
userId,
teamId: 1,
};
}
});
}
6 changes: 4 additions & 2 deletions packages/lib/server/username.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ const usernameCheckForSignup = async ({

const username = slugify(usernameRaw);

const user = await prisma.user.findFirst({
const user = await prisma.user.findUnique({
where: {
email,
},
Expand Down Expand Up @@ -207,7 +207,9 @@ const usernameCheckForSignup = async ({
// The only way to differentiate b/w 'a new email that was invited to an Org' and 'a user that was created using regular signup' is to check if the user is a member of an org.
// If username is in global namespace
if (!userIsAMemberOfAnOrg) {
response.available = false;
const isClaimingAlreadySetUsername = user.username === username;
const isClaimingUnsetUsername = !user.username;
response.available = isClaimingUnsetUsername || isClaimingAlreadySetUsername;
// There are no premium users outside an organization only
response.premium = await isPremiumUserName(username);
}
Expand Down
15 changes: 11 additions & 4 deletions packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,22 +227,29 @@ export async function createNewUsersConnectToOrgIfExists({
}) {
// fail if we have invalid emails
usernamesOrEmails.forEach((usernameOrEmail) => checkInputEmailIsValid(usernameOrEmail));

// from this point we know usernamesOrEmails contains only emails
await prisma.$transaction(
async (tx) => {
for (let index = 0; index < usernamesOrEmails.length; index++) {
const usernameOrEmail = usernamesOrEmails[index];
// Weird but orgId is defined only if the invited user email matches orgAutoAcceptEmail
const { orgId, autoAccept } = connectionInfoMap[usernameOrEmail];
const [emailUser, emailDomain] = usernameOrEmail.split("@");
const username =

// An org member can't change username during signup, so we set the username
const orgMemberUsername =
emailDomain === autoAcceptEmailDomain
? slugify(emailUser)
: slugify(`${emailUser}-${emailDomain.split(".")[0]}`);

// As a regular team member is allowed to change username during signup, we don't set any username for him
const regularTeamMemberUsername = null;

const isBecomingAnOrgMember = parentId || input.isOrg;

const createdUser = await tx.user.create({
data: {
username,
username: isBecomingAnOrgMember ? orgMemberUsername : regularTeamMemberUsername,
email: usernameOrEmail,
verified: true,
invitedTo: input.teamId,
Expand All @@ -254,7 +261,7 @@ export async function createNewUsersConnectToOrgIfExists({
data: [
{
uid: ProfileRepository.generateProfileUid(),
username,
username: orgMemberUsername,
organizationId: orgId,
},
],
Expand Down

0 comments on commit 2f1316a

Please sign in to comment.