feat: convert InsightsBookingService to use Prisma.sql raw queries

calcom/cal.com#22345
Golden recall: 0% Extra findings: 5

Golden Comments (0/2 found)

# Comment Severity Status FriendlyReviewer Detail
#1 In getBaseConditions(), the else if (filterConditions) and final else branches are unreachable. This is because getAuthorizationConditions() always returns a non-null Prisma.Sql object, making authConditions always truthy, which means only the first two if/else if conditions are ever evaluated. LOW ✗ Not found FR's findings all target the tests' robustness — row order not guaranteed (L348), ADMIN never exercised (L437), $queryRaw environment-sensitive (L482), RoutingService divergence (L43), user-scope requirement (L114) — none evaluates the service's control flow, where getAuthorizationConditions() always returns a non-null Prisma.Sql, making authConditions always truthy and the two later branches unreachable. See packages/lib/server/service/insightsBooking.ts:76
#2 Fetching userIdsFromOrg only when teamsFromOrg.length > 0 can exclude org-level members for orgs without child teams; consider deriving from teamIds (which includes orgId) or removing the guard so org-only orgs still include member user bookings. MEDIUM ✗ Not found No candidate touches the teamsFromOrg.length > 0 guard on userIdsFromOrg: the review covers parameter binding, authz gating, ordering and caching, but never questions which users the conditions actually include, so org-level members of orgs without child teams are excluded unnoticed. See packages/lib/server/service/insightsBooking.ts:158

Supplementary Findings (5 findings)

Finding File Severity Legitimate?
The stated basis of the conversion — matching the InsightsRoutingService tests — does not exist: insightsRouting.ts still uses Prisma object notation, so the two sibling services now diverge insightsBooking.ts:43 MEDIUM ✓ Real: the PR's stated premise is false
The org-scope assertion expects an exact, ordered Prisma.sql array for teamIds/userIds, but neither findAllByParentId nor findAllByTeamIds guarantees row order — fragile comparison, and the author could not run the tests integration-test.ts:348 MEDIUM ✓ Real risk of intermittent failures
The ADMIN role is never positively exercised (only OWNER positive, MEMBER denial), and the diff deletes the caching describe block while the service still caches — the new cache behavior is now untested integration-test.ts:437 MEDIUM ✓ Real: ADMIN regression undetectable, cache untested
The $queryRaw test depends on denormalization trigger migrations being applied in the test DB and on exact single-row equality in a possibly shared DB — environment-sensitive integration-test.ts:482 MEDIUM ✓ Real: environment-sensitive assertion
For scope === "user", the branch is only reachable after isOrgOwnerOrAdmin(...), so a plain team member requesting their own user-scoped bookings gets NOTHING_CONDITION; pre-existing, but an unusual authorization model insightsBooking.ts:114 LOW ✓ Borderline: pre-existing behavior, flagged as worth confirming

Honest Analysis

FriendlyReviewer found neither golden comment of this PR — recall 0%. Both goldens are logic questions about the service itself — whether branches of getBaseConditions() are reachable (G1, LOW) and whether the teamsFromOrg.length > 0 guard excludes org-level members (G2, MEDIUM) — and the review never evaluates that logic. Its five findings all target the integration tests: the non-existent RoutingService basis (L43), unguaranteed row ordering (L348), the never-exercised ADMIN branch and removed caching tests (L437), the environment-sensitive $queryRaw (L482), and the user-scope authorization model (L114).

The miss pattern is consistent: FR audited the diff as a test conversion and verified the things a test conversion should be checked for — parameter binding, authz preservation, determinism — then concluded "no functional or security bug was confirmed in the service logic", which is precisely the area where both goldens sit. The unreachable-branch question requires reading getBaseConditions() control flow rather than the test file, and none of the review's candidates stepped into the service implementation.

