Compare commits
86
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76bd90b228 | ||
|
|
d908403a99 | ||
|
|
0fe775962f | ||
|
|
412a6992c7 | ||
|
|
cfd1992733 | ||
|
|
e86d6169ba | ||
|
|
a379bd2b52 | ||
|
|
1137047606 | ||
|
|
ad4cf0cc3a | ||
|
|
e7d2e0dc2f | ||
|
|
aa51218428 | ||
|
|
38bc5fb71e | ||
|
|
411516ddb9 | ||
|
|
c6c17ac388 | ||
|
|
6838133865 | ||
|
|
4b2bb2271a | ||
|
|
20b7ac8c0d | ||
|
|
ca7c432728 | ||
|
|
3b966191e3 | ||
|
|
a7889984a0 | ||
|
|
3c72d7aaa0 | ||
|
|
ece0030978 | ||
|
|
a26a769119 | ||
|
|
3313de1eec | ||
|
|
7a6e16631b | ||
|
|
ce4d71ba2e | ||
|
|
300cd31efb | ||
|
|
f5bb734441 | ||
|
|
e33560ef82 | ||
|
|
84f34b2244 | ||
|
|
086bae03d3 | ||
|
|
4a0a429774 | ||
|
|
c4142ab4e7 | ||
|
|
95f8046db9 | ||
|
|
d8a87e9f04 | ||
|
|
86dcd6f0ff | ||
|
|
411a14dd3d | ||
|
|
03bc8db64a | ||
|
|
fe7fd8bc79 | ||
|
|
92eb41c117 | ||
|
|
a1fca56aac | ||
|
|
f3630399ee | ||
|
|
5375c656af | ||
|
|
1792d49634 | ||
|
|
de98578fdd | ||
|
|
c0a59a2837 | ||
|
|
29201006d5 | ||
|
|
7a1c780088 | ||
|
|
550f34c128 | ||
|
|
d6b17b76db | ||
|
|
c42e803cfa | ||
|
|
0233c3a11a | ||
|
|
71a2b6956f | ||
|
|
8282557ebe | ||
|
|
083a162d0e | ||
|
|
60487ea02e | ||
|
|
60a8841439 | ||
|
|
184d1eb751 | ||
|
|
9fea30a44d | ||
|
|
04efcbcdb6 | ||
|
|
2b29b5607d | ||
|
|
20f19c2509 | ||
|
|
5d0e66763e | ||
|
|
bcfbc24e93 | ||
|
|
37d805b007 | ||
|
|
6a246e6c57 | ||
|
|
3a0229f60d | ||
|
|
0bf95ec568 | ||
|
|
6f05142447 | ||
|
|
e02108d535 | ||
|
|
b7e668217d | ||
|
|
472ffef5cf | ||
|
|
5acdb208f5 | ||
|
|
7bf5d9cf64 | ||
|
|
34a80180ef | ||
|
|
3bbba9c6cb | ||
|
|
b052c2e029 | ||
|
|
4eb884e2a6 | ||
|
|
3f8ce8ff50 | ||
|
|
67a2371c05 | ||
|
|
80250d32bd | ||
|
|
0fadbe1ce9 | ||
|
|
9c3ee262b4 | ||
|
|
b62eba8128 | ||
|
|
ef114d7727 | ||
|
|
be7d127c9e |
+8
-8
@@ -103,19 +103,14 @@ NEXT_PUBLIC_INTERCOM_APP_ID=
|
|||||||
# Secret to enable Intercom Identity Verification
|
# Secret to enable Intercom Identity Verification
|
||||||
INTERCOM_SECRET=
|
INTERCOM_SECRET=
|
||||||
|
|
||||||
|
# Intercom api token for free users
|
||||||
|
INTERCOM_API_TOKEN=
|
||||||
|
|
||||||
# Posthog Config
|
# Posthog Config
|
||||||
NEXT_PUBLIC_POSTHOG_KEY=
|
NEXT_PUBLIC_POSTHOG_KEY=
|
||||||
|
|
||||||
NEXT_PUBLIC_POSTHOG_HOST=
|
NEXT_PUBLIC_POSTHOG_HOST=
|
||||||
|
|
||||||
# plain.com config
|
|
||||||
|
|
||||||
PLAIN_API_KEY=
|
|
||||||
PLAIN_API_URL=https://api.plain.com/v1
|
|
||||||
PLAIN_HMAC_SECRET_KEY=
|
|
||||||
NEXT_PUBLIC_PLAIN_CHAT_ID=
|
|
||||||
PLAIN_CHAT_HMAC_SECRET_KEY=
|
|
||||||
NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS=
|
|
||||||
|
|
||||||
|
|
||||||
# Dub Config
|
# Dub Config
|
||||||
@@ -386,6 +381,11 @@ UNKEY_ROOT_KEY=
|
|||||||
# Used for Cal.ai Voice AI Agents
|
# Used for Cal.ai Voice AI Agents
|
||||||
# https://retellai.com
|
# https://retellai.com
|
||||||
RETELL_AI_KEY=
|
RETELL_AI_KEY=
|
||||||
|
# Local testing overrides for Retell AI
|
||||||
|
RETELL_AI_TEST_MODE=false
|
||||||
|
# JSON mapping of local to production event type IDs (e.g. {"50": 747280} without any string)
|
||||||
|
RETELL_AI_TEST_EVENT_TYPE_MAP=
|
||||||
|
RETELL_AI_TEST_CAL_API_KEY=
|
||||||
|
|
||||||
# Used for buying phone number for cal ai voice agent
|
# Used for buying phone number for cal ai voice agent
|
||||||
STRIPE_PHONE_NUMBER_MONTHLY_PRICE_ID=
|
STRIPE_PHONE_NUMBER_MONTHLY_PRICE_ID=
|
||||||
|
|||||||
@@ -19,5 +19,17 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ["packages/app-store/**/*.{ts,tsx,js,jsx}"],
|
||||||
|
rules: {
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
paths: ["@calcom/trpc"],
|
||||||
|
patterns: ["@calcom/trpc/*"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
has-files-requiring-all-checks:
|
has-files-requiring-all-checks:
|
||||||
- "!(**.md|.github/CODEOWNERS|docs/**|help/**|apps/web/public/static/locales/**/common.json|i18n.lock)"
|
- "!(**.md|**.mdx|.github/CODEOWNERS|docs/**|help/**|apps/web/public/static/locales/**/common.json|i18n.lock)"
|
||||||
- name: Get Latest Commit SHA
|
- name: Get Latest Commit SHA
|
||||||
id: get_sha
|
id: get_sha
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
Vendored
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
undecided:
|
||||||
|
- calcom-monorepo
|
||||||
Vendored
@@ -43,7 +43,7 @@ export const bookingCancelSchema = z.object({
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
allRemainingBookings: z.boolean().optional(),
|
allRemainingBookings: z.boolean().optional(),
|
||||||
cancelSubsequentBookings: z.boolean().optional(),
|
cancelSubsequentBookings: z.boolean().optional(),
|
||||||
cancellationReason: z.string().optional().default("Not Provided"),
|
cancellationReason: z.string().min(1).optional(),
|
||||||
seatReferenceUid: z.string().optional(),
|
seatReferenceUid: z.string().optional(),
|
||||||
cancelledBy: z.string().email({ message: "Invalid email" }).optional(),
|
cancelledBy: z.string().email({ message: "Invalid email" }).optional(),
|
||||||
internalNote: z
|
internalNote: z
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type { NextApiRequest } from "next";
|
|||||||
import { OAuth2UniversalSchema } from "@calcom/app-store/_utils/oauth/universalSchema";
|
import { OAuth2UniversalSchema } from "@calcom/app-store/_utils/oauth/universalSchema";
|
||||||
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
||||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||||
import prisma from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
|
|
||||||
import { schemaCredentialPatchParams, schemaCredentialPatchBody } from "~/lib/validations/credential-sync";
|
import { schemaCredentialPatchParams, schemaCredentialPatchBody } from "~/lib/validations/credential-sync";
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ async function handler(req: NextApiRequest) {
|
|||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
key,
|
key: key as unknown as Prisma.InputJsonValue,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
|||||||
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||||
import prisma from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||||
|
|
||||||
import { schemaCredentialPostBody, schemaCredentialPostParams } from "~/lib/validations/credential-sync";
|
import { schemaCredentialPostBody, schemaCredentialPostParams } from "~/lib/validations/credential-sync";
|
||||||
@@ -92,7 +93,7 @@ async function handler(req: NextApiRequest) {
|
|||||||
data: {
|
data: {
|
||||||
userId,
|
userId,
|
||||||
appId: appSlug,
|
appId: appSlug,
|
||||||
key,
|
key: key as unknown as Prisma.InputJsonValue,
|
||||||
type: appMetadata.type,
|
type: appMetadata.type,
|
||||||
},
|
},
|
||||||
select: credentialForCalendarServiceSelect,
|
select: credentialForCalendarServiceSelect,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { NextApiRequest } from "next";
|
|||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { MembershipRole } from "@calcom/prisma/client";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { schemaEventTypeCreateBodyParams, schemaEventTypeReadPublic } from "~/lib/validations/event-type";
|
import { schemaEventTypeCreateBodyParams, schemaEventTypeReadPublic } from "~/lib/validations/event-type";
|
||||||
import { canUserAccessTeamWithRole } from "~/pages/api/teams/[teamId]/_auth-middleware";
|
import { canUserAccessTeamWithRole } from "~/pages/api/teams/[teamId]/_auth-middleware";
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe("POST /api/attendees", () => {
|
|||||||
|
|
||||||
const userBooking = { id: 1 };
|
const userBooking = { id: 1 };
|
||||||
|
|
||||||
prismaMock.booking.findFirst.mockResolvedValue(userBooking);
|
prismaMock.booking.findFirst.mockResolvedValue(userBooking as any);
|
||||||
|
|
||||||
const attendeeData = {
|
const attendeeData = {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ beforeEach(() => {
|
|||||||
id: userId,
|
id: userId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
});
|
} as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ beforeEach(() => {
|
|||||||
id: userId,
|
id: userId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
});
|
} as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ beforeEach(() => {
|
|||||||
id: userId,
|
id: userId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
});
|
} as any);
|
||||||
(getAccessibleUsers as any).mockResolvedValue([userId]);
|
(getAccessibleUsers as any).mockResolvedValue([userId]);
|
||||||
(retrieveOrgScopedAccessibleUsers as any).mockResolvedValue([userId]);
|
(retrieveOrgScopedAccessibleUsers as any).mockResolvedValue([userId]);
|
||||||
|
|
||||||
prismaMock.membership.findMany.mockResolvedValue([
|
prismaMock.membership.findMany.mockResolvedValue([
|
||||||
{
|
{
|
||||||
|
// @ts-expect-error Will be fixed by Prisma 6.7.0 upgrade mock changes - which uses vitest-mock-extended
|
||||||
team: {
|
team: {
|
||||||
id: 1,
|
id: 1,
|
||||||
isOrganization: true,
|
isOrganization: true,
|
||||||
@@ -48,7 +49,7 @@ beforeEach(() => {
|
|||||||
id: userId,
|
id: userId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
},
|
},
|
||||||
]);
|
] as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import { getEventTypesFromDB } from "@calcom/features/bookings/lib/handleNewBook
|
|||||||
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
|
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
|
||||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||||
import { buildBooking, buildEventType, buildWebhook, buildUser } from "@calcom/lib/test/builder";
|
import { buildBooking, buildEventType, buildWebhook, buildUser } from "@calcom/lib/test/builder";
|
||||||
import prisma from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
import type { Booking } from "@calcom/prisma/client";
|
import type { Booking } from "@calcom/prisma/client";
|
||||||
import { CreationSource } from "@calcom/prisma/enums";
|
import { CreationSource, BookingStatus } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import handler from "../../../pages/api/bookings/_post";
|
import handler from "../../../pages/api/bookings/_post";
|
||||||
|
|
||||||
@@ -303,6 +303,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -340,6 +341,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -376,6 +378,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ profileId: null }),
|
...buildEventType({ profileId: null }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -417,6 +420,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ profileId: null, length: 15 }),
|
...buildEventType({ profileId: null, length: 15 }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -468,7 +472,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.booking.findUnique.mockResolvedValue({
|
prismaMock.booking.findUnique.mockResolvedValue({
|
||||||
...originalBooking,
|
...originalBooking,
|
||||||
status: "cancelled",
|
status: "CANCELLED",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||||
@@ -492,6 +496,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ profileId: null, length: 15 }),
|
...buildEventType({ profileId: null, length: 15 }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -532,7 +537,7 @@ describe("POST /api/bookings", () => {
|
|||||||
const previousBooking = await prisma.booking.findUnique({
|
const previousBooking = await prisma.booking.findUnique({
|
||||||
where: { uid: "original-booking-uid" },
|
where: { uid: "original-booking-uid" },
|
||||||
});
|
});
|
||||||
expect(previousBooking?.status).toBe("cancelled");
|
expect(previousBooking?.status).toBe(BookingStatus.CANCELLED);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Creates source as api_v1", async () => {
|
test("Creates source as api_v1", async () => {
|
||||||
@@ -556,6 +561,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ profileId: null, length: 15 }),
|
...buildEventType({ profileId: null, length: 15 }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -627,6 +633,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ profileId: null, length: 15 }),
|
...buildEventType({ profileId: null, length: 15 }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
@@ -684,6 +691,7 @@ describe("POST /api/bookings", () => {
|
|||||||
|
|
||||||
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
|
||||||
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
...buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }),
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
profile: { organizationId: null },
|
profile: { organizationId: null },
|
||||||
hosts: [],
|
hosts: [],
|
||||||
users: [buildUser()],
|
users: [buildUser()],
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ describe("DELETE /api/event-types/[id]", () => {
|
|||||||
// Mocking team.findUnique
|
// Mocking team.findUnique
|
||||||
prismaMock.team.findUnique.mockResolvedValue({
|
prismaMock.team.findUnique.mockResolvedValue({
|
||||||
id: teamId,
|
id: teamId,
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
members: [
|
members: [
|
||||||
{ userId: memberUser, role: MembershipRole.MEMBER, teamId: teamId },
|
{ userId: memberUser, role: MembershipRole.MEMBER, teamId: teamId },
|
||||||
{ userId: adminUser, role: MembershipRole.ADMIN, teamId: teamId },
|
{ userId: adminUser, role: MembershipRole.ADMIN, teamId: teamId },
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ describe("GET /api/event-types/[id]", () => {
|
|||||||
|
|
||||||
prismaMock.team.findFirst.mockResolvedValue({
|
prismaMock.team.findFirst.mockResolvedValue({
|
||||||
id: teamId,
|
id: teamId,
|
||||||
|
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
|
||||||
members: [
|
members: [
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
"@axiomhq/winston": "^1.2.0",
|
"@axiomhq/winston": "^1.2.0",
|
||||||
"@calcom/platform-constants": "*",
|
"@calcom/platform-constants": "*",
|
||||||
"@calcom/platform-enums": "*",
|
"@calcom/platform-enums": "*",
|
||||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.335",
|
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.349",
|
||||||
"@calcom/platform-types": "*",
|
"@calcom/platform-types": "*",
|
||||||
"@calcom/platform-utils": "*",
|
"@calcom/platform-utils": "*",
|
||||||
"@calcom/prisma": "*",
|
"@calcom/prisma": "*",
|
||||||
@@ -55,9 +55,9 @@
|
|||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@nestjs/swagger": "^7.3.0",
|
"@nestjs/swagger": "^7.3.0",
|
||||||
"@nestjs/throttler": "6.2.1",
|
"@nestjs/throttler": "6.2.1",
|
||||||
"@sentry/nestjs": "^8.37.1",
|
"@sentry/nestjs": "^9.15.0",
|
||||||
"@sentry/node": "^8.8.0",
|
"@sentry/node": "^9.15.0",
|
||||||
"@sentry/profiling-node": "^8.37.1",
|
"@sentry/profiling-node": "^9.15.0",
|
||||||
"@snyk/protect": "latest",
|
"@snyk/protect": "latest",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"bull": "^4.12.4",
|
"bull": "^4.12.4",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { BookingsController_2024_04_15 } from "@/ee/bookings/2024-04-15/controllers/bookings.controller";
|
import { BookingsController_2024_04_15 } from "@/ee/bookings/2024-04-15/controllers/bookings.controller";
|
||||||
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
|
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
|
||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
||||||
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
|
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
|
||||||
@@ -41,6 +42,7 @@ import { Module } from "@nestjs/common";
|
|||||||
OAuthClientUsersService,
|
OAuthClientUsersService,
|
||||||
PlatformBookingsService,
|
PlatformBookingsService,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { ApiQuery, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger";
|
import { ApiQuery, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger";
|
||||||
import { CreationSource } from "@prisma/client";
|
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { NextApiRequest } from "next/types";
|
import { NextApiRequest } from "next/types";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
@@ -58,6 +57,7 @@ import {
|
|||||||
getBookingForReschedule,
|
getBookingForReschedule,
|
||||||
ErrorCode,
|
ErrorCode,
|
||||||
} from "@calcom/platform-libraries";
|
} from "@calcom/platform-libraries";
|
||||||
|
import { CreationSource } from "@calcom/platform-libraries";
|
||||||
import {
|
import {
|
||||||
GetBookingsInput_2024_04_15,
|
GetBookingsInput_2024_04_15,
|
||||||
CancelBookingInput_2024_04_15,
|
CancelBookingInput_2024_04_15,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { OutputBookingReferencesService_2024_08_13 } from "@/ee/bookings/2024-08
|
|||||||
import { OutputBookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/output.service";
|
import { OutputBookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/output.service";
|
||||||
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
|
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
|
||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
||||||
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
|
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
|
||||||
@@ -71,6 +72,7 @@ import { Module } from "@nestjs/common";
|
|||||||
ApiKeysRepository,
|
ApiKeysRepository,
|
||||||
PlatformBookingsService,
|
PlatformBookingsService,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
slug: seatedEventTypeSlug,
|
slug: seatedEventTypeSlug,
|
||||||
length: 60,
|
length: 60,
|
||||||
seatsPerTimeSlot: 3,
|
seatsPerTimeSlot: 3,
|
||||||
|
seatsShowAttendees: true,
|
||||||
},
|
},
|
||||||
user.id
|
user.id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
let apiKeyString: string;
|
let apiKeyString: string;
|
||||||
|
|
||||||
let seatedEventTypeId: number;
|
let seatedEventTypeId: number;
|
||||||
|
let seatedEventTypeIdAttendeesDisabledId: number;
|
||||||
|
|
||||||
const seatedEventSlug = `seated-bookings-event-type-${randomString()}`;
|
const seatedEventSlug = `seated-bookings-event-type-${randomString()}`;
|
||||||
|
const seatedEventSlugAttendeesDisabled = `seated-bookings-event-type-attendees-disabled-${randomString()}`;
|
||||||
|
|
||||||
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
|
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
|
||||||
let createdSeatedBooking2: CreateSeatedBookingOutput_2024_08_13;
|
let createdSeatedBooking2: CreateSeatedBookingOutput_2024_08_13;
|
||||||
@@ -121,6 +123,28 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
);
|
);
|
||||||
seatedEventTypeId = seatedEvent.id;
|
seatedEventTypeId = seatedEvent.id;
|
||||||
|
|
||||||
|
const seatedEventAttendeesDisabled = await eventTypesRepositoryFixture.create(
|
||||||
|
{
|
||||||
|
title: `seated-bookings-2024-08-13-event-type-attendees-disabled-${randomString()}`,
|
||||||
|
slug: seatedEventSlugAttendeesDisabled,
|
||||||
|
length: 60,
|
||||||
|
seatsPerTimeSlot: 5,
|
||||||
|
seatsShowAttendees: false,
|
||||||
|
seatsShowAvailabilityCount: false,
|
||||||
|
locations: [{ type: "inPerson", address: "via 10, rome, italy" }],
|
||||||
|
metadata: {
|
||||||
|
disableStandardEmails: {
|
||||||
|
all: {
|
||||||
|
attendee: true,
|
||||||
|
host: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
seatedEventTypeIdAttendeesDisabledId = seatedEventAttendeesDisabled.id;
|
||||||
|
|
||||||
app = moduleRef.createNestApplication();
|
app = moduleRef.createNestApplication();
|
||||||
bootstrap(app as NestExpressApplication);
|
bootstrap(app as NestExpressApplication);
|
||||||
|
|
||||||
@@ -414,6 +438,63 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should book an event type with attendees disabled", async () => {
|
||||||
|
const body: CreateBookingInput_2024_08_13 = {
|
||||||
|
start: new Date(Date.UTC(2030, 0, 9, 14, 0, 0)).toISOString(),
|
||||||
|
eventTypeId: seatedEventTypeIdAttendeesDisabledId,
|
||||||
|
attendee: {
|
||||||
|
name: nameAttendeeOne,
|
||||||
|
email: emailAttendeeOne,
|
||||||
|
timeZone: "Europe/Rome",
|
||||||
|
language: "it",
|
||||||
|
},
|
||||||
|
bookingFieldsResponses: {
|
||||||
|
codingLanguage: "TypeScript",
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
userId: "100",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post("/v2/bookings")
|
||||||
|
.send(body)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||||
|
.expect(201)
|
||||||
|
.then(async (response) => {
|
||||||
|
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseDataIsCreateSeatedBooking(responseBody.data)).toBe(true);
|
||||||
|
|
||||||
|
if (responseDataIsCreateSeatedBooking(responseBody.data)) {
|
||||||
|
const data: CreateSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||||
|
expect(data.seatUid).toBeDefined();
|
||||||
|
expect(data.id).toBeDefined();
|
||||||
|
expect(data.uid).toBeDefined();
|
||||||
|
expect(data.hosts[0].id).toEqual(user.id);
|
||||||
|
expect(data.status).toEqual("accepted");
|
||||||
|
expect(data.start).toEqual(body.start);
|
||||||
|
expect(data.end).toEqual(
|
||||||
|
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||||
|
);
|
||||||
|
expect(data.duration).toEqual(60);
|
||||||
|
expect(data.eventTypeId).toEqual(seatedEventTypeIdAttendeesDisabledId);
|
||||||
|
expect(data.eventType).toEqual({
|
||||||
|
id: seatedEventTypeIdAttendeesDisabledId,
|
||||||
|
slug: seatedEventSlugAttendeesDisabled,
|
||||||
|
});
|
||||||
|
expect(data.attendees.length).toEqual(0);
|
||||||
|
expect(data.location).toBeDefined();
|
||||||
|
expect(data.absentHost).toEqual(false);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Invalid response data - expected seated booking but received non array response"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("cancel seated booking", () => {
|
describe("cancel seated booking", () => {
|
||||||
describe("cancel seated booking as attendee", () => {
|
describe("cancel seated booking as attendee", () => {
|
||||||
it("should cancel seated booking", async () => {
|
it("should cancel seated booking", async () => {
|
||||||
|
|||||||
@@ -576,10 +576,13 @@ export class BookingsService_2024_08_13 {
|
|||||||
return this.outputService.getOutputRecurringBooking(booking);
|
return this.outputService.getOutputRecurringBooking(booking);
|
||||||
}
|
}
|
||||||
if (isRecurring && isSeated) {
|
if (isRecurring && isSeated) {
|
||||||
return this.outputService.getOutputRecurringSeatedBooking(booking);
|
return this.outputService.getOutputRecurringSeatedBooking(
|
||||||
|
booking,
|
||||||
|
!!booking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (isSeated) {
|
if (isSeated) {
|
||||||
return this.outputService.getOutputSeatedBooking(booking);
|
return this.outputService.getOutputSeatedBooking(booking, !!booking.eventType?.seatsShowAttendees);
|
||||||
}
|
}
|
||||||
return this.outputService.getOutputBooking(booking);
|
return this.outputService.getOutputBooking(booking);
|
||||||
}
|
}
|
||||||
@@ -591,7 +594,10 @@ export class BookingsService_2024_08_13 {
|
|||||||
const ids = recurringBooking.map((booking) => booking.id);
|
const ids = recurringBooking.map((booking) => booking.id);
|
||||||
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
|
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
|
||||||
if (isRecurringSeated) {
|
if (isRecurringSeated) {
|
||||||
return this.outputService.getOutputRecurringSeatedBookings(ids);
|
return this.outputService.getOutputRecurringSeatedBookings(
|
||||||
|
ids,
|
||||||
|
!!recurringBooking[0].eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.outputService.getOutputRecurringBookings(ids);
|
return this.outputService.getOutputRecurringBookings(ids);
|
||||||
@@ -657,9 +663,19 @@ export class BookingsService_2024_08_13 {
|
|||||||
if (isRecurring && !isSeated) {
|
if (isRecurring && !isSeated) {
|
||||||
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
|
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
|
||||||
} else if (isRecurring && isSeated) {
|
} else if (isRecurring && isSeated) {
|
||||||
formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted));
|
formattedBookings.push(
|
||||||
|
this.outputService.getOutputRecurringSeatedBooking(
|
||||||
|
formatted,
|
||||||
|
!!formatted.eventType?.seatsShowAttendees
|
||||||
|
)
|
||||||
|
);
|
||||||
} else if (isSeated) {
|
} else if (isSeated) {
|
||||||
formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted));
|
formattedBookings.push(
|
||||||
|
await this.outputService.getOutputSeatedBooking(
|
||||||
|
formatted,
|
||||||
|
!!formatted.eventType?.seatsShowAttendees
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
|
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { UsersRepository } from "@/modules/users/users.repository";
|
|||||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Logger } from "@nestjs/common";
|
import { Logger } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { CreationSource } from "@prisma/client";
|
|
||||||
import { isURL, isPhoneNumber } from "class-validator";
|
import { isURL, isPhoneNumber } from "class-validator";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
@@ -30,6 +29,7 @@ import { NextApiRequest } from "next/types";
|
|||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { CreationSource } from "@calcom/platform-libraries";
|
||||||
import { EventTypeMetaDataSchema } from "@calcom/platform-libraries/event-types";
|
import { EventTypeMetaDataSchema } from "@calcom/platform-libraries/event-types";
|
||||||
import {
|
import {
|
||||||
CancelBookingInput,
|
CancelBookingInput,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type DatabaseBooking = Booking & {
|
|||||||
eventType: {
|
eventType: {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
seatsShowAttendees?: boolean | null;
|
||||||
} | null;
|
} | null;
|
||||||
attendees: {
|
attendees: {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -167,6 +168,7 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
|
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
|
||||||
if (databaseMetadata === null) return {};
|
if (databaseMetadata === null) return {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
|
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
|
||||||
|
|
||||||
return userDefinedMetadata;
|
return userDefinedMetadata;
|
||||||
@@ -266,11 +268,14 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
databaseBooking: DatabaseBooking,
|
databaseBooking: DatabaseBooking,
|
||||||
seatUid: string
|
seatUid: string
|
||||||
): Promise<CreateSeatedBookingOutput_2024_08_13> {
|
): Promise<CreateSeatedBookingOutput_2024_08_13> {
|
||||||
const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking);
|
const getSeatedBookingOutput = await this.getOutputSeatedBooking(
|
||||||
|
databaseBooking,
|
||||||
|
!!databaseBooking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
return { ...getSeatedBookingOutput, seatUid };
|
return { ...getSeatedBookingOutput, seatUid };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
|
async getOutputSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
|
||||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||||
@@ -317,41 +322,43 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
||||||
|
|
||||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
parsed.attendees = showAttendees
|
||||||
const { responses } = safeParse(
|
? databaseBooking.attendees.map((attendee) => {
|
||||||
seatedBookingDataSchema,
|
const { responses } = safeParse(
|
||||||
attendee.bookingSeat?.data,
|
seatedBookingDataSchema,
|
||||||
defaultSeatedBookingData,
|
attendee.bookingSeat?.data,
|
||||||
false
|
defaultSeatedBookingData,
|
||||||
);
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const attendeeData = {
|
const attendeeData = {
|
||||||
name: attendee.name,
|
name: attendee.name,
|
||||||
email: attendee.email,
|
email: attendee.email,
|
||||||
timeZone: attendee.timeZone,
|
timeZone: attendee.timeZone,
|
||||||
language: attendee.locale,
|
language: attendee.locale,
|
||||||
absent: !!attendee.noShow,
|
absent: !!attendee.noShow,
|
||||||
seatUid: attendee.bookingSeat?.referenceUid,
|
seatUid: attendee.bookingSeat?.referenceUid,
|
||||||
bookingFieldsResponses: {},
|
bookingFieldsResponses: {},
|
||||||
};
|
};
|
||||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||||
attendeeParsed.metadata = safeParse(
|
attendeeParsed.metadata = safeParse(
|
||||||
seatedBookingMetadataSchema,
|
seatedBookingMetadataSchema,
|
||||||
attendee.bookingSeat?.metadata,
|
attendee.bookingSeat?.metadata,
|
||||||
defaultSeatedBookingMetadata,
|
defaultSeatedBookingMetadata,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// note(Lauris): as of now email is not returned for privacy
|
// note(Lauris): as of now email is not returned for privacy
|
||||||
delete attendeeParsed.bookingFieldsResponses.email;
|
delete attendeeParsed.bookingFieldsResponses.email;
|
||||||
|
|
||||||
return attendeeParsed;
|
return attendeeParsed;
|
||||||
});
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutputRecurringSeatedBookings(bookingsIds: number[]) {
|
async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean) {
|
||||||
const transformed = [];
|
const transformed = [];
|
||||||
|
|
||||||
for (const bookingId of bookingsIds) {
|
for (const bookingId of bookingsIds) {
|
||||||
@@ -361,7 +368,7 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
||||||
}
|
}
|
||||||
|
|
||||||
transformed.push(this.getOutputRecurringSeatedBooking(databaseBooking));
|
transformed.push(this.getOutputRecurringSeatedBooking(databaseBooking, showAttendees));
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
||||||
@@ -386,11 +393,14 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
databaseBooking: DatabaseBooking,
|
databaseBooking: DatabaseBooking,
|
||||||
seatUid: string
|
seatUid: string
|
||||||
): CreateRecurringSeatedBookingOutput_2024_08_13 {
|
): CreateRecurringSeatedBookingOutput_2024_08_13 {
|
||||||
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(databaseBooking);
|
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(
|
||||||
|
databaseBooking,
|
||||||
|
!!databaseBooking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
return { ...getRecurringSeatedBookingOutput, seatUid };
|
return { ...getRecurringSeatedBookingOutput, seatUid };
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking) {
|
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
|
||||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||||
@@ -430,35 +440,37 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
parsed.attendees = showAttendees
|
||||||
const { responses } = safeParse(
|
? databaseBooking.attendees.map((attendee) => {
|
||||||
seatedBookingDataSchema,
|
const { responses } = safeParse(
|
||||||
attendee.bookingSeat?.data,
|
seatedBookingDataSchema,
|
||||||
defaultSeatedBookingData,
|
attendee.bookingSeat?.data,
|
||||||
false
|
defaultSeatedBookingData,
|
||||||
);
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const attendeeData = {
|
const attendeeData = {
|
||||||
name: attendee.name,
|
name: attendee.name,
|
||||||
email: attendee.email,
|
email: attendee.email,
|
||||||
timeZone: attendee.timeZone,
|
timeZone: attendee.timeZone,
|
||||||
language: attendee.locale,
|
language: attendee.locale,
|
||||||
absent: !!attendee.noShow,
|
absent: !!attendee.noShow,
|
||||||
seatUid: attendee.bookingSeat?.referenceUid,
|
seatUid: attendee.bookingSeat?.referenceUid,
|
||||||
bookingFieldsResponses: {},
|
bookingFieldsResponses: {},
|
||||||
};
|
};
|
||||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||||
attendeeParsed.metadata = safeParse(
|
attendeeParsed.metadata = safeParse(
|
||||||
seatedBookingMetadataSchema,
|
seatedBookingMetadataSchema,
|
||||||
attendee.bookingSeat?.metadata,
|
attendee.bookingSeat?.metadata,
|
||||||
defaultSeatedBookingMetadata,
|
defaultSeatedBookingMetadata,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// note(Lauris): as of now email is not returned for privacy
|
// note(Lauris): as of now email is not returned for privacy
|
||||||
delete attendeeParsed.bookingFieldsResponses.email;
|
delete attendeeParsed.bookingFieldsResponses.email;
|
||||||
return attendeeParsed;
|
return attendeeParsed;
|
||||||
});
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
import { CalendarsController } from "@/ee/calendars/controllers/calendars.controller";
|
import { CalendarsController } from "@/ee/calendars/controllers/calendars.controller";
|
||||||
import { AppleCalendarService } from "@/ee/calendars/services/apple-calendar.service";
|
import { AppleCalendarService } from "@/ee/calendars/services/apple-calendar.service";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { GoogleCalendarService } from "@/ee/calendars/services/gcal.service";
|
import { GoogleCalendarService } from "@/ee/calendars/services/gcal.service";
|
||||||
import { IcsFeedService } from "@/ee/calendars/services/ics-feed.service";
|
import { IcsFeedService } from "@/ee/calendars/services/ics-feed.service";
|
||||||
@@ -10,16 +11,18 @@ import { OutlookService } from "@/ee/calendars/services/outlook.service";
|
|||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||||
import { UsersModule } from "@/modules/users/users.module";
|
import { UsersModule } from "@/modules/users/users.module";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, UsersModule, TokensModule],
|
imports: [PrismaModule, UsersModule, TokensModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
OutlookService,
|
OutlookService,
|
||||||
GoogleCalendarService,
|
GoogleCalendarService,
|
||||||
AppleCalendarService,
|
AppleCalendarService,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
DeletedCalendarCredentialsOutputDto,
|
DeletedCalendarCredentialsOutputDto,
|
||||||
} from "@/ee/calendars/outputs/delete-calendar-credentials.output";
|
} from "@/ee/calendars/outputs/delete-calendar-credentials.output";
|
||||||
import { AppleCalendarService } from "@/ee/calendars/services/apple-calendar.service";
|
import { AppleCalendarService } from "@/ee/calendars/services/apple-calendar.service";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { GoogleCalendarService } from "@/ee/calendars/services/gcal.service";
|
import { GoogleCalendarService } from "@/ee/calendars/services/gcal.service";
|
||||||
import { IcsFeedService } from "@/ee/calendars/services/ics-feed.service";
|
import { IcsFeedService } from "@/ee/calendars/services/ics-feed.service";
|
||||||
@@ -79,6 +80,7 @@ const calendarStateSchema = z.object({
|
|||||||
export class CalendarsController {
|
export class CalendarsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly calendarsService: CalendarsService,
|
private readonly calendarsService: CalendarsService,
|
||||||
|
private readonly calendarsCacheService: CalendarsCacheService,
|
||||||
private readonly outlookService: OutlookService,
|
private readonly outlookService: OutlookService,
|
||||||
private readonly googleCalendarService: GoogleCalendarService,
|
private readonly googleCalendarService: GoogleCalendarService,
|
||||||
private readonly appleCalendarService: AppleCalendarService,
|
private readonly appleCalendarService: AppleCalendarService,
|
||||||
@@ -307,6 +309,8 @@ export class CalendarsController {
|
|||||||
credentialId
|
credentialId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(user.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: SUCCESS_STATUS,
|
status: SUCCESS_STATUS,
|
||||||
data: plainToClass(
|
data: plainToClass(
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { RedisService } from "@/modules/redis/redis.service";
|
||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
|
import type { getConnectedDestinationCalendarsAndEnsureDefaultsInDb } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
|
export const REDIS_CALENDARS_CACHE_KEY = (userId: number) => `apiv2:user:${userId}:calendars`;
|
||||||
|
export const CALENDARS_CACHE_TTL_MS = 10_000;
|
||||||
|
|
||||||
|
type ConnectedDestinationCalendars = Awaited<
|
||||||
|
ReturnType<typeof getConnectedDestinationCalendarsAndEnsureDefaultsInDb>
|
||||||
|
>;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CalendarsCacheService {
|
||||||
|
constructor(private readonly redisService: RedisService) {}
|
||||||
|
|
||||||
|
async deleteConnectedAndDestinationCalendarsCache(userId: number) {
|
||||||
|
await this.redisService.del(REDIS_CALENDARS_CACHE_KEY(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConnectedAndDestinationCalendarsCache(userId: number) {
|
||||||
|
const cachedResult = await this.redisService.get<ConnectedDestinationCalendars>(
|
||||||
|
REDIS_CALENDARS_CACHE_KEY(userId)
|
||||||
|
);
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setConnectedAndDestinationCalendarsCache(
|
||||||
|
userId: number,
|
||||||
|
calendars: ConnectedDestinationCalendars
|
||||||
|
): Promise<void> {
|
||||||
|
await this.redisService.set<ConnectedDestinationCalendars>(REDIS_CALENDARS_CACHE_KEY(userId), calendars, {
|
||||||
|
ttl: CALENDARS_CACHE_TTL_MS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
import {
|
import {
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
@@ -37,7 +38,8 @@ export class CalendarsService {
|
|||||||
private readonly appsRepository: AppsRepository,
|
private readonly appsRepository: AppsRepository,
|
||||||
private readonly calendarsRepository: CalendarsRepository,
|
private readonly calendarsRepository: CalendarsRepository,
|
||||||
private readonly dbWrite: PrismaWriteService,
|
private readonly dbWrite: PrismaWriteService,
|
||||||
private readonly selectedCalendarsRepository: SelectedCalendarsRepository
|
private readonly selectedCalendarsRepository: SelectedCalendarsRepository,
|
||||||
|
private readonly calendarsCacheService: CalendarsCacheService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private buildNonDelegationCredentials<TCredential>(credentials: TCredential[]) {
|
private buildNonDelegationCredentials<TCredential>(credentials: TCredential[]) {
|
||||||
@@ -52,11 +54,17 @@ export class CalendarsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getCalendars(userId: number) {
|
async getCalendars(userId: number) {
|
||||||
|
const cachedResult = await this.calendarsCacheService.getConnectedAndDestinationCalendarsCache(userId);
|
||||||
|
|
||||||
|
if (cachedResult) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
|
||||||
const userWithCalendars = await this.usersRepository.findByIdWithCalendars(userId);
|
const userWithCalendars = await this.usersRepository.findByIdWithCalendars(userId);
|
||||||
if (!userWithCalendars) {
|
if (!userWithCalendars) {
|
||||||
throw new NotFoundException("User not found");
|
throw new NotFoundException("User not found");
|
||||||
}
|
}
|
||||||
return getConnectedDestinationCalendarsAndEnsureDefaultsInDb({
|
const result = await getConnectedDestinationCalendarsAndEnsureDefaultsInDb({
|
||||||
user: {
|
user: {
|
||||||
...userWithCalendars,
|
...userWithCalendars,
|
||||||
allSelectedCalendars: userWithCalendars.selectedCalendars,
|
allSelectedCalendars: userWithCalendars.selectedCalendars,
|
||||||
@@ -68,6 +76,10 @@ export class CalendarsService {
|
|||||||
eventTypeId: null,
|
eventTypeId: null,
|
||||||
prisma: this.dbWrite.prisma as unknown as PrismaClient,
|
prisma: this.dbWrite.prisma as unknown as PrismaClient,
|
||||||
});
|
});
|
||||||
|
console.log("saving cache", JSON.stringify(result));
|
||||||
|
await this.calendarsCacheService.setConnectedAndDestinationCalendarsCache(userId, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBusyTimes(
|
async getBusyTimes(
|
||||||
@@ -187,6 +199,8 @@ export class CalendarsService {
|
|||||||
userId,
|
userId,
|
||||||
calendarType
|
calendarType
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkCalendarCredentialValidity(userId: number, credentialId: number, type: string) {
|
async checkCalendarCredentialValidity(userId: number, credentialId: number, type: string) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { ICSFeedCalendarApp } from "@/ee/calendars/calendars.interface";
|
import { ICSFeedCalendarApp } from "@/ee/calendars/calendars.interface";
|
||||||
import { CreateIcsFeedOutputResponseDto } from "@/ee/calendars/input/create-ics.output";
|
import { CreateIcsFeedOutputResponseDto } from "@/ee/calendars/input/create-ics.output";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
|
import { RedisService } from "@/modules/redis/redis.service";
|
||||||
import { BadRequestException, UnauthorizedException, Logger } from "@nestjs/common";
|
import { BadRequestException, UnauthorizedException, Logger } from "@nestjs/common";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
@@ -13,7 +15,9 @@ import { IcsFeedCalendarService } from "@calcom/platform-libraries/app-store";
|
|||||||
export class IcsFeedService implements ICSFeedCalendarApp {
|
export class IcsFeedService implements ICSFeedCalendarApp {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly calendarsService: CalendarsService,
|
private readonly calendarsService: CalendarsService,
|
||||||
private readonly credentialRepository: CredentialsRepository
|
private readonly calendarsCacheService: CalendarsCacheService,
|
||||||
|
private readonly credentialRepository: CredentialsRepository,
|
||||||
|
private readonly redisService: RedisService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private logger = new Logger("IcsFeedService");
|
private logger = new Logger("IcsFeedService");
|
||||||
@@ -58,6 +62,9 @@ export class IcsFeedService implements ICSFeedCalendarApp {
|
|||||||
data.key,
|
data.key,
|
||||||
userId
|
userId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: SUCCESS_STATUS,
|
status: SUCCESS_STATUS,
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
+216
-41
@@ -12,6 +12,7 @@ import { NestExpressApplication } from "@nestjs/platform-express";
|
|||||||
import { Test } from "@nestjs/testing";
|
import { Test } from "@nestjs/testing";
|
||||||
import { PlatformOAuthClient, Team, User, Schedule, EventType } from "@prisma/client";
|
import { PlatformOAuthClient, Team, User, Schedule, EventType } from "@prisma/client";
|
||||||
import * as request from "supertest";
|
import * as request from "supertest";
|
||||||
|
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
|
||||||
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
||||||
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
||||||
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
|
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
|
||||||
@@ -30,6 +31,7 @@ import {
|
|||||||
NoticeThresholdUnitEnum,
|
NoticeThresholdUnitEnum,
|
||||||
FrequencyInput,
|
FrequencyInput,
|
||||||
} from "@calcom/platform-enums";
|
} from "@calcom/platform-enums";
|
||||||
|
import { SchedulingType } from "@calcom/platform-libraries";
|
||||||
import {
|
import {
|
||||||
ApiSuccessResponse,
|
ApiSuccessResponse,
|
||||||
CreateEventTypeInput_2024_06_14,
|
CreateEventTypeInput_2024_06_14,
|
||||||
@@ -39,7 +41,6 @@ import {
|
|||||||
NotesDefaultFieldInput_2024_06_14,
|
NotesDefaultFieldInput_2024_06_14,
|
||||||
UpdateEventTypeInput_2024_06_14,
|
UpdateEventTypeInput_2024_06_14,
|
||||||
} from "@calcom/platform-types";
|
} from "@calcom/platform-types";
|
||||||
import { SchedulingType } from "@calcom/prisma/enums";
|
|
||||||
|
|
||||||
const orderBySlug = (a: { slug: string }, b: { slug: string }) => {
|
const orderBySlug = (a: { slug: string }, b: { slug: string }) => {
|
||||||
if (a.slug < b.slug) return -1;
|
if (a.slug < b.slug) return -1;
|
||||||
@@ -88,11 +89,16 @@ describe("Event types Endpoints", () => {
|
|||||||
let schedulesRepostoryFixture: SchedulesRepositoryFixture;
|
let schedulesRepostoryFixture: SchedulesRepositoryFixture;
|
||||||
let profileRepositoryFixture: ProfileRepositoryFixture;
|
let profileRepositoryFixture: ProfileRepositoryFixture;
|
||||||
let membershipsRepositoryFixture: MembershipRepositoryFixture;
|
let membershipsRepositoryFixture: MembershipRepositoryFixture;
|
||||||
|
let apiKeysRepositoryFixture: ApiKeysRepositoryFixture;
|
||||||
|
let apiKeyString: string;
|
||||||
|
let apiKeyOrgUser: string;
|
||||||
|
|
||||||
const userEmail = `event-types-2024-06-14-user-${randomString()}@api.com`;
|
const userEmail = `event-types-2024-06-14-user-${randomString()}@api.com`;
|
||||||
const falseTestEmail = `event-types-2024-06-14-false-user-${randomString()}@api.com`;
|
const falseTestEmail = `event-types-2024-06-14-false-user-${randomString()}@api.com`;
|
||||||
const name = `event-types-2024-06-14-user-${randomString()}`;
|
const name = `event-types-2024-06-14-user-${randomString()}`;
|
||||||
const username = name;
|
const username = name;
|
||||||
let eventType: EventTypeOutput_2024_06_14;
|
let eventType: EventTypeOutput_2024_06_14;
|
||||||
|
let hiddenEventType: EventTypeOutput_2024_06_14;
|
||||||
let user: User;
|
let user: User;
|
||||||
let orgUser: User;
|
let orgUser: User;
|
||||||
let falseTestUser: User;
|
let falseTestUser: User;
|
||||||
@@ -165,13 +171,10 @@ describe("Event types Endpoints", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const moduleRef = await withApiAuth(
|
const moduleRef = await Test.createTestingModule({
|
||||||
userEmail,
|
providers: [PrismaExceptionFilter, HttpExceptionFilter],
|
||||||
Test.createTestingModule({
|
imports: [AppModule, UsersModule, EventTypesModule_2024_06_14, TokensModule],
|
||||||
providers: [PrismaExceptionFilter, HttpExceptionFilter],
|
})
|
||||||
imports: [AppModule, UsersModule, EventTypesModule_2024_06_14, TokensModule],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.overrideGuard(PermissionsGuard)
|
.overrideGuard(PermissionsGuard)
|
||||||
.useValue({
|
.useValue({
|
||||||
canActivate: () => true,
|
canActivate: () => true,
|
||||||
@@ -198,12 +201,20 @@ describe("Event types Endpoints", () => {
|
|||||||
name,
|
name,
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
|
apiKeysRepositoryFixture = new ApiKeysRepositoryFixture(moduleRef);
|
||||||
|
const { keyString } = await apiKeysRepositoryFixture.createApiKey(user.id, null);
|
||||||
|
apiKeyString = `cal_test_${keyString}`;
|
||||||
|
|
||||||
orgUser = await userRepositoryFixture.create({
|
orgUser = await userRepositoryFixture.create({
|
||||||
email: `event-types-2024-06-14-org-user-${randomString()}@example.com`,
|
email: `event-types-2024-06-14-org-user-${randomString()}@example.com`,
|
||||||
name: `event-types-2024-06-14-org-user-${randomString()}`,
|
name: `event-types-2024-06-14-org-user-${randomString()}`,
|
||||||
username: `event-types-2024-06-14-org-user-${randomString()}`,
|
username: `event-types-2024-06-14-org-user-${randomString()}`,
|
||||||
});
|
});
|
||||||
|
const { keyString: orgUserApiKeyString } = await apiKeysRepositoryFixture.createApiKey(
|
||||||
|
orgUser.id,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
apiKeyOrgUser = `cal_test_${orgUserApiKeyString}`;
|
||||||
|
|
||||||
profileRepositoryFixture.create({
|
profileRepositoryFixture.create({
|
||||||
uid: `usr-${orgUser.id}`,
|
uid: `usr-${orgUser.id}`,
|
||||||
@@ -334,6 +345,7 @@ describe("Event types Endpoints", () => {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
@@ -370,6 +382,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
|
|
||||||
@@ -395,6 +408,7 @@ describe("Event types Endpoints", () => {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
@@ -504,6 +518,7 @@ describe("Event types Endpoints", () => {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(201)
|
.expect(201)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
@@ -576,21 +591,52 @@ describe("Event types Endpoints", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create a hidden event type", async () => {
|
||||||
|
const body: CreateEventTypeInput_2024_06_14 = {
|
||||||
|
title: "Coding class hidden",
|
||||||
|
slug: "coding-class-hidden",
|
||||||
|
description: "Let's learn how to code like a pro.",
|
||||||
|
lengthInMinutes: 60,
|
||||||
|
locations: [
|
||||||
|
{
|
||||||
|
type: "integration",
|
||||||
|
integration: "cal-video",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hidden: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.post("/api/v2/event-types")
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
|
.send(body)
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||||
|
const createdEventType = responseBody.data;
|
||||||
|
expect(createdEventType).toHaveProperty("id");
|
||||||
|
expect(createdEventType.title).toEqual(body.title);
|
||||||
|
expect(createdEventType.hidden).toEqual(body.hidden);
|
||||||
|
expect(createdEventType.ownerId).toEqual(user.id);
|
||||||
|
hiddenEventType = responseBody.data;
|
||||||
|
});
|
||||||
|
|
||||||
it(`/GET/event-types by username`, async () => {
|
it(`/GET/event-types by username`, async () => {
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types?username=${username}`)
|
.get(`/api/v2/event-types?username=${user.username}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
expect(responseBody.data).toBeDefined();
|
expect(responseBody.data).toBeDefined();
|
||||||
expect(responseBody.data?.length).toEqual(1);
|
expect(responseBody.data?.length).toEqual(2);
|
||||||
|
|
||||||
const fetchedEventType = responseBody.data?.[0];
|
const fetchedEventType = responseBody.data?.find((et) => et.id === eventType.id);
|
||||||
|
const fetchedHiddenEventType = responseBody.data?.find((et) => et.id === hiddenEventType.id);
|
||||||
|
|
||||||
expect(fetchedEventType?.id).toEqual(eventType.id);
|
expect(fetchedEventType?.id).toEqual(eventType.id);
|
||||||
expect(fetchedEventType?.title).toEqual(eventType.title);
|
expect(fetchedEventType?.title).toEqual(eventType.title);
|
||||||
@@ -602,33 +648,149 @@ describe("Event types Endpoints", () => {
|
|||||||
eventType.bookingFields.sort(orderBySlug).filter((f) => ("hidden" in f ? !f?.hidden : true))
|
eventType.bookingFields.sort(orderBySlug).filter((f) => ("hidden" in f ? !f?.hidden : true))
|
||||||
);
|
);
|
||||||
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||||
expect(fetchedEventType.bookingLimitsCount).toEqual(eventType.bookingLimitsCount);
|
expect(fetchedEventType?.bookingLimitsCount).toEqual(eventType.bookingLimitsCount);
|
||||||
expect(fetchedEventType.onlyShowFirstAvailableSlot).toEqual(eventType.onlyShowFirstAvailableSlot);
|
expect(fetchedEventType?.onlyShowFirstAvailableSlot).toEqual(eventType.onlyShowFirstAvailableSlot);
|
||||||
expect(fetchedEventType.bookingLimitsDuration).toEqual(eventType.bookingLimitsDuration);
|
expect(fetchedEventType?.bookingLimitsDuration).toEqual(eventType.bookingLimitsDuration);
|
||||||
expect(fetchedEventType.offsetStart).toEqual(eventType.offsetStart);
|
expect(fetchedEventType?.offsetStart).toEqual(eventType.offsetStart);
|
||||||
expect(fetchedEventType.bookingWindow).toEqual(eventType.bookingWindow);
|
expect(fetchedEventType?.bookingWindow).toEqual(eventType.bookingWindow);
|
||||||
expect(fetchedEventType.bookerLayouts).toEqual(eventType.bookerLayouts);
|
expect(fetchedEventType?.bookerLayouts).toEqual(eventType.bookerLayouts);
|
||||||
expect(fetchedEventType.confirmationPolicy).toEqual(eventType.confirmationPolicy);
|
expect(fetchedEventType?.confirmationPolicy).toEqual(eventType.confirmationPolicy);
|
||||||
expect(fetchedEventType.recurrence).toEqual(eventType.recurrence);
|
expect(fetchedEventType?.recurrence).toEqual(eventType.recurrence);
|
||||||
expect(fetchedEventType.customName).toEqual(eventType.customName);
|
expect(fetchedEventType?.customName).toEqual(eventType.customName);
|
||||||
expect(fetchedEventType.requiresBookerEmailVerification).toEqual(
|
expect(fetchedEventType?.requiresBookerEmailVerification).toEqual(
|
||||||
eventType.requiresBookerEmailVerification
|
eventType.requiresBookerEmailVerification
|
||||||
);
|
);
|
||||||
expect(fetchedEventType.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
|
expect(fetchedEventType?.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
|
||||||
expect(fetchedEventType.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
|
expect(fetchedEventType?.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
|
||||||
expect(fetchedEventType.hideOrganizerEmail).toEqual(eventType.hideOrganizerEmail);
|
expect(fetchedEventType?.hideOrganizerEmail).toEqual(eventType.hideOrganizerEmail);
|
||||||
expect(fetchedEventType.lockTimeZoneToggleOnBookingPage).toEqual(
|
expect(fetchedEventType?.lockTimeZoneToggleOnBookingPage).toEqual(
|
||||||
eventType.lockTimeZoneToggleOnBookingPage
|
eventType.lockTimeZoneToggleOnBookingPage
|
||||||
);
|
);
|
||||||
expect(fetchedEventType.color).toEqual(eventType.color);
|
expect(fetchedEventType?.color).toEqual(eventType.color);
|
||||||
|
expect(fetchedEventType?.hidden).toEqual(false);
|
||||||
|
|
||||||
|
expect(fetchedHiddenEventType?.id).toEqual(hiddenEventType.id);
|
||||||
|
expect(fetchedHiddenEventType?.hidden).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`/GET/event-types by username should not return hidden event type if auth of non event type owner provided`, async () => {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.get(`/api/v2/event-types?username=${user.username}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyOrgUser}`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseBody.data?.length).toEqual(1);
|
||||||
|
|
||||||
|
const fetchedEventType = responseBody.data?.find((et) => et.id === eventType.id);
|
||||||
|
|
||||||
|
expect(fetchedEventType?.id).toEqual(eventType.id);
|
||||||
|
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||||
|
expect(fetchedEventType?.hidden).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`/GET/event-types by username should not return hidden event type if no auth provided`, async () => {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.get(`/api/v2/event-types?username=${user.username}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseBody.data?.length).toEqual(1);
|
||||||
|
|
||||||
|
const fetchedEventType = responseBody.data?.find((et) => et.id === eventType.id);
|
||||||
|
|
||||||
|
expect(fetchedEventType?.id).toEqual(eventType.id);
|
||||||
|
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||||
|
expect(fetchedEventType?.hidden).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`/GET/event-types by username and eventSlug`, async () => {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.get(`/api/v2/event-types?username=${user.username}&eventSlug=${eventType.slug}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseBody.data?.length).toEqual(1);
|
||||||
|
|
||||||
|
const fetchedEventType = responseBody.data?.find((et) => et.id === eventType.id);
|
||||||
|
|
||||||
|
expect(fetchedEventType?.id).toEqual(eventType.id);
|
||||||
|
expect(fetchedEventType?.title).toEqual(eventType.title);
|
||||||
|
expect(fetchedEventType?.description).toEqual(eventType.description);
|
||||||
|
expect(fetchedEventType?.lengthInMinutes).toEqual(eventType.lengthInMinutes);
|
||||||
|
expect(fetchedEventType?.lengthInMinutesOptions).toEqual(eventType.lengthInMinutesOptions);
|
||||||
|
expect(fetchedEventType?.locations).toEqual(eventType.locations);
|
||||||
|
expect(fetchedEventType?.bookingFields.sort(orderBySlug)).toEqual(
|
||||||
|
eventType.bookingFields.sort(orderBySlug).filter((f) => ("hidden" in f ? !f?.hidden : true))
|
||||||
|
);
|
||||||
|
expect(fetchedEventType?.ownerId).toEqual(user.id);
|
||||||
|
expect(fetchedEventType?.bookingLimitsCount).toEqual(eventType.bookingLimitsCount);
|
||||||
|
expect(fetchedEventType?.onlyShowFirstAvailableSlot).toEqual(eventType.onlyShowFirstAvailableSlot);
|
||||||
|
expect(fetchedEventType?.bookingLimitsDuration).toEqual(eventType.bookingLimitsDuration);
|
||||||
|
expect(fetchedEventType?.offsetStart).toEqual(eventType.offsetStart);
|
||||||
|
expect(fetchedEventType?.bookingWindow).toEqual(eventType.bookingWindow);
|
||||||
|
expect(fetchedEventType?.bookerLayouts).toEqual(eventType.bookerLayouts);
|
||||||
|
expect(fetchedEventType?.confirmationPolicy).toEqual(eventType.confirmationPolicy);
|
||||||
|
expect(fetchedEventType?.recurrence).toEqual(eventType.recurrence);
|
||||||
|
expect(fetchedEventType?.customName).toEqual(eventType.customName);
|
||||||
|
expect(fetchedEventType?.requiresBookerEmailVerification).toEqual(
|
||||||
|
eventType.requiresBookerEmailVerification
|
||||||
|
);
|
||||||
|
expect(fetchedEventType?.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
|
||||||
|
expect(fetchedEventType?.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
|
||||||
|
expect(fetchedEventType?.hideOrganizerEmail).toEqual(eventType.hideOrganizerEmail);
|
||||||
|
expect(fetchedEventType?.lockTimeZoneToggleOnBookingPage).toEqual(
|
||||||
|
eventType.lockTimeZoneToggleOnBookingPage
|
||||||
|
);
|
||||||
|
expect(fetchedEventType?.color).toEqual(eventType.color);
|
||||||
|
expect(fetchedEventType?.hidden).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`/GET/event-types by username and eventSlug should not return hidden event type if auth of non event type owner provided`, async () => {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.get(`/api/v2/event-types?username=${user.username}&eventSlug=${hiddenEventType.slug}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyOrgUser}`)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseBody.data?.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`/GET/event-types by username and eventSlug should not return hidden event type if no auth provided`, async () => {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.get(`/api/v2/event-types?username=${user.username}&eventSlug=${hiddenEventType.slug}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
|
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseBody.data?.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`/GET/event-types by username and orgSlug`, async () => {
|
it(`/GET/event-types by username and orgSlug`, async () => {
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}`)
|
.get(`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
@@ -647,8 +809,7 @@ describe("Event types Endpoints", () => {
|
|||||||
`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}&eventSlug=${orgUserEventType1.slug}`
|
`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}&eventSlug=${orgUserEventType1.slug}`
|
||||||
)
|
)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
@@ -663,8 +824,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types?username=${orgUser.username}&orgId=${organization.id}`)
|
.get(`/api/v2/event-types?username=${orgUser.username}&orgId=${organization.id}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
@@ -705,6 +865,7 @@ describe("Event types Endpoints", () => {
|
|||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
@@ -732,6 +893,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const createResponse = await request(app.getHttpServer())
|
const createResponse = await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(201);
|
.expect(201);
|
||||||
|
|
||||||
@@ -748,6 +910,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send({
|
.send({
|
||||||
seats: {
|
seats: {
|
||||||
@@ -779,6 +942,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
@@ -799,6 +963,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
const createResponse = await request(app.getHttpServer())
|
const createResponse = await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(201);
|
.expect(201);
|
||||||
@@ -817,6 +982,7 @@ describe("Event types Endpoints", () => {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send({
|
.send({
|
||||||
seats: {
|
seats: {
|
||||||
seatsPerTimeSlot: 4,
|
seatsPerTimeSlot: 4,
|
||||||
@@ -844,6 +1010,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const createResponse = await request(app.getHttpServer())
|
const createResponse = await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(201);
|
.expect(201);
|
||||||
|
|
||||||
@@ -860,6 +1027,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send({
|
.send({
|
||||||
locations: [
|
locations: [
|
||||||
@@ -897,6 +1065,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
@@ -918,6 +1087,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
const createResponse = await request(app.getHttpServer())
|
const createResponse = await request(app.getHttpServer())
|
||||||
.post("/api/v2/event-types")
|
.post("/api/v2/event-types")
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(201);
|
.expect(201);
|
||||||
@@ -935,6 +1105,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send({
|
.send({
|
||||||
confirmationPolicy: {
|
confirmationPolicy: {
|
||||||
@@ -1034,6 +1205,7 @@ describe("Event types Endpoints", () => {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${eventType.id}`)
|
.patch(`/api/v2/event-types/${eventType.id}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
@@ -1135,6 +1307,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${eventType.id}`)
|
.patch(`/api/v2/event-types/${eventType.id}`)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
@@ -1152,6 +1325,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.patch(`/api/v2/event-types/${eventType.id}`)
|
.patch(`/api/v2/event-types/${eventType.id}`)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
.send(body)
|
.send(body)
|
||||||
.expect(400);
|
.expect(400);
|
||||||
@@ -1161,8 +1335,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types/${eventType.id}`)
|
.get(`/api/v2/event-types/${eventType.id}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||||
@@ -1203,8 +1376,7 @@ describe("Event types Endpoints", () => {
|
|||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types?username=${username}&eventSlug=${eventType.slug}`)
|
.get(`/api/v2/event-types?username=${username}&eventSlug=${eventType.slug}`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||||
@@ -1245,13 +1417,16 @@ describe("Event types Endpoints", () => {
|
|||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.get(`/api/v2/event-types/1000`)
|
.get(`/api/v2/event-types/1000`)
|
||||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
.set("Authorization", `Bearer whatever`)
|
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should delete event type", async () => {
|
it("should delete event type", async () => {
|
||||||
return request(app.getHttpServer()).delete(`/api/v2/event-types/${eventType.id}`).expect(200);
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/v2/event-types/${eventType.id}`)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||||
|
.expect(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
|||||||
+28
-24
@@ -6,11 +6,22 @@ import { UpdateEventTypeOutput_2024_06_14 } from "@/ee/event-types/event-types_2
|
|||||||
import { EventTypeResponseTransformPipe } from "@/ee/event-types/event-types_2024_06_14/pipes/event-type-response.transformer";
|
import { EventTypeResponseTransformPipe } from "@/ee/event-types/event-types_2024_06_14/pipes/event-type-response.transformer";
|
||||||
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
|
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
|
||||||
import { InputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/input-event-types.service";
|
import { InputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/input-event-types.service";
|
||||||
|
import { OutputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/output-event-types.service";
|
||||||
import { VERSION_2024_06_14_VALUE } from "@/lib/api-versions";
|
import { VERSION_2024_06_14_VALUE } from "@/lib/api-versions";
|
||||||
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
|
import {
|
||||||
|
API_KEY_OR_ACCESS_TOKEN_HEADER,
|
||||||
|
OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER,
|
||||||
|
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
|
||||||
|
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
|
||||||
|
} from "@/lib/docs/headers";
|
||||||
|
import {
|
||||||
|
AuthOptionalUser,
|
||||||
|
GetOptionalUser,
|
||||||
|
} from "@/modules/auth/decorators/get-optional-user/get-optional-user.decorator";
|
||||||
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
|
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
|
||||||
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
|
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
|
||||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||||
|
import { OptionalApiAuthGuard } from "@/modules/auth/guards/optional-api-auth/optional-api-auth.guard";
|
||||||
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
|
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
|
||||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||||
import {
|
import {
|
||||||
@@ -40,7 +51,6 @@ import {
|
|||||||
UpdateEventTypeInput_2024_06_14,
|
UpdateEventTypeInput_2024_06_14,
|
||||||
GetEventTypesQuery_2024_06_14,
|
GetEventTypesQuery_2024_06_14,
|
||||||
CreateEventTypeInput_2024_06_14,
|
CreateEventTypeInput_2024_06_14,
|
||||||
EventTypeOutput_2024_06_14,
|
|
||||||
} from "@calcom/platform-types";
|
} from "@calcom/platform-types";
|
||||||
|
|
||||||
@Controller({
|
@Controller({
|
||||||
@@ -62,7 +72,8 @@ export class EventTypesController_2024_06_14 {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly eventTypesService: EventTypesService_2024_06_14,
|
private readonly eventTypesService: EventTypesService_2024_06_14,
|
||||||
private readonly inputEventTypesService: InputEventTypesService_2024_06_14,
|
private readonly inputEventTypesService: InputEventTypesService_2024_06_14,
|
||||||
private readonly eventTypeResponseTransformPipe: EventTypeResponseTransformPipe
|
private readonly eventTypeResponseTransformPipe: EventTypeResponseTransformPipe,
|
||||||
|
private readonly outputEventTypesService: OutputEventTypesService_2024_06_14
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Post("/")
|
@Post("/")
|
||||||
@@ -109,30 +120,23 @@ export class EventTypesController_2024_06_14 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get("/")
|
@Get("/")
|
||||||
@ApiOperation({ summary: "Get all event types" })
|
@ApiOperation({
|
||||||
|
summary: "Get all event types",
|
||||||
|
description:
|
||||||
|
"Hidden event types are returned only if authentication is provided and it belongs to the event type owner.",
|
||||||
|
})
|
||||||
|
@UseGuards(OptionalApiAuthGuard)
|
||||||
|
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
|
||||||
|
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
|
||||||
|
@ApiHeader(OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER)
|
||||||
async getEventTypes(
|
async getEventTypes(
|
||||||
@Query() queryParams: GetEventTypesQuery_2024_06_14
|
@Query() queryParams: GetEventTypesQuery_2024_06_14,
|
||||||
|
@GetOptionalUser() authUser: AuthOptionalUser
|
||||||
): Promise<GetEventTypesOutput_2024_06_14> {
|
): Promise<GetEventTypesOutput_2024_06_14> {
|
||||||
const eventTypes = await this.eventTypesService.getEventTypes(queryParams);
|
const eventTypes = await this.eventTypesService.getEventTypes(queryParams, authUser);
|
||||||
if (!eventTypes || eventTypes.length === 0) {
|
|
||||||
throw new NotFoundException(`Event types not found`);
|
|
||||||
}
|
|
||||||
const eventTypesFormatted = this.eventTypeResponseTransformPipe.transform(eventTypes);
|
const eventTypesFormatted = this.eventTypeResponseTransformPipe.transform(eventTypes);
|
||||||
const eventTypesWithoutHiddenFields = eventTypesFormatted.map((eventType) => {
|
const eventTypesWithoutHiddenFields =
|
||||||
return {
|
this.outputEventTypesService.getResponseEventTypesWithoutHiddenFields(eventTypesFormatted);
|
||||||
...eventType,
|
|
||||||
bookingFields: Array.isArray(eventType?.bookingFields)
|
|
||||||
? eventType?.bookingFields
|
|
||||||
.map((field) => {
|
|
||||||
if ("hidden" in field) {
|
|
||||||
return field.hidden !== true ? field : null;
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
})
|
|
||||||
.filter((f) => f)
|
|
||||||
: [],
|
|
||||||
};
|
|
||||||
}) as EventTypeOutput_2024_06_14[];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: SUCCESS_STATUS,
|
status: SUCCESS_STATUS,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { EventTypesController_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/controllers/event-types.controller";
|
import { EventTypesController_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/controllers/event-types.controller";
|
||||||
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
|
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
|
||||||
@@ -11,6 +12,7 @@ import { AppsRepository } from "@/modules/apps/apps.repository";
|
|||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
import { MembershipsModule } from "@/modules/memberships/memberships.module";
|
import { MembershipsModule } from "@/modules/memberships/memberships.module";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsModule } from "@/modules/selected-calendars/selected-calendars.module";
|
import { SelectedCalendarsModule } from "@/modules/selected-calendars/selected-calendars.module";
|
||||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||||
import { UsersService } from "@/modules/users/services/users.service";
|
import { UsersService } from "@/modules/users/services/users.service";
|
||||||
@@ -18,7 +20,7 @@ import { UsersRepository } from "@/modules/users/users.repository";
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, MembershipsModule, TokensModule, SelectedCalendarsModule],
|
imports: [PrismaModule, MembershipsModule, TokensModule, SelectedCalendarsModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
EventTypesRepository_2024_06_14,
|
EventTypesRepository_2024_06_14,
|
||||||
EventTypesService_2024_06_14,
|
EventTypesService_2024_06_14,
|
||||||
@@ -29,6 +31,7 @@ import { Module } from "@nestjs/common";
|
|||||||
SchedulesRepository_2024_06_11,
|
SchedulesRepository_2024_06_11,
|
||||||
EventTypeResponseTransformPipe,
|
EventTypeResponseTransformPipe,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
|
|||||||
@@ -75,6 +75,16 @@ export class EventTypesRepository_2024_06_14 {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUserEventTypesPublic(userId: number) {
|
||||||
|
return this.dbRead.prisma.eventType.findMany({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
hidden: false,
|
||||||
|
},
|
||||||
|
include: { users: true, schedule: true, destinationCalendar: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getEventTypeById(eventTypeId: number) {
|
async getEventTypeById(eventTypeId: number) {
|
||||||
return this.dbRead.prisma.eventType.findUnique({
|
return this.dbRead.prisma.eventType.findUnique({
|
||||||
where: { id: eventTypeId },
|
where: { id: eventTypeId },
|
||||||
|
|||||||
+47
-13
@@ -3,6 +3,7 @@ import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_20
|
|||||||
import { InputEventTransformed_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/transformed";
|
import { InputEventTransformed_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/transformed";
|
||||||
import { SystemField, CustomField } from "@/ee/event-types/event-types_2024_06_14/transformers";
|
import { SystemField, CustomField } from "@/ee/event-types/event-types_2024_06_14/transformers";
|
||||||
import { SchedulesRepository_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.repository";
|
import { SchedulesRepository_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.repository";
|
||||||
|
import { AuthOptionalUser } from "@/modules/auth/decorators/get-optional-user/get-optional-user.decorator";
|
||||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
@@ -38,6 +39,7 @@ export class EventTypesService_2024_06_14 {
|
|||||||
}
|
}
|
||||||
await this.checkCanCreateEventType(user.id, body);
|
await this.checkCanCreateEventType(user.id, body);
|
||||||
const eventTypeUser = await this.getUserToCreateEvent(user);
|
const eventTypeUser = await this.getUserToCreateEvent(user);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { destinationCalendar: _destinationCalendar, ...rest } = body;
|
const { destinationCalendar: _destinationCalendar, ...rest } = body;
|
||||||
|
|
||||||
const { eventType: eventTypeCreated } = await createEventType({
|
const { eventType: eventTypeCreated } = await createEventType({
|
||||||
@@ -93,34 +95,47 @@ export class EventTypesService_2024_06_14 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEventTypeByUsernameAndSlug(
|
async getEventTypeByUsernameAndSlug(params: {
|
||||||
username: string,
|
username: string;
|
||||||
eventTypeSlug: string,
|
eventTypeSlug: string;
|
||||||
orgSlug?: string,
|
orgSlug?: string;
|
||||||
orgId?: number
|
orgId?: number;
|
||||||
) {
|
authUser?: AuthOptionalUser;
|
||||||
const user = await this.usersRepository.findByUsername(username, orgSlug, orgId);
|
}) {
|
||||||
|
const user = await this.usersRepository.findByUsername(params.username, params.orgSlug, params.orgId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventType = await this.eventTypesRepository.getUserEventTypeBySlug(user.id, eventTypeSlug);
|
const eventType = await this.eventTypesRepository.getUserEventTypeBySlug(user.id, params.eventTypeSlug);
|
||||||
|
|
||||||
if (!eventType) {
|
if (!eventType) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventType.hidden && params.authUser?.id !== user.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ownerId: user.id,
|
ownerId: user.id,
|
||||||
...eventType,
|
...eventType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEventTypesByUsername(username: string, orgSlug?: string, orgId?: number) {
|
async getEventTypesByUsername(params: {
|
||||||
const user = await this.usersRepository.findByUsername(username, orgSlug, orgId);
|
username: string;
|
||||||
|
orgSlug?: string;
|
||||||
|
orgId?: number;
|
||||||
|
authUser?: AuthOptionalUser;
|
||||||
|
}) {
|
||||||
|
const user = await this.usersRepository.findByUsername(params.username, params.orgSlug, params.orgId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
if (params.authUser?.id !== user.id) {
|
||||||
|
return await this.getUserEventTypesPublic(user.id);
|
||||||
|
}
|
||||||
return await this.getUserEventTypes(user.id);
|
return await this.getUserEventTypes(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +187,14 @@ export class EventTypesService_2024_06_14 {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUserEventTypesPublic(userId: number) {
|
||||||
|
const eventTypes = await this.eventTypesRepository.getUserEventTypesPublic(userId);
|
||||||
|
|
||||||
|
return eventTypes.map((eventType) => {
|
||||||
|
return { ownerId: userId, ...eventType };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getEventTypesPublicByUsername(username: string): Promise<EventTypesPublic> {
|
async getEventTypesPublicByUsername(username: string): Promise<EventTypesPublic> {
|
||||||
const user = await this.usersRepository.findByUsername(username);
|
const user = await this.usersRepository.findByUsername(username);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -181,15 +204,26 @@ export class EventTypesService_2024_06_14 {
|
|||||||
return await getEventTypesPublic(user.id);
|
return await getEventTypesPublic(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEventTypes(queryParams: GetEventTypesQuery_2024_06_14) {
|
async getEventTypes(queryParams: GetEventTypesQuery_2024_06_14, authUser?: AuthOptionalUser) {
|
||||||
const { username, eventSlug, usernames, orgSlug, orgId } = queryParams;
|
const { username, eventSlug, usernames, orgSlug, orgId } = queryParams;
|
||||||
if (username && eventSlug) {
|
if (username && eventSlug) {
|
||||||
const eventType = await this.getEventTypeByUsernameAndSlug(username, eventSlug, orgSlug, orgId);
|
const eventType = await this.getEventTypeByUsernameAndSlug({
|
||||||
|
username,
|
||||||
|
eventTypeSlug: eventSlug,
|
||||||
|
orgSlug,
|
||||||
|
orgId,
|
||||||
|
authUser,
|
||||||
|
});
|
||||||
return eventType ? [eventType] : [];
|
return eventType ? [eventType] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
return await this.getEventTypesByUsername(username, orgSlug, orgId);
|
return await this.getEventTypesByUsername({
|
||||||
|
username,
|
||||||
|
orgSlug,
|
||||||
|
orgId,
|
||||||
|
authUser,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usernames) {
|
if (usernames) {
|
||||||
|
|||||||
+24
-1
@@ -31,6 +31,7 @@ import {
|
|||||||
EventTypeOutput_2024_06_14,
|
EventTypeOutput_2024_06_14,
|
||||||
OutputUnknownLocation_2024_06_14,
|
OutputUnknownLocation_2024_06_14,
|
||||||
OutputUnknownBookingField_2024_06_14,
|
OutputUnknownBookingField_2024_06_14,
|
||||||
|
OutputBookingField_2024_06_14,
|
||||||
} from "@calcom/platform-types";
|
} from "@calcom/platform-types";
|
||||||
|
|
||||||
type EventTypeRelations = {
|
type EventTypeRelations = {
|
||||||
@@ -253,7 +254,7 @@ export class OutputEventTypesService_2024_06_14 {
|
|||||||
type: "unknown",
|
type: "unknown",
|
||||||
slug: "unknown",
|
slug: "unknown",
|
||||||
bookingField: JSON.stringify(bookingField),
|
bookingField: JSON.stringify(bookingField),
|
||||||
});
|
} satisfies OutputUnknownBookingField_2024_06_14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,4 +345,26 @@ export class OutputEventTypesService_2024_06_14 {
|
|||||||
seatsShowAvailabilityCount: !!seatsShowAvailabilityCount,
|
seatsShowAvailabilityCount: !!seatsShowAvailabilityCount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getResponseEventTypesWithoutHiddenFields(
|
||||||
|
eventTypes: EventTypeOutput_2024_06_14[]
|
||||||
|
): EventTypeOutput_2024_06_14[] {
|
||||||
|
return eventTypes.map((eventType) => this.getResponseEventTypeWithoutHiddenFields(eventType));
|
||||||
|
}
|
||||||
|
|
||||||
|
getResponseEventTypeWithoutHiddenFields(eventType: EventTypeOutput_2024_06_14): EventTypeOutput_2024_06_14 {
|
||||||
|
if (!Array.isArray(eventType?.bookingFields) || eventType.bookingFields.length === 0) return eventType;
|
||||||
|
|
||||||
|
const visibleBookingFields: OutputBookingField_2024_06_14[] = [];
|
||||||
|
for (const bookingField of eventType.bookingFields) {
|
||||||
|
if ("hidden" in bookingField && bookingField.hidden === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visibleBookingFields.push(bookingField);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...eventType,
|
||||||
|
bookingFields: visibleBookingFields,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { GcalController } from "@/ee/gcal/gcal.controller";
|
import { GcalController } from "@/ee/gcal/gcal.controller";
|
||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
@@ -6,6 +7,7 @@ import { GCalService } from "@/modules/apps/services/gcal.service";
|
|||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
|
import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
@@ -13,7 +15,7 @@ import { Module } from "@nestjs/common";
|
|||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, TokensModule, OAuthClientModule],
|
imports: [PrismaModule, TokensModule, OAuthClientModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
ConfigService,
|
ConfigService,
|
||||||
@@ -21,6 +23,7 @@ import { ConfigService } from "@nestjs/config";
|
|||||||
SelectedCalendarsRepository,
|
SelectedCalendarsRepository,
|
||||||
GCalService,
|
GCalService,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
UsersRepository,
|
UsersRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MembershipRole } from "@prisma/client";
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export const SYSTEM_ADMIN_ROLE = "SYSADMIN";
|
export const SYSTEM_ADMIN_ROLE = "SYSADMIN";
|
||||||
export const ORG_ROLES = [
|
export const ORG_ROLES = [
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/inde
|
|||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
import { RedisService } from "@/modules/redis/redis.service";
|
import { RedisService } from "@/modules/redis/redis.service";
|
||||||
import { TeamsEventTypesModule } from "@/modules/teams/event-types/teams-event-types.module";
|
import { TeamsEventTypesModule } from "@/modules/teams/event-types/teams-event-types.module";
|
||||||
|
import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
|
||||||
import { UsersService } from "@/modules/users/services/users.service";
|
import { UsersService } from "@/modules/users/services/users.service";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
@@ -36,6 +37,7 @@ import { Module } from "@nestjs/common";
|
|||||||
SchedulesAtomsService,
|
SchedulesAtomsService,
|
||||||
VerificationAtomsService,
|
VerificationAtomsService,
|
||||||
RedisService,
|
RedisService,
|
||||||
|
TeamsRepository,
|
||||||
],
|
],
|
||||||
exports: [EventTypesAtomService],
|
exports: [EventTypesAtomService],
|
||||||
controllers: [
|
controllers: [
|
||||||
|
|||||||
@@ -64,4 +64,47 @@ export class AtomsRepository {
|
|||||||
|
|
||||||
return userTeams;
|
return userTeams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSecondaryEmails(userId: number) {
|
||||||
|
return await this.dbRead.prisma.secondaryEmail.findMany({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
emailVerified: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getExistingSecondaryEmailByUserAndEmail(userId: number, email: string) {
|
||||||
|
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
|
||||||
|
where: {
|
||||||
|
userId_email: { userId, email },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return existingSecondaryEmailRecord?.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getExistingSecondaryEmail(email: string) {
|
||||||
|
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
|
||||||
|
where: {
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return existingSecondaryEmailRecord?.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSecondaryEmail(userId: number, email: string) {
|
||||||
|
const existingSecondaryEmailRecord = await this.dbWrite.prisma.secondaryEmail.create({
|
||||||
|
data: {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
emailVerified: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return existingSecondaryEmailRecord?.email;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||||
import { Throttle } from "@/lib/endpoint-throttler-decorator";
|
import { Throttle } from "@/lib/endpoint-throttler-decorator";
|
||||||
|
import { AddVerifiedEmailInput } from "@/modules/atoms/inputs/add-verified-email.input";
|
||||||
import { CheckEmailVerificationRequiredParams } from "@/modules/atoms/inputs/check-email-verification-required-params";
|
import { CheckEmailVerificationRequiredParams } from "@/modules/atoms/inputs/check-email-verification-required-params";
|
||||||
|
import { GetVerifiedEmailsParams } from "@/modules/atoms/inputs/get-verified-emails-params";
|
||||||
import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verification-email.input";
|
import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verification-email.input";
|
||||||
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
|
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
|
||||||
|
import { GetVerifiedEmailsOutput } from "@/modules/atoms/outputs/get-verified-emails-output";
|
||||||
import { SendVerificationEmailOutput } from "@/modules/atoms/outputs/send-verification-email.output";
|
import { SendVerificationEmailOutput } from "@/modules/atoms/outputs/send-verification-email.output";
|
||||||
import { VerifyEmailCodeOutput } from "@/modules/atoms/outputs/verify-email-code.output";
|
import { VerifyEmailCodeOutput } from "@/modules/atoms/outputs/verify-email-code.output";
|
||||||
import { VerificationAtomsService } from "@/modules/atoms/services/verification-atom.service";
|
import { VerificationAtomsService } from "@/modules/atoms/services/verification-atom.service";
|
||||||
@@ -105,4 +108,44 @@ export class AtomsVerificationController {
|
|||||||
status: SUCCESS_STATUS,
|
status: SUCCESS_STATUS,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("/emails/verified-emails")
|
||||||
|
@Version(VERSION_NEUTRAL)
|
||||||
|
@UseGuards(ApiAuthGuard)
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async getVerifiedEmails(
|
||||||
|
@Query() query: GetVerifiedEmailsParams,
|
||||||
|
@GetUser() user: UserWithProfile
|
||||||
|
): Promise<GetVerifiedEmailsOutput> {
|
||||||
|
const verifiedEmails = await this.verificationService.getVerifiedEmails({
|
||||||
|
userId: user.id,
|
||||||
|
userEmail: user.email,
|
||||||
|
teamId: query.teamId ? Number(query.teamId) : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: verifiedEmails,
|
||||||
|
status: SUCCESS_STATUS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("/emails/verified-emails")
|
||||||
|
@Version(VERSION_NEUTRAL)
|
||||||
|
@UseGuards(ApiAuthGuard)
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async addVerifiedEmails(
|
||||||
|
@Body() body: AddVerifiedEmailInput,
|
||||||
|
@GetUser() user: UserWithProfile
|
||||||
|
): Promise<ApiResponse<{ emailVerified: boolean }>> {
|
||||||
|
const emailVerified = await this.verificationService.addVerifiedEmail({
|
||||||
|
userId: user.id,
|
||||||
|
existingPrimaryEmail: user.email,
|
||||||
|
email: body.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: { emailVerified },
|
||||||
|
status: SUCCESS_STATUS,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsEmail } from "class-validator";
|
||||||
|
|
||||||
|
export class AddVerifiedEmailInput {
|
||||||
|
@ApiProperty({ example: "user@example.com" })
|
||||||
|
@IsEmail()
|
||||||
|
email!: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsNumber, IsOptional, IsString, IsEmail } from "class-validator";
|
||||||
|
|
||||||
|
export class GetVerifiedEmailsParams {
|
||||||
|
@ApiPropertyOptional({ example: "12345" })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
teamId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetVerifiedEmailsInput {
|
||||||
|
@ApiProperty()
|
||||||
|
@IsNumber()
|
||||||
|
userId!: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsEmail()
|
||||||
|
@IsString()
|
||||||
|
userEmail!: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
teamId?: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { Expose } from "class-transformer";
|
||||||
|
import { IsString, IsArray } from "class-validator";
|
||||||
|
|
||||||
|
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||||
|
|
||||||
|
export class GetVerifiedEmailsOutput {
|
||||||
|
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
|
||||||
|
@IsString()
|
||||||
|
@Expose()
|
||||||
|
readonly status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
@IsString({ each: true })
|
||||||
|
@ApiProperty({ type: [String] })
|
||||||
|
readonly data!: string[];
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { AtomsRepository } from "@/modules/atoms/atoms.repository";
|
||||||
import { CheckEmailVerificationRequiredParams } from "@/modules/atoms/inputs/check-email-verification-required-params";
|
import { CheckEmailVerificationRequiredParams } from "@/modules/atoms/inputs/check-email-verification-required-params";
|
||||||
|
import { GetVerifiedEmailsInput } from "@/modules/atoms/inputs/get-verified-emails-params";
|
||||||
import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verification-email.input";
|
import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verification-email.input";
|
||||||
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
|
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
|
||||||
|
import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
|
||||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||||
import { Injectable, BadRequestException, UnauthorizedException } from "@nestjs/common";
|
import { Injectable, BadRequestException, UnauthorizedException } from "@nestjs/common";
|
||||||
|
|
||||||
@@ -13,6 +16,11 @@ import {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VerificationAtomsService {
|
export class VerificationAtomsService {
|
||||||
|
constructor(
|
||||||
|
private readonly atomsRepository: AtomsRepository,
|
||||||
|
private readonly teamsRepository: TeamsRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
async checkEmailVerificationRequired(input: CheckEmailVerificationRequiredParams) {
|
async checkEmailVerificationRequired(input: CheckEmailVerificationRequiredParams) {
|
||||||
return await checkEmailVerificationRequired(input);
|
return await checkEmailVerificationRequired(input);
|
||||||
}
|
}
|
||||||
@@ -61,4 +69,78 @@ export class VerificationAtomsService {
|
|||||||
isVerifyingEmail: input.isVerifyingEmail,
|
isVerifyingEmail: input.isVerifyingEmail,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getVerifiedEmails(input: GetVerifiedEmailsInput): Promise<string[]> {
|
||||||
|
const { userId, userEmail, teamId } = input;
|
||||||
|
const userEmailWithoutOauthClientId = this.removeClientIdFromEmail(userEmail);
|
||||||
|
|
||||||
|
if (teamId) {
|
||||||
|
const verifiedEmails: string[] = [];
|
||||||
|
const teamMembers = await this.teamsRepository.getTeamMemberEmails(teamId);
|
||||||
|
|
||||||
|
if (teamMembers.length === 0) {
|
||||||
|
return verifiedEmails;
|
||||||
|
}
|
||||||
|
|
||||||
|
teamMembers.forEach((member) => {
|
||||||
|
const memberEmailWithoutOauthClientId = this.removeClientIdFromEmail(member.email);
|
||||||
|
|
||||||
|
verifiedEmails.push(memberEmailWithoutOauthClientId);
|
||||||
|
member.secondaryEmails.forEach((secondaryEmail) => {
|
||||||
|
verifiedEmails.push(this.removeClientIdFromEmail(secondaryEmail.email));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return verifiedEmails;
|
||||||
|
}
|
||||||
|
|
||||||
|
let verifiedEmails = [userEmailWithoutOauthClientId];
|
||||||
|
|
||||||
|
const secondaryEmails = await this.atomsRepository.getSecondaryEmails(userId);
|
||||||
|
verifiedEmails = verifiedEmails.concat(
|
||||||
|
secondaryEmails.map((secondaryEmail) => this.removeClientIdFromEmail(secondaryEmail.email))
|
||||||
|
);
|
||||||
|
|
||||||
|
return verifiedEmails;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addVerifiedEmail({
|
||||||
|
userId,
|
||||||
|
existingPrimaryEmail,
|
||||||
|
email,
|
||||||
|
}: {
|
||||||
|
userId: number;
|
||||||
|
existingPrimaryEmail: string;
|
||||||
|
email: string;
|
||||||
|
}): Promise<boolean> {
|
||||||
|
const existingSecondaryEmail = await this.atomsRepository.getExistingSecondaryEmailByUserAndEmail(
|
||||||
|
userId,
|
||||||
|
email
|
||||||
|
);
|
||||||
|
const alreadyExistingEmail = await this.atomsRepository.getExistingSecondaryEmail(email);
|
||||||
|
|
||||||
|
if (alreadyExistingEmail) {
|
||||||
|
throw new BadRequestException("Email already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingPrimaryEmail === email || existingSecondaryEmail === email) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.atomsRepository.addSecondaryEmail(userId, email);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeClientIdFromEmail(email: string): string {
|
||||||
|
const [localPart, domain] = email.split("@");
|
||||||
|
const localPartSegments = localPart.split("+");
|
||||||
|
|
||||||
|
localPartSegments.pop();
|
||||||
|
|
||||||
|
const baseEmail = localPartSegments.join("+");
|
||||||
|
const normalizedEmail = `${baseEmail}@${domain}`;
|
||||||
|
|
||||||
|
return normalizedEmail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export const MembershipRoles = Reflector.createDecorator<MembershipRole[]>();
|
export const MembershipRoles = Reflector.createDecorator<MembershipRole[]>();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { UserWithProfile } from "@/modules/users/users.repository";
|
|||||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||||
import { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
|
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationRolesGuard implements CanActivate {
|
export class OrganizationRolesGuard implements CanActivate {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BookingReferencesRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/booking-references.repository";
|
import { BookingReferencesRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/booking-references.repository";
|
||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { GoogleCalendarService as GCalService } from "@/ee/calendars/services/gcal.service";
|
import { GoogleCalendarService as GCalService } from "@/ee/calendars/services/gcal.service";
|
||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
@@ -8,13 +9,14 @@ import { GoogleCalendarService } from "@/modules/cal-unified-calendars/services/
|
|||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TokensModule],
|
imports: [TokensModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
GCalService,
|
GCalService,
|
||||||
GoogleCalendarService,
|
GoogleCalendarService,
|
||||||
@@ -22,6 +24,7 @@ import { Module } from "@nestjs/common";
|
|||||||
BookingReferencesRepository_2024_08_13,
|
BookingReferencesRepository_2024_08_13,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
SelectedCalendarsRepository,
|
SelectedCalendarsRepository,
|
||||||
PrismaReadService,
|
PrismaReadService,
|
||||||
PrismaWriteService,
|
PrismaWriteService,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
@@ -6,15 +7,17 @@ import { DestinationCalendarsController } from "@/modules/destination-calendars/
|
|||||||
import { DestinationCalendarsRepository } from "@/modules/destination-calendars/destination-calendars.repository";
|
import { DestinationCalendarsRepository } from "@/modules/destination-calendars/destination-calendars.repository";
|
||||||
import { DestinationCalendarsService } from "@/modules/destination-calendars/services/destination-calendars.service";
|
import { DestinationCalendarsService } from "@/modules/destination-calendars/services/destination-calendars.service";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule],
|
imports: [PrismaModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
DestinationCalendarsService,
|
DestinationCalendarsService,
|
||||||
DestinationCalendarsRepository,
|
DestinationCalendarsRepository,
|
||||||
UsersRepository,
|
UsersRepository,
|
||||||
|
|||||||
+3
@@ -1,4 +1,5 @@
|
|||||||
import { ConnectedCalendar, Calendar } from "@/ee/calendars/outputs/connected-calendars.output";
|
import { ConnectedCalendar, Calendar } from "@/ee/calendars/outputs/connected-calendars.output";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { DestinationCalendarsRepository } from "@/modules/destination-calendars/destination-calendars.repository";
|
import { DestinationCalendarsRepository } from "@/modules/destination-calendars/destination-calendars.repository";
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
@@ -7,6 +8,7 @@ import { Injectable, NotFoundException } from "@nestjs/common";
|
|||||||
export class DestinationCalendarsService {
|
export class DestinationCalendarsService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly calendarsService: CalendarsService,
|
private readonly calendarsService: CalendarsService,
|
||||||
|
private readonly calendarsCacheService: CalendarsCacheService,
|
||||||
private readonly destinationCalendarsRepository: DestinationCalendarsRepository
|
private readonly destinationCalendarsRepository: DestinationCalendarsRepository
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ export class DestinationCalendarsService {
|
|||||||
delegatedCalendar ? delegationCredentialId : undefined
|
delegatedCalendar ? delegationCredentialId : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);
|
||||||
return {
|
return {
|
||||||
userId: updatedCalendarUserId,
|
userId: updatedCalendarUserId,
|
||||||
integration: updatedCalendarIntegration,
|
integration: updatedCalendarIntegration,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MembershipsRepository {
|
export class MembershipsRepository {
|
||||||
|
|||||||
+2
-4
@@ -1,5 +1,4 @@
|
|||||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||||
import { Locales } from "@/lib/enums/locales";
|
|
||||||
import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-roles.decorator";
|
import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-roles.decorator";
|
||||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||||
import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
|
import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
|
||||||
@@ -7,7 +6,6 @@ import { GetManagedUsersInput } from "@/modules/oauth-clients/controllers/oauth-
|
|||||||
import { CreateManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/create-managed-user.output";
|
import { CreateManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/create-managed-user.output";
|
||||||
import { GetManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-user.output";
|
import { GetManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-user.output";
|
||||||
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
|
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
|
||||||
import { ManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/managed-user.output";
|
|
||||||
import { TOKENS_DOCS } from "@/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller";
|
import { TOKENS_DOCS } from "@/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller";
|
||||||
import { KeysResponseDto } from "@/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto";
|
import { KeysResponseDto } from "@/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto";
|
||||||
import { OAuthClientGuard } from "@/modules/oauth-clients/guards/oauth-client-guard";
|
import { OAuthClientGuard } from "@/modules/oauth-clients/guards/oauth-client-guard";
|
||||||
@@ -34,10 +32,10 @@ import {
|
|||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiOperation, ApiTags as DocsTags, ApiHeader } from "@nestjs/swagger";
|
import { ApiOperation, ApiTags as DocsTags, ApiHeader } from "@nestjs/swagger";
|
||||||
import { User, MembershipRole } from "@prisma/client";
|
import { User } from "@prisma/client";
|
||||||
import { plainToInstance } from "class-transformer";
|
|
||||||
|
|
||||||
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
|
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
@Controller({
|
@Controller({
|
||||||
path: "/v2/oauth-clients/:clientId/users",
|
path: "/v2/oauth-clients/:clientId/users",
|
||||||
|
|||||||
+2
-1
@@ -17,10 +17,11 @@ import { OAuthClientWebhooksService } from "@/modules/webhooks/services/oauth-cl
|
|||||||
import { WebhooksService } from "@/modules/webhooks/services/webhooks.service";
|
import { WebhooksService } from "@/modules/webhooks/services/webhooks.service";
|
||||||
import { Controller, Post, Body, UseGuards, Get, Param, Query, Delete, Patch } from "@nestjs/common";
|
import { Controller, Post, Body, UseGuards, Get, Param, Query, Delete, Patch } from "@nestjs/common";
|
||||||
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
|
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
|
||||||
import { Webhook, MembershipRole } from "@prisma/client";
|
import { Webhook } from "@prisma/client";
|
||||||
import { plainToClass } from "class-transformer";
|
import { plainToClass } from "class-transformer";
|
||||||
|
|
||||||
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
|
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
import { SkipTakePagination } from "@calcom/platform-types";
|
import { SkipTakePagination } from "@calcom/platform-types";
|
||||||
|
|
||||||
import { OAuthClientGuard } from "../../guards/oauth-client-guard";
|
import { OAuthClientGuard } from "../../guards/oauth-client-guard";
|
||||||
|
|||||||
+1
-3
@@ -5,12 +5,10 @@ import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-role
|
|||||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||||
import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
|
import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
|
||||||
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
|
import { GetManagedUsersOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/get-managed-users.output";
|
||||||
import { ManagedUserOutput } from "@/modules/oauth-clients/controllers/oauth-client-users/outputs/managed-user.output";
|
|
||||||
import { CreateOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto";
|
import { CreateOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto";
|
||||||
import { GetOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
|
import { GetOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
|
||||||
import { GetOAuthClientsResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto";
|
import { GetOAuthClientsResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto";
|
||||||
import { OAuthClientGuard } from "@/modules/oauth-clients/guards/oauth-client-guard";
|
import { OAuthClientGuard } from "@/modules/oauth-clients/guards/oauth-client-guard";
|
||||||
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
|
|
||||||
import { OAuthClientUsersOutputService } from "@/modules/oauth-clients/services/oauth-clients-users-output.service";
|
import { OAuthClientUsersOutputService } from "@/modules/oauth-clients/services/oauth-clients-users-output.service";
|
||||||
import { OAuthClientsService } from "@/modules/oauth-clients/services/oauth-clients/oauth-clients.service";
|
import { OAuthClientsService } from "@/modules/oauth-clients/services/oauth-clients/oauth-clients.service";
|
||||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||||
@@ -37,9 +35,9 @@ import {
|
|||||||
ApiExcludeEndpoint,
|
ApiExcludeEndpoint,
|
||||||
ApiHeader,
|
ApiHeader,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
import { User, MembershipRole } from "@prisma/client";
|
|
||||||
|
|
||||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
import { CreateOAuthClientInput, UpdateOAuthClientInput, Pagination } from "@calcom/platform-types";
|
import { CreateOAuthClientInput, UpdateOAuthClientInput, Pagination } from "@calcom/platform-types";
|
||||||
|
|
||||||
@Controller({
|
@Controller({
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
|
||||||
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
|
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
|
||||||
@@ -49,6 +50,7 @@ import { JwtService } from "@nestjs/jwt";
|
|||||||
TokensRepository,
|
TokensRepository,
|
||||||
OAuthFlowService,
|
OAuthFlowService,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-us
|
|||||||
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { BadRequestException, ConflictException, Injectable, Logger } from "@nestjs/common";
|
import { BadRequestException, ConflictException, Injectable, Logger } from "@nestjs/common";
|
||||||
import { User, CreationSource, PlatformOAuthClient } from "@prisma/client";
|
import { User, PlatformOAuthClient } from "@prisma/client";
|
||||||
|
|
||||||
import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries";
|
import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries";
|
||||||
|
import { CreationSource } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OAuthClientUsersService {
|
export class OAuthClientUsersService {
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,5 @@
|
|||||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/attributes/options/inputs/create-organization-attribute-option.input";
|
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/attributes/options/inputs/create-organization-attribute-option.input";
|
||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { AttributeType } from "@prisma/client";
|
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
@@ -12,6 +11,8 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
|
|
||||||
|
import { AttributeType } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateOrganizationAttributeInput {
|
export class CreateOrganizationAttributeInput {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { AttributeType } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsEnum, IsOptional, IsString } from "class-validator";
|
import { IsBoolean, IsEnum, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
|
import { AttributeType } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class UpdateOrganizationAttributeInput {
|
export class UpdateOrganizationAttributeInput {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
+2
-1
@@ -6,7 +6,7 @@ import { UsersModule } from "@/modules/users/users.module";
|
|||||||
import { INestApplication } from "@nestjs/common";
|
import { INestApplication } from "@nestjs/common";
|
||||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||||
import { Test } from "@nestjs/testing";
|
import { Test } from "@nestjs/testing";
|
||||||
import { SchedulingType, User } from "@prisma/client";
|
import { User } from "@prisma/client";
|
||||||
import * as request from "supertest";
|
import * as request from "supertest";
|
||||||
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
||||||
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
ConfirmationPolicyEnum,
|
ConfirmationPolicyEnum,
|
||||||
NoticeThresholdUnitEnum,
|
NoticeThresholdUnitEnum,
|
||||||
} from "@calcom/platform-enums";
|
} from "@calcom/platform-enums";
|
||||||
|
import { SchedulingType } from "@calcom/platform-libraries";
|
||||||
import {
|
import {
|
||||||
ApiSuccessResponse,
|
ApiSuccessResponse,
|
||||||
CreateTeamEventTypeInput_2024_06_14,
|
CreateTeamEventTypeInput_2024_06_14,
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
|
|||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { SchedulingType } from "@calcom/platform-libraries";
|
||||||
import {
|
import {
|
||||||
CreateTeamEventTypeInput_2024_06_14,
|
CreateTeamEventTypeInput_2024_06_14,
|
||||||
UpdateTeamEventTypeInput_2024_06_14,
|
UpdateTeamEventTypeInput_2024_06_14,
|
||||||
HostPriority,
|
HostPriority,
|
||||||
} from "@calcom/platform-types";
|
} from "@calcom/platform-types";
|
||||||
import { SchedulingType } from "@calcom/prisma/client";
|
|
||||||
|
|
||||||
export type TransformedCreateTeamEventTypeInput = Awaited<
|
export type TransformedCreateTeamEventTypeInput = Awaited<
|
||||||
ReturnType<InstanceType<typeof InputOrganizationsEventTypesService>["transformInputCreateTeamEventType"]>
|
ReturnType<InstanceType<typeof InputOrganizationsEventTypesService>["transformInputCreateTeamEventType"]>
|
||||||
|
|||||||
@@ -2,9 +2,17 @@ import { OutputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types
|
|||||||
import { TeamsEventTypesRepository } from "@/modules/teams/event-types/teams-event-types.repository";
|
import { TeamsEventTypesRepository } from "@/modules/teams/event-types/teams-event-types.repository";
|
||||||
import { UsersRepository } from "@/modules/users/users.repository";
|
import { UsersRepository } from "@/modules/users/users.repository";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import type { EventType, User, Schedule, Host, DestinationCalendar, CalVideoSettings } from "@prisma/client";
|
import type {
|
||||||
import { SchedulingType, Team } from "@prisma/client";
|
Team,
|
||||||
|
EventType,
|
||||||
|
User,
|
||||||
|
Schedule,
|
||||||
|
Host,
|
||||||
|
DestinationCalendar,
|
||||||
|
CalVideoSettings,
|
||||||
|
} from "@prisma/client";
|
||||||
|
|
||||||
|
import { SchedulingType } from "@calcom/platform-libraries";
|
||||||
import { HostPriority, TeamEventTypeResponseHost } from "@calcom/platform-types";
|
import { HostPriority, TeamEventTypeResponseHost } from "@calcom/platform-types";
|
||||||
|
|
||||||
type EventTypeRelations = {
|
type EventTypeRelations = {
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateOrgMembershipDto {
|
export class CreateOrgMembershipDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class UpdateOrgMembershipDto {
|
export class UpdateOrgMembershipDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
+1
-1
@@ -1,9 +1,9 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { IsEnum, IsString, ValidateNested } from "class-validator";
|
import { IsEnum, IsString, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
import { OrgTeamOutputDto } from "@calcom/platform-types";
|
import { OrgTeamOutputDto } from "@calcom/platform-types";
|
||||||
|
|
||||||
export class OrgMeTeamOutputDto extends OrgTeamOutputDto {
|
export class OrgMeTeamOutputDto extends OrgTeamOutputDto {
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateOrgTeamMembershipDto {
|
export class CreateOrgTeamMembershipDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class UpdateOrgTeamMembershipDto {
|
export class UpdateOrgTeamMembershipDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
|
import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
|
||||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsString, IsOptional, IsBoolean, IsEnum } from "class-validator";
|
import { IsString, IsOptional, IsBoolean, IsEnum } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateOrganizationUserInput extends CreateUserInput {
|
export class CreateOrganizationUserInput extends CreateUserInput {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
+2
-1
@@ -4,10 +4,11 @@ import { UpdateOrganizationUserInput } from "@/modules/organizations/users/index
|
|||||||
import { OrganizationsUsersRepository } from "@/modules/organizations/users/index/organizations-users.repository";
|
import { OrganizationsUsersRepository } from "@/modules/organizations/users/index/organizations-users.repository";
|
||||||
import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
|
import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
|
||||||
import { Injectable, ConflictException, ForbiddenException } from "@nestjs/common";
|
import { Injectable, ConflictException, ForbiddenException } from "@nestjs/common";
|
||||||
import { Team, CreationSource } from "@prisma/client";
|
import { Team } from "@prisma/client";
|
||||||
import { plainToInstance } from "class-transformer";
|
import { plainToInstance } from "class-transformer";
|
||||||
|
|
||||||
import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries";
|
import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries";
|
||||||
|
import { CreationSource } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationsUsersService {
|
export class OrganizationsUsersService {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
|
||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { AppsRepository } from "@/modules/apps/apps.repository";
|
import { AppsRepository } from "@/modules/apps/apps.repository";
|
||||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||||
@@ -7,6 +8,7 @@ import { OrganizationsMembershipRepository } from "@/modules/organizations/membe
|
|||||||
import { OrganizationsMembershipOutputService } from "@/modules/organizations/memberships/services/organizations-membership-output.service";
|
import { OrganizationsMembershipOutputService } from "@/modules/organizations/memberships/services/organizations-membership-output.service";
|
||||||
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
|
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
|
import { RedisModule } from "@/modules/redis/redis.module";
|
||||||
import { SelectedCalendarsController } from "@/modules/selected-calendars/controllers/selected-calendars.controller";
|
import { SelectedCalendarsController } from "@/modules/selected-calendars/controllers/selected-calendars.controller";
|
||||||
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
|
||||||
import { SelectedCalendarsService } from "@/modules/selected-calendars/services/selected-calendars.service";
|
import { SelectedCalendarsService } from "@/modules/selected-calendars/services/selected-calendars.service";
|
||||||
@@ -14,11 +16,12 @@ import { UsersRepository } from "@/modules/users/users.repository";
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule],
|
imports: [PrismaModule, RedisModule],
|
||||||
providers: [
|
providers: [
|
||||||
SelectedCalendarsRepository,
|
SelectedCalendarsRepository,
|
||||||
CalendarsRepository,
|
CalendarsRepository,
|
||||||
CalendarsService,
|
CalendarsService,
|
||||||
|
CalendarsCacheService,
|
||||||
UsersRepository,
|
UsersRepository,
|
||||||
CredentialsRepository,
|
CredentialsRepository,
|
||||||
AppsRepository,
|
AppsRepository,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
|
||||||
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
|
||||||
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
|
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
|
||||||
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
|
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
|
||||||
@@ -23,6 +24,7 @@ type SelectedCalendarsInputDelegationCredential = SelectedCalendarsInputDto & {
|
|||||||
export class SelectedCalendarsService {
|
export class SelectedCalendarsService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly calendarsService: CalendarsService,
|
private readonly calendarsService: CalendarsService,
|
||||||
|
private readonly calendarsCacheService: CalendarsCacheService,
|
||||||
private readonly selectedCalendarsRepository: SelectedCalendarsRepository,
|
private readonly selectedCalendarsRepository: SelectedCalendarsRepository,
|
||||||
private readonly organizationsMembershipService: OrganizationsMembershipService,
|
private readonly organizationsMembershipService: OrganizationsMembershipService,
|
||||||
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository
|
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository
|
||||||
@@ -50,6 +52,8 @@ export class SelectedCalendarsService {
|
|||||||
credentialId
|
credentialId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(user.id);
|
||||||
|
|
||||||
return userSelectedCalendar;
|
return userSelectedCalendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +126,7 @@ export class SelectedCalendarsService {
|
|||||||
externalId,
|
externalId,
|
||||||
delegationCredentialId
|
delegationCredentialId
|
||||||
);
|
);
|
||||||
|
await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(user.id);
|
||||||
return removedCalendarEntry;
|
return removedCalendarEntry;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum, IsInt } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateTeamMembershipInput {
|
export class CreateTeamMembershipInput {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@ApiProperty({ type: Number })
|
@ApiProperty({ type: Number })
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
import { IsBoolean, IsOptional, IsEnum } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class UpdateTeamMembershipInput {
|
export class UpdateTeamMembershipInput {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { Expose, Transform, Type } from "class-transformer";
|
import { Expose, Transform, Type } from "class-transformer";
|
||||||
import { IsBoolean, IsInt, IsObject, IsOptional, IsString, ValidateNested } from "class-validator";
|
import { IsBoolean, IsInt, IsObject, IsOptional, IsString, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
class MembershipUserOutputDto {
|
class MembershipUserOutputDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { IsEnum, IsString, ValidateNested } from "class-validator";
|
import { IsEnum, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
import { TeamOutputDto } from "@calcom/platform-types";
|
import { TeamOutputDto } from "@calcom/platform-types";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { IsEnum, IsString, ValidateNested } from "class-validator";
|
import { IsEnum, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
import { TeamOutputDto } from "@calcom/platform-types";
|
import { TeamOutputDto } from "@calcom/platform-types";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { MembershipRole } from "@prisma/client";
|
|
||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { IsEnum, IsString, ValidateNested } from "class-validator";
|
import { IsEnum, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
import { TeamOutputDto } from "@calcom/platform-types";
|
import { TeamOutputDto } from "@calcom/platform-types";
|
||||||
|
|||||||
@@ -120,4 +120,30 @@ export class TeamsRepository {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTeamMemberEmails(teamId: number) {
|
||||||
|
return this.dbRead.prisma.user.findMany({
|
||||||
|
where: {
|
||||||
|
teams: {
|
||||||
|
some: {
|
||||||
|
teamId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
secondaryEmails: {
|
||||||
|
where: {
|
||||||
|
emailVerified: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-us
|
|||||||
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import type { Profile, User, Team, Prisma } from "@prisma/client";
|
import type { Profile, User, Team, Prisma } from "@prisma/client";
|
||||||
import { CreationSource } from "@prisma/client";
|
|
||||||
|
import { CreationSource } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export type UserWithProfile = User & {
|
export type UserWithProfile = User & {
|
||||||
movedToProfile?: (Profile & { organization: Pick<Team, "isPlatform" | "id" | "slug" | "name"> }) | null;
|
movedToProfile?: (Profile & { organization: Pick<Team, "isPlatform" | "id" | "slug" | "name"> }) | null;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger";
|
||||||
import { WebhookTriggerEvents } from "@prisma/client";
|
|
||||||
import { IsArray, IsBoolean, IsEnum, IsOptional, IsString } from "class-validator";
|
import { IsArray, IsBoolean, IsEnum, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
|
import { WebhookTriggerEvents } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class CreateWebhookInputDto {
|
export class CreateWebhookInputDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { WebhookTriggerEvents } from "@prisma/client";
|
|
||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { IsBoolean, IsEnum, IsInt, IsString, ValidateNested, IsArray } from "class-validator";
|
import { IsBoolean, IsEnum, IsInt, IsString, ValidateNested, IsArray } from "class-validator";
|
||||||
|
|
||||||
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||||
|
import { WebhookTriggerEvents } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class WebhookOutputDto {
|
export class WebhookOutputDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ import {
|
|||||||
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
||||||
BaseWorkflowTriggerDto,
|
BaseWorkflowTriggerDto,
|
||||||
BEFORE_EVENT,
|
BEFORE_EVENT,
|
||||||
|
BOOKING_NO_SHOW_UPDATED,
|
||||||
|
BOOKING_PAID,
|
||||||
|
BOOKING_PAYMENT_INITIATED,
|
||||||
|
BOOKING_REJECTED,
|
||||||
|
BOOKING_REQUESTED,
|
||||||
EVENT_CANCELLED,
|
EVENT_CANCELLED,
|
||||||
NEW_EVENT,
|
NEW_EVENT,
|
||||||
OnAfterCalVideoGuestsNoShowTriggerDto,
|
OnAfterCalVideoGuestsNoShowTriggerDto,
|
||||||
@@ -41,6 +46,11 @@ import {
|
|||||||
OnBeforeEventTriggerDto,
|
OnBeforeEventTriggerDto,
|
||||||
OnCancelTriggerDto,
|
OnCancelTriggerDto,
|
||||||
OnCreationTriggerDto,
|
OnCreationTriggerDto,
|
||||||
|
OnNoShowUpdateTriggerDto,
|
||||||
|
OnPaidTriggerDto,
|
||||||
|
OnPaymentInitiatedTriggerDto,
|
||||||
|
OnRejectedTriggerDto,
|
||||||
|
OnRequestedTriggerDto,
|
||||||
OnRescheduleTriggerDto,
|
OnRescheduleTriggerDto,
|
||||||
RESCHEDULE_EVENT,
|
RESCHEDULE_EVENT,
|
||||||
} from "./workflow-trigger.input";
|
} from "./workflow-trigger.input";
|
||||||
@@ -73,6 +83,11 @@ export type TriggerDtoType =
|
|||||||
| OnRescheduleTriggerDto
|
| OnRescheduleTriggerDto
|
||||||
| OnCancelTriggerDto
|
| OnCancelTriggerDto
|
||||||
| OnAfterCalVideoGuestsNoShowTriggerDto
|
| OnAfterCalVideoGuestsNoShowTriggerDto
|
||||||
|
| OnRejectedTriggerDto
|
||||||
|
| OnRequestedTriggerDto
|
||||||
|
| OnPaymentInitiatedTriggerDto
|
||||||
|
| OnPaidTriggerDto
|
||||||
|
| OnNoShowUpdateTriggerDto
|
||||||
| OnAfterCalVideoHostsNoShowTriggerDto;
|
| OnAfterCalVideoHostsNoShowTriggerDto;
|
||||||
|
|
||||||
@ApiExtraModels(
|
@ApiExtraModels(
|
||||||
@@ -81,6 +96,11 @@ export type TriggerDtoType =
|
|||||||
OnCancelTriggerDto,
|
OnCancelTriggerDto,
|
||||||
OnCreationTriggerDto,
|
OnCreationTriggerDto,
|
||||||
OnRescheduleTriggerDto,
|
OnRescheduleTriggerDto,
|
||||||
|
OnNoShowUpdateTriggerDto,
|
||||||
|
OnRejectedTriggerDto,
|
||||||
|
OnRequestedTriggerDto,
|
||||||
|
OnPaymentInitiatedTriggerDto,
|
||||||
|
OnPaidTriggerDto,
|
||||||
OnAfterCalVideoGuestsNoShowTriggerDto,
|
OnAfterCalVideoGuestsNoShowTriggerDto,
|
||||||
OnAfterCalVideoHostsNoShowTriggerDto,
|
OnAfterCalVideoHostsNoShowTriggerDto,
|
||||||
WorkflowEmailAddressStepDto,
|
WorkflowEmailAddressStepDto,
|
||||||
@@ -112,6 +132,11 @@ export class CreateWorkflowDto {
|
|||||||
{ $ref: getSchemaPath(OnRescheduleTriggerDto) },
|
{ $ref: getSchemaPath(OnRescheduleTriggerDto) },
|
||||||
{ $ref: getSchemaPath(OnAfterCalVideoGuestsNoShowTriggerDto) },
|
{ $ref: getSchemaPath(OnAfterCalVideoGuestsNoShowTriggerDto) },
|
||||||
{ $ref: getSchemaPath(OnAfterCalVideoHostsNoShowTriggerDto) },
|
{ $ref: getSchemaPath(OnAfterCalVideoHostsNoShowTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnRejectedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnRequestedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnPaidTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnPaymentInitiatedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnNoShowUpdateTriggerDto) },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@@ -126,6 +151,11 @@ export class CreateWorkflowDto {
|
|||||||
{ value: OnRescheduleTriggerDto, name: RESCHEDULE_EVENT },
|
{ value: OnRescheduleTriggerDto, name: RESCHEDULE_EVENT },
|
||||||
{ value: OnAfterCalVideoGuestsNoShowTriggerDto, name: AFTER_GUESTS_CAL_VIDEO_NO_SHOW },
|
{ value: OnAfterCalVideoGuestsNoShowTriggerDto, name: AFTER_GUESTS_CAL_VIDEO_NO_SHOW },
|
||||||
{ value: OnAfterCalVideoHostsNoShowTriggerDto, name: AFTER_HOSTS_CAL_VIDEO_NO_SHOW },
|
{ value: OnAfterCalVideoHostsNoShowTriggerDto, name: AFTER_HOSTS_CAL_VIDEO_NO_SHOW },
|
||||||
|
{ value: OnRequestedTriggerDto, name: BOOKING_REQUESTED },
|
||||||
|
{ value: OnRejectedTriggerDto, name: BOOKING_REJECTED },
|
||||||
|
{ value: OnPaymentInitiatedTriggerDto, name: BOOKING_PAYMENT_INITIATED },
|
||||||
|
{ value: OnPaidTriggerDto, name: BOOKING_PAID },
|
||||||
|
{ value: OnNoShowUpdateTriggerDto, name: BOOKING_NO_SHOW_UPDATED },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -135,6 +165,11 @@ export class CreateWorkflowDto {
|
|||||||
| OnCreationTriggerDto
|
| OnCreationTriggerDto
|
||||||
| OnRescheduleTriggerDto
|
| OnRescheduleTriggerDto
|
||||||
| OnCancelTriggerDto
|
| OnCancelTriggerDto
|
||||||
|
| OnRejectedTriggerDto
|
||||||
|
| OnRequestedTriggerDto
|
||||||
|
| OnPaidTriggerDto
|
||||||
|
| OnPaymentInitiatedTriggerDto
|
||||||
|
| OnNoShowUpdateTriggerDto
|
||||||
| OnAfterCalVideoGuestsNoShowTriggerDto
|
| OnAfterCalVideoGuestsNoShowTriggerDto
|
||||||
| OnAfterCalVideoHostsNoShowTriggerDto;
|
| OnAfterCalVideoHostsNoShowTriggerDto;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ import {
|
|||||||
AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
||||||
OnAfterCalVideoHostsNoShowTriggerDto,
|
OnAfterCalVideoHostsNoShowTriggerDto,
|
||||||
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
||||||
|
OnNoShowUpdateTriggerDto,
|
||||||
|
OnRejectedTriggerDto,
|
||||||
|
OnRequestedTriggerDto,
|
||||||
|
OnPaymentInitiatedTriggerDto,
|
||||||
|
OnPaidTriggerDto,
|
||||||
|
BOOKING_REQUESTED,
|
||||||
|
BOOKING_REJECTED,
|
||||||
|
BOOKING_PAYMENT_INITIATED,
|
||||||
|
BOOKING_PAID,
|
||||||
|
BOOKING_NO_SHOW_UPDATED,
|
||||||
} from "./workflow-trigger.input";
|
} from "./workflow-trigger.input";
|
||||||
|
|
||||||
export type UpdateWorkflowStepDto =
|
export type UpdateWorkflowStepDto =
|
||||||
@@ -119,6 +129,11 @@ export class UpdateWhatsAppAttendeePhoneWorkflowStepDto extends WorkflowPhoneWha
|
|||||||
OnCancelTriggerDto,
|
OnCancelTriggerDto,
|
||||||
OnCreationTriggerDto,
|
OnCreationTriggerDto,
|
||||||
OnRescheduleTriggerDto,
|
OnRescheduleTriggerDto,
|
||||||
|
OnNoShowUpdateTriggerDto,
|
||||||
|
OnRejectedTriggerDto,
|
||||||
|
OnRequestedTriggerDto,
|
||||||
|
OnPaymentInitiatedTriggerDto,
|
||||||
|
OnPaidTriggerDto,
|
||||||
OnAfterCalVideoGuestsNoShowTriggerDto,
|
OnAfterCalVideoGuestsNoShowTriggerDto,
|
||||||
OnAfterCalVideoHostsNoShowTriggerDto,
|
OnAfterCalVideoHostsNoShowTriggerDto,
|
||||||
UpdateEmailAddressWorkflowStepDto,
|
UpdateEmailAddressWorkflowStepDto,
|
||||||
@@ -155,6 +170,11 @@ export class UpdateWorkflowDto {
|
|||||||
{ $ref: getSchemaPath(OnRescheduleTriggerDto) },
|
{ $ref: getSchemaPath(OnRescheduleTriggerDto) },
|
||||||
{ $ref: getSchemaPath(OnAfterCalVideoGuestsNoShowTriggerDto) },
|
{ $ref: getSchemaPath(OnAfterCalVideoGuestsNoShowTriggerDto) },
|
||||||
{ $ref: getSchemaPath(OnAfterCalVideoHostsNoShowTriggerDto) },
|
{ $ref: getSchemaPath(OnAfterCalVideoHostsNoShowTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnRejectedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnRequestedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnPaidTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnPaymentInitiatedTriggerDto) },
|
||||||
|
{ $ref: getSchemaPath(OnNoShowUpdateTriggerDto) },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -170,6 +190,11 @@ export class UpdateWorkflowDto {
|
|||||||
{ value: OnRescheduleTriggerDto, name: RESCHEDULE_EVENT },
|
{ value: OnRescheduleTriggerDto, name: RESCHEDULE_EVENT },
|
||||||
{ value: OnAfterCalVideoGuestsNoShowTriggerDto, name: AFTER_GUESTS_CAL_VIDEO_NO_SHOW },
|
{ value: OnAfterCalVideoGuestsNoShowTriggerDto, name: AFTER_GUESTS_CAL_VIDEO_NO_SHOW },
|
||||||
{ value: OnAfterCalVideoHostsNoShowTriggerDto, name: AFTER_HOSTS_CAL_VIDEO_NO_SHOW },
|
{ value: OnAfterCalVideoHostsNoShowTriggerDto, name: AFTER_HOSTS_CAL_VIDEO_NO_SHOW },
|
||||||
|
{ value: OnRequestedTriggerDto, name: BOOKING_REQUESTED },
|
||||||
|
{ value: OnRejectedTriggerDto, name: BOOKING_REJECTED },
|
||||||
|
{ value: OnPaymentInitiatedTriggerDto, name: BOOKING_PAYMENT_INITIATED },
|
||||||
|
{ value: OnPaidTriggerDto, name: BOOKING_PAID },
|
||||||
|
{ value: OnNoShowUpdateTriggerDto, name: BOOKING_NO_SHOW_UPDATED },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -179,6 +204,11 @@ export class UpdateWorkflowDto {
|
|||||||
| OnCreationTriggerDto
|
| OnCreationTriggerDto
|
||||||
| OnRescheduleTriggerDto
|
| OnRescheduleTriggerDto
|
||||||
| OnCancelTriggerDto
|
| OnCancelTriggerDto
|
||||||
|
| OnRejectedTriggerDto
|
||||||
|
| OnRequestedTriggerDto
|
||||||
|
| OnPaidTriggerDto
|
||||||
|
| OnPaymentInitiatedTriggerDto
|
||||||
|
| OnNoShowUpdateTriggerDto
|
||||||
| OnAfterCalVideoGuestsNoShowTriggerDto
|
| OnAfterCalVideoGuestsNoShowTriggerDto
|
||||||
| OnAfterCalVideoHostsNoShowTriggerDto;
|
| OnAfterCalVideoHostsNoShowTriggerDto;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { WorkflowActions, WorkflowTemplates } from "@prisma/client";
|
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import { IsNumber, IsBoolean, IsString, ValidateNested, IsIn } from "class-validator";
|
import { IsNumber, IsBoolean, IsString, ValidateNested, IsIn } from "class-validator";
|
||||||
|
|
||||||
|
import { WorkflowActions, WorkflowTemplates } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export const EMAIL_HOST = "email_host";
|
export const EMAIL_HOST = "email_host";
|
||||||
export const EMAIL_ATTENDEE = "email_attendee";
|
export const EMAIL_ATTENDEE = "email_attendee";
|
||||||
export const EMAIL_ADDRESS = "email_address";
|
export const EMAIL_ADDRESS = "email_address";
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { TimeUnit, WorkflowTriggerEvents } from "@prisma/client";
|
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import { IsIn, IsNumber, IsString, ValidateNested } from "class-validator";
|
import { IsIn, IsNumber, IsString, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
|
import { TimeUnit, WorkflowTriggerEvents } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export const BEFORE_EVENT = "beforeEvent";
|
export const BEFORE_EVENT = "beforeEvent";
|
||||||
export const EVENT_CANCELLED = "eventCancelled";
|
export const EVENT_CANCELLED = "eventCancelled";
|
||||||
export const NEW_EVENT = "newEvent";
|
export const NEW_EVENT = "newEvent";
|
||||||
@@ -10,6 +11,11 @@ export const AFTER_EVENT = "afterEvent";
|
|||||||
export const RESCHEDULE_EVENT = "rescheduleEvent";
|
export const RESCHEDULE_EVENT = "rescheduleEvent";
|
||||||
export const AFTER_HOSTS_CAL_VIDEO_NO_SHOW = "afterHostsCalVideoNoShow";
|
export const AFTER_HOSTS_CAL_VIDEO_NO_SHOW = "afterHostsCalVideoNoShow";
|
||||||
export const AFTER_GUESTS_CAL_VIDEO_NO_SHOW = "afterGuestsCalVideoNoShow";
|
export const AFTER_GUESTS_CAL_VIDEO_NO_SHOW = "afterGuestsCalVideoNoShow";
|
||||||
|
export const BOOKING_REJECTED = "bookingRejected";
|
||||||
|
export const BOOKING_REQUESTED = "bookingRequested";
|
||||||
|
export const BOOKING_PAYMENT_INITIATED = "bookingPaymentInitiated";
|
||||||
|
export const BOOKING_PAID = "bookingPaid";
|
||||||
|
export const BOOKING_NO_SHOW_UPDATED = "bookingNoShowUpdated";
|
||||||
export const WORKFLOW_TRIGGER_TYPES = [
|
export const WORKFLOW_TRIGGER_TYPES = [
|
||||||
BEFORE_EVENT,
|
BEFORE_EVENT,
|
||||||
EVENT_CANCELLED,
|
EVENT_CANCELLED,
|
||||||
@@ -18,6 +24,11 @@ export const WORKFLOW_TRIGGER_TYPES = [
|
|||||||
RESCHEDULE_EVENT,
|
RESCHEDULE_EVENT,
|
||||||
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
||||||
AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
||||||
|
BOOKING_REJECTED,
|
||||||
|
BOOKING_REQUESTED,
|
||||||
|
BOOKING_PAYMENT_INITIATED,
|
||||||
|
BOOKING_PAID,
|
||||||
|
BOOKING_NO_SHOW_UPDATED,
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const WORKFLOW_TRIGGER_TO_ENUM = {
|
export const WORKFLOW_TRIGGER_TO_ENUM = {
|
||||||
@@ -28,6 +39,11 @@ export const WORKFLOW_TRIGGER_TO_ENUM = {
|
|||||||
[RESCHEDULE_EVENT]: WorkflowTriggerEvents.RESCHEDULE_EVENT,
|
[RESCHEDULE_EVENT]: WorkflowTriggerEvents.RESCHEDULE_EVENT,
|
||||||
[AFTER_HOSTS_CAL_VIDEO_NO_SHOW]: WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
[AFTER_HOSTS_CAL_VIDEO_NO_SHOW]: WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
||||||
[AFTER_GUESTS_CAL_VIDEO_NO_SHOW]: WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
[AFTER_GUESTS_CAL_VIDEO_NO_SHOW]: WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
||||||
|
[BOOKING_REJECTED]: WorkflowTriggerEvents.BOOKING_REJECTED,
|
||||||
|
[BOOKING_REQUESTED]: WorkflowTriggerEvents.BOOKING_REQUESTED,
|
||||||
|
[BOOKING_PAYMENT_INITIATED]: WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED,
|
||||||
|
[BOOKING_NO_SHOW_UPDATED]: WorkflowTriggerEvents.BOOKING_NO_SHOW_UPDATED,
|
||||||
|
[BOOKING_PAID]: WorkflowTriggerEvents.BOOKING_PAID,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ENUM_TO_WORKFLOW_TRIGGER = {
|
export const ENUM_TO_WORKFLOW_TRIGGER = {
|
||||||
@@ -38,6 +54,11 @@ export const ENUM_TO_WORKFLOW_TRIGGER = {
|
|||||||
[WorkflowTriggerEvents.RESCHEDULE_EVENT]: RESCHEDULE_EVENT,
|
[WorkflowTriggerEvents.RESCHEDULE_EVENT]: RESCHEDULE_EVENT,
|
||||||
[WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW]: AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
[WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW]: AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
|
||||||
[WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW]: AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
[WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW]: AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
|
||||||
|
[WorkflowTriggerEvents.BOOKING_REJECTED]: BOOKING_REJECTED,
|
||||||
|
[WorkflowTriggerEvents.BOOKING_REQUESTED]: BOOKING_REQUESTED,
|
||||||
|
[WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED]: BOOKING_PAYMENT_INITIATED,
|
||||||
|
[WorkflowTriggerEvents.BOOKING_PAID]: BOOKING_PAID,
|
||||||
|
[WorkflowTriggerEvents.BOOKING_NO_SHOW_UPDATED]: BOOKING_NO_SHOW_UPDATED,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const HOUR = "hour";
|
export const HOUR = "hour";
|
||||||
@@ -108,6 +129,41 @@ export class OnCancelTriggerDto {
|
|||||||
type: typeof EVENT_CANCELLED = EVENT_CANCELLED;
|
type: typeof EVENT_CANCELLED = EVENT_CANCELLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class OnRejectedTriggerDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Trigger type for the workflow",
|
||||||
|
})
|
||||||
|
type: typeof BOOKING_REJECTED = BOOKING_REJECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnRequestedTriggerDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Trigger type for the workflow",
|
||||||
|
})
|
||||||
|
type: typeof BOOKING_REQUESTED = BOOKING_REQUESTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnPaymentInitiatedTriggerDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Trigger type for the workflow",
|
||||||
|
})
|
||||||
|
type: typeof BOOKING_PAYMENT_INITIATED = BOOKING_PAYMENT_INITIATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnPaidTriggerDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Trigger type for the workflow",
|
||||||
|
})
|
||||||
|
type: typeof BOOKING_PAID = BOOKING_PAID;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnNoShowUpdateTriggerDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Trigger type for the workflow",
|
||||||
|
})
|
||||||
|
type: typeof BOOKING_NO_SHOW_UPDATED = BOOKING_NO_SHOW_UPDATED;
|
||||||
|
}
|
||||||
|
|
||||||
export class TriggerOffsetDTO {
|
export class TriggerOffsetDTO {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: "Offset before/after the trigger time; required for BEFORE_EVENT and AFTER_EVENT only",
|
description: "Offset before/after the trigger time; required for BEFORE_EVENT and AFTER_EVENT only",
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
|||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { TimeUnit, Workflow, WorkflowStep, WorkflowTriggerEvents } from "@prisma/client";
|
|
||||||
|
|
||||||
|
import { TimeUnit, WorkflowTriggerEvents } from "@calcom/platform-libraries";
|
||||||
import { TUpdateInputSchema } from "@calcom/platform-libraries/workflows";
|
import { TUpdateInputSchema } from "@calcom/platform-libraries/workflows";
|
||||||
import { updateWorkflow } from "@calcom/platform-libraries/workflows";
|
import { updateWorkflow } from "@calcom/platform-libraries/workflows";
|
||||||
import { PrismaClient } from "@calcom/prisma";
|
import { PrismaClient } from "@calcom/prisma";
|
||||||
|
import type { Workflow, WorkflowStep } from "@calcom/prisma/client";
|
||||||
|
|
||||||
export type WorkflowType = Workflow & { activeOn: { eventTypeId: number }[]; steps: WorkflowStep[] };
|
export type WorkflowType = Workflow & { activeOn: { eventTypeId: number }[]; steps: WorkflowStep[] };
|
||||||
|
|
||||||
|
|||||||
@@ -9858,6 +9858,7 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "EventTypesController_2024_06_14_getEventTypes",
|
"operationId": "EventTypesController_2024_06_14_getEventTypes",
|
||||||
"summary": "Get all event types",
|
"summary": "Get all event types",
|
||||||
|
"description": "Hidden event types are returned only if authentication is provided and it belongs to the event type owner.",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "cal-api-version",
|
"name": "cal-api-version",
|
||||||
@@ -9913,6 +9914,33 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"description": "value must be `Bearer <token>` where `<token>` is api key prefixed with cal_ or managed user access token",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-cal-secret-key",
|
||||||
|
"in": "header",
|
||||||
|
"description": "For platform customers - OAuth client secret key",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-cal-client-id",
|
||||||
|
"in": "header",
|
||||||
|
"description": "For platform customers - OAuth client ID",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -22554,7 +22582,12 @@
|
|||||||
"afterEvent",
|
"afterEvent",
|
||||||
"rescheduleEvent",
|
"rescheduleEvent",
|
||||||
"afterHostsCalVideoNoShow",
|
"afterHostsCalVideoNoShow",
|
||||||
"afterGuestsCalVideoNoShow"
|
"afterGuestsCalVideoNoShow",
|
||||||
|
"bookingRejected",
|
||||||
|
"bookingRequested",
|
||||||
|
"bookingPaymentInitiated",
|
||||||
|
"bookingPaid",
|
||||||
|
"bookingNoShowUpdated"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"offset": {
|
"offset": {
|
||||||
@@ -22923,6 +22956,71 @@
|
|||||||
"type"
|
"type"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"OnNoShowUpdateTriggerDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "bookingNoShowUpdated",
|
||||||
|
"description": "Trigger type for the workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OnRejectedTriggerDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "bookingRejected",
|
||||||
|
"description": "Trigger type for the workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OnRequestedTriggerDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "bookingRequested",
|
||||||
|
"description": "Trigger type for the workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OnPaymentInitiatedTriggerDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "bookingPaymentInitiated",
|
||||||
|
"description": "Trigger type for the workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OnPaidTriggerDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "bookingPaid",
|
||||||
|
"description": "Trigger type for the workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
"OnAfterCalVideoGuestsNoShowTriggerDto": {
|
"OnAfterCalVideoGuestsNoShowTriggerDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -23568,7 +23666,12 @@
|
|||||||
"afterEvent",
|
"afterEvent",
|
||||||
"rescheduleEvent",
|
"rescheduleEvent",
|
||||||
"afterHostsCalVideoNoShow",
|
"afterHostsCalVideoNoShow",
|
||||||
"afterGuestsCalVideoNoShow"
|
"afterGuestsCalVideoNoShow",
|
||||||
|
"bookingRejected",
|
||||||
|
"bookingRequested",
|
||||||
|
"bookingPaymentInitiated",
|
||||||
|
"bookingPaid",
|
||||||
|
"bookingNoShowUpdated"
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"description": "Trigger type for the workflow"
|
"description": "Trigger type for the workflow"
|
||||||
@@ -23642,6 +23745,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/OnAfterCalVideoHostsNoShowTriggerDto"
|
"$ref": "#/components/schemas/OnAfterCalVideoHostsNoShowTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnRejectedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnRequestedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnPaidTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnPaymentInitiatedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnNoShowUpdateTriggerDto"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -24298,6 +24416,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/OnAfterCalVideoHostsNoShowTriggerDto"
|
"$ref": "#/components/schemas/OnAfterCalVideoHostsNoShowTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnRejectedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnRequestedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnPaidTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnPaymentInitiatedTriggerDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/OnNoShowUpdateTriggerDto"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { TestingModule } from "@nestjs/testing";
|
import { TestingModule } from "@nestjs/testing";
|
||||||
import { Membership, MembershipRole, Prisma, Team, User } from "@prisma/client";
|
import { Membership, Prisma, Team, User } from "@prisma/client";
|
||||||
|
|
||||||
|
import { MembershipRole } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
export class MembershipRepositoryFixture {
|
export class MembershipRepositoryFixture {
|
||||||
private prismaReadClient: PrismaReadService["prisma"];
|
private prismaReadClient: PrismaReadService["prisma"];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { _generateMetadata, getTranslate } from "app/_utils";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProvider } from "@calcom/prisma/client";
|
import { IdentityProvider } from "@calcom/prisma/enums";
|
||||||
import { Button } from "@calcom/ui/components/button";
|
import { Button } from "@calcom/ui/components/button";
|
||||||
import { Icon } from "@calcom/ui/components/icon";
|
import { Icon } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import type { PageProps as _PageProps } from "app/_types";
|
|||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { StripeService } from "@calcom/lib/server/service/stripe";
|
|
||||||
|
|
||||||
import VerifyPage from "~/auth/verify-view";
|
import VerifyPage from "~/auth/verify-view";
|
||||||
|
|
||||||
const querySchema = z.object({
|
const querySchema = z.object({
|
||||||
@@ -14,18 +12,10 @@ const querySchema = z.object({
|
|||||||
|
|
||||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||||
const p = { ...(await params), ...(await searchParams) };
|
const p = { ...(await params), ...(await searchParams) };
|
||||||
const { sessionId, stripeCustomerId } = querySchema.parse(p);
|
const { sessionId } = querySchema.parse(p);
|
||||||
|
|
||||||
const data = await StripeService.getCheckoutSession({
|
|
||||||
stripeCustomerId,
|
|
||||||
checkoutSessionId: sessionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { hasPaymentFailed } = data;
|
|
||||||
|
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
() =>
|
() => (sessionId ? "Payment Page" : `Verify your email`),
|
||||||
hasPaymentFailed ? "Your payment failed" : sessionId ? "Payment successful!" : `Verify your email`,
|
|
||||||
() => "",
|
() => "",
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
|
import { hasInsightsPermission } from "@calcom/features/insights/server/hasInsightsPermission";
|
||||||
|
import { prisma } from "@calcom/prisma";
|
||||||
|
|
||||||
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
export async function checkInsightsPagePermission() {
|
||||||
|
const featuresRepository = new FeaturesRepository(prisma);
|
||||||
|
const insightsEnabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("insights");
|
||||||
|
|
||||||
|
if (!insightsEnabled) {
|
||||||
|
redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
redirect("/auth/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPermission = await hasInsightsPermission({
|
||||||
|
userId: session.user.id,
|
||||||
|
organizationId: session.user.org?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasPermission) {
|
||||||
|
redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
@@ -1,34 +1,22 @@
|
|||||||
import { getTranslate } from "app/_utils";
|
import { getTranslate } from "app/_utils";
|
||||||
import { notFound } from "next/navigation";
|
|
||||||
|
|
||||||
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
|
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import Shell from "@calcom/features/shell/Shell";
|
||||||
import { prisma } from "@calcom/prisma";
|
|
||||||
|
|
||||||
import UpgradeTipWrapper from "./UpgradeTipWrapper";
|
import UpgradeTipWrapper from "./UpgradeTipWrapper";
|
||||||
|
|
||||||
export default async function InsightsLayout({ children }: { children: React.ReactNode }) {
|
export default async function InsightsLayout({ children }: { children: React.ReactNode }) {
|
||||||
const featuresRepository = new FeaturesRepository(prisma);
|
|
||||||
const insightsEnabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("insights");
|
|
||||||
|
|
||||||
if (!insightsEnabled) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const t = await getTranslate();
|
const t = await getTranslate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<div>
|
<Shell
|
||||||
<Shell
|
withoutMain={false}
|
||||||
withoutMain={false}
|
heading={t("insights")}
|
||||||
heading={t("insights")}
|
subtitle={t("insights_subtitle")}
|
||||||
subtitle={t("insights_subtitle")}
|
actions={<div className={`mb-2 flex items-center gap-2 ${CTA_CONTAINER_CLASS_NAME}`} />}>
|
||||||
actions={<div className={`mb-2 flex items-center gap-2 ${CTA_CONTAINER_CLASS_NAME}`} />}>
|
<UpgradeTipWrapper>{children}</UpgradeTipWrapper>
|
||||||
<UpgradeTipWrapper>{children}</UpgradeTipWrapper>
|
</Shell>
|
||||||
</Shell>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { cookies, headers } from "next/headers";
|
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
|
||||||
|
|
||||||
import InsightsPage from "~/insights/insights-view";
|
import InsightsPage from "~/insights/insights-view";
|
||||||
|
|
||||||
|
import { checkInsightsPagePermission } from "./checkInsightsPagePermission";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("insights"),
|
(t) => t("insights"),
|
||||||
@@ -18,7 +16,7 @@ export const generateMetadata = async () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const ServerPage = async () => {
|
const ServerPage = async () => {
|
||||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
const session = await checkInsightsPagePermission();
|
||||||
|
|
||||||
const { timeZone } = await prisma.user.findUniqueOrThrow({
|
const { timeZone } = await prisma.user.findUniqueOrThrow({
|
||||||
where: { id: session?.user.id ?? -1 },
|
where: { id: session?.user.id ?? -1 },
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { _generateMetadata } from "app/_utils";
|
|||||||
|
|
||||||
import InsightsVirtualQueuesPage from "~/insights/insights-virtual-queues-view";
|
import InsightsVirtualQueuesPage from "~/insights/insights-virtual-queues-view";
|
||||||
|
|
||||||
|
import { checkInsightsPagePermission } from "../checkInsightsPagePermission";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("insights"),
|
(t) => t("insights"),
|
||||||
@@ -12,5 +14,7 @@ export const generateMetadata = async () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
|
await checkInsightsPagePermission();
|
||||||
|
|
||||||
return <InsightsVirtualQueuesPage />;
|
return <InsightsVirtualQueuesPage />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { _generateMetadata } from "app/_utils";
|
|||||||
|
|
||||||
import InsightsRoutingPage from "~/insights/insights-routing-view";
|
import InsightsRoutingPage from "~/insights/insights-routing-view";
|
||||||
|
|
||||||
|
import { checkInsightsPagePermission } from "../checkInsightsPagePermission";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("insights"),
|
(t) => t("insights"),
|
||||||
@@ -12,5 +14,7 @@ export const generateMetadata = async () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
|
await checkInsightsPagePermission();
|
||||||
|
|
||||||
return <InsightsRoutingPage />;
|
return <InsightsRoutingPage />;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -2,6 +2,8 @@ import { _generateMetadata } from "app/_utils";
|
|||||||
|
|
||||||
import OrgAttributesEditPage from "@calcom/ee/organizations/pages/settings/attributes/attributes-edit-view";
|
import OrgAttributesEditPage from "@calcom/ee/organizations/pages/settings/attributes/attributes-edit-view";
|
||||||
|
|
||||||
|
import { validateUserHasOrgAdmin } from "../../../../actions/validateUserHasOrgAdmin";
|
||||||
|
|
||||||
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
|
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("attribute"),
|
(t) => t("attribute"),
|
||||||
@@ -12,6 +14,8 @@ export const generateMetadata = async ({ params }: { params: Promise<{ id: strin
|
|||||||
);
|
);
|
||||||
|
|
||||||
const OrgAttributesEditPageWrapper = async () => {
|
const OrgAttributesEditPageWrapper = async () => {
|
||||||
|
await validateUserHasOrgAdmin();
|
||||||
|
|
||||||
return <OrgAttributesEditPage />;
|
return <OrgAttributesEditPage />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+4
@@ -2,6 +2,8 @@ import { _generateMetadata } from "app/_utils";
|
|||||||
|
|
||||||
import OrgAttributesCreatePage from "@calcom/ee/organizations/pages/settings/attributes/attributes-create-view";
|
import OrgAttributesCreatePage from "@calcom/ee/organizations/pages/settings/attributes/attributes-create-view";
|
||||||
|
|
||||||
|
import { validateUserHasOrgAdmin } from "../../../actions/validateUserHasOrgAdmin";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("attribute"),
|
(t) => t("attribute"),
|
||||||
@@ -12,6 +14,8 @@ export const generateMetadata = async () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const OrgAttributesCreatePageWrapper = async () => {
|
const OrgAttributesCreatePageWrapper = async () => {
|
||||||
|
await validateUserHasOrgAdmin();
|
||||||
|
|
||||||
return <OrgAttributesCreatePage />;
|
return <OrgAttributesCreatePage />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user