diff --git a/apps/web/lib/orgMigration.test.ts b/apps/web/lib/orgMigration.test.ts
index 080ac4131d..a107a8012a 100644
--- a/apps/web/lib/orgMigration.test.ts
+++ b/apps/web/lib/orgMigration.test.ts
@@ -696,7 +696,7 @@ describe("orgMigration", () => {
});
describe("when user email matches orgAutoAcceptEmail", () => {
- const orgMetadata = {
+ const orgSettings = {
orgAutoAcceptEmail: "org1.com",
} as const;
@@ -725,8 +725,10 @@ describe("orgMigration", () => {
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- metadata: {
- ...orgMetadata,
+ organizationSettings: {
+ create: {
+ ...orgSettings,
+ },
},
});
@@ -784,8 +786,10 @@ describe("orgMigration", () => {
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- metadata: {
- ...orgMetadata,
+ organizationSettings: {
+ create: {
+ ...orgSettings,
+ },
},
});
@@ -837,7 +841,11 @@ describe("orgMigration", () => {
name: data.targetOrg.name,
metadata: {
requestedSlug: data.targetOrg.requestedSlug,
- ...orgMetadata,
+ },
+ organizationSettings: {
+ create: {
+ ...orgSettings,
+ },
},
});
@@ -901,9 +909,7 @@ describe("orgMigration", () => {
id: data.targetOrg.id,
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- metadata: {
- isOrganization: true,
- },
+ isOrganization: true,
},
});
@@ -1564,9 +1570,9 @@ async function createOrg(
return await prismock.team.create({
data: {
...data,
+ isOrganization: true,
metadata: {
...(data.metadata || {}),
- isOrganization: true,
},
},
});
diff --git a/apps/web/lib/orgMigration.ts b/apps/web/lib/orgMigration.ts
index c09304c315..ef1921d840 100644
--- a/apps/web/lib/orgMigration.ts
+++ b/apps/web/lib/orgMigration.ts
@@ -1,5 +1,6 @@
import { getOrgUsernameFromEmail } from "@calcom/features/auth/signup/utils/getOrgUsernameFromEmail";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
+import { isOrganization } from "@calcom/lib/entityPermissionUtils";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
@@ -46,7 +47,7 @@ export async function moveUserToOrg({
const teamMetadata = teamMetadataSchema.parse(team?.metadata);
- if (!teamMetadata?.isOrganization) {
+ if (!isOrganization({ team })) {
throw new Error(`Team with ID:${targetOrgId} is not an Org`);
}
@@ -60,7 +61,7 @@ export async function moveUserToOrg({
if (!targetOrgUsername) {
targetOrgUsername = getOrgUsernameFromEmail(
userToMoveToOrg.email,
- targetOrganization.metadata.orgAutoAcceptEmail || ""
+ team.organizationSettings?.orgAutoAcceptEmail || ""
);
}
@@ -195,7 +196,7 @@ export async function moveTeamToOrg({
const teamMetadata = teamMetadataSchema.parse(possibleOrg?.metadata);
- if (!teamMetadata?.isOrganization) {
+ if (!isOrganization({ team: possibleOrg })) {
throw new Error(`${targetOrg.id} is not an Org`);
}
@@ -204,7 +205,7 @@ export async function moveTeamToOrg({
await addTeamRedirect({
oldTeamSlug,
teamSlug: updatedTeam.slug,
- orgSlug: targetOrganization.slug || orgMetadata.requestedSlug || null,
+ orgSlug: targetOrganization.slug || orgMetadata?.requestedSlug || null,
});
await setOrgSlugIfNotSet({ slug: targetOrganization.slug }, orgMetadata, targetOrg.id);
if (moveMembers) {
@@ -328,13 +329,14 @@ async function setOrgSlugIfNotSet(
},
orgMetadata: {
requestedSlug?: string | undefined;
- },
+ } | null,
targetOrgId: number
) {
if (targetOrganization.slug) {
return;
}
- if (!orgMetadata.requestedSlug) {
+
+ if (!orgMetadata?.requestedSlug) {
throw new HttpError({
statusCode: 400,
message: `Org with id: ${targetOrgId} doesn't have a slug. Tried using requestedSlug but that's also not present. So, all migration done but failed to set the Organization slug. Please set it manually`,
@@ -371,6 +373,9 @@ async function getTeamOrThrowError(targetOrgId: number) {
where: {
id: targetOrgId,
},
+ include: {
+ organizationSettings: true,
+ },
});
if (!team) {
@@ -620,6 +625,7 @@ async function moveTeamsWithoutMembersToOrg({
id: true,
slug: true,
metadata: true,
+ isOrganization: true,
},
});
@@ -631,7 +637,7 @@ async function moveTeamsWithoutMembersToOrg({
};
})
// Remove Orgs from the list
- .filter((team) => !team.metadata?.isOrganization);
+ .filter((team) => !isOrganization({ team }));
const teamIdsToBeMovedToOrg = teamsToBeMovedToOrg.map((t) => t.id);
@@ -784,6 +790,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use
id: true,
slug: true,
metadata: true,
+ isOrganization: true,
},
});
@@ -795,7 +802,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use
};
})
// Remove Orgs from the list
- .filter((team) => !team.metadata?.isOrganization);
+ .filter((team) => !isOrganization({ team }));
const teamIdsToBeRemovedFromOrg = teamsToBeRemovedFromOrg.map((t) => t.id);
diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx
index 1230b038ac..1c671b6bab 100644
--- a/apps/web/pages/signup.tsx
+++ b/apps/web/pages/signup.tsx
@@ -609,14 +609,16 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
team: {
select: {
metadata: true,
+ isOrganization: true,
parentId: true,
parent: {
select: {
slug: true,
- metadata: true,
+ organizationSettings: true,
},
},
slug: true,
+ organizationSettings: true,
},
},
},
@@ -665,8 +667,8 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
};
const isATeamInOrganization = tokenTeam?.parentId !== null;
- const isOrganization = tokenTeam.metadata?.isOrganization;
// Detect if the team is an org by either the metadata flag or if it has a parent team
+ const isOrganization = tokenTeam.isOrganization;
const isOrganizationOrATeamInOrganization = isOrganization || isATeamInOrganization;
// If we are dealing with an org, the slug may come from the team itself or its parent
const orgSlug = isOrganizationOrATeamInOrganization
@@ -683,9 +685,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const isValidEmail = checkValidEmail(verificationToken.identifier);
const isOrgInviteByLink = isOrganizationOrATeamInOrganization && !isValidEmail;
- const parentMetaDataForSubteam = tokenTeam?.parent?.metadata
- ? teamMetadataSchema.parse(tokenTeam.parent.metadata)
- : null;
+ const parentOrgSettings = tokenTeam?.parent?.organizationSettings ?? null;
return {
props: {
@@ -698,15 +698,15 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
? getOrgUsernameFromEmail(
verificationToken.identifier,
(isOrganization
- ? tokenTeam.metadata?.orgAutoAcceptEmail
- : parentMetaDataForSubteam?.orgAutoAcceptEmail) || ""
+ ? tokenTeam.organizationSettings?.orgAutoAcceptEmail
+ : parentOrgSettings?.orgAutoAcceptEmail) || ""
)
: slugify(username),
}
: null,
orgSlug,
orgAutoAcceptEmail: isOrgInviteByLink
- ? tokenTeam?.metadata?.orgAutoAcceptEmail ?? parentMetaDataForSubteam?.orgAutoAcceptEmail ?? null
+ ? tokenTeam?.organizationSettings?.orgAutoAcceptEmail ?? parentOrgSettings?.orgAutoAcceptEmail ?? null
: null,
},
};
diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx
index 6b80836974..830acfb6ef 100644
--- a/apps/web/pages/team/[slug].tsx
+++ b/apps/web/pages/team/[slug].tsx
@@ -74,7 +74,7 @@ function TeamPage({
return (
@@ -178,7 +178,7 @@ function TeamPage({
title: markdownStrippedBio,
profile: {
name: `${team.name}`,
- image: `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`,
+ image: `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`,
},
}}
/>
@@ -190,7 +190,7 @@ function TeamPage({
imageSrc={
isValidOrgDomain
? `/org/${currentOrgDomain}/avatar.png`
- : `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`
+ : `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`
}
size="lg"
/>
@@ -208,7 +208,7 @@ function TeamPage({
>
)}
- {metadata?.isOrganization ? (
+ {team?.isOrganization ? (
) : (
<>
@@ -311,7 +311,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
// Taking care of sub-teams and orgs
if (
(!isValidOrgDomain && team?.parent) ||
- (!isValidOrgDomain && !!metadata?.isOrganization) ||
+ (!isValidOrgDomain && !!team?.isOrganization) ||
!isOrganizationFeatureEnabled
) {
return { notFound: true } as const;
diff --git a/apps/web/playwright/fixtures/orgs.ts b/apps/web/playwright/fixtures/orgs.ts
index 265fcd73a1..ea67350304 100644
--- a/apps/web/playwright/fixtures/orgs.ts
+++ b/apps/web/playwright/fixtures/orgs.ts
@@ -43,8 +43,8 @@ async function createOrgInDb({
data: {
name: name,
slug: slug,
+ isOrganization: true,
metadata: {
- isOrganization: true,
...(requestedSlug
? {
requestedSlug,
diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts
index 4d6788330b..cc7473c725 100644
--- a/apps/web/playwright/fixtures/users.ts
+++ b/apps/web/playwright/fixtures/users.ts
@@ -104,18 +104,21 @@ const createTeamAndAddUser = async (
const slug = `${isOrg ? "org" : "team"}-${workerInfo.workerIndex}-${Date.now()}`;
const data: PrismaType.TeamCreateInput = {
name: `user-id-${user.id}'s ${isOrg ? "Org" : "Team"}`,
+ isOrganization: isOrg,
};
data.metadata = {
...(isUnpublished ? { requestedSlug: slug } : {}),
- ...(isOrg
- ? {
- isOrganization: true,
- isOrganizationVerified: !!isOrgVerified,
- orgAutoAcceptEmail: user.email.split("@")[1],
- isOrganizationConfigured: false,
- }
- : {}),
};
+ if (isOrg) {
+ data.organizationSettings = {
+ create: {
+ isOrganizationVerified: !!isOrgVerified,
+ orgAutoAcceptEmail: user.email.split("@")[1],
+ isOrganizationConfigured: false,
+ },
+ };
+ }
+
data.slug = !isUnpublished ? slug : undefined;
if (isOrg && hasSubteam) {
const team = await createTeamAndAddUser({ user }, workerInfo);
@@ -501,7 +504,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
},
};
})
- .find((membership) => !membership.team?.metadata?.isOrganization);
+ .find((membership) => !membership.team.isOrganization);
if (!membership) {
throw new Error("No team found for user");
}
@@ -512,10 +515,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
where: {
userId: user.id,
team: {
- metadata: {
- path: ["isOrganization"],
- equals: true,
- },
+ isOrganization: true,
},
},
include: { team: { include: { children: true } } },
diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts
index 0cf2530174..113bb510b8 100644
--- a/packages/features/auth/lib/next-auth-options.ts
+++ b/packages/features/auth/lib/next-auth-options.ts
@@ -61,20 +61,10 @@ const checkIfUserShouldBelongToOrg = async (idP: IdentityProvider, email: string
if (!ORGANIZATIONS_AUTOLINK || idP !== "GOOGLE") return { orgUsername, orgId: undefined };
const existingOrg = await prisma.team.findFirst({
where: {
- AND: [
- {
- metadata: {
- path: ["isOrganizationVerified"],
- equals: true,
- },
- },
- {
- metadata: {
- path: ["orgAutoAcceptEmail"],
- equals: apexDomain,
- },
- },
- ],
+ organizationSettings: {
+ isOrganizationVerified: true,
+ orgAutoAcceptEmail: apexDomain,
+ },
},
select: {
id: true,
diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts
index 47e86e779a..d95f1d5352 100644
--- a/packages/features/auth/signup/handlers/calcomHandler.ts
+++ b/packages/features/auth/signup/handlers/calcomHandler.ts
@@ -14,7 +14,7 @@ import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager";
import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUsername";
import { prisma } from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
-import { signupSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils";
+import { signupSchema } from "@calcom/prisma/zod-utils";
import { joinAnyChildTeamOnOrgInvite } from "../utils/organization";
import {
@@ -129,8 +129,6 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
},
});
if (team) {
- const teamMetadata = teamMetadataSchema.parse(team?.metadata);
-
const user = await prisma.user.upsert({
where: { email },
update: {
@@ -149,7 +147,6 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
// Wrapping in a transaction as if one fails we want to rollback the whole thing to preventa any data inconsistencies
const { membership } = await createOrUpdateMemberships({
- teamMetadata,
user,
team,
});
diff --git a/packages/features/auth/signup/handlers/selfHostedHandler.ts b/packages/features/auth/signup/handlers/selfHostedHandler.ts
index 60e1111432..3ccb2fc498 100644
--- a/packages/features/auth/signup/handlers/selfHostedHandler.ts
+++ b/packages/features/auth/signup/handlers/selfHostedHandler.ts
@@ -12,7 +12,6 @@ import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUse
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
import { signupSchema } from "@calcom/prisma/zod-utils";
-import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { joinAnyChildTeamOnOrgInvite } from "../utils/organization";
import {
@@ -69,8 +68,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
if (team) {
- const teamMetadata = teamMetadataSchema.parse(team?.metadata);
-
const user = await prisma.user.upsert({
where: { email: userEmail },
update: {
@@ -88,7 +85,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
const { membership } = await createOrUpdateMemberships({
- teamMetadata,
user,
team,
});
diff --git a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
index 593fb50ebd..2380da13a6 100644
--- a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
+++ b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
@@ -1,21 +1,16 @@
-import type z from "zod";
-
import { prisma } from "@calcom/prisma";
import type { Team, User } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
-import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
export const createOrUpdateMemberships = async ({
- teamMetadata,
user,
team,
}: {
user: Pick;
- team: Pick;
- teamMetadata: z.infer;
+ team: Pick;
}) => {
return await prisma.$transaction(async (tx) => {
- if (teamMetadata?.isOrganization) {
+ if (team.isOrganization) {
await tx.user.update({
where: {
id: user.id,
diff --git a/packages/features/ee/organizations/api/subteams.ts b/packages/features/ee/organizations/api/subteams.ts
index 352b922bb4..ad935527c2 100644
--- a/packages/features/ee/organizations/api/subteams.ts
+++ b/packages/features/ee/organizations/api/subteams.ts
@@ -4,7 +4,6 @@ import z from "zod";
import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
-import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
const querySchema = z.object({
org: z.string({ required_error: "org slug is required" }),
@@ -20,13 +19,16 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
} = parsedQuery;
if (!slug) return res.status(400).json({ message: "Org is needed" });
- const org = await prisma.team.findFirst({ where: { slug }, select: { children: true, metadata: true } });
+ const org = await prisma.team.findFirst({
+ where: { slug },
+ select: { children: true, isOrganization: true },
+ });
if (!org) return res.status(400).json({ message: "Org doesn't exist" });
- const metadata = teamMetadataSchema.parse(org?.metadata);
+ const isOrganization = org.isOrganization;
- if (!metadata?.isOrganization) return res.status(400).json({ message: "Team is not an org" });
+ if (!isOrganization) return res.status(400).json({ message: "Team is not an org" });
return res.status(200).json({ slugs: org.children.map((ch) => ch.slug) });
}
diff --git a/packages/features/ee/organizations/lib/orgDomains.ts b/packages/features/ee/organizations/lib/orgDomains.ts
index ebb0c9d73e..6a2cbc1050 100644
--- a/packages/features/ee/organizations/lib/orgDomains.ts
+++ b/packages/features/ee/organizations/lib/orgDomains.ts
@@ -139,10 +139,7 @@ export function whereClauseForOrgWithSlugOrRequestedSlug(slug: string) {
},
},
],
- metadata: {
- path: ["isOrganization"],
- equals: true,
- },
+ isOrganization: true,
} satisfies Prisma.TeamWhereInput;
}
diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
index 620210633e..4dd43bb817 100644
--- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
@@ -6,7 +6,7 @@ import { z } from "zod";
import NoSSR from "@calcom/core/components/NoSSR";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
-import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
+import type { orgSettingsSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import { Button, Form, Meta, TextField, showToast } from "@calcom/ui";
@@ -43,7 +43,7 @@ const OrgEditView = ({ orgId }: { orgId: number }) => {
type FormValues = {
name: Team["name"];
slug: Team["slug"];
- metadata: z.infer;
+ organizationSettings: z.infer;
};
const OrgForm = ({
@@ -81,6 +81,10 @@ const OrgForm = ({
mutation.mutate({
id: org.id,
...values,
+ organizationSettings: {
+ ...org.organizationSettings,
+ orgAutoAcceptEmail: values.organizationSettings?.orgAutoAcceptEmail,
+ },
});
};
@@ -96,7 +100,7 @@ const OrgForm = ({
label="Domain for which invitations are auto-accepted"
placeholder="abc.com"
required
- {...form.register("metadata.orgAutoAcceptEmail")}
+ {...form.register("organizationSettings.orgAutoAcceptEmail")}
/>