The supplementary findings are honest and individually legitimate — L43 is the most valuable, since it invalidates the PR's stated premise — but "4 medium, 1 low" overstates their weight: all five are non-blocking robustness notes on tests, and the verdict's "3 ok, 4 mitigated" reads as reassurance on a change the author could not even run. For a PR whose substance is a test rewrite, FR delivered useful review hygiene but missed both logic-level defects; a 0/2 recall with a clean bill of health on the service logic is the takeaway.

FriendlyReviewer's full review

As posted on the PR — the complete output from FriendlyReviewer.

friendly-reviewer bot commented now

Hi there 👋

⛅ Tech 🌤️ Feat
4 medium, 1 low 3 ok, 4 mitigated

This PR converts the InsightsBookingService integration tests from Prisma object-notation expectations to direct Prisma.sql template-literal comparisons, following the pattern previously established in the InsightsRoutingService tests. The functional core of the conversion is sound: all user-controlled inputs (userId, orgId, teamId, teamIds, filter ids) are safely parameterized via Prisma.sql ${param} interpolation with no string concatenation (no SQL injection), the authorization gating (isOrgOwnerOrAdmin, NOTHING_CONDITION = 1=0) is preserved, AND/OR precedence and identifier quoting are correct, and the blast radius is limited to the integration test itself.

However, the review surfaces several non-blocking robustness/consistency concerns that warrant attention before merge: (1) the stated "same patterns as InsightsRoutingService" basis does not actually exist in the current tree — insightsRouting.ts still uses Prisma object notation, so the two sibling services now diverge; (2) the org-scope SQL assertion depends on unguaranteed row ordering from findAllByParentId/findAllByTeamIds and may fail intermittently; (3) the ADMIN role is never positively exercised and the caching tests were removed, leaving the Prisma.Sql cache behavior untested; (4) the final $queryRaw test relies on denormalization triggers being present in the test database and on exact single-row equality, making it environment-sensitive; and (5) the scope:"user" branch still requires org owner/admin (pre-existing behavior, worth confirming as intended). No functional or security bug was confirmed in the service logic.


Worth checking

  • 🟡 packages/lib/server/service/insightsBooking.ts (L43)
    The stated basis of this conversion — matching the InsightsRoutingService integration tests — does not exist in the tree: insightsRouting.ts still uses Prisma object notation (NOTHING = { id: -1 }, findMany with Prisma.RoutingFormResponseDenormalizedWhereInput, returning { AND: conditions } objects). This PR converts InsightsBookingService to raw Prisma.Sql (NOTHING_CONDITION = Prisma.sql`1=0`, getBaseConditions()), so the two sibling services now have different authorization-condition contracts and testing conventions. Reconcile the consistency premise: confirm RoutingService is converted in a separate MR (and link it) or document that the services are intentionally diverging.
  • 🟡 packages/lib/server/service/__tests__/insightsBooking.integration-test.ts (L348)
    The org-scope SQL assertion expects an exact, ordered Prisma.sql array for teamIds ([org.id, team.id, team2.id, team3.id] from findAllByParentId) and userIds ([user.id, user2.id, user3.id] from findAllByTeamIds then Set). Neither findAllByParentId (team.ts findMany with no orderBy) nor findAllByTeamIds (membership.ts findMany with no orderBy) guarantees row order, so the .toEqual(Prisma.sql ...) comparison is fragile and can fail intermittently on seed-order changes, extra pre-existing data, or a different query plan. The author could not run the tests, so this was never verified. Make the service build these conditions deterministically (e.g., sort teamIds/uniqueUserIds) or make the test order-independent. The team-scope test is fine (single team/user).
  • 🟡 packages/lib/server/service/__tests__/insightsBooking.integration-test.ts (L437)
    The ADMIN role is never positively exercised: all positive authz tests use MembershipRole.OWNER and the only denial test uses MEMBER, so a regression in the ADMIN branch of isOrgOwnerOrAdmin would go undetected. Additionally, the diff deletes the entire "Caching" describe block, even though the service still caches via cachedAuthConditions/cachedFilterConditions — the new Prisma.Sql cache behavior (especially that null from getFilterConditions is cached distinctly from "not yet built") is now untested. Add an ADMIN-role positive case (e.g., a team/org-scope test with orgRole: ADMIN) and restore a minimal caching test asserting second calls return the same Prisma.sql instance/equality without re-querying.
  • 🟡 packages/lib/server/service/__tests__/insightsBooking.integration-test.ts (L482)
    The $queryRaw test executes SQL against the BookingTimeStatusDenormalized view backed by BookingDenormalized, which is only populated by DB triggers installed by migrations (20250502130843_setup_triggers_for_booking_denormalized / 20250520143014_booking_denormalized_backfill). The test seeds via prisma.booking.create() and asserts exactly [{ id: userBooking.id }]. This only holds if (1) the test DB has those trigger migrations applied (otherwise the view is empty and the assertion fails as []) and (2) no residual matching row exists in a shared DB (the exact single-row toEqual is sensitive to leftovers matching userId = X AND teamId IS NULL AND (eventTypeId = E OR eventParentId = E)). Confirm the test DB applies these migrations and consider asserting results "contains the userBooking id" (e.g., expect(results.map(r => r.id)).toContain(userBooking.id)) instead of exact single-row equality unless a clean/isolated DB is guaranteed.
Small things (take or leave)
  • 🔵 packages/lib/server/service/insightsBooking.ts (L114)
    For scope === "user", the branch is only reachable after isOrgOwnerOrAdmin(...) returns true, so a plain team member (even one requesting only their own user-scoped bookings) receives NOTHING_CONDITION. This is carried over from the pre-conversion implementation (not introduced here) and the "user scope" test therefore has to seed the actor as OWNER to reach the branch. This may be intentional, but it is an unusual authorization model for a "user" scope; confirm it is intended and that org ADMINS can reach it (per the ADMIN-role finding). Flagging so it is not mistaken for new behavior.

✅ Feature-level checklist

Looks good

  • Replace Prisma object notation expectations with direct Prisma.sql template literal comparisons.
    All authorization and filter condition tests now assert directly against Prisma.sql templates instead of inspecting .strings/.values or object notation.
  • Add NOTHING_CONDITION = Prisma.sql`1=0` constant for consistency.
    The constant is present and used consistently to deny access (including the scope:"user" denial for non-owner/admin members).
  • Check parameter binding — ensure all user inputs in the SQL templates are properly parameterized (no injection risks).
    All user-controlled inputs are passed via Prisma.sql ${param} interpolation with no string interpolation into SQL text. No injection risk found.

Partially covered

  • ⚠️ Modify the final integration test to use $queryRaw for actual database querying.
    The test uses $queryRaw, but it relies on denormalization trigger migrations being applied in the test DB and on exact single-row equality, making it environment-sensitive. Recommend a "contains" assertion unless a clean/isolated DB is guaranteed.
  • ⚠️ Verify the tests actually pass (run integration tests manually) — TZ=UTC yarn test ...
    The author encountered test-runner configuration issues and could not run the tests. The org-scope assertion depends on unguaranteed row ordering and the $queryRaw test on trigger presence, so unverified execution leaves real risk of intermittent or environment-dependent failures.
  • ⚠️ Verify authorization security — test with different user roles (owner, admin, member) to ensure no privilege escalation.
    Parameter binding is safe (no injection) and the authz gate is preserved, but the ADMIN branch of isOrgOwnerOrAdmin is never positively exercised — only OWNER (positive) and MEMBER (denial) are tested. A regression in the ADMIN path would go undetected.
  • ⚠️ Test edge cases — empty teams, missing users, null values, boundary conditions.
    Deny-when-empty behavior is logically preserved, but caching behavior (including null-distinct cache semantics) is now untested after the caching describe block was removed, and the coverage of edge cases is reduced as a result.