* checkpoint 1
* checkpoint 2
* update extension header
* fix some minor ios ui bugs
* fix(companion): restore username fetching for URL fallback display
Addresses Cubic AI review feedback (confidence 9/10): The username was
hardcoded to 'username' but still used by BasicsTab as a fallback for
URL display when bookingUrl is unavailable. This restores the useEffect
that fetches the actual username from CalComAPIService.getUsername().
Co-Authored-By: unknown <>
* complete devins code change
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add db schema
* Add `CredentialRepository.findByTeamIdAndSlugs`
* Add enabled app slugs for attribute syncing
* Create repository for `IntegrationAttributeSync`
* Create zod schemas
* Create `AttributeSyncUserRuleOutputMapper`
* Create `IntegrationAttributeSyncService`
* Create DI contianer
* Create trpc endpoints
* Create page
* Include team name in `CredentialRepository.findByTeamIdAndSlugs`
* Update schema and relations
* Update types and schemas
* Add more methods to IntegrationAttributeSyncRepository
* Add more services to `IntegrationAttributeSyncService`
- getById
- Init updateIncludeRulesAndMappings
* Refactor `getTeams.handler` to use repository
* Create `createAttributeSync` trpc endpoint
* Create `updateAttributeSync` trpc endpoint
* Add router to trpc
* Create attribute sync child components
* Pass custom actions to `FormCard`
* Create `IntegrationAttributeSyncCard`
* Pass inital props via server side
* Fix prop
* Only refetch on mutation
* Fixes
* Add form error when duplicate field and attribute combo
* Add `updateTransactionWithRUleAndMappings` logic
* Adjust zod schemas
* Service add `updateIncludeRulesAndMappings`
* Pass orgId from server to component
* Rename types
* Add deleteById method to repository
* Add name to integrationAttributeSync
* Add deleteById method to service
* Rename method
* Add deleteAttributeSync trpc endpoint
* Make the IntegrationAttributeSyncCard a dummy component
* test: add tests for IntegrationAttributeSync feature
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Move creating a attribute sync record to the service
* Add i18n strings
* Safe select credential in find by id and team
* Fix default credentialId value in form
* Update repository return types
* Add i18n string
* Make credentialId optional for form schema
* Fix label
* Add cascade deletes
* Add verification that syncs belong to org
* Create mapper for repository output
* Type fixes
* Remove old test file
* Pass `attributeOptions` from parent to children
* Infer types from zod schema
* Type fixes
* Type fix
* Clean up
* Add i18n strings
* Remove unused file
* Address feedback
* Add migration file
* Address feedback
* Add validation for new integration values
* Remove unused router
* Move away from z.infer to z.ZodType
* Clean up comments
* Type fix
* Type fixes
* Type fix
* fix: add passthrough to syncFormDataSchema to preserve extra fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove incorrect test that expected extra fields to pass through syncFormDataSchema
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add indexes
* Add aria label
* Address feedback - consistent validation
* Fix import paths for attribute types
* refactor: change attributeSyncRules array to singular attributeSyncRule
The database schema enforces a one-to-one relationship between
IntegrationAttributeSync and AttributeSyncRule (via @unique constraint),
and the UI only supports a single rule. This change makes the TypeScript
type match the database schema and UI behavior.
Changes:
- Update IntegrationAttributeSync interface to use attributeSyncRule: AttributeSyncRule | null
- Update mapper to return singular rule instead of wrapping in array
- Update UI component to access sync.attributeSyncRule directly
- Update IIntegrationAttributeSyncUpdateParams Omit type
- Update tests to use attributeSyncRule: null instead of attributeSyncRules: []
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: implement FeatureOptInService (#25805)
* feat: implement FeatureOptInService WIP
* clean up
* feat: consolidate feature repositories and add updateFeatureForUser
- Implement updateFeatureForUser in FeaturesRepository (similar to updateFeatureForTeam)
- Move getUserFeatureState and getTeamFeatureState from PrismaFeatureOptInRepository to FeaturesRepository
- Update FeatureOptInService to use only FeaturesRepository
- Add setUserFeatureState and setTeamFeatureState methods to FeatureOptInService
- Update _router.ts to remove PrismaFeatureOptInRepository usage
- Remove PrismaFeatureOptInRepository.ts and FeatureOptInRepositoryInterface.ts
- Update features.repository.interface.ts and features.repository.mock.ts
- Add integration tests for updateFeatureForUser, getUserFeatureState, getTeamFeatureState
- Update service.integration-test.ts to use FeaturesRepository
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: rename updateFeatureForUser to setUserFeatureState
Rename to match the convention used for setTeamFeatureState
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: return FeatureState type from getUserFeatureState and getTeamFeatureState
* fix integration tests
* clean up logics
* update services and router
* refactor: change getUserFeatureState and getTeamFeatureState to accept featureIds array
- Renamed getUserFeatureState to getUserFeatureStates
- Renamed getTeamFeatureState to getTeamFeatureStates
- Changed parameter from featureId: string to featureIds: string[]
- Changed return type from FeatureState to Record<string, FeatureState>
- Updated FeatureOptInService to use the new batch methods
- Added tests for querying multiple features in a single call
- Optimized listFeaturesForTeam to fetch all feature states in one query
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: add getFeatureStateForTeams for batch querying multiple teams
- Added getFeatureStateForTeams method to query a single feature across multiple teams in one call
- Updated FeatureOptInService.resolveFeatureStateAcrossTeams to use the new batch method
- Replaces N+1 queries with a single database query for team states
- Added comprehensive integration tests for the new method
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: combine org and team state queries into single call
- Include orgId in the teamIds array passed to getFeatureStateForTeams
- Extract org state and team states from the combined result
- Reduces database queries from 3 to 2 in resolveFeatureStateAcrossTeams
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: use team.isOrganization and clarify computeEffectiveState comment
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: use MembershipRepository.findAllByUserId with isOrganization
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: add featureId validation using isOptInFeature type guard
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* less queries
* add fallback value
* fix type error
* move files
* add autoOptInFeatures column
* use autoOptInFeatures flag within FeatureOptInService
* add setUserAutoOptIn and setTeamAutoOptIn
* fix computeEffectiveState logic
* rewrite computeEffectiveState
* clean up integration tests
* clean up in afterEach
* fix type error
* refactor: use FeaturesRepository methods instead of direct Prisma calls
Replace all manual userFeatures and teamFeatures Prisma operations with
the new setUserFeatureState and setTeamFeatureState repository methods.
Changes include:
- Admin handlers (assignFeatureToTeam, unassignFeatureFromTeam)
- Test fixtures and integration tests
- Playwright fixtures
- Development scripts
This ensures consistent feature flag management through the repository
pattern and supports the new tri-state semantics (enabled/disabled/inherit).
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* clean up
* fix the logic
* extract some logic into applyAutoOptIn()
* remove wrong code
* refactor: convert setUserFeatureState and setTeamFeatureState to object params with discriminated union
- Convert multiple positional parameters to single object parameter
- Use discriminated union types: assignedBy required for enabled/disabled, omitted for inherit
- Update all callers across repository, service, handlers, fixtures, and tests
* fix type error
* use Promise.all
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Prevent duplicate field and attribute mappings
* Add validation that attribute belongs to the org
* fix: address Cubic AI review feedback
- Add @@index([credentialId]) to IntegrationAttributeSync model for efficient cascade deletes and credential-based queries (confidence: 9/10)
- Fix translation key from 'credential_required' to 'attribute_sync_credential_required' to match existing locale definition (confidence: 10/10)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address remaining cubic review feedback
- RuleBuilder.tsx: Use unique IDs for React keys instead of array index to prevent reconciliation bugs when removing conditions
- updateAttributeSync.handler.ts: Add early organization check before service calls for consistency
- createAttributeSync.handler.ts: Add CredentialNotFoundError class for type-safe error handling instead of string matching
- IntegrationAttributeSyncService.test.ts: Replace expect.fail() with proper Vitest rejects.toSatisfy() pattern
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Pull test file from main
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
* fix: resolve API v2 test flakes by using UUID-only slug
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: add beforeAll cleanup to prevent test flakes from leftover data
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(api): add bookingUrl field to event types API v2 response
Add a new bookingUrl field to EventTypeOutput_2024_06_14 that contains
the full, correct booking URL for each event type. This fixes the issue
where companion app and Chrome extension were hardcoding cal.com URLs
instead of using organization subdomains.
Changes:
- Add bookingUrl field to EventTypeOutput_2024_06_14 type definition
- Update repository to include organization data when fetching users
- Add buildBookingUrl method to compute URL using getOrgFullOrigin
- Include bookingUrl in getResponseEventType return object
The booking URL is computed using the first user's organization slug
(if any) to generate the correct subdomain URL (e.g., i.cal.com/keith/30min
for organization users instead of cal.com/keith/30min).
* fix: include organization data in getEventTypeByIdWithHosts
Update getEventTypeByIdWithHosts to use usersInclude to fetch
organization slug data, ensuring bookingUrl is computed correctly
for the GET /v2/event-types/{eventTypeId} endpoint.
* test: add unit tests and E2E assertions for bookingUrl field
* fix(api-v2): correct bookingUrl format for org users and fix double slashes
Fix issues with bookingUrl field in event types API v2 response:
- Remove trailing slashes from base URLs to prevent double slashes
- Use profile.username instead of user.username for organization users
(profile contains clean username without org suffix)
- Include user profiles in repository queries to access profile data
- Create local org-domains utility to replace @calcom/features dependency
which isn't available in API v2 runtime
Changes:
- Add apps/api/v2/src/lib/org-domains.ts with getOrgFullOrigin function
adapted from @calcom/features/ee/organizations/lib/orgDomains
- Update event-types.repository.ts to include profiles in usersInclude
- Update buildBookingUrl() to prioritize profile data when available
- Update unit tests to cover organization user profile scenarios
Fixes:
- Double slashes in URLs (e.g., http://localhost:3000//user/slug)
- Incorrect username format for org users (e.g., owner1-acme instead
of owner1)
- Missing organization subdomain in booking URLs (e.g., should be
http://acme.localhost:3000/owner1/30min not
http://localhost:3000/owner1-acme/30min)
* deslop ai code
* handle empty username in both code and write test for it, use select instead of include
* feat(api-v2): reuse core org domain logic for event type bookingUrl
Export getOrgFullOrigin and subdomainSuffix from @calcom/platform-libraries/organizations
to reuse existing core logic instead of duplicating it in API v2. This ensures consistency
across the codebase and reduces maintenance burden.
- Export getOrgFullOrigin and subdomainSuffix from platform-libraries/organizations
- Update output-event-types.service to import from @calcom/platform-libraries/organizations
- Remove duplicate org-domains.ts file from API v2
- Update test mocks to use platform-libraries import path
- buildBookingUrl method now uses core getOrgFullOrigin function
This addresses feedback to reuse core code rather than introducing duplicate logic.
* test(api-v2): exclude bookingUrl from output comparison in e2e test
* refactor(api-v2): reuse core logic for event type bookingUrl
Replace duplicated username/org extraction logic in buildBookingUrl with
core functions. Use getBookerBaseUrlSync from @calcom/platform-libraries/organizations
for base URL generation, and add enrichUserWithProfile method that follows
the same pattern as core's UserRepository.enrichUsersWithTheirProfiles.
The enrichment logic is applied synchronously since profiles are already
fetched by the repository, avoiding the need to make the service async.
This ensures consistency with core patterns while maintaining the existing
synchronous API contract.
- Replace getOrgFullOrigin with getBookerBaseUrlSync
- Add enrichUserWithProfile method following core enrichment pattern
- Simplify buildBookingUrl to use enriched user data
- Update tests to mock getBookerBaseUrlSync and verify enrichment flow
* trailing slash cubic comment
* address review comments
* address review comments 2
* better code
* Updated the test to give the user an organization
* address review comments again
* Revert "address review comments again"
This reverts commit 622ea3fc8dcd7f5c3113614afcfc86beadecca0e.
* allow both the full UserWithProfile and the partial EventTypeUser (from select queries) to use the same getUserMainProfile() method, eliminating code duplication without changing the performance-optimized repository queries.
* reveiw points
* feat: add DEVIN_ACTIONS_PAT instructions for pushing to forked PRs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: simplify PAT instructions to be high-level
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: allow workflow_dispatch to run on draft PRs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: only process targeted PR when pr_number is provided
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add critical fork PR error handling instruction to stale PR workflow
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* use cross-platform rimraf for build scripts
* fix: use cross-platform rimraf for build scripts
* fix: replace cp with npx shx cp for cross-platform compatibility
* Update schema.prisma
* fix: use pull_request_target for conflict resolver label trigger
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update if condition to check for pull_request_target event name
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
resolver validation
- Change git diff from HEAD^1 to HEAD^2 to show PRs contribution
(matches GitHubs Files changed view) instead of what main brought in
- Replace fixed 50-file threshold with relative threshold (original PR files + 10)
- A 20-file PR with 100 files in merge = bad
- A 100-file PR with 108 files in merge = fine
* docs: add moduleLoader pattern documentation to knowledge-base
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* docs: rewrite DI section to use moduleLoader pattern as primary approach
- Replace manual module loading examples with moduleLoader pattern
- Add Mistake 4 about manual module loading being not type-safe
- Update Core Concepts to explain moduleLoader components
- Show both depsMap (multiple deps) and dep (single dep) usage
- Emphasize build-time type safety throughout
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* docs: extract DI pattern to separate file
- Move DI documentation from knowledge-base.md to di-pattern.md
- Add brief reference in knowledge-base.md pointing to the new file
- Keeps knowledge-base.md focused on quick notes while DI gets its own detailed guide
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: make OOO team member select close after selection
Replace the custom always-visible scrollable list with a proper Select
component that closes after selection like a normal dropdown. The Select
component uses onMenuScrollToBottom for infinite scroll and onInputChange
for search functionality.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: also convert Team OOO member select to use Select component
Both 'Select team member' sections now use the proper Select component
that closes after selection. Removed unused useInViewObserver import.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add menuPlacement='bottom' to Team OOO member select
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Update devin-conflict-resolver.yml to check maintainerCanModify for fork PRs
- Instead of skipping fork PRs entirely, now checks if maintainer access is enabled
- Posts friendly comment asking contributors to enable 'Allow edits from maintainers'
- Adds 'maintainer-access-requested' label to track which PRs have been notified
- Update stale-pr-devin-completion.yml to post similar comment when triggered
- Both workflows avoid duplicate comments by checking for existing requests
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: only auto-fix Cubic suggestions with confidence >= 9/10
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Apply suggestion from @keithwillcode
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This adds the withSentryConfig wrapper to the Next.js configuration,
which is required for Sentry tracing to work properly. The wrapper is
only applied in production when both NEXT_PUBLIC_SENTRY_DSN and
SENTRY_TRACES_SAMPLE_RATE environment variables are set.
Without this wrapper, Sentry cannot properly instrument the build for
performance monitoring, causing spans (including calendar telemetry)
to not be recorded despite the SDK being initialized.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add comprehensive validation tests for event-types/[id] GET endpoint
- Add integration tests for complex validation scenarios
- Test locations, booking fields, metadata, and seats validation
- Test edge cases that could cause validation mismatches between validators and DB results
- Ensure API response structure matches schemaEventTypeReadPublic schema
- Successfully catch validation discrepancies like missing displayLocationPublicly and assignAllTeamMembers fields
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: add explicit type annotations for booking fields find callbacks
- Fix TypeScript errors on lines 388 and 395
- Provide proper type annotations for find method callback parameters
- Replace any[] with specific { label: string; value: string }[] type
- Ensure CI type checking passes
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: convert event-types integration test to use real database
- Replace prismaMock with real Prisma client operations
- Create actual database records for comprehensive test scenarios
- Add proper setup/teardown with unique timestamps for test isolation
- Maintain all existing validation test coverage
- Follow Cal.com integration test patterns for database operations
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: restore original mock-based test alongside integration test
- Restored original _get.test.ts with prismaMock for unit testing
- Kept _get.integration.test.ts with real database for integration testing
- Both tests provide complementary coverage: mocks for fast unit tests, real DB for validation testing
- Fixed type annotations in find callbacks to maintain type safety
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: rename integration test to use proper naming convention
- Rename _get.integration.test.ts to _get.integration-test.ts
- Follows Cal.com vitest workspace pattern for integration tests
- Ensures integration test runs in proper CI workflow with database setup
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: update tests to use handler return value instead of res._getData()
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: add proper type assertions for locations and bookingFields in tests
Co-Authored-By: unknown <>
* fix: create schedule for test user to fix foreign key constraint in integration tests
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: keith@cal.com <keithwillcode@gmail.com>
* chore: Integrate booking confirmation booking audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use BookingStatus type instead of string for booking.status
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Make booking-audit integration test utils reusable
* refactor: enhance handlePaymentSuccess to accept appSlug and actor identification
- Updated handlePaymentSuccess function to accept an object with paymentId, bookingId, appSlug, and traceContext.
- Introduced getActor function to determine the actor based on appSlug and credentialId.
- Modified webhook handlers for Alby, BTCPayServer, HitPay, PayPal, and Stripe to pass the new parameters.
- Improved logging for missing credentialId in payment processing.
- Adjusted related schemas to ensure proper type handling for booking status and actor identification.
* fix: Correct import paths and getActor function call
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use ActorIdentification type instead of AuditActor in getActor
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add guard clause for undefined actor in fireBookingAcceptedEvent
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add actionSource to all confirm calls
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Integrate actor identification and action source updates across booking services
- Added `makeUserActor` to various booking service files to enhance actor identification.
- Updated action source handling in booking confirmation and related functions to include "MAGIC_LINK".
- Refactored schemas to accommodate new actor and action source requirements, ensuring consistent type handling.
- Improved error handling and logging for booking actions to enhance traceability.
* Remvoe formatting changes
* Add test
* refactor: Enhance booking confirmation tests and event handling
- Updated `confirm.handler.test.ts` to improve test coverage for booking acceptance and rejection scenarios, including bulk bookings.
- Refactored the mock implementation of `BookingEventHandlerService` to streamline event handling for accepted and rejected bookings.
- Adjusted the `handleConfirmation` function to utilize `acceptedBookings` for better clarity and maintainability.
- Added tests to ensure correct invocation of event handler methods for both single and bulk booking confirmations and rejections.
* fix tests
* refactor: Use confirmHandler directly in link and verify-booking-token routes
Refactors the link and verify-booking-token API routes to call confirmHandler directly instead of using the tRPC caller pattern. This simplifies the code by removing the need for session getter, legacy request building, and context creation.
Changes:
- apps/web/app/api/link/route.ts: Use confirmHandler directly with ctx and input
- apps/web/app/api/verify-booking-token/route.ts: Use confirmHandler directly with ctx and input
- Updated tests to mock confirmHandler instead of tRPC caller
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix ts errors due to merge frommain
* feat: introduce getAppActor utility for actor creation
This commit adds a new utility function, getAppActor, to streamline the process of creating actor objects for apps based on their slug and credential ID. The function is integrated into handlePaymentSuccess and handleStripePaymentSuccess, replacing the previous inline actor creation logic. This refactor enhances code maintainability and readability by centralizing actor creation logic.
Changes:
- New file: packages/app-store/_utils/getAppActor.ts
- Updated handlePaymentSuccess and handleStripePaymentSuccess to use getAppActor
- Removed redundant actor creation code from these functions
* refactor: Update appSlug in payment success handlers to use appConfig.slug
This commit modifies the appSlug parameter in the handlePaymentSuccess function across multiple payment webhook handlers to utilize the appConfig.slug value instead of hardcoded strings. This change enhances consistency and maintainability of the code.
Changes:
- Updated appSlug in handlePaymentSuccess for btcpayserver, hitpay, and paypal webhooks.
- Adjusted a test case to reflect the new appSlug value for consistency.
* test: Enhance booking token verification and audit action tests
This commit adds additional assertions to the booking token verification tests to ensure that the confirmHandler is not called when an error occurs. It also introduces a mechanism to clean up additional booking UIDs after each test in the accepted action integration tests, improving test reliability. Furthermore, it updates the action source schema comment for clarity.
Changes:
- Updated tests in `verify-booking-token` to check that `mockConfirmHandler` is not called on error.
- Implemented cleanup logic for additional booking UIDs in `accepted-action.integration-test.ts`.
- Clarified comment in `actionSource.ts` regarding the schema for action sources.
- Refactored `handleConfirmation.ts` and `confirm.handler.ts` to include tracing logger for error handling in booking events.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: Integrate add guests booking audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: Add actionSource parameter to support API_V2 audit logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: update attendee audit action service to use new email schema
- Modified the `AttendeeAddedAuditActionService` to replace the old attendees schema with a new `emailSchema` for better validation.
- Adjusted the `addGuestsHandler` to pass the action source explicitly and updated the audit logging to reflect the new structure.
- Cleaned up the code for better readability and consistency.
* test: add happy path integration test for attendee added action
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(billing): add database schema for monthly proration tracking
add new database models and migration for tracking seat changes and monthly proration on annual subscriptions
- new models: SeatChangeLog, MonthlyProration
- new enums: SeatChangeType, ProrationStatus
- extend TeamBilling and OrganizationBilling with billingPeriod, pricePerSeat, paidSeats fields
- add feature flag 'monthly-proration' (disabled by default)
- squashed migration: 20260106093811_add_monthly_proration_tracking
the schema supports:
- tracking seat additions/removals per month with metadata
- storing proration calculations and stripe invoice details
- high-water mark billing via paidSeats field
- proper foreign key relationships and indexes for query performance
* feat(billing): add repository layer for monthly proration
add repository pattern for proration data access
- MonthlyProrationRepository: crud operations for proration records
- create, update, and query proration records
- status management (pending, invoice_created, charged, failed)
- retry count tracking
- MonthlyProrationTeamRepository: team-specific queries with billing context
- fetch teams with billing information
- query annual teams with unprocessed seat changes
- update billing info and paidSeats
- metadata fallback for legacy teams without billing records
- update IBillingRepository interface to include proration fields
these repositories provide clean data access layer without business logic
* feat(billing): add seat change tracking service
add service for logging and querying seat changes per month
- SeatChangeTrackingService: track seat additions and removals
- log seat additions with user and metadata context
- log seat removals with optional triggered-by info
- calculate monthly changes (additions, removals, net change)
- retrieve unprocessed changes for a given month
- mark changes as processed after proration calculation
- auto-detect billing entity (team vs organization)
- stripe-subscription-utils: extract billing data from stripe subscriptions
- determine billing period (monthly vs annually)
- extract price per seat and quantity
includes comprehensive unit tests
* feat(billing): add billing period service
add service to determine billing period and proration eligibility
- BillingPeriodService: query and manage billing period information
- check if team is on annual vs monthly plan
- check if team is in trial period
- determine if monthly proration should apply (annual + not in trial + feature flag)
- lazy load billing data from stripe if missing in database
- backfill missing billing data automatically
supports both TeamBilling and OrganizationBilling models
includes comprehensive unit tests with stripe mocking
* feat(billing): add monthly proration service
add core service for calculating and processing monthly seat prorations
- MonthlyProrationService: orchestrate monthly proration workflow
- process monthly prorations for teams with seat changes
- calculate prorated amounts based on remaining subscription days
- create stripe invoice items for seat additions
- handle payment success and failure webhooks
- track high-water mark (paidSeats) for billing
- update stripe subscription quantities after payment
- lazy load and backfill missing billing data from stripe
- support retry logic for failed payments
calculation logic:
- only charge for net seat increases (additions - removals, capped at 0)
- prorate based on remaining days in annual subscription
- track separately from stripe's built-in proration to aggregate monthly
includes unit tests and integration tests with stripe mocking
* refactor(billing): extract prisma logic from services to repositories
move database access logic from services to repository layer for better separation of concerns
- add SeatChangeLogRepository for seat change log data access
- add BillingPeriodRepository for billing period queries and updates
- refactor SeatChangeTrackingService to use SeatChangeLogRepository
- refactor BillingPeriodService to use BillingPeriodRepository
- add create methods to MonthlyProrationTeamRepository for billing records
- refactor MonthlyProrationService to use repository for billing creation
- remove direct prisma calls from services (except FeaturesRepository dependency)
services now focus on business logic while repositories handle data access
* refactor(billing): inject prisma via constructor in repositories
make repositories more testable by accepting prisma client via constructor
- SeatChangeLogRepository: accept optional prisma in constructor
- BillingPeriodRepository: accept optional prisma in constructor
- MonthlyProrationRepository: accept optional prisma in constructor
- MonthlyProrationTeamRepository: accept optional prisma in constructor
all repositories default to global prisma instance when not provided
enables easier mocking in tests by injecting test prisma instances
* refactor: remove verbose comments and unnecessary try-catch
* refactor: change pricePerSeat and proratedAmount to cents (Int)
store monetary values as integers in cents instead of floats for precision
remove unnecessary schema comments
* fix tests and add back try catch error handlign
* Update packages/features/ee/billing/service/proration/__tests__/MonthlyProrationService.integration-test.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* add faeture flag to config.ts
* Update packages/features/ee/billing/service/proration/__tests__/MonthlyProrationService.integration-test.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* restore schema
* add hooks types for flag
* fix: correct pricePerSeat calculations for cents-based storage
MonthlyProrationService now correctly handles pricePerSeat stored as
cents (INTEGER) instead of dollars (DOUBLE PRECISION). Removed duplicate
conversions to cents that were causing 100x price inflation.
* Use cents for money values
* fix type error
* Move integration to test to use cents value
* fix integration import
* mock subscription
* include paid seats tracking in proration service
* add mocks for invoice creation
* feat: use stripebilling service instead of using stripe directly
* fix validation erorr handler in proration service
* fix: fix sub
* use correct seat value
* fix(billing): mark seat changes as processed when netChange is zero
When a team has additions and removals that net to zero, the function
was returning null without calling markAsProcessed(). This left seat
change logs in an unprocessed state indefinitely, causing subsequent
proration runs to re-query the same changes repeatedly.
Changed the early return condition from checking netChange === 0 to
checking if there are no changes at all (additions === 0 && removals === 0).
This ensures that when there are actual seat changes that net to zero,
a proration record is still created and seat changes are marked as processed.
Co-Authored-By: unknown <>
* update test to reflect new return type of proration
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
* fix: return 400 instead of 500 for invalid eventTypeId in booking flow
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use ErrorWithCode instead of HttpError in BotDetectionService
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use vi.stubEnv for safer environment variable handling in tests
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(api): remove OAuth client ID suffix from email in booking API responses
Fixes#25494 | Linear: CAL-6843
When managed users create or receive bookings, their emails were being returned with an internal OAuth client ID suffix (e.g., bob+cuid123@example.com). This suffix is used internally for user identification but should not be exposed in API responses.
Changes:
- Add cleanOAuthEmailSuffix() helper using CUID regex pattern
- Clean email suffix in hosts[], attendees[], bookingFieldsResponses.email, bookingFieldsResponses.guests[], and reassignedTo.email
- Pattern consistent with google-calendar.service.ts implementation
Affected output methods:
- getOutputBooking
- getOutputRecurringBooking
- getOutputSeatedBooking
- getOutputRecurringSeatedBooking
- getOutputReassignedBooking
- getHost
* refactor(api): preserve original email, add displayEmail field
Per team discussion, keep original email unchanged to avoid breaking changes for platform customers.
Add displayEmail field with CUID suffix removed for display purposes
* feat(api): add displayEmail to booking output DTOs
Add displayEmail property to BookingAttendee, BookingHost and ReassignedToDto for API documentation and type safety
* test(api): add e2e tests for displayEmail fields in managed user bookings
Add tests to verify that displayEmail fields correctly strip CUID suffix from OAuth managed user emails in booking API responses:
- Test host displayEmail returns email without CUID suffix
- Test attendee displayEmail returns email without CUID suffix
- Test bookingFieldsResponses.displayEmail returns clean email
- Test displayGuests array returns emails without CUID suffix
* false positive breaking change
* false positive breaking change
* test(api): update existing e2e tests to expect displayEmail field
* fix(api): add missing displayEmail to seated booking test assertions
The seated booking tests were missing displayEmail in the attendee
assertions for the second booking test and cancel-as-host test,
causing CI test failures
---------
Co-authored-by: cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
* refactor: Extract schedule determination to seperate fn
* refactor: Extract schedule detection to seperate file and add tests
* RouterOutputs should be used in hooks, not direct type imports