Compare commits

...
Author SHA1 Message Date
emrysal 43f4caaba4 chore: release v5.7.0
Post release / release (push) Canceled after 0s
2025-09-15 16:22:52 +00:00
Udit TakkarandAlex van Andel 0c2ba81269 fix: phone number billing bug (#23851)
* fix: phone number billing bug

* fix: phone number billing bug

* fix: use raw code

* fix: use zod

* chore: update test

* Remove newline.

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-09-15 17:17:05 +01:00
Morgan 3aca339c95 fix: catch HttpError in getRoutedUrl (#23844)
* fix: catch HttpError in getRoutedUrl

* fixup! fix: catch HttpError in getRoutedUrl
2025-09-15 14:34:43 +00:00
sean-brydonandAlex van Andel 41553f6c01 feat: PBAC remove dogfood for safe checking (#23846)
* Remove dogfood for safe checking

* chore: Remove now redundant test cases

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-09-15 14:57:11 +01:00
Eunjae Lee 3661860433 docs: add search tools priority (#23843) 2025-09-15 13:57:25 +01:00
Keith Williams f0210d06ea chore: Upgrade prisma-kysely (#23825) 2025-09-15 13:44:31 +01:00
Rajiv Sahal fcfb8cc9a3 fix: update validation for billing check endpoint (#23842) 2025-09-15 11:57:58 +00:00
88391c7942 fix: Handle account user lookup fields for queued form responses (#23811)
* Add constants for queued response id query string

* Ignore eslint - app store imports

* Return `routingFormResponseId` or `queuedFormResponseId` from URL params

* Pass routingForm params to `getTeamMemberEmailForResponseOrContact`

* Handle getting form responses for queued form responses

* Type fix

* add test

* Add tests

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2025-09-15 11:16:50 +00:00
Eunjae Lee e9c6af562e docs: update knowledge base regarding auth checks in page (#23672) 2025-09-15 09:33:06 +00:00
Lauris Skraucis 18bac4a063 feat: v2 toggle transcription settings (#23836) 2025-09-15 09:13:22 +00:00
0332118724 feat: add e2e tests for insights charts (#23777)
* feat: add e2e tests for insights ChartCard components

- Test that all ChartCard components render with expected titles
- Test graceful handling of TRPC call failures
- Test ChartCard rendering for organization users
- Wait for page load and TRPC calls before checking chart presence
- Use text-based selectors to find ChartCard titles in h2 elements
- Remove page.waitForTimeout() calls to comply with ESLint rules

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* clean up e2e tests

* fix titles

* still render charts if no data

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2025-09-15 11:00:00 +02:00
Eunjae LeeandDevin AI 34b62f7516 feat: implement generalized navigation permission system with PBAC (#23706)
* feat: implement generalized navigation permission system with PBAC

- Create NavigationPermissionsProvider context for server-side permission data
- Add checkNavigationPermissions utility using PermissionCheckService
- Update main navigation layout to check permissions server-side
- Enhance useShouldDisplayNavigationItem for generalized permission filtering
- Support permission mapping for insights, workflows, routing, teams, members
- Use unstable_cache for performance optimization
- Replace insights-specific logic with scalable PBAC-based system

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: correct flag check logic in useShouldDisplayNavigationItem

- Only return false when flag is explicitly false
- Allow navigation permission checks to run when flags are truthy
- Fixes bug where truthy flags would short-circuit permission checks

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: resolve linting errors in embed-core files

- Replace forbidden non-null assertion with null check in embed.ts
- Replace 'any' type with 'unknown' in embed.test.ts
- Replace non-null assertion with proper null check in EmbedElement.test.ts
- Remove unused variable declaration

These are pre-existing linting issues unrelated to navigation permissions feature.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: resolve server-client boundary issue in NavigationPermissionsProvider

- Add 'use client' directive to NavigationPermissionsProvider.ts
- Replace JSX syntax with React.createElement to fix TypeScript compilation
- Create NavigationPermissionsWrapper.tsx as client component bridge
- Update layout.tsx to use wrapper instead of provider directly
- Ensure proper separation between server-side permission checking and client-side context consumption

This fixes the architectural issue where useContext was being used in server components.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* Revert "fix: resolve linting errors in embed-core files"

This reverts commit 4f481bae576376c9f9177215f7872c8bf7132894.

* refactor: simplify NavigationPermissionsProvider approach

- Remove NavigationPermissionsWrapper.tsx (revert to simpler approach)
- Rename NavigationPermissionsProvider.ts to .tsx for JSX support
- Update layout.tsx to use NavigationPermissionsProvider directly
- Keep 'use client' directive for proper server-client boundary handling
- Maintain all navigation permission functionality with cleaner architecture

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* clean up

* return true for no team situation

* fix: prevent hidden navigation items from reappearing via stored expansion state

- Modified usePersistedExpansionState hook to accept shouldDisplay parameter
- Clear stored expansion state for items that shouldn't be displayed
- Prevent state persistence for items without permissions
- Updated both NavigationItem and MobileNavigationMoreItem components
- Use safe sessionStorage import from @calcom/lib/webstorage

Fixes bug where clicking 'Workflows' would restore 'Insights' menu visibility
even when user lacks insights permissions due to sessionStorage state restoration.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: add sessionStorage to webstorage module and export NavigationItemName type

- Added sessionStorage wrapper to @calcom/lib/webstorage with safe error handling
- Export NavigationItemName type from NavigationPermissionsProvider for proper type access
- Resolves TypeScript compilation errors in navigation permission system

These changes enable the collapsible menu state bug fix to compile properly.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: improve separation of concerns in usePersistedExpansionState hook

- Remove shouldDisplay parameter from usePersistedExpansionState hook
- Move state clearing logic for hidden items to NavigationItem component level
- Add setIsExpanded to useEffect dependency arrays to fix ESLint warnings
- Maintain bug fix functionality while improving code design
- Better separation between expansion state management and display permissions

This addresses user feedback about inappropriate coupling between concerns.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix provider

* fix unstable_cache

* refactor: move navigation permission checks to client-side with tRPC

- Replace server-side unstable_cache permission checks with client-side tRPC query
- Add getNavigationPermissions query to PBAC router
- Update NavigationPermissionsProvider to use tRPC with loading states
- Remove server-side checkNavigationPermissions function
- Improve initial page load performance by deferring permission checks

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: centralize navigation item definitions to eliminate repetition

- Create NAVIGATION_ITEMS_CONFIG as single source of truth for navigation items
- Auto-generate NAVIGATION_PERMISSION_MAP from centralized config
- Update Navigation.tsx to use centralized definitions for teams, routing, workflows, insights
- Fix import issues in NavigationPermissionsProvider.tsx
- Eliminate hardcoded repetition of navigation item properties across codebase

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* clean up

* clean up permissions

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-15 10:55:51 +02:00
Eunjae LeeandDevin AI da1863069d feat: sort bookingUserId options alphabetically on the /insights/routing page (#23791)
* feat: sort bookingUserId options alphabetically in insights routing faceted values

- Add .sort((a, b) => a.label.localeCompare(b.label)) to bookingUserId options
- Makes user dropdown more human-friendly with alphabetical ordering
- Follows existing sorting pattern used in FilterSegmentSelect.tsx
- Fix ESLint warning by replacing Table<any> with Table<unknown>

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: use generic type parameter instead of unknown in Table type

- Replace Table<unknown> with <TData>(_: Table<TData>) to fix CI type errors
- Maintains type safety while avoiding ESLint any type warning
- Allows hook to work with any table type through generic parameter

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-15 09:53:30 +01:00
Joe Au-YeungandHariom Balhara 31270ca86b feat: Write same metadata to Stripe for booking payment and no show fee payment (#23634)
* Add `findBookingById` to `BookingRepository`

* Create `generateMetadata` to Stripe `PaymentService`

* `create` method to use `generateMetadata`

* Remove unused trpc endpoint

* Pass booking id to chargeCard method

* Use generateMetadata in chargeCard method

* Remove unused stripeAppKeys

* Add error handling if booking doesn't contain attendee

* Rename  to

* Return attendees in a consistent order

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2025-09-15 14:20:05 +05:30
Morgan 7e876e3457 fix: use class CreateDelegationCredentialInput instead of type for api v2 (#23831) 2025-09-15 07:59:30 +00:00
Peer Richelsen cbfd991ab7 chore: small cal.ai updates (#23827)
* Update page.tsx

* fixed close button

* typo

* fixed i18n cal.ai
2025-09-14 20:21:56 +01:00
emrysal d7cd185da1 chore: release v5.6.20
Post release / release (push) Canceled after 0s
2025-09-14 17:52:38 +00:00
Udit Takkar 416504240c feat: call history V1 (#23761)
* feat: call history

* chore

* refactor: move to insights

* fix: types

* chore: improvements

* fix: memberhsip logic

* chore: improvements

* fix: update types

* fix: type errors

* fix: type errors
2025-09-14 12:04:47 -03:00
cfadd2e892 feat: cal ai promo design (#23651)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-09-14 12:00:12 -03:00
Benny JooandDevin AI 3aab13b4da refactor: circular deps between app store and lib [3] (#23742)
* getEnabledAppsFromCredentials

* wip

* wip

* mv more

* fix: update test mocking for PrismockClient in processPaymentRefund.test.ts

- Replace prismaMock with prismock for proper PrismockClient usage
- Update mock data structure to include required fields (id, userId, teamId, etc.)
- Create app and credential records separately to handle PrismockClient limitations
- Replace 'as any' type casting with vi.mocked() for proper type safety
- Adjust test expectations to match PrismockClient's actual return structure
- Add proper cleanup in beforeEach hook for both credential and app records

Fixes failing unit tests after migration from packages/lib to packages/features/bookings/lib/payment

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* publish platform libraries

* fix import

* bump version

* fix apiv2

* publish platform-libraries

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-14 11:24:16 -03:00
Benny JooandDevin AI 76bba1ca05 refactor: remove circular dependency between appstore and trpc (#23768)
* mv PaymentPage to web

* mv 2 more components to web

* refactor

* update imports

* mv

* revert

* fix

* fix

* update

* migrate relevant test too

* move setup components to web from appstore

* move wipemycalother

* mv

* fix typechecks

* mv more routing forms components

* fix

* fix

* fix

* fix: resolve type errors in SingleForm.tsx after migration

- Updated SingleFormComponentProps type to include enriched form properties
- Added proper typing for user, team, nonOrgUsername, nonOrgTeamslug, userOrigin, teamOrigin
- Fixes type errors that occurred after migrating SingleForm.tsx from packages/app-store/routing-forms/components/
- Resolves both SingleForm.tsx and TestForm.tsx type errors

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* delete

* refactors

* fix

* fix

* fix

* remove usage of trpc client from OmniInstallAppButton.tsx

* fix test

* import only type

* fix test

* remove trpc package from appstore package.json

* remove remaining trpc usage

* update test

* fix type checks

* fix errors

* fix

* make it error

* nit

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-14 12:51:05 +00:00
Eunjae Lee 06fcea4116 docs: update insights chart dev guide for Prisma v6 (#23793) 2025-09-13 12:48:49 +00:00
Benny Joo a7fecaf578 fix: platform library export-verifyEmailCodeUnAuthenticated (#23814)
* fix apiv2

* fix import
2025-09-13 08:16:04 -03:00
Anik Dhabal Babu 639940d1bf fix: couldn't get adapter issue for few apps (#23813)
* fix: couldn't get adapter issue for few apps

* Update videoClient.ts
2025-09-13 10:35:30 +00:00
d7397d4563 fix: managed event types inherit team hideBranding setting (#23562)
* fix: managed event types inherit team hideBranding setting

- Add getParentTeamForBranding helper to query parent team data
- Update shouldHideBrandingForEvent to use parent team when eventType.team is null
- Ensures CTA 'create your own booking link with Cal' respects team branding settings
- Follows existing pattern used by eventType.team?.id ?? eventType.parent?.teamId

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* chore: update yarn.lock dependencies

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* Get parent event type team branding

* Use data from getEventTypeFromDB

* Undo yarn.lock changes

* Undo yarn.lock changes from latest main

* Address code commit

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-13 08:35:23 +01:00
Udit Takkar cf07621de7 fix: make cal ai div clickable (#23807) 2025-09-12 20:16:40 +00:00
Syed Ali Shahbaz 110615b509 chore: add webhook architecture skeleton (#23247)
* add migration guide and dto

* add factory

* add notifier

* add repo

* add services

* coderabbit review --1

* coderabbit review --2

* coderabbit review --3

* further improvement

* --

* fix

* bookingWebhookFactory consideration and type fixes

* cleanup

* fix types

* DI part1

* DI --part 2

* remove migrationGuide as we're WIP

* using evyweb for DI -- 1

* DI --final

* separate func instead of private class

* adds a todo migration file

* adjust structure

* address feedback

* remove todo_migrate

* --1

* fix type

* address feedback

* add TODO comment

* address requested changes --1

* address feedback --2

* restructure as per feedback

* rename camelcase
2025-09-12 20:02:50 +00:00
Udit Takkar 3915348d03 feat: support web calls in webhook (#23788)
* feat: support web calls in webhook

* fix: test

* chore: formating

* fix: type error

* chore: improvements

* chore: improvements
2025-09-12 19:04:19 +00:00
Neha Prasadandunknown 5230694e2c fix: dynamic adapter imports not working in self-hosted instances (#23784)
* export const CalendarServiceMap

* fix: resolve linting issues in video and calendar adapter fixes

- Remove unused isCalendarService function
- Fix any type usage in getCalendar
- Add eslint-disable for necessary app-store imports in videoClient
- Maintain architectural separation while fixing core functionality

* revert

---------

Co-authored-by: unknown <adhabal2002@gmail.com>
2025-09-12 19:02:17 +00:00
Benny Joo 68f2fc9174 perf: remove trpc basecamp3 router (#23785)
* remove trpc basecamp3 router

* update

* define business logic in appstore package

* cleaner

* cleaner code

* refactor

* use zod

* cleaner

* update
2025-09-12 15:32:50 -03:00
a63802dea7 feat: redirect authenticated users from signup page (#23766)
* feat: redirect authenticated users from signup page

- Add session check in signup getServerSideProps
- Redirect to redirect parameter if present, otherwise to root
- Follows existing authentication patterns in codebase

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix test

* fix test

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: unknown <adhabal2002@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-09-12 23:36:00 +05:30
Carina WollendorferandCarinaWolli 2be388373f fix: adjust free monthly credits for orgs (#23798)
* add org monthly credits env variable

* use fallback for env variable

* remove comment

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-09-12 17:47:13 +00:00
Anik Dhabal Babu a62c74f79e fix: make cancellation reason mandatory when cancelledBy param is set to host email (#23764)
* fix: make cancelllation Reason mandatory when cancelledBy set as the host

* update

* tweak

* show error message
2025-09-12 18:28:24 +01:00
Rajiv Sahal 1a8b5d145f fix: api v2 typing issues (#23786)
* fix: move validateRoundRobinSlotAvailability to core libraries

* fix: imports

* fix: update CreateCalendarCredentialsInput DTO

* cleanup
2025-09-12 18:27:56 +01:00
Lauris Skraucis 794c99239a fix: processWorkflowStep format cal event (#23802)
* fix: processWorkflowStep format cal event

* chore: update platform libraries
2025-09-12 18:27:37 +01:00
Benny Joo 1fee90322d refactor: move app-store dependent seed files from prisma to scripts (#23799)
* move seed.ts

* update package.json

* wip

* revert
2025-09-12 18:27:20 +01:00
Anik Dhabal Babu ee70111c45 fix: RateLimit verify email and fix (#23718)
* fix: RateLimit verify email

* update

* client chnages

* type error

* fix

* add test

* Update event-types.e2e.ts

* minor update

* Update event-types.e2e.ts

* fix test

* handle it in api v2
2025-09-12 18:27:05 +01:00
Lauris Skraucis 79169de8d8 fix: updating managed user email (#23794) 2025-09-12 18:26:52 +01:00
Morgan 74288b0ecb fix: OptionalApiAuthGuard when only client id is provided (#23783) 2025-09-12 12:16:52 +03:00
Udit Takkar a977fb4970 feat: add number to call in web call (#23769) 2025-09-12 12:16:39 +03:00
79a09362c9 refactor: remove circular dependency between prisma and app-store packages (#23475)
* refactor: remove circular dependency between prisma and app-store packages

- Replace EventTypeAppMetadataSchema with z.record(z.any()).optional() pattern
- Remove appDataSchemas import from packages/prisma/zod-utils.ts
- Add null checks in consuming packages for flexible validation
- Fix test file that no longer needs @ts-expect-error directive

This breaks the circular dependency while maintaining all functionality
by moving strict validation to the business logic layer where operations
actually happen, following existing patterns in the codebase.

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* refactor: remove EventTypeAppMetadataSchema exports from prisma package

- Remove EventTypeAppMetadataSchema and eventTypeAppMetadataOptionalSchema exports from prisma/zod-utils.ts
- Update all importing files to use local z.record(z.any()).optional() schemas
- Replace type annotations with Record<string, any> where appropriate
- Maintain validation functionality while breaking circular dependency
- All TypeScript compilation now passes without errors

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* refactor: complete removal of EventTypeAppMetadataSchema from remaining files

- Update handleSeats/createNewSeat.ts to use local schema
- Update payment handlers to use local schemas
- Update eventTypes update handler to use local schema
- All files now define their own validation instead of importing from prisma
- Circular dependency completely eliminated

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: resolve TypeScript compilation errors

- Remove duplicate z import from handleConfirmation.ts
- Remove duplicate appDataSchemas import from update.handler.ts
- Fix index signature errors in handlePayment.ts by using appData variable consistently
- All type checks now pass successfully

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix: add type casting for appSlug in eventTypeService

- Cast appSlug as keyof typeof apps to resolve index signature error
- Maintains type safety while allowing dynamic property access

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* fix

* remove

* make zod-utils.ts in app-store

* update imports

* fix

* fix

* fix

* revert unrelated change

* update imports

* fix

* fix

* revert

* fix

* fix

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: hbjORbj <sldisek783@gmail.com>
2025-09-12 05:57:09 -03:00
Alex van Andel 94a50086e6 Instead of type casting user.id, coerce string to number (#23781) 2025-09-12 02:46:07 +01:00
emrysal 18ba54522c chore: release v5.6.19
Post release / release (push) Canceled after 0s
2025-09-11 23:29:05 +00:00
Alex van Andel 5483597a5b Revert "fix: Don’t allow hosts to cancel a booking without providing a reason through v1 (#23647)"
This reverts commit 3c72d7aaa0.
2025-09-12 00:13:02 +01:00
Alex van Andel 4ed0901957 fix: prisma client binary cannot be found on vercel (#23772) 2025-09-11 23:18:39 +01:00
Eunjae LeeandDevin AI dbbf3ae2a2 fix: extract Prisma.sql queries for $queryRaw calls with Prisma.sql parameters (Prisma v6 upgrade) (#23779)
* fix: extract Prisma.sql queries for  calls with Prisma.sql parameters

- Extract SQL queries to separate Prisma.sql variables before passing to
- Fix PrismaPhoneNumberRepository.ts findManyWithUserAccess and findPhoneNumbersFromUserId methods
- Fix PrismaAgentRepository.ts multiple methods with whereCondition parameters
- Fix PermissionRepository.ts checkRolePermissions and getTeamIdsWithPermissions methods
- Fix virtual-queues.ts getUserRelevantTeamRoutingForms method
- Fix tasker/repository.ts hasNewerScanTaskForStepId method
- Add missing Prisma imports where needed
- Fix lint warnings: replace any with JsonValue and use explicit select for permissions
- Skip primitive parameter cases as instructed
- Compatible with Prisma v6 requirements

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* revert unnecessary changes

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-11 23:18:12 +01:00
Eunjae Lee c3985e3efb fix: including Prisma.sql for Prisma v6 upgrade (#23775) 2025-09-11 23:16:12 +01:00
c28eb90928 chore: Upgrade prisma to 6.7.0 (#21264)
* chore: Upgrade prisma to 6.7.0

* Build fixes

* type fixes

Signed-off-by: Omar López <zomars@me.com>

* Update schema.prisma

* Patching

* Revert "Update schema.prisma"

This reverts commit 47d8618bf89ef4d007b30084df766f17281e21a1.

* Revert "Patching"

This reverts commit a1d2e3040e71690a44d4324db95d73b4d68c6adb.

* Revert schema changes

Signed-off-by: Omar López <zomars@me.com>

* WIP

Signed-off-by: Omar López <zomars@me.com>

* Update getPublicEvent.ts

* Update imports

Signed-off-by: Omar López <zomars@me.com>

* Update gitignore

Signed-off-by: Omar López <zomars@me.com>

* update remaining imports

Signed-off-by: Omar López <zomars@me.com>

* Delete .cursor/config.json

* Discard changes to packages/features/eventtypes/lib/getPublicEvent.ts

* Update _get.ts

* Update user.ts

* Update .gitignore

* update

* Update WorkflowStepContainer.tsx

* Update next-auth-custom-adapter.ts

* Update getPublicEvent.ts

* Update workflow.ts

* Update next-auth-custom-adapter.ts

* Update next-auth-options.ts

* Update bookingScenario.ts

* fix missing imports

* upgrades prismock

Signed-off-by: Omar López <zomars@me.com>

* patches prismock

Signed-off-by: Omar López <zomars@me.com>

* Update reschedule.test.ts

* Update prisma.ts

* patch prismock

Signed-off-by: Omar López <zomars@me.com>

* fix enums imports

Signed-off-by: Omar López <zomars@me.com>

* Revert "Update prisma.ts"

This reverts commit 64edcf8db54171ff4456c209d563b5d431d99619.

* Revert "patch prismock"

This reverts commit e95819113dc9d88e7130947aa120cd42710977c8.

* fix patch

* Fix test that overrun the boundary, it shouldn't test too much

* Move prisma import to changeSMSLockState

* Bring back broken test without illegal imports

* Merge with main and fix filter hosts by same round robin host

* Fixed buildDryRunBooking fn tests

* Fix and move ooo create or update handler test

* Fix packages/features/eventtypes/lib/isCurrentlyAvailable.test.ts

* Fix packages/trpc/server/routers/viewer/organizations/listMembers.handler.test.ts

* Mock @calcom/prisma

* Fix: verify-email.test.ts

* fix: Moved WebhookService test and fixed default import mock

* Fix: Added missing prisma mock, handleNewBooking uses that of course

* We're not testing createContext here

* fix: Prisma mock fix for listMembers.test.ts

* More fixes to broken testcases

* Forgot to remove borked test

* Prevent the need to mock a lot of dependencies by moving out buildBaseWhereCondition to its own file

* Temporarily skip getCalendarEvents, needs a rewrite

* Fix: turns out you can access protected in testcases

* fix further mocks

* Added packages/features/insights/server/buildBaseWhereCondition.ts, types

* Always great to have a mock and then not use it

* And one less again.

* fix: confirm.handler.test, didn't mock prisma

* fix: Address minor nit by @eunjae & fix ImpersonationProvider test

* Updated isPrismaAvailableCheck that doesn't crash on import

* fix: Get Prisma directly from the client, it usually involves the Validator and does not need 'local' inclusion

* Add zod-prisma-types without the generator enabled (commented out)

* Uncomment and see what happens

* Change method of import as imports did not work in Input Schemas

* Remove custom 'zod' booking model, it does not belong with Prisma

* Fix all other global Model imports

* Rewrite most schema includes AND remove barrel file

* Add bookingCreateBodySchema to features/bookings

* Flurry of type fixes for compatibility with new zod gen

* Refactor out the custom prisma type createEventTypeInput

* Work around nullable eventTypeLocations

* HandlePayment type fix

* More fixes, final fix remaining is CompleteEventType

* Should fix a bunch more booking related type errors

* Missed one

* Some props missing from BookingCreateBodySchema

* Fix location type in handleChildrenEventTypes

* Little bit hacky imo but it works

* Final type error \o/

* Forgot to include Prisma

* Do not include zod-utils in booker/types

* Oops, was already including Booker/types

* Fix membership type, also disallow updating createdAt/updatedAt, make part of patch/post

* Fix api v1 type errors

* Fix EventTypeDescription typings

* Remove getParserWithGeneric, use userBodySchema with UserSchema

* use centralized timeZoneSchema

* Implement feedback by @zomars

* Couple of WIP pushes

* Fix tests

* Type fixes in `handleChildrenEventTypes` test

* Try and parse metadata before use

* Change zod-prisma-types configuration for optimal performance

* Fix prisma validator error in `prisma/selects/credential`

* Disable seperate relations model, hits a bug

* Import absolute - this makes rollup work in @platform/libraries

* Attempt at removing resolutions override

* Refactor using `Prisma.validator` to `satisfies`

* Build atoms using @calcom/prisma/client

* Build atoms using @calcom/prisma/client

* fixes

* Update eventTypeSelect.ts

* Adjust `eventTypeMetaDataSchemaWithUntypedApps` from `unknown` to `record(any)`

* `EventTypeDescription` rely on `descriptionAsSafeHTML` instead of `description`

* Add `seatsPerTimeSlot` to event type public select

* Fix typing in `users-public-view` getServerSide props

* Add missing `schedulingType` to prop

* chore: bump platform libraries

* Function return type is illegal, not sure how this passed eslint (#21567)

* Merged with main

* Update updateTokenObject.ts

* Update handleResponse.ts

* Update index.ts

* Update handleChildrenEventTypes.ts

* Update booking-idempotency-key.ts

* Update WebhookService.test.ts

* Update events.test.ts

* Update queued-response.test.ts

* Update events.test.ts

* Update getRoutedUrl.test.ts

* fix: type checks

Signed-off-by: Omar López <zomars@me.com>

* fixes

Signed-off-by: Omar López <zomars@me.com>

* chore: bump platform libraries

* Update yarn.lock

* more fixes

Signed-off-by: Omar López <zomars@me.com>

* fixes

Signed-off-by: Omar López <zomars@me.com>

* biuld fixes

* chore: bump platform libraries

* Update conferencing.repository.ts

* Update conferencing.repository.ts

* Update getCalendarsEvents.test.ts

* Update vite.config.js

* chore: bump platform libraries

* Update users.ts

* Discard changes to docs/api-reference/v2/openapi.json

* Update vite.config.ts

* updated platform libraries

* Update get.handler.test.ts

* Update get.handler.test.ts

* Update schema.prisma

* Discard changes to docs/api-reference/v2/openapi.json

* Update next-auth-custom-adapter.ts

* Update team.ts

* Flurry of type fixes

* Fix majority of insight related type errors

* Type fixes for unlink of account

* Make user nullable again

* Fixed a bunch of unit tests and one type error

* Attempted mock fix

* Attempted fix for Attribute type

* Ensure default import becomes prisma, but not direct usage

* Import default as prisma in prisma.module

* Add attributeOption to attribute type

* Fix calcom/prisma mock

* Refactor Prisma client imports to @calcom/prisma/client

Updated all imports from '@prisma/client' to '@calcom/prisma/client' across tests and repository files for consistency and to use the correct Prisma client package. This change improves maintainability and ensures the correct client is referenced throughout the codebase.

* Undo removal of max-warnings=0 to get main to merge

* Remove unit tests for e2e fixtures, provide new prisma mock

* Mock @calcom/prisma in event manager

* Mock @calcom/prisma in event manager

* Add correct format even with --no-verify

* Mock prisma in CalendarManager

* Add mock for permission-check.service

* Better injection in PrismaApiKeyRepository imports

* More mock fixes :)

* Fix listMembers.handler.test

* Fix User import

* Appropriately adjust all types to be imported as types, there were a lot of types imported as normal deps

* Why was this a thing?

* Strictly speaking; Not using prismock anymore

* Ditched patch file for prismock

* Fix output.service.ts platform type imports, need concrete for plainToClass

* Better typing and tests for unlinkConnectedAccount.handler

* Small type fix

* Disable calendar cache tests as they are dependent on prismock

* chore: bump platform lib

* getRoutedUrl test remove of unused import

* Extract select to external const on getEventTypesFromDB

* Direct select of userSelect from selects/user

* fix type error from merging 23653

* Fixed integration tests by removing hardcoded values that were possible due to mocking, but as its now directly hitting the db no longer

* fix: vite config atoms prisma client type location

* revert: example app prisma client

* revert: example app prisma client

* bump platform libs

* fix: use class instead of type for DI of PlatformBookingsService

* update platform libs

* remove unused variable

* chore: generate prisma client for api v2

* fix: api v2 e2e

* fix: atoms e2e

* fix: atoms e2e

* fix: atoms e2e

* fix: api v2 e2e

* fix: tsconfig apiv2 enums

* publish libraries

* Simplify check for existence teamId

---------

Signed-off-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>
Co-authored-by: cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
2025-09-11 15:27:50 +01:00
Amit Sharma a55d56f260 fix: Intercom not opening with openSupport param (#23755)
* fix: Intercom not opening with openSupport param

* remove logs

* remove unused
2025-09-11 18:11:42 +05:30
1d25265c56 feat: workflows v3 UI (#22772)
* early UI refresher of workflows using v3 designs. needs tons of testing, might have broken

* Delete .claude/settings.local.json

* fix: bunch of design fixes, merge conflict fixes

* fix: e2e and type-check

* fix: await button click

* review fixes

* code rabbit fixes

* fix styles in variables dropdown

* fix text truncate

* fix: unit tests

* fix: unit tests

* chore: add missing i18n

* review fixes

* review fixes

* fix testing info message

* move timeSectionText up

* fix which team does this apply to info message

* disable set up agent button with read only

* fix: cal.ai breaking on mobile screen

---------

Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit222001@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-09-11 17:01:42 +05:30
emrysal 65f6689025 chore: release v5.6.18
Post release / release (push) Canceled after 0s
2025-09-11 10:33:13 +00:00
Hariom Balhara 92005b93c3 All handleNewBooking tests import from getNewBookingHandler (#23741) 2025-09-11 06:58:16 -03:00
Anik Dhabal Babu 0a7e573117 fix: scdules reset to default (#23759) 2025-09-11 04:58:30 +00:00
Anik Dhabal Babu 943c1a3c03 fix: events not removed from the previous host calendar due to wrong credentials usage (#23756)
* fix: events not removed from the previous host claendar

* fix test

* Update roundRobinManualReassignment.test.ts
2025-09-11 04:03:50 +00:00
Benny Joo 9156848503 refactor: circular deps between app store and lib [2] (#23734)
* mv getDefaultLocations

* fix

* 2 more utils

* update imports

* fix

* move getConnectedApps

* move handlePaymentSuccess

* update imports

* fix import

* publish platform libraries
2025-09-10 23:18:59 -03:00
emrysal 2e6ad8be9a chore: release v5.6.17
Post release / release (push) Canceled after 0s
2025-09-10 23:17:01 +00:00
Calcom BotandLingo.dev 1aca18324d feat: update translations via @LingoDotDev (#23753)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-09-10 21:38:05 +05:30
Udit TakkarandPeer Richelsen a8a3e48465 feat: add web phone call for cal ai (#23527)
* feat: add web phone call for cal ai

* fix: web call

* refactor: improvements

* chore

* refactor: web call improvements

* chore: error message

* refactor: improvements

* fix: err

* refactor: feedback and improvements

* fix: type errors

* refactor: hasAvailable

* chore: use orgid

* fix: types

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2025-09-10 12:41:10 -03:00
Volnei Munhoz 2e96600529 Revert "fix(workflows): hide organizer email in workflow emails (#23525)" (#23739)
This reverts commit 7f200df1ac.
2025-09-10 11:49:38 +00:00
Hariom Balhara b9074aeb81 Better ioctopus type-safety (#23541) 2025-09-10 17:10:25 +05:30
Udit Takkar 13add03479 chore: update vitest (#23732) 2025-09-10 16:59:25 +05:30
Hariom Balhara bb2dcc51c6 Ignore requests from bot for headless router (#23733) 2025-09-10 16:59:04 +05:30
RomitandKartik Saini 8f82e78597 fix: loading skeleton is rendered even when no attributes filter is selected (#23656)
* fix

* fix tests

* fix tests

* test fidx

* chore

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-09-10 16:57:56 +05:30
Eunjae LeeandDevin AI 34d2a13080 fix: move admin permission check to server-side to prevent flash (#23517)
- Move permission validation from AdminLayoutAppDirClient.tsx (client) to layout.tsx (server)
- Add server-side redirect using next/navigation redirect() function
- Remove client-side useEffect permission check to eliminate flash
- Follow same pattern as organizations layout for consistent auth handling
- Users without admin role are redirected to /settings/my-account/profile before any content renders

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-10 11:15:37 +01:00
Eunjae LeeandDevin AI 6066ed206b fix: Fix booking count discrepancy between KPI cards and trends chart (#23619)
* fix: correct booking count in EventTrendsChart by restructuring SQL query

- Replace JOIN-based query with CTE approach to count distinct bookings
- Separate booking stats from guest stats to prevent duplicate counting
- Preserve no-show guest functionality with LEFT JOIN
- Ensure EventTrendsChart 'Created' sum matches BookingKPICards 'events_created'

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: use positional GROUP BY references to resolve PostgreSQL error

- Replace column expressions in GROUP BY with positional references (1, 2, 3)
- Fixes PostgreSQL error 42803 caused by parameter resolution in CTE queries
- Maintains the same query logic while avoiding column reference conflicts

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* address feedback

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-10 11:13:29 +01:00
d30d2792cd fix: hide notes (#22061)
* fix: hide notes

* test: re-enable confirmHandler tests and add tests for hide notes fix

* fix: replace deprecated getDate with hardcoded dates for timezone safety

* fix: hide notes test

* refactor: move test from trpc to web package

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
2025-09-10 10:51:41 +01:00
ba50268634 feat: support auto create agent (#23493)
* feat: support auto create agent

* feat: add prompts

* chore: add number to call

* fix typo

---------

Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-09-10 08:42:39 +00:00
Benny Joo 4ca78f46df chore: bump platform libraries version (#23730) 2025-09-10 08:37:17 +00:00
Eunjae LeeandAnik Dhabal Babu 3a7ca0246c docs: add knowledge base against unnecessary Day.js usage (#23719)
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-09-10 09:54:48 +02:00
Vansh GilhotraandAnik Dhabal Babu 2cc8b1c2af fix: Use db-deploy instead of db-migrate for yarn dx in non-interactive environments (#23253)
Fixes the issue where 'yarn dx' fails in non-interactive environments
(CI/CD, Docker, automated scripts) by changing Turbo dependencies:

- @calcom/prisma#db-seed: db-migrate → db-deploy
- @calcom/prisma#dx: db-migrate → db-up

This ensures that non-interactive 'prisma migrate deploy' is used
instead of interactive 'prisma migrate dev', allowing yarn dx to
work correctly in automated environments.

Resolves: Cal.com App Store performance optimization - yarn dx failure

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-09-10 06:46:46 +00:00
Lauris Skraucis a2d0cbfe5b chore: v2 dont allow mixing platform teams/users with non-platform (#20296)
* chore: v2 dont allow mixing platform teams/users with non-platform

* chore: finish main merge

* test: e2e

* refactor: simplify can add user to team logic

* refactor: fetch only needed oauth data

* refactor: cleanup
2025-09-09 21:24:18 +02:00
PallavandKartik Saini d65c0753ef conventional left aligned dropdown (#23643)
Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-09-09 16:02:43 +00:00
github-actions[bot] 76bd90b228 chore: version packages (#23714)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-09 15:23:30 +00:00
Benny Joo d908403a99 refactor: circular deps between app store and lib [1] (#23653)
* move dailyApiFetcher.ts to app store package

* update imports

* bookingLocationService

* revert

* move findFieldValueByIdentifier

* wip

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* move to features package

* update imports

* update imports

* move

* fix typechecks

* move getCalendarLinks

* mv event test
2025-09-09 14:38:29 +00:00
Amit SharmaandUdit Takkar 0fe775962f fix: intercom showing on cal video page (#23713)
* feat: support auto create agent

* fix: intercom showing on cal.video page

* Revert "feat: support auto create agent"

This reverts commit 084319c419b3baa44a3a82c0a2d79a4242a64d4d.

---------

Co-authored-by: Udit Takkar <udit222001@gmail.com>
2025-09-09 14:28:59 +00:00
Calcom BotandLingo.dev 412a6992c7 feat: update translations via @LingoDotDev (#23715)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-09-09 11:04:39 -03:00
Rajiv Sahal cfd1992733 feat: add customReplyToEmail for EventTypeSettings atom (#23686)
* init: endpoint for fetching verified emails

* fix: enable custom reply to email in frontend

* init endpoint to add verified emails

* add verified emails option for platform in frontend

* fixup: move useGetVerifiedEmails hook to correct folder

* update atoms module

* fixup: teamId should be string

* add methond to fetch team member emails

* update logic to fetch and add emails

* fixup: append client id with email

* fixup: pass teamId for fetching verified emails

* fixup: simplify check for existing emails

* fix: cleanup comments

* fix: implement code rabbit feedback

* fix: add translations

* fixup: update transaltions

* fix: update logic for addVerifiedEmail

* add changesets

* fix: implement PR feedback
2025-09-09 16:52:28 +03:00
RomitandAnik Dhabal Babu e86d6169ba fix (#23705)
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-09-09 19:05:17 +05:30
sean-brydonandcoderabbitai[bot] a379bd2b52 fix: org layout authentication (#23707)
* refactor layout to not check session

* add actions for orgs + org admins

* update types on actions to be correctly non nullable

* Add tests for utils

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* restore lock file

* rename _actions to actions

* update tests to be mocked

* restore lock

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-09-09 22:27:00 +09:00
Eunjae LeeandDevin AI 1137047606 feat: add PBAC permission checks to insights (#23592)
* feat: add PBAC permission checks to insights

- Add layout-level protection for insights pages with session.user.org.id check
- Add API-level protection for insights tRPC endpoints using insightsPbacProcedure
- Enhance routing form insights with PBAC team filtering
- Use insights.read permission with OWNER/ADMIN fallback roles
- Replace all userBelongsToTeamProcedure with insightsPbacProcedure in insights router
- Fix lint issues: remove unused variables and functions, fix non-null assertion

Implements Permission-Based Access Control (PBAC) for insights functionality following the same patterns as event type PBAC implementation from PR #22618.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: clean up unused imports in routing-forms

- Remove unused isFormCreateEditAllowed import
- Remove unused TRPCError import
- Change MembershipRole to type import for better tree-shaking

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* address feedback

* clean up permission check

* revert unnecessary changes

* fix procedure

* allow MEMBER for insights

* support teams not under orgs

* fix type error

* revert unnecessary changes

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-09 14:26:20 +01:00
Lauris Skraucis ad4cf0cc3a fix: v2 hidden event types (#23584)
* fix: return hidden event types only if owner auth provided

* chore: add tests

* chore: remove optional chaining when defined
2025-09-09 13:23:50 +02:00
RomitandKartik Saini e7d2e0dc2f fix: Prevent loading of dashboard GTM container on booking success page (#22620)
* moved bookings folder

* removed test

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-09-09 16:31:07 +05:30
Alex van Andel aa51218428 perf: move to disable prisma client extension inference (#23692)
* perf: move to disable prisma client extension inference

* Prisma doesn't like it when you pass Record<string, unknown>

* API v1 type fixes

* Missed one

* Fix unit test fail due to faulty expect

* Assigning to prisma InputJsonValue/Array must be done as object, not interface

* Fix @calcom/web ts error, teams not defined

* Run eslint formatter

* fixed the routingFormHelpers file causing a failing app store e2e test
2025-09-09 10:56:58 +00:00
Yadong (Adam) Zhang 38bc5fb71e chore(apps/web): add Scan for React performance issues and eliminate slow renders in web app (#23696)
* chore: add Scan for React performance issues and eliminate slow renders in web app

* chore(apps/web): always install latest `react-scan`.
2025-09-09 10:50:44 +00:00
411516ddb9 fix: duplicate event created in attendee calendar due to incorrect icalUid (#23658)
* chre: update reassigment to delete old events

* fix

* Update yarn.lock

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: unknown <adhabal2002@gmail.com>
2025-09-09 10:29:14 +00:00
Morgan c6c17ac388 fix: delegated credentials timezone outlook (#23700) 2025-09-09 11:25:48 +01:00
emrysal 6838133865 chore: release v5.6.16
Post release / release (push) Canceled after 0s
2025-09-09 10:21:58 +00:00
Calcom BotandLingo.dev 4b2bb2271a feat: update translations via @LingoDotDev (#23703)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-09-09 06:47:06 -03:00
20b7ac8c0d feat: implement tiered Intercom chat system replacing Plain (#23285)
* feat: implement tiered Intercom chat system replacing Plain

- Add TieredIntercomChat component with customer tier detection
- Add IntercomContactForm for free users using Intercom API
- Add /api/intercom-conversation endpoint for free user support
- Update UserDropdown to use tiered chat logic
- Replace Plain chat with Intercom in app providers
- Remove all Plain chat components and related files
- Use useHasPaidPlan hook for customer tier detection

Paying customers see Intercom widget, free users see contact form
that creates conversations via Intercom API.

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix: add missing environment variables to turbo.json globalEnv

- Add NEXT_PUBLIC_WEBAPP_URL, NEXT_PUBLIC_WEBSITE_URL, NEXT_PUBLIC_STRIPE_PUBLIC_KEY
- Add NEXT_PUBLIC_IS_E2E, NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE_MONTHLY
- Add NEXT_PUBLIC_BOOKER_NUMBER_OF_DAYS_TO_LOAD, NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID
- Resolves turbo/no-undeclared-env-vars ESLint errors blocking commits

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix: resolve linting errors in platform examples base package

- Fix prettier/prettier formatting (quotes and comma)
- Add underscore prefix to unused variables
- Resolves CI failure in @calcom/base#lint check

Co-Authored-By: peer@cal.com <peer@cal.com>

* remove plain usage

* placement fixes

* fix: pop closing immediately issue

* stuff

* proper error and additional user info

* add key to .env.example and remove unused plain route

* fix conversation route

* refactor intercom dynamic provider

* code rabbit fixes

* feat: introduce tiered support feature flag

* fix: type check

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-09-09 18:20:22 +09:00
Calcom BotandLingo.dev ca7c432728 feat: update translations via @LingoDotDev (#23699)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-09-09 08:49:09 +00:00
SomandHariom Balhara 3b966191e3 fix: prevent footer doubling with CSS position absolute for grid layouts (#23368)
* fix: prevent footer doubling with CSS position absolute for grid layouts

* fix: use :host selector for Shadow DOM compatibility in footer doubling fix

* Add better comments and updae playground to be ab le to test it

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2025-09-09 08:13:15 +00:00
a7889984a0 fix: design improvements for log in screen for cal video (#22359)
* fix: design improvements for log in screen for cal video

Signed-off-by: bandhan-majumder <bandhanmajumder16@gmail.com>

* fix: type

Signed-off-by: bandhan-majumder <bandhanmajumder16@gmail.com>

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* add: light mode

* Update videos-single-view.tsx

* add suggestions 

added padding change and removed hard coded values

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* Update apps/web/modules/videos/views/videos-single-view.tsx

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>

* Update common.json

* Update videos-single-view.tsx

* remove isLoggedin from LogInOverlayProps

* type fix

* screen flash fixed

* rabbit suggestions

* rabbit suggestions

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* flakey test

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* Update videos-single-view.tsx

* add: deps

* coderabbit

---------

Signed-off-by: bandhan-majumder <bandhanmajumder16@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-09-09 07:44:29 +00:00
Anik Dhabal Babu 3c72d7aaa0 fix: Don’t allow hosts to cancel a booking without providing a reason through v1 (#23647)
* fix: hosts is cancelling booking through apiV1

* tweak
2025-09-09 05:30:51 +00:00
Romit ece0030978 fix (#23685) 2025-09-09 04:20:20 +00:00
Anik Dhabal Babu a26a769119 fix: send awaiting payment email only to the current seat (#23630) 2025-09-09 03:50:24 +00:00
Anik Dhabal Babu 3313de1eec fix: change internal note preset fkey to cascade (#23613) 2025-09-09 03:13:31 +00:00
Anik Dhabal Babu 7a6e16631b fix: guests are not adding when reschedule booking (#23620)
* fix: guests are not adding when reschedule booking

* Update addGuests.handler.ts

* add types
2025-09-09 03:12:41 +00:00
Anik Dhabal Babu ce4d71ba2e fix: workflow attach ics file is in base64 format (#23680) 2025-09-09 03:11:34 +00:00
300cd31efb chore: redis cache for calendars in api v2 (#23622)
* chore: implement redis cache for calendars api v2

* fixup! chore: implement redis cache for calendars api v2

* fixup! fixup! chore: implement redis cache for calendars api v2

* fix leverage genetic type of redis.get

* refactor: address supalarry's PR feedback

- Create CalendarsCacheService to separate cache responsibilities
- Rename cache methods to be more descriptive (getConnectedAndDestinationCalendarsCache, setConnectedAndDestinationCalendarsCache)
- Remove unused redisService injection from CalendarsController
- Move all cache-related functionality to dedicated service

Addresses comments 9, 10, and 11 from supalarry on PR #23622

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* refactor: address supalarry's latest PR feedback

- Remove redundant deleteConnectedAndDestinationCalendarsCache from CalendarsService
- Update controller to directly inject and use CalendarsCacheService
- Extract TTL constant and remove manual JSON.stringify in cache service

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: update IcsFeedService to use CalendarsCacheService directly

- Inject CalendarsCacheService into IcsFeedService constructor
- Replace calendarsService.deleteCalendarCache with calendarsCacheService.deleteConnectedAndDestinationCalendarsCache
- Completes the refactoring to eliminate all old cache method references

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: update destination-calendars and selected-calendars services to use CalendarsCacheService directly

- Inject CalendarsCacheService in both service constructors
- Replace calendarsService.deleteCalendarCache calls with calendarsCacheService.deleteConnectedAndDestinationCalendarsCache
- Add CalendarsCacheService to module providers for proper dependency injection
- Completes migration of all cache method references to use dedicated cache service

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* chore: use CalendarsCacheService

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-08 21:21:55 +00:00
Rajiv Sahal f5bb734441 fix: platform onboarding (#23581)
* fix: platform onboarding

* chore: better function naming

* fix: implement PR feedback
2025-09-08 19:04:33 +00:00
emrysal e33560ef82 chore: release v5.6.15
Post release / release (push) Canceled after 0s
2025-09-08 12:44:40 +00:00
devin-ai-integration[bot]andmorgan@cal.com 84f34b2244 feat: add 5000ms staleTime to useConnectedCalendars hook (#23674)
- Add staleTime: 5000 to useQuery configuration in useConnectedCalendars
- Prevents unnecessary refetches for 5 seconds after data becomes stale
- Improves performance by reducing redundant API calls

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
2025-09-08 12:19:22 +00:00
Benny Joo 086bae03d3 refactor: move a constant to a dedicated file in App store package (#23671)
* define DailyLocationType in app-store/locations.ts

* update imports
2025-09-08 08:38:46 -03:00
4a0a429774 refactor: Migrate getRoutedUrl from @calcom/lib to @calcom/features (#23666)
* mkdir

* update imports

* chore: update platform libraries

---------

Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>
2025-09-08 11:28:22 +00:00
Benny Joo c4142ab4e7 refactor: Move handlePayment to Features Package (#23670)
* mv

* update imports
2025-09-08 07:32:29 -03:00
Benny Joo 95f8046db9 refactor: Error code - remove circular dep between app store <-> lib (#23669)
* remove stripe.types.ts

* use stripe-error defined in packages/lib

* refactor
2025-09-08 07:31:28 -03:00
Lauris Skraucis d8a87e9f04 fix: v2 seated bookings (#23514)
* fix: email manage link missing seat uid

* fix: toggle seated booking attendees

* fix: email manage link seat uid

* chore: update platform libraries
2025-09-08 09:50:36 +00:00
Keith Williams 86dcd6f0ff perf: Create 'heavy' eventTypes router (#23664)
* perf: Create 'heavy' eventTypes router

* Fixing type checks

* Fixing platform libraries imports
2025-09-08 08:24:30 +00:00
Anik Dhabal Babu 411a14dd3d fix: unit test flakiness (#23668)
* fix: unit test flakyness

* Update CalendarService.test.ts
2025-09-08 03:47:40 +00:00
Benny Joo 03bc8db64a refactor: Remove StripeService from @calcom/lib and define it in @calcom/trpc (#23655)
* remove packages/lib/server/stripe

* simplify metadata

* define business logic directly in trpc handler
2025-09-08 02:02:56 +00:00
Benny Joo fe7fd8bc79 chore: add an eslint rule to warn importing trpc package in app store package (#23665) 2025-09-08 10:36:34 +09:00
Benny Joo 92eb41c117 refactor: Migrate trpc routers in App store package to Trpc package (#23536)
* migrate trpc routers in app package to trpc package

* delete

* delete

* delete

* fix

* fix

* fix

* format

* format

* fix

* fix

* fix

* fix

* remove unused file

* fix

* fix

* wip
2025-09-07 22:17:33 -03:00
Benny Joo a1fca56aac refactor: Remove App Store's Type Dependency on Trpc Package (#23640)
* remove trpc type interface

* refactors

* refactors
2025-09-08 00:59:51 +00:00
1248 changed files with 21939 additions and 12338 deletions
+58
View File
@@ -318,3 +318,61 @@ dates.map((date) => dayjs.utc(date).add(1, "day").format());
// ✅ Best - Use native Date when possible
dates.map((date) => new Date(date.valueOf() + 24 * 60 * 60 * 1000));
```
## Next.js App Directory: Authorisation Checks in Pages
This can include checking session.user exists or session.org etc.
### TL;DR:
Don’t put permission checks in layout.tsx! Always put them directly inside your page.tsx or relevant server components for every restricted route.
### Why Not to Use Layouts for Permission Checks
* Layouts don’t intercept all requests: If a user navigates directly or refreshes a protected route, layout checks might be skipped, exposing sensitive content.
* APIs and server actions bypass layouts: Sensitive operations running on the server can’t be guarded by checks in the layout.
* Risk of data leaks: Only page/server-level checks ensure that unauthorized users never get protected data.
### ✅ How To Secure Routes (The Right Way)
* Check permissions inside page.tsx or the actual server component.
* Perform all session/user/role validation before querying or rendering sensitive content.
* Redirect or return nothing to unauthorized users, before running restricted code.
### 🛠️ Example: Page-Level Permission Check
```tsx
// app/admin/page.tsx
import { redirect } from "next/navigation";
import { getUserSession } from "@/lib/auth";
export default async function AdminPage() {
const session = await getUserSession();
if (!session || session.user.role !== "admin") {
redirect("/"); // Or show an error
}
// Protected content here
return <div>Welcome, Admin!</div>;
}
```
### 🧠 Key Reminders
* Put permission guards in every restricted page.tsx.
* Never assume layouts are secure for guarding data.
* Validate users before any sensitive queries or rendering.
## Avoid using Dayjs if you don’t need to be strictly tz aware.
When doing logic like Dayjs.startOf(".."), you can instead use date-fns' `startOfMonth(dateObj)` / `endOfDay(dateObj)`;
When doing logic that depends on Browser locale, use i18n.language (prefer to deconstruct) like: `const { i18n: { language } } = useLocale();`, in combination with built-in Intl.
Note that with Date, you’re dealing with System time, so it’s not suited to everywhere (such as in the Booker, where instead we’ll likely migrate to Temporal) - but in most cases the above are suitable.
The main reason for doing so is that Dayjs uses a useful, but highly risky plugin system, which has led us to create `@calcom/dayjs` - this is heavy however, because it pre-loads ALL plugins, including locale handling. It’s a non-ideal solution to a problem that unfortunately exists due to Dayjs.
+4 -8
View File
@@ -103,19 +103,14 @@ NEXT_PUBLIC_INTERCOM_APP_ID=
# Secret to enable Intercom Identity Verification
INTERCOM_SECRET=
# Intercom api token for free users
INTERCOM_API_TOKEN=
# Posthog Config
NEXT_PUBLIC_POSTHOG_KEY=
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
@@ -208,6 +203,7 @@ NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN=0
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE=
STRIPE_TEAM_MONTHLY_PRICE_ID=
NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID=
ORG_MONTHLY_CREDITS=
STRIPE_TEAM_PRODUCT_ID=
# It is a price ID in the product with id STRIPE_ORG_PRODUCT_ID
STRIPE_ORG_MONTHLY_PRICE_ID=
+17
View File
@@ -19,5 +19,22 @@ module.exports = {
],
},
},
{
files: ["packages/app-store/**/*.{ts,tsx,js,jsx}"],
rules: {
"no-restricted-imports": [
"error",
{
allowTypeImports: false,
patterns: [
{
group: ["@calcom/trpc/*", "@trpc/*"],
message: "tRPC imports are blocked in packages/app-store. Move UI to apps/web/components/apps or introduce an API boundary.",
},
],
},
],
},
},
],
};
+2 -1
View File
@@ -66,7 +66,8 @@ tsconfig.tsbuildinfo
.turbo
# Prisma generated files
packages/prisma/zod/*.ts
packages/prisma/client/*
packages/prisma/zod/**/*.ts
packages/prisma/enums
# Builds
+2
View File
@@ -0,0 +1,2 @@
undecided:
- calcom-monorepo
View File
View File
+3
View File
@@ -0,0 +1,3 @@
undecided:
- calcom-monorepo
- "@calcom/app-store"
View File
View File
View File
+10
View File
@@ -13,6 +13,16 @@
- `yarn test <filename> -- --integrationTestsOnly` - Run integration tests
- `yarn e2e <filename> --grep "<testName>"` - Run specific E2E test
## Tool Preferences
### Search Tools Priority
Use tools in this order of preference:
1. **ast-grep** - For AST-based code searches (if available)
2. **rg (ripgrep)** - For fast text searches
3. **grep** - As fallback for text searches
## 📚 Detailed Documentation
- **[.agents/README.md](.agents/README.md)** - Complete development guide
+1 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { _ApiKeyModel as ApiKey } from "@calcom/prisma/zod";
import { ApiKeySchema as ApiKey } from "@calcom/prisma/zod/modelSchema/ApiKeySchema";
export const apiKeyCreateBodySchema = ApiKey.pick({
note: true,
+3 -3
View File
@@ -1,11 +1,11 @@
import { z } from "zod";
import { emailSchema } from "@calcom/lib/emailSchema";
import { _AttendeeModel as Attendee } from "@calcom/prisma/zod";
import { AttendeeSchema } from "@calcom/prisma/zod/modelSchema/AttendeeSchema";
import { timeZone } from "~/lib/validations/shared/timeZone";
export const schemaAttendeeBaseBodyParams = Attendee.pick({
export const schemaAttendeeBaseBodyParams = AttendeeSchema.pick({
bookingId: true,
email: true,
name: true,
@@ -31,7 +31,7 @@ const schemaAttendeeEditParams = z
export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeEditParams);
export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeCreateParams);
export const schemaAttendeeReadPublic = Attendee.pick({
export const schemaAttendeeReadPublic = AttendeeSchema.pick({
id: true,
bookingId: true,
name: true,
+5 -4
View File
@@ -1,16 +1,17 @@
import { z } from "zod";
import { _AvailabilityModel as Availability, _ScheduleModel as Schedule } from "@calcom/prisma/zod";
import { denullishShape } from "@calcom/prisma/zod-utils";
import { AvailabilitySchema } from "@calcom/prisma/zod/modelSchema/AvailabilitySchema";
import { ScheduleSchema } from "@calcom/prisma/zod/modelSchema/ScheduleSchema";
export const schemaAvailabilityBaseBodyParams = /** We make all these properties required */ denullishShape(
Availability.pick({
AvailabilitySchema.pick({
/** We need to pass the schedule where this availability belongs to */
scheduleId: true,
})
);
export const schemaAvailabilityReadPublic = Availability.pick({
export const schemaAvailabilityReadPublic = AvailabilitySchema.pick({
id: true,
startTime: true,
endTime: true,
@@ -19,7 +20,7 @@ export const schemaAvailabilityReadPublic = Availability.pick({
days: true,
// eventTypeId: true /** @deprecated */,
// userId: true /** @deprecated */,
}).merge(z.object({ success: z.boolean().optional(), Schedule: Schedule.partial() }).partial());
}).merge(z.object({ success: z.boolean().optional(), Schedule: ScheduleSchema.partial() }).partial());
const schemaAvailabilityCreateParams = z
.object({
@@ -1,7 +1,7 @@
import { _BookingReferenceModel as BookingReference } from "@calcom/prisma/zod";
import { denullishShape } from "@calcom/prisma/zod-utils";
import { BookingReferenceSchema } from "@calcom/prisma/zod/modelSchema/BookingReferenceSchema";
export const schemaBookingReferenceBaseBodyParams = BookingReference.pick({
export const schemaBookingReferenceBaseBodyParams = BookingReferenceSchema.pick({
type: true,
bookingId: true,
uid: true,
@@ -11,7 +11,7 @@ export const schemaBookingReferenceBaseBodyParams = BookingReference.pick({
deleted: true,
}).partial();
export const schemaBookingReferenceReadPublic = BookingReference.pick({
export const schemaBookingReferenceReadPublic = BookingReferenceSchema.pick({
id: true,
type: true,
bookingId: true,
@@ -22,7 +22,7 @@ export const schemaBookingReferenceReadPublic = BookingReference.pick({
deleted: true,
});
export const schemaBookingCreateBodyParams = BookingReference.omit({ id: true, bookingId: true })
.merge(denullishShape(BookingReference.pick({ bookingId: true })))
export const schemaBookingCreateBodyParams = BookingReferenceSchema.omit({ id: true, bookingId: true })
.merge(denullishShape(BookingReferenceSchema.pick({ bookingId: true })))
.strict();
export const schemaBookingEditBodyParams = schemaBookingCreateBodyParams.partial();
+25 -25
View File
@@ -1,15 +1,13 @@
import { z } from "zod";
import {
_AttendeeModel,
_BookingModel as Booking,
_EventTypeModel,
_PaymentModel,
_TeamModel,
_UserModel,
} from "@calcom/prisma/zod";
import { extendedBookingCreateBody } from "@calcom/features/bookings/lib/bookingCreateBodySchema";
import { iso8601 } from "@calcom/prisma/zod-utils";
import { extendedBookingCreateBody } from "@calcom/prisma/zod/custom/booking";
import { AttendeeSchema } from "@calcom/prisma/zod/modelSchema/AttendeeSchema";
import { BookingSchema as Booking } from "@calcom/prisma/zod/modelSchema/BookingSchema";
import { EventTypeSchema } from "@calcom/prisma/zod/modelSchema/EventTypeSchema";
import { PaymentSchema } from "@calcom/prisma/zod/modelSchema/PaymentSchema";
import { TeamSchema } from "@calcom/prisma/zod/modelSchema/TeamSchema";
import { UserSchema } from "@calcom/prisma/zod/modelSchema/UserSchema";
import { schemaQueryUserId } from "./shared/queryUserId";
@@ -72,17 +70,16 @@ export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams
.merge(schemaBookingEditParams)
.omit({ uid: true });
const teamSchema = _TeamModel.pick({
const teamSchema = TeamSchema.pick({
name: true,
slug: true,
});
export const schemaBookingReadPublic = Booking.extend({
eventType: _EventTypeModel
.pick({
title: true,
slug: true,
})
eventType: EventTypeSchema.pick({
title: true,
slug: true,
})
.merge(
z.object({
team: teamSchema.nullish(),
@@ -91,7 +88,7 @@ export const schemaBookingReadPublic = Booking.extend({
.nullish(),
attendees: z
.array(
_AttendeeModel.pick({
AttendeeSchema.pick({
id: true,
email: true,
name: true,
@@ -100,17 +97,15 @@ export const schemaBookingReadPublic = Booking.extend({
})
)
.optional(),
user: _UserModel
.pick({
email: true,
name: true,
timeZone: true,
locale: true,
})
.nullish(),
user: UserSchema.pick({
email: true,
name: true,
timeZone: true,
locale: true,
}).nullish(),
payment: z
.array(
_PaymentModel.pick({
PaymentSchema.pick({
id: true,
success: true,
paymentOption: true,
@@ -140,3 +135,8 @@ export const schemaBookingReadPublic = Booking.extend({
rescheduledBy: true,
createdAt: true,
});
export {
bookingCreateSchemaLegacyPropsForApi,
bookingCreateBodySchemaForApi,
} from "@calcom/features/bookings/lib/bookingCreateBodySchema";
@@ -1,8 +1,8 @@
import { z } from "zod";
import { _DestinationCalendarModel as DestinationCalendar } from "@calcom/prisma/zod";
import { DestinationCalendarSchema } from "@calcom/prisma/zod/modelSchema/DestinationCalendarSchema";
export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.pick({
export const schemaDestinationCalendarBaseBodyParams = DestinationCalendarSchema.pick({
integration: true,
externalId: true,
eventTypeId: true,
@@ -38,7 +38,7 @@ export const schemaDestinationCalendarEditBodyParams = schemaDestinationCalendar
schemaDestinationCalendarEditParams
);
export const schemaDestinationCalendarReadPublic = DestinationCalendar.pick({
export const schemaDestinationCalendarReadPublic = DestinationCalendarSchema.pick({
id: true,
integration: true,
externalId: true,
@@ -1,10 +1,10 @@
import { _EventTypeCustomInputModel as EventTypeCustomInput } from "@calcom/prisma/zod";
import { EventTypeCustomInputSchema } from "@calcom/prisma/zod/modelSchema/EventTypeCustomInputSchema";
export const schemaEventTypeCustomInputBaseBodyParams = EventTypeCustomInput.omit({
export const schemaEventTypeCustomInputBaseBodyParams = EventTypeCustomInputSchema.omit({
id: true,
});
export const schemaEventTypeCustomInputPublic = EventTypeCustomInput.omit({});
export const schemaEventTypeCustomInputPublic = EventTypeCustomInputSchema.omit({});
export const schemaEventTypeCustomInputBodyParams = schemaEventTypeCustomInputBaseBodyParams.strict();
+5 -4
View File
@@ -6,8 +6,9 @@ import {
MIN_EVENT_DURATION_MINUTES,
} from "@calcom/lib/constants";
import slugify from "@calcom/lib/slugify";
import { _EventTypeModel as EventType, _HostModel } from "@calcom/prisma/zod";
import { customInputSchema, eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import { EventTypeSchema } from "@calcom/prisma/zod/modelSchema/EventTypeSchema";
import { HostSchema } from "@calcom/prisma/zod/modelSchema/HostSchema";
import { Frequency } from "~/lib/types";
@@ -24,7 +25,7 @@ const recurringEventInputSchema = z.object({
tzid: timeZone.optional(),
});
const hostSchema = _HostModel.pick({
const hostSchema = HostSchema.pick({
isFixed: true,
userId: true,
scheduleId: true,
@@ -35,7 +36,7 @@ export const childrenSchema = z.object({
userId: z.number().int(),
});
export const schemaEventTypeBaseBodyParams = EventType.pick({
export const schemaEventTypeBaseBodyParams = EventTypeSchema.pick({
title: true,
description: true,
slug: true,
@@ -117,7 +118,7 @@ const schemaEventTypeEditParams = z
.strict();
export const schemaEventTypeEditBodyParams = schemaEventTypeBaseBodyParams.merge(schemaEventTypeEditParams);
export const schemaEventTypeReadPublic = EventType.pick({
export const schemaEventTypeReadPublic = EventTypeSchema.pick({
id: true,
title: true,
slug: true,
+12 -5
View File
@@ -1,19 +1,24 @@
import { z } from "zod";
import { MembershipRole } from "@calcom/prisma/enums";
import { _MembershipModel as Membership, _TeamModel } from "@calcom/prisma/zod";
import { stringOrNumber } from "@calcom/prisma/zod-utils";
import { MembershipSchema } from "@calcom/prisma/zod/modelSchema/MembershipSchema";
import { TeamSchema } from "@calcom/prisma/zod/modelSchema/TeamSchema";
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
export const schemaMembershipBaseBodyParams = Membership.omit({});
export const schemaMembershipBaseBodyParams = MembershipSchema.omit({});
const schemaMembershipRequiredParams = z.object({
teamId: z.number(),
});
export const membershipCreateBodySchema = Membership.omit({ id: true })
export const membershipCreateBodySchema = MembershipSchema.omit({
id: true,
createdAt: true,
updatedAt: true,
})
.partial({
accepted: true,
role: true,
@@ -26,11 +31,13 @@ export const membershipCreateBodySchema = Membership.omit({ id: true })
...v,
}));
export const membershipEditBodySchema = Membership.omit({
export const membershipEditBodySchema = MembershipSchema.omit({
/** To avoid complication, let's avoid updating these, instead you can delete and create a new invite */
teamId: true,
userId: true,
id: true,
createdAt: true,
updatedAt: true,
})
.partial({
accepted: true,
@@ -43,7 +50,7 @@ export const schemaMembershipBodyParams = schemaMembershipBaseBodyParams.merge(
schemaMembershipRequiredParams
);
export const schemaMembershipPublic = Membership.merge(z.object({ team: _TeamModel }).partial());
export const schemaMembershipPublic = MembershipSchema.merge(z.object({ team: TeamSchema }).partial());
/** We extract userId and teamId from compound ID string */
export const membershipIdSchema = schemaQueryIdAsString
+2 -2
View File
@@ -1,6 +1,6 @@
import { _PaymentModel as Payment } from "@calcom/prisma/zod";
import { PaymentSchema } from "@calcom/prisma/zod/modelSchema/PaymentSchema";
export const schemaPaymentPublic = Payment.pick({
export const schemaPaymentPublic = PaymentSchema.pick({
id: true,
amount: true,
success: true,
+3 -3
View File
@@ -1,10 +1,10 @@
import { z } from "zod";
import { _ReminderMailModel as ReminderMail } from "@calcom/prisma/zod";
import { ReminderMailSchema } from "@calcom/prisma/zod/modelSchema/ReminderMailSchema";
export const schemaReminderMailBaseBodyParams = ReminderMail.omit({ id: true }).partial();
export const schemaReminderMailBaseBodyParams = ReminderMailSchema.omit({ id: true }).partial();
export const schemaReminderMailPublic = ReminderMail.omit({});
export const schemaReminderMailPublic = ReminderMailSchema.omit({});
const schemaReminderMailRequiredParams = z.object({
referenceId: z.number().int(),
+5 -4
View File
@@ -1,11 +1,12 @@
import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { _ScheduleModel as Schedule, _AvailabilityModel as Availability } from "@calcom/prisma/zod";
import { AvailabilitySchema } from "@calcom/prisma/zod/modelSchema/AvailabilitySchema";
import { ScheduleSchema } from "@calcom/prisma/zod/modelSchema/ScheduleSchema";
import { timeZone } from "./shared/timeZone";
const schemaScheduleBaseBodyParams = Schedule.omit({ id: true, timeZone: true }).partial();
const schemaScheduleBaseBodyParams = ScheduleSchema.omit({ id: true, timeZone: true }).partial();
export const schemaSingleScheduleBodyParams = schemaScheduleBaseBodyParams.merge(
z.object({ userId: z.number().optional(), timeZone: timeZone.optional() })
@@ -17,12 +18,12 @@ export const schemaCreateScheduleBodyParams = schemaScheduleBaseBodyParams.merge
export const schemaSchedulePublic = z
.object({ id: z.number() })
.merge(Schedule)
.merge(ScheduleSchema)
.merge(
z.object({
availability: z
.array(
Availability.pick({
AvailabilitySchema.pick({
id: true,
eventTypeId: true,
date: true,
@@ -1,28 +1,22 @@
import z from "zod";
import { _SelectedCalendarModel as SelectedCalendar } from "@calcom/prisma/zod";
import { SelectedCalendarSchema } from "@calcom/prisma/zod/modelSchema/SelectedCalendarSchema";
import { schemaQueryIdAsString } from "./shared/queryIdString";
import { schemaQueryIdParseInt } from "./shared/queryIdTransformParseInt";
export const schemaSelectedCalendarBaseBodyParams = SelectedCalendar;
export const schemaSelectedCalendarBaseBodyParams = SelectedCalendarSchema;
export const schemaSelectedCalendarPublic = SelectedCalendar.omit({});
export const schemaSelectedCalendarPublic = SelectedCalendarSchema.omit({});
export const schemaSelectedCalendarBodyParams = schemaSelectedCalendarBaseBodyParams
.partial({
.pick({
integration: true,
externalId: true,
userId: true,
})
.omit({
// id will be set by the database
id: true,
// No eventTypeId support in API v1
eventTypeId: true,
/** No watch related fields support in API v1 */
watchAttempts: true,
unwatchAttempts: true,
maxAttempts: true,
.partial({
userId: true,
});
export const schemaSelectedCalendarUpdateBodyParams = schemaSelectedCalendarBaseBodyParams
+3 -3
View File
@@ -1,8 +1,8 @@
import { z } from "zod";
import { _TeamModel as Team } from "@calcom/prisma/zod";
import { TeamSchema } from "@calcom/prisma/zod/modelSchema/TeamSchema";
export const schemaTeamBaseBodyParams = Team.omit({ id: true, createdAt: true }).partial({
export const schemaTeamBaseBodyParams = TeamSchema.omit({ id: true, createdAt: true }).partial({
hideBranding: true,
metadata: true,
pendingPayment: true,
@@ -28,6 +28,6 @@ const schemaOwnerId = z.object({
export const schemaTeamCreateBodyParams = schemaTeamBodyParams.merge(schemaOwnerId).strict();
export const schemaTeamReadPublic = Team.omit({});
export const schemaTeamReadPublic = TeamSchema.omit({});
export const schemaTeamsReadPublic = z.array(schemaTeamReadPublic);
+3 -3
View File
@@ -2,8 +2,8 @@ import { z } from "zod";
import { emailSchema } from "@calcom/lib/emailSchema";
import { checkUsername } from "@calcom/lib/server/checkUsername";
import { _UserModel as User } from "@calcom/prisma/zod";
import { iso8601 } from "@calcom/prisma/zod-utils";
import { UserSchema } from "@calcom/prisma/zod/modelSchema/UserSchema";
import { isValidBase64Image } from "~/lib/utils/isValidBase64Image";
import { timeZone } from "~/lib/validations/shared/timeZone";
@@ -68,7 +68,7 @@ const usernameSchema = z
});
// @note: These are the values that are editable via PATCH method on the user Model
export const schemaUserBaseBodyParams = User.pick({
export const schemaUserBaseBodyParams = UserSchema.pick({
name: true,
email: true,
username: true,
@@ -151,7 +151,7 @@ export const schemaUserCreateBodyParams = schemaUserBaseBodyParams
.strict();
// @note: These are the values that are always returned when reading a user
export const schemaUserReadPublic = User.pick({
export const schemaUserReadPublic = UserSchema.pick({
id: true,
username: true,
name: true,
+3 -3
View File
@@ -1,9 +1,9 @@
import { z } from "zod";
import { WEBHOOK_TRIGGER_EVENTS } from "@calcom/features/webhooks/lib/constants";
import { _WebhookModel as Webhook } from "@calcom/prisma/zod";
import { WebhookSchema } from "@calcom/prisma/zod/modelSchema/WebhookSchema";
const schemaWebhookBaseBodyParams = Webhook.pick({
const schemaWebhookBaseBodyParams = WebhookSchema.pick({
userId: true,
eventTypeId: true,
eventTriggers: true,
@@ -38,7 +38,7 @@ export const schemaWebhookEditBodyParams = schemaWebhookBaseBodyParams
.partial()
.strict();
export const schemaWebhookReadPublic = Webhook.pick({
export const schemaWebhookReadPublic = WebhookSchema.pick({
id: true,
userId: true,
eventTypeId: true,
+7
View File
@@ -1,5 +1,6 @@
const { withAxiom } = require("next-axiom");
const { withSentryConfig } = require("@sentry/nextjs");
const { PrismaPlugin } = require("@prisma/nextjs-monorepo-workaround-plugin");
const plugins = [withAxiom];
@@ -14,6 +15,12 @@ const nextConfig = {
"@calcom/prisma",
"@calcom/trpc",
],
webpack: (config, { isServer }) => {
if (isServer) {
config.plugins = [...config.plugins, new PrismaPlugin()];
}
return config;
},
async headers() {
return [
{
+1
View File
@@ -30,6 +30,7 @@
"@calcom/lib": "*",
"@calcom/prisma": "*",
"@calcom/trpc": "*",
"@prisma/nextjs-monorepo-workaround-plugin": "^6.16.1",
"@sentry/nextjs": "^9.15.0",
"bcryptjs": "^2.4.3",
"memory-cache": "^0.2.0",
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import type { Ensure } from "@calcom/types/utils";
import { apiKeyPublicSchema } from "~/lib/validations/api-key";
+1 -1
View File
@@ -1,4 +1,3 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { v4 } from "uuid";
@@ -6,6 +5,7 @@ import { generateUniqueAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { apiKeyCreateBodySchema, apiKeyPublicSchema } from "~/lib/validations/api-key";
+1 -1
View File
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaAttendeeReadPublic } from "~/lib/validations/attendee";
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import {
schemaBookingEditBodyParams,
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaBookingReferenceReadPublic } from "~/lib/validations/booking-reference";
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import {
schemaBookingCreateBodyParams,
@@ -1,7 +1,7 @@
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { prisma } from "@calcom/prisma";
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
@@ -34,7 +34,14 @@ async function authMiddleware(req: NextApiRequest) {
where: { id: userId },
select: {
email: true,
bookings: true,
bookings: {
where: {
id,
},
select: {
id: true,
},
},
},
});
+1 -1
View File
@@ -1,9 +1,9 @@
import type { Prisma, Booking } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma, Booking } from "@calcom/prisma/client";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
import { buildWhereClause } from "~/lib/utils/bookings/get/buildWhereClause";
@@ -3,7 +3,8 @@ import type { NextApiRequest } from "next";
import { OAuth2UniversalSchema } from "@calcom/app-store/_utils/oauth/universalSchema";
import { symmetricDecrypt } from "@calcom/lib/crypto";
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";
@@ -71,7 +72,7 @@ async function handler(req: NextApiRequest) {
userId,
},
data: {
key,
key: key as unknown as Prisma.InputJsonValue,
},
select: {
id: true,
@@ -6,7 +6,8 @@ import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { symmetricDecrypt } from "@calcom/lib/crypto";
import { HttpError } from "@calcom/lib/http-error";
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 { schemaCredentialPostBody, schemaCredentialPostParams } from "~/lib/validations/credential-sync";
@@ -92,7 +93,7 @@ async function handler(req: NextApiRequest) {
data: {
userId,
appId: appSlug,
key,
key: key as unknown as Prisma.InputJsonValue,
type: appMetadata.type,
},
select: credentialForCalendarServiceSelect,
@@ -80,7 +80,13 @@ export async function patchHandler(req: NextApiRequest) {
const { query } = req;
const { id } = schemaQueryIdParseInt.parse(query);
const data = schemaEventTypeCustomInputEditBodyParams.parse(req.body);
const result = await prisma.eventTypeCustomInput.update({ where: { id }, data });
const result = await prisma.eventTypeCustomInput.update({
where: { id },
data: {
...data,
options: data.options === null ? [] : data.options,
},
});
return { event_type_custom_input: schemaEventTypeCustomInputPublic.parse(result) };
}
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaEventTypeCustomInputPublic } from "~/lib/validations/event-type-custom-input";
+5 -1
View File
@@ -92,7 +92,11 @@ async function postHandler(req: NextApiRequest) {
}
const data = await prisma.eventTypeCustomInput.create({
data: { ...body, eventType: { connect: { id: eventTypeId } } },
data: {
...body,
options: body.options === null ? [] : body.options,
eventType: { connect: { id: eventTypeId } },
},
});
return {
@@ -1,4 +1,3 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import type { z } from "zod";
@@ -7,6 +6,7 @@ import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { PrismaClient } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
import {
@@ -1,10 +1,10 @@
import { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import type { z } from "zod";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { SchedulingType } from "@calcom/prisma/enums";
import type { schemaEventTypeBaseBodyParams } from "~/lib/validations/event-type";
@@ -210,6 +210,7 @@ export async function patchHandler(req: NextApiRequest) {
hosts = [],
bookingLimits,
durationLimits,
locations,
/** FIXME: Updating event-type children from API not supported for now */
children: _,
...parsedBody
@@ -217,8 +218,10 @@ export async function patchHandler(req: NextApiRequest) {
const data: Prisma.EventTypeUpdateArgs["data"] = {
...parsedBody,
teamId: parsedBody.teamId === null ? undefined : parsedBody.teamId,
bookingLimits: bookingLimits === null ? Prisma.DbNull : bookingLimits,
durationLimits: durationLimits === null ? Prisma.DbNull : durationLimits,
locations: locations === null ? Prisma.DbNull : locations,
};
if (hosts) {
+8 -4
View File
@@ -1,9 +1,9 @@
import { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import { schemaEventTypeCreateBodyParams, schemaEventTypeReadPublic } from "~/lib/validations/event-type";
@@ -265,14 +265,17 @@ import ensureOnlyMembersAsHosts from "./_utils/ensureOnlyMembersAsHosts";
* description: Authorization information is missing or invalid.
*/
async function postHandler(req: NextApiRequest) {
const { userId, isSystemWideAdmin, body } = req;
const { userId: ctxUserId, isSystemWideAdmin, body } = req;
const {
hosts = [],
bookingLimits,
durationLimits,
locations,
/** FIXME: Adding event-type children from API not supported for now */
children: _,
// if userId is not passed, we will use the userId from the context
userId = ctxUserId,
...parsedBody
} = schemaEventTypeCreateBodyParams.parse(body || {});
@@ -282,6 +285,7 @@ async function postHandler(req: NextApiRequest) {
users: !!parsedBody.teamId ? undefined : { connect: { id: userId } },
bookingLimits: bookingLimits === null ? Prisma.DbNull : bookingLimits,
durationLimits: durationLimits === null ? Prisma.DbNull : durationLimits,
locations: locations === null ? Prisma.DbNull : locations,
};
await checkPermissions(req);
@@ -291,8 +295,8 @@ async function postHandler(req: NextApiRequest) {
await checkUserMembership(req);
}
if (isSystemWideAdmin && parsedBody.userId && !parsedBody.teamId) {
data = { ...parsedBody, users: { connect: { id: parsedBody.userId } } };
if (isSystemWideAdmin && userId && parsedBody.teamId === undefined) {
data = { ...parsedBody, users: { connect: { id: userId } } };
}
await checkTeamEventEditPermission(req, parsedBody);
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { membershipIdSchema, schemaMembershipPublic } from "~/lib/validations/membership";
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import {
membershipEditBodySchema,
@@ -43,7 +43,13 @@ export async function patchHandler(req: NextApiRequest) {
const { query } = req;
const userId_teamId = membershipIdSchema.parse(query);
const data = membershipEditBodySchema.parse(req.body);
const args: Prisma.MembershipUpdateArgs = { where: { userId_teamId }, data };
const args: Prisma.MembershipUpdateArgs = {
where: { userId_teamId },
data: {
...data,
updatedAt: new Date(),
},
};
await checkPermissions(req);
+1 -1
View File
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaMembershipPublic } from "~/lib/validations/membership";
import {
+2 -5
View File
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { membershipCreateBodySchema, schemaMembershipPublic } from "~/lib/validations/membership";
@@ -25,10 +25,7 @@ import { membershipCreateBodySchema, schemaMembershipPublic } from "~/lib/valida
async function postHandler(req: NextApiRequest) {
const data = membershipCreateBodySchema.parse(req.body);
const args: Prisma.MembershipCreateArgs = {
data: {
createdAt: new Date(),
...data,
},
data,
};
await checkPermissions(req);
+1 -1
View File
@@ -1,10 +1,10 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { z } from "zod";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaSchedulePublic } from "~/lib/validations/schedule";
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
+1 -1
View File
@@ -1,10 +1,10 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaCreateScheduleBodyParams, schemaSchedulePublic } from "~/lib/validations/schedule";
@@ -1,4 +1,3 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
@@ -6,6 +5,7 @@ import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import type { UpdateArguments } from "@calcom/lib/server/repository/selectedCalendar";
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import {
schemaSelectedCalendarPublic,
@@ -1,10 +1,10 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import {
schemaSelectedCalendarBodyParams,
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import { schemaQueryTeamId } from "~/lib/validations/shared/queryTeamId";
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaQueryTeamId } from "~/lib/validations/shared/queryTeamId";
import { schemaTeamReadPublic } from "~/lib/validations/team";
@@ -1,4 +1,3 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { purchaseTeamOrOrgSubscription } from "@calcom/features/ee/teams/lib/payments";
@@ -6,6 +5,7 @@ import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { TRPCError } from "@trpc/server";
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { z } from "zod";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaEventTypeReadPublic } from "~/lib/validations/event-type";
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaTeamsReadPublic } from "~/lib/validations/team";
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
import { schemaQuerySingleOrMultipleUserEmails } from "~/lib/validations/shared/queryUserEmail";
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
import { schemaWebhookEditBodyParams, schemaWebhookReadPublic } from "~/lib/validations/webhook";
+1 -1
View File
@@ -1,9 +1,9 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
import { schemaWebhookReadPublic } from "~/lib/validations/webhook";
+1 -1
View File
@@ -1,10 +1,10 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { v4 as uuidv4 } from "uuid";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { schemaWebhookCreateBodyParams, schemaWebhookReadPublic } from "~/lib/validations/webhook";
+1 -1
View File
@@ -77,7 +77,7 @@ describe("POST /api/attendees", () => {
const userBooking = { id: 1 };
prismaMock.booking.findFirst.mockResolvedValue(userBooking);
prismaMock.booking.findFirst.mockResolvedValue(userBooking as any);
const attendeeData = {
id: 1,
@@ -25,7 +25,7 @@ beforeEach(() => {
id: userId,
email: "test@example.com",
name: "Test User",
});
} as any);
});
afterEach(() => {
@@ -20,7 +20,7 @@ beforeEach(() => {
id: userId,
email: "test@example.com",
name: "Test User",
});
} as any);
});
afterEach(() => {
@@ -1,10 +1,10 @@
import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
import { createMocks } from "node-mocks-http";
import { describe, it, expect, test } from "vitest";
import { describe, it, expect, test, beforeAll, afterAll } from "vitest";
import { prisma } from "@calcom/prisma";
import type { User, Team, Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import authMiddleware from "../../../pages/api/bookings/[id]/_auth-middleware";
@@ -12,229 +12,206 @@ import authMiddleware from "../../../pages/api/bookings/[id]/_auth-middleware";
type CustomNextApiRequest = NextApiRequest & Request;
type CustomNextApiResponse = NextApiResponse & Response;
const createEventTypeEventSelect = {
bookings: true,
} satisfies Prisma.EventTypeSelect;
describe("Booking ownership and access in Middleware", () => {
const adminUserId = 1;
const ownerUserId = 2;
const memberUserId = 3;
const orgOwnerUserId = 4;
const adminUserEmail = "admin@example.com";
const ownerUserEmail = "owner@example.com";
const memberUserEmail = "member@example.com";
const orgOwnerUserEmail = "org-owner@example.com";
let adminUserRef: User;
let ownerUserRef: User;
let orgOwnerUserRef: User;
let memberUserRef: User;
let orgRef: Team;
let createEventResult1: Prisma.EventTypeGetPayload<{ select: typeof createEventTypeEventSelect }>;
let createEventResult2: Prisma.EventTypeGetPayload<{ select: typeof createEventTypeEventSelect }>;
// mock user data
function buildMockData() {
beforeAll(async () => {
//Create Users
prismock.user.create({
const createAdminUser = prisma.user.create({
data: {
id: adminUserId,
username: "admin",
username: `admin-${Date.now()}`,
name: "Admin User",
email: adminUserEmail,
email: `admin+${Date.now()}@example.com`,
},
});
prismock.user.create({
const createOwnerUser = prisma.user.create({
data: {
id: ownerUserId,
username: "owner",
username: `owner-${Date.now()}`,
name: "Owner User",
email: ownerUserEmail,
email: `owner+${Date.now()}@example.com`,
},
});
prismock.user.create({
const createOrgOwnerUser = prisma.user.create({
data: {
id: orgOwnerUserId,
username: "org-owner",
username: `org-owner-${Date.now()}`,
name: "Org Owner",
email: orgOwnerUserEmail,
email: `org-owner+${Date.now()}@example.com`,
},
});
prismock.user.create({
const createMemberUser = prisma.user.create({
data: {
id: memberUserId,
username: "member",
username: `member-${Date.now()}`,
name: "Member User",
email: memberUserEmail,
email: `member+${Date.now()}@example.com`,
},
});
[adminUserRef, ownerUserRef, orgOwnerUserRef, memberUserRef] = await Promise.all([
createAdminUser,
createOwnerUser,
createOrgOwnerUser,
createMemberUser,
]);
//create Org & Team
orgRef = await prisma.team.create({
data: {
name: "Org",
slug: `org-${Date.now()}`,
isOrganization: true,
children: {
create: {
name: "Team 1",
slug: `team1-${Date.now()}`,
members: {
createMany: {
data: [
{
userId: adminUserRef.id,
role: MembershipRole.ADMIN,
accepted: true,
},
{
userId: ownerUserRef.id,
role: MembershipRole.OWNER,
accepted: true,
},
{
userId: memberUserRef.id,
role: MembershipRole.MEMBER,
accepted: true,
},
],
},
},
},
},
members: {
createMany: {
data: [
{
userId: ownerUserRef.id,
role: MembershipRole.OWNER,
accepted: true,
},
{
userId: memberUserRef.id,
role: MembershipRole.MEMBER,
accepted: true,
},
{
userId: adminUserRef.id,
role: MembershipRole.MEMBER,
accepted: true,
},
],
},
},
},
});
//create eventTypes
const createEventTypeEvent1 = prisma.eventType.create({
data: {
title: "Event 1",
slug: `event-1-${Date.now()}`,
userId: ownerUserRef.id,
length: 60,
bookings: {
create: {
id: 2,
uid: "2",
uid: `booking-2-${Date.now()}`,
title: "Booking 2",
eventTypeId: 1,
userId: memberUserRef.id,
startTime: "2024-08-30T06:45:00.000Z",
endTime: "2024-08-30T07:45:00.000Z",
attendees: {
create: {
name: "Member User",
email: memberUserEmail,
email: memberUserRef.email,
timeZone: "UTC",
},
},
},
},
},
select: createEventTypeEventSelect,
});
//create team
prismock.team.create({
const createEventTypeEvent2 = prisma.eventType.create({
data: {
id: 1,
name: "Team 1",
slug: "team1",
members: {
createMany: {
data: [
{
userId: adminUserId,
role: MembershipRole.ADMIN,
accepted: true,
},
{
userId: ownerUserId,
role: MembershipRole.OWNER,
accepted: true,
},
{
userId: memberUserId,
role: MembershipRole.MEMBER,
accepted: true,
},
],
},
},
},
});
//create Org
prismock.team.create({
data: {
id: 2,
name: "Org",
slug: "org",
isOrganization: true,
children: {
connect: {
id: 1,
},
},
members: {
createMany: {
data: [
{
userId: orgOwnerUserId,
role: MembershipRole.OWNER,
accepted: true,
},
{
userId: memberUserId,
role: MembershipRole.MEMBER,
accepted: true,
},
{
userId: ownerUserId,
role: MembershipRole.MEMBER,
accepted: true,
},
{
userId: adminUserId,
role: MembershipRole.MEMBER,
accepted: true,
},
],
},
},
},
});
//create eventTypes
prismock.eventType.create({
data: {
id: 1,
title: "Event 1",
slug: "event",
length: 60,
bookings: {
connect: {
id: 2,
},
},
},
});
prismock.eventType.create({
data: {
id: 2,
title: "Event 2",
slug: "event",
slug: `event-2-${Date.now()}`,
length: 60,
teamId: 1,
teamId: orgRef.id,
bookings: {
connect: {
id: 1,
},
},
},
});
//link eventType to teams
prismock.eventType.update({
where: {
id: 1,
},
data: {
owner: {
connect: {
id: ownerUserId,
},
},
},
});
prismock.eventType.update({
where: {
id: 2,
},
data: {
team: {
connect: {
id: 1,
},
},
},
});
//link team to org
prismock.team.update({
where: {
id: 1,
},
data: {
parentId: 2,
},
});
// Call Prisma to create booking with attendees
prismock.booking.create({
data: {
id: 1,
uid: "1",
title: "Booking 1",
userId: 1,
startTime: "2024-08-30T06:45:00.000Z",
endTime: "2024-08-30T07:45:00.000Z",
eventTypeId: 2,
attendees: {
create: {
name: "Admin User",
email: adminUserEmail,
timeZone: "UTC",
uid: `booking-1-${Date.now()}`,
title: "Booking 1",
userId: adminUserRef.id,
startTime: "2024-08-30T06:45:00.000Z",
endTime: "2024-08-30T07:45:00.000Z",
attendees: {
create: {
name: "Admin User",
email: adminUserRef.email,
timeZone: "UTC",
},
},
},
},
},
select: createEventTypeEventSelect,
});
}
[createEventResult1, createEventResult2] = await Promise.all([
createEventTypeEvent1,
createEventTypeEvent2,
]);
});
afterAll(async () => {
console.log("Cleaning up org", orgRef.id);
await prisma.team.delete({
where: {
id: orgRef.id,
},
});
console.log("Cleaning up users", [
adminUserRef.id,
ownerUserRef.id,
orgOwnerUserRef.id,
memberUserRef.id,
]);
await prisma.user.deleteMany({
where: {
id: {
in: [adminUserRef.id, ownerUserRef.id, orgOwnerUserRef.id, memberUserRef.id],
},
},
});
});
test("should not throw error for bookings where user is an attendee", async () => {
console.log(createEventResult1.bookings[0].id);
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
query: {
id: 2,
id: createEventResult1.bookings[0].id,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = memberUserId;
req.userId = memberUserRef.id;
await expect(authMiddleware(req)).resolves.not.toThrow();
});
@@ -244,10 +221,9 @@ describe("Booking ownership and access in Middleware", () => {
query: {
id: 1,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = memberUserId;
req.userId = memberUserRef.id;
await expect(authMiddleware(req)).rejects.toThrow();
});
@@ -256,12 +232,11 @@ describe("Booking ownership and access in Middleware", () => {
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
query: {
id: 2,
id: createEventResult2.bookings[0].id,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = ownerUserId;
req.userId = ownerUserRef.id;
await expect(authMiddleware(req)).resolves.not.toThrow();
});
@@ -269,21 +244,19 @@ describe("Booking ownership and access in Middleware", () => {
const { req: req1 } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
query: {
id: 1,
id: createEventResult2.bookings[0].id,
},
prisma: prismock,
prisma,
});
const { req: req2 } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
query: {
id: 1,
id: createEventResult2.bookings[0].id,
},
prisma: prismock,
prisma,
});
buildMockData();
req1.userId = adminUserId;
req2.userId = ownerUserId;
req1.userId = adminUserRef.id;
req2.userId = ownerUserRef.id;
await expect(authMiddleware(req1)).resolves.not.toThrow();
await expect(authMiddleware(req2)).resolves.not.toThrow();
@@ -292,13 +265,12 @@ describe("Booking ownership and access in Middleware", () => {
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
query: {
id: 1,
id: createEventResult2.bookings[0].id,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = memberUserId;
req.userId = memberUserRef.id;
await expect(authMiddleware(req)).rejects.toThrow();
});
@@ -308,10 +280,9 @@ describe("Booking ownership and access in Middleware", () => {
query: {
id: 2,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = adminUserId;
req.userId = adminUserRef.id;
req.isSystemWideAdmin = true;
await authMiddleware(req);
@@ -323,12 +294,11 @@ describe("Booking ownership and access in Middleware", () => {
query: {
id: 1,
},
prisma: prismock,
prisma,
});
buildMockData();
req.userId = orgOwnerUserId;
req.userId = orgOwnerUserRef.id;
req.isOrganizationOwnerOrAdmin = true;
await authMiddleware(req);
await expect(authMiddleware(req)).rejects.toThrow();
});
});
+3 -2
View File
@@ -30,12 +30,13 @@ beforeEach(() => {
id: userId,
email: "test@example.com",
name: "Test User",
});
} as any);
(getAccessibleUsers as any).mockResolvedValue([userId]);
(retrieveOrgScopedAccessibleUsers as any).mockResolvedValue([userId]);
prismaMock.membership.findMany.mockResolvedValue([
{
// @ts-expect-error Will be fixed by Prisma 6.7.0 upgrade mock changes - which uses vitest-mock-extended
team: {
id: 1,
isOrganization: true,
@@ -48,7 +49,7 @@ beforeEach(() => {
id: userId,
email: "test@example.com",
},
]);
] as any);
});
afterEach(() => {
+12 -4
View File
@@ -11,9 +11,9 @@ import { getEventTypesFromDB } from "@calcom/features/bookings/lib/handleNewBook
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import { ErrorCode } from "@calcom/lib/errorCodes";
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 { CreationSource } from "@calcom/prisma/enums";
import { CreationSource, BookingStatus } from "@calcom/prisma/enums";
import handler from "../../../pages/api/bookings/_post";
@@ -303,6 +303,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -340,6 +341,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -376,6 +378,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -417,6 +420,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -468,7 +472,7 @@ describe("POST /api/bookings", () => {
prismaMock.booking.findUnique.mockResolvedValue({
...originalBooking,
status: "cancelled",
status: "CANCELLED",
});
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
@@ -492,6 +496,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -532,7 +537,7 @@ describe("POST /api/bookings", () => {
const previousBooking = await prisma.booking.findUnique({
where: { uid: "original-booking-uid" },
});
expect(previousBooking?.status).toBe("cancelled");
expect(previousBooking?.status).toBe(BookingStatus.CANCELLED);
});
test("Creates source as api_v1", async () => {
@@ -556,6 +561,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -627,6 +633,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -684,6 +691,7 @@ describe("POST /api/bookings", () => {
prismaMock.eventType.findUniqueOrThrow.mockResolvedValue({
...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 },
hosts: [],
users: [buildUser()],
@@ -50,6 +50,7 @@ describe("DELETE /api/event-types/[id]", () => {
// Mocking team.findUnique
prismaMock.team.findUnique.mockResolvedValue({
id: teamId,
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
members: [
{ userId: memberUser, role: MembershipRole.MEMBER, teamId: teamId },
{ userId: adminUser, role: MembershipRole.ADMIN, teamId: teamId },
@@ -84,6 +84,7 @@ describe("GET /api/event-types/[id]", () => {
prismaMock.team.findFirst.mockResolvedValue({
id: teamId,
// @ts-expect-error requires mockDeep which will be introduced in the Prisma 6.7.0 upgrade, ignore for now.
members: [
{
userId,
@@ -79,7 +79,7 @@ describe("POST /api/selected-calendars", () => {
integration: "google",
externalId: "ext123",
userId: 444444,
id: "xxx-xxx",
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
eventTypeId: null,
delegationCredentialId: null,
domainWideDelegationCredentialId: null,
@@ -120,7 +120,7 @@ describe("POST /api/selected-calendars", () => {
req.userId = 333333;
prismaMock.selectedCalendar.create.mockResolvedValue({
id: "xxx-xxx",
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
credentialId: 1,
integration: "google",
externalId: "ext123",
+2 -1
View File
@@ -41,4 +41,5 @@ lerna-debug.log*
.env.production.local
.env.*
!.env.example
!.env.appStore.example
!.env.appStore.example
/client/*
+1 -1
View File
@@ -38,7 +38,7 @@
"@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.344",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.359",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
"@calcom/prisma": "*",
+1 -1
View File
@@ -18,7 +18,7 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository
import { randomString } from "test/utils/randomString";
import { X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
import { User, PlatformOAuthClient, Team, RateLimit } from "@calcom/prisma/client";
import type { User, PlatformOAuthClient, Team, RateLimit } from "@calcom/prisma/client";
describe("AppController", () => {
describe("Rate limiting", () => {
@@ -1,6 +1,7 @@
import { BookingsController_2024_04_15 } from "@/ee/bookings/2024-04-15/controllers/bookings.controller";
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
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 { 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";
@@ -14,6 +15,7 @@ import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repo
import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-clients-users.service";
import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { ProfilesModule } from "@/modules/profiles/profiles.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
import { TokensModule } from "@/modules/tokens/tokens.module";
@@ -32,6 +34,7 @@ import { Module } from "@nestjs/common";
EventTypesModule_2024_04_15,
SchedulesModule_2024_04_15,
EventTypesModule_2024_06_14,
ProfilesModule,
],
providers: [
TokensRepository,
@@ -41,6 +44,7 @@ import { Module } from "@nestjs/common";
OAuthClientUsersService,
PlatformBookingsService,
CalendarsService,
CalendarsCacheService,
CredentialsRepository,
AppsRepository,
CalendarsRepository,
@@ -8,12 +8,10 @@ import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { TokensModule } from "@/modules/tokens/tokens.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
@@ -22,9 +20,10 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository
import { randomString } from "test/utils/randomString";
import { withApiAuth } from "test/utils/withApiAuth";
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { handleNewBooking } from "@calcom/platform-libraries";
import { ApiSuccessResponse, ApiResponse, ApiErrorResponse } from "@calcom/platform-types";
import type { ApiSuccessResponse, ApiErrorResponse } from "@calcom/platform-types";
import type { User } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-04-15", () => {
describe("User Authenticated", () => {
@@ -63,8 +63,8 @@ import {
CancelBookingInput_2024_04_15,
Status_2024_04_15,
} from "@calcom/platform-types";
import { ApiResponse } from "@calcom/platform-types";
import { PrismaClient } from "@calcom/prisma";
import type { ApiResponse } from "@calcom/platform-types";
import type { PrismaClient } from "@calcom/prisma";
type BookingRequest = Request & {
userId?: number;
@@ -18,7 +18,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { PlatformOAuthClient, Team, User } from "@prisma/client";
import * as request from "supertest";
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
@@ -33,6 +32,7 @@ import {
CreateEventTypeInput_2024_06_14,
EventTypeOutput_2024_06_14,
} from "@calcom/platform-types";
import type { PlatformOAuthClient, Team, User } from "@calcom/prisma/client";
const CLIENT_REDIRECT_URI = "http://localhost:4321";
@@ -1,7 +1,8 @@
import { BookingReferencesFilterInput_2024_08_13 } from "@/ee/bookings/2024-08-13/inputs/booking-references-filter.input";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { Injectable } from "@nestjs/common";
import type { Prisma } from "@prisma/client";
import type { Prisma } from "@calcom/prisma/client";
@Injectable()
export class BookingReferencesRepository_2024_08_13 {
@@ -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 { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
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 { 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";
@@ -29,6 +30,7 @@ import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.se
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { ProfilesModule } from "@/modules/profiles/profiles.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
import { StripeModule } from "@/modules/stripe/stripe.module";
@@ -55,6 +57,7 @@ import { Module } from "@nestjs/common";
TeamsModule,
TeamsEventTypesModule,
MembershipsModule,
ProfilesModule,
],
providers: [
TokensRepository,
@@ -71,6 +74,7 @@ import { Module } from "@nestjs/common";
ApiKeysRepository,
PlatformBookingsService,
CalendarsService,
CalendarsCacheService,
CredentialsRepository,
AppsRepository,
CalendarsRepository,
@@ -1,7 +1,8 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import type { Prisma } from "@prisma/client";
import type { Prisma } from "@calcom/prisma/client";
@Injectable()
export class BookingsRepository_2024_08_13 {
@@ -12,7 +12,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
@@ -37,7 +36,7 @@ import {
RescheduleBookingInput_2024_08_13,
CancelBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
import type { User, Team } from "@calcom/prisma/client";
jest.spyOn(AttendeeScheduledEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
@@ -26,7 +26,7 @@ import {
GetSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { BookingOutput_2024_08_13 } from "@calcom/platform-types";
import { Booking, PlatformOAuthClient, Team, User, EventType } from "@calcom/prisma/client";
import type { Booking, PlatformOAuthClient, Team, User, EventType } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Booking fields", () => {
@@ -117,6 +117,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
slug: seatedEventTypeSlug,
length: 60,
seatsPerTimeSlot: 3,
seatsShowAttendees: true,
},
user.id
);
@@ -9,7 +9,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -20,7 +19,7 @@ import { randomString } from "test/utils/randomString";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import { Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { User, Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Calendar Links", () => {
@@ -9,7 +9,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -21,7 +20,7 @@ import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import { BookingOutput_2024_08_13, GetBookingOutput_2024_08_13 } from "@calcom/platform-types";
import { Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { User, Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Bookings confirmation", () => {
@@ -40,7 +39,6 @@ describe("Bookings Endpoints 2024-08-13", () => {
let user: User;
let eventTypeId: number;
const eventTypeSlug = `confirm-bookings-2024-08-13-event-type-${randomString()}`;
let createdBooking1: Booking;
let createdBooking2: Booking;
@@ -1,19 +1,16 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -35,14 +32,13 @@ import {
OrganizerRequestEmail,
AttendeeDeclinedEmail,
} from "@calcom/platform-libraries/emails";
import {
import type {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
GetBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
import type { User, Team } from "@calcom/prisma/client";
jest
.spyOn(AttendeeScheduledEmail.prototype, "getHtml")
@@ -11,7 +11,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { HostsRepositoryFixture } from "test/fixtures/repository/hosts.repository.fixture";
@@ -34,13 +33,13 @@ import {
OrganizerRescheduledEmail,
OrganizerScheduledEmail,
} from "@calcom/platform-libraries/emails";
import {
import type {
BookingOutput_2024_08_13,
CancelBookingInput_2024_08_13,
CreateBookingInput_2024_08_13,
RescheduleBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
import type { User, Team } from "@calcom/prisma/client";
// Mock all email sending prototypes
jest
@@ -13,7 +13,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -31,14 +30,14 @@ import {
OrganizerCancelledEmail,
AttendeeCancelledEmail,
} from "@calcom/platform-libraries/emails";
import {
import type {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
RecurringBookingOutput_2024_08_13,
CancelBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
import type { User, Team } from "@calcom/prisma/client";
jest
.spyOn(AttendeeScheduledEmail.prototype, "getHtml")
@@ -378,7 +377,7 @@ describe("Bookings Endpoints 2024-08-13 user emails", () => {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.createdBookingUid = responseBody.data.uid;
emailsEnabledSetup.createdBookingUid = data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibly recurring bookings"
@@ -14,7 +14,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { PlatformOAuthClient, Team, User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -40,6 +39,7 @@ import {
GetBookingOutput_2024_08_13,
GetBookingsOutput_2024_08_13,
} from "@calcom/platform-types";
import type { PlatformOAuthClient, Team, User } from "@calcom/prisma/client";
const CLIENT_REDIRECT_URI = "http://localhost:4321";
@@ -10,7 +10,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { Booking, User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -25,8 +24,8 @@ import { randomString } from "test/utils/randomString";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import { CreateBookingInput_2024_08_13, BookingOutput_2024_08_13 } from "@calcom/platform-types";
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { CreateBookingInput_2024_08_13 } from "@calcom/platform-types";
import type { Booking, User, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Reassign bookings", () => {
@@ -321,10 +320,6 @@ describe("Bookings Endpoints 2024-08-13", () => {
return client;
}
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
afterAll(async () => {
await oauthClientRepositoryFixture.delete(oAuthClient.id);
await teamRepositoryFixture.delete(organization.id);
@@ -10,7 +10,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -22,12 +21,12 @@ import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import { AttendeeCancelledEmail, OrganizerCancelledEmail } from "@calcom/platform-libraries/emails";
import {
import type {
CreateRecurringBookingInput_2024_08_13,
RecurringBookingOutput_2024_08_13,
CancelBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { User, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Creating recurring bookings", () => {
@@ -48,7 +47,6 @@ describe("Bookings Endpoints 2024-08-13", () => {
const maxRecurrenceCount = 3;
let recurringEventTypeId: number;
const recurringEventSlug = `recurring-bookings-2024-08-13-event-type-${randomString()}`;
let createdRecurringBooking: RecurringBookingOutput_2024_08_13[];
beforeAll(async () => {
const moduleRef = await withApiAuth(
@@ -11,7 +11,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import { DateTime } from "luxon";
import * as request from "supertest";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
@@ -23,7 +22,7 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository
import { randomString } from "test/utils/randomString";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
import type {
CancelBookingInput_2024_08_13,
CancelSeatedBookingInput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
@@ -31,9 +30,9 @@ import {
GetBookingsOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
RescheduleSeatedBookingInput_2024_08_13,
CreateBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { CreateBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
import type { Team, User } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Seated bookings", () => {
@@ -53,8 +52,10 @@ describe("Bookings Endpoints 2024-08-13", () => {
let apiKeyString: string;
let seatedEventTypeId: number;
let seatedEventTypeIdAttendeesDisabledId: number;
const seatedEventSlug = `seated-bookings-event-type-${randomString()}`;
const seatedEventSlugAttendeesDisabled = `seated-bookings-event-type-attendees-disabled-${randomString()}`;
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
let createdSeatedBooking2: CreateSeatedBookingOutput_2024_08_13;
@@ -121,6 +122,28 @@ describe("Bookings Endpoints 2024-08-13", () => {
);
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();
bootstrap(app as NestExpressApplication);
@@ -414,6 +437,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 as attendee", () => {
it("should cancel seated booking", async () => {
@@ -11,7 +11,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -26,7 +25,7 @@ import { randomString } from "test/utils/randomString";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
import type {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
@@ -34,7 +33,7 @@ import {
GetSeatedBookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { User, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("Team bookings", () => {
@@ -14,7 +14,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { EventType, User, Workflow } from "@prisma/client";
import { advanceTo, clear } from "jest-date-mock";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
@@ -34,25 +33,22 @@ import {
VERSION_2024_08_13,
X_CAL_CLIENT_ID,
} from "@calcom/platform-constants";
import { AttendeeScheduledEmail, OrganizerScheduledEmail } from "@calcom/platform-libraries/emails";
import { EventManager } from "@calcom/platform-libraries/event-types";
import {
import { FAILED_EVENT_TYPE_IDENTIFICATION_ERROR_MESSAGE } from "@calcom/platform-types";
import type {
CreateEventTypeInput_2024_06_14,
FAILED_EVENT_TYPE_IDENTIFICATION_ERROR_MESSAGE,
GetBookingOutput_2024_08_13,
GetBookingsOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
CreateRecurringBookingInput_2024_08_13,
RecurringBookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
MarkAbsentBookingInput_2024_08_13,
CancelBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { EventType, User, Workflow, Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("User bookings", () => {
@@ -2917,14 +2913,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
},
};
const beforeCreate = new Date();
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const afterCreate = new Date();
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
@@ -10,7 +10,6 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { EventType, User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -21,13 +20,13 @@ import { randomString } from "test/utils/randomString";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
import type {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
CreateRecurringBookingInput_2024_08_13,
RecurringBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
import type { PlatformOAuthClient, Team, EventType, User } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("User bookings", () => {
@@ -62,8 +62,8 @@ import {
RescheduleBookingInput,
CancelBookingInput,
} from "@calcom/platform-types";
import { PrismaClient } from "@calcom/prisma";
import { EventType, User, Team } from "@calcom/prisma/client";
import type { PrismaClient } from "@calcom/prisma";
import type { EventType, User, Team } from "@calcom/prisma/client";
type CreatedBooking = {
hosts: { id: number }[];
@@ -576,10 +576,13 @@ export class BookingsService_2024_08_13 {
return this.outputService.getOutputRecurringBooking(booking);
}
if (isRecurring && isSeated) {
return this.outputService.getOutputRecurringSeatedBooking(booking);
return this.outputService.getOutputRecurringSeatedBooking(
booking,
!!booking.eventType?.seatsShowAttendees
);
}
if (isSeated) {
return this.outputService.getOutputSeatedBooking(booking);
return this.outputService.getOutputSeatedBooking(booking, !!booking.eventType?.seatsShowAttendees);
}
return this.outputService.getOutputBooking(booking);
}
@@ -591,7 +594,10 @@ export class BookingsService_2024_08_13 {
const ids = recurringBooking.map((booking) => booking.id);
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
if (isRecurringSeated) {
return this.outputService.getOutputRecurringSeatedBookings(ids);
return this.outputService.getOutputRecurringSeatedBookings(
ids,
!!recurringBooking[0].eventType?.seatsShowAttendees
);
}
return this.outputService.getOutputRecurringBookings(ids);
@@ -657,9 +663,19 @@ export class BookingsService_2024_08_13 {
if (isRecurring && !isSeated) {
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
} else if (isRecurring && isSeated) {
formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted));
formattedBookings.push(
this.outputService.getOutputRecurringSeatedBooking(
formatted,
!!formatted.eventType?.seatsShowAttendees
)
);
} else if (isSeated) {
formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted));
formattedBookings.push(
await this.outputService.getOutputSeatedBooking(
formatted,
!!formatted.eventType?.seatsShowAttendees
)
);
} else {
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
}
@@ -31,7 +31,7 @@ import { z } from "zod";
import { CreationSource } from "@calcom/platform-libraries";
import { EventTypeMetaDataSchema } from "@calcom/platform-libraries/event-types";
import {
import type {
CancelBookingInput,
CancelBookingInput_2024_08_13,
CancelSeatedBookingInput_2024_08_13,
@@ -44,8 +44,8 @@ import {
RescheduleBookingInput_2024_08_13,
RescheduleSeatedBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { BookingInputLocation_2024_08_13 } from "@calcom/platform-types/bookings/2024-08-13/inputs/location.input";
import { EventType } from "@calcom/prisma/client";
import type { BookingInputLocation_2024_08_13 } from "@calcom/platform-types/bookings/2024-08-13/inputs/location.input";
import type { EventType } from "@calcom/prisma/client";
type BookingRequest = NextApiRequest & {
userId: number | undefined;
@@ -176,6 +176,7 @@ export class InputBookingsService_2024_08_13 {
metadata: inputBooking.metadata || {},
hasHashedBookingLink: false,
guests,
verificationCode: inputBooking.emailVerificationCode,
// note(Lauris): responses with name and email are required by the handleNewBooking
responses: {
...(inputBooking.bookingFieldsResponses || {}),
@@ -471,6 +472,7 @@ export class InputBookingsService_2024_08_13 {
metadata: inputBooking.metadata || {},
hasHashedBookingLink: false,
guests,
verificationCode: inputBooking.emailVerificationCode,
// note(Lauris): responses with name and email are required by the handleNewBooking
responses: {
...(inputBooking.bookingFieldsResponses || {}),
@@ -600,6 +602,7 @@ export class InputBookingsService_2024_08_13 {
guests: [],
responses: { ...bookingResponses },
rescheduleUid: inputBooking.seatUid,
verificationCode: inputBooking.emailVerificationCode,
};
}
@@ -661,6 +664,7 @@ export class InputBookingsService_2024_08_13 {
guests: bookingResponses.guests,
responses: { ...bookingResponses, rescheduledReason: inputBooking.reschedulingReason },
rescheduleUid: bookingUid,
verificationCode: inputBooking.emailVerificationCode,
};
}
@@ -13,16 +13,19 @@ import { z } from "zod";
import { bookingMetadataSchema } from "@calcom/platform-libraries";
import {
BookingOutput_2024_08_13,
CreateRecurringSeatedBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
GetRecurringSeatedBookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
ReassignBookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
SeatedAttendee,
BookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { Booking, BookingSeat } from "@calcom/prisma/client";
import type {
CreateRecurringSeatedBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
ReassignBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import type { Booking, BookingSeat } from "@calcom/prisma/client";
import { BookingStatus } from "@calcom/prisma/enums";
export const bookingResponsesSchema = z
.object({
@@ -68,6 +71,7 @@ type DatabaseBooking = Booking & {
eventType: {
id: number;
slug: string;
seatsShowAttendees?: boolean | null;
} | null;
attendees: {
name: string;
@@ -167,6 +171,7 @@ export class OutputBookingsService_2024_08_13 {
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
if (databaseMetadata === null) return {};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
return userDefinedMetadata;
@@ -223,8 +228,8 @@ export class OutputBookingsService_2024_08_13 {
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
cancelledByEmail: databaseBooking.status === "CANCELLED" ? databaseBooking.cancelledBy : undefined,
databaseBooking.status === BookingStatus.CANCELLED ? databaseBooking.cancellationReason : undefined,
cancelledByEmail: databaseBooking.status === BookingStatus.CANCELLED ? databaseBooking.cancelledBy : undefined,
reschedulingReason: bookingResponses?.rescheduledReason,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime,
@@ -266,11 +271,14 @@ export class OutputBookingsService_2024_08_13 {
databaseBooking: DatabaseBooking,
seatUid: string
): Promise<CreateSeatedBookingOutput_2024_08_13> {
const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking);
const getSeatedBookingOutput = await this.getOutputSeatedBooking(
databaseBooking,
!!databaseBooking.eventType?.seatsShowAttendees
);
return { ...getSeatedBookingOutput, seatUid };
}
async getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
async getOutputSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes;
@@ -293,7 +301,7 @@ export class OutputBookingsService_2024_08_13 {
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
databaseBooking.status === BookingStatus.CANCELLED ? databaseBooking.cancellationReason : undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime,
end: databaseBooking.endTime,
@@ -317,41 +325,43 @@ export class OutputBookingsService_2024_08_13 {
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
parsed.attendees = databaseBooking.attendees.map((attendee) => {
const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData,
false
);
parsed.attendees = showAttendees
? databaseBooking.attendees.map((attendee) => {
const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData,
false
);
const attendeeData = {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: attendee.locale,
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata,
false
);
// note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email;
const attendeeData = {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: attendee.locale,
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata,
false
);
// note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email;
return attendeeParsed;
});
return attendeeParsed;
})
: [];
return parsed;
}
async getOutputRecurringSeatedBookings(bookingsIds: number[]) {
async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean) {
const transformed = [];
for (const bookingId of bookingsIds) {
@@ -361,7 +371,7 @@ export class OutputBookingsService_2024_08_13 {
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());
@@ -386,11 +396,14 @@ export class OutputBookingsService_2024_08_13 {
databaseBooking: DatabaseBooking,
seatUid: string
): CreateRecurringSeatedBookingOutput_2024_08_13 {
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(databaseBooking);
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(
databaseBooking,
!!databaseBooking.eventType?.seatsShowAttendees
);
return { ...getRecurringSeatedBookingOutput, seatUid };
}
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking) {
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes;
@@ -405,7 +418,7 @@ export class OutputBookingsService_2024_08_13 {
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
databaseBooking.status === BookingStatus.CANCELLED ? databaseBooking.cancellationReason : undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime,
end: databaseBooking.endTime,
@@ -430,35 +443,37 @@ export class OutputBookingsService_2024_08_13 {
});
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
parsed.attendees = databaseBooking.attendees.map((attendee) => {
const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData,
false
);
parsed.attendees = showAttendees
? databaseBooking.attendees.map((attendee) => {
const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData,
false
);
const attendeeData = {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: attendee.locale,
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata,
false
);
// note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email;
return attendeeParsed;
});
const attendeeData = {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: attendee.locale,
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata,
false
);
// note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email;
return attendeeParsed;
})
: [];
return parsed;
}
@@ -4,7 +4,7 @@ import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";
import { PlatformOAuthClient } from "@calcom/prisma/client";
import type { PlatformOAuthClient } from "@calcom/prisma/client";
@Injectable()
export class PlatformBookingsService {

Some files were not shown because too many files have changed in this diff Show More