Skip to content

feat: convert InsightsBookingService to use Prisma.sql raw queries - #7

Open
CodingKylo wants to merge 1 commit into
insights-query-foundationfrom
insights-performance-optimization
Open

feat: convert InsightsBookingService to use Prisma.sql raw queries#7
CodingKylo wants to merge 1 commit into
insights-query-foundationfrom
insights-performance-optimization

Conversation

@CodingKylo

Copy link
Copy Markdown

Martian Code Review Benchmark PR (mirrored from source #5)

…22345)

* fix: use raw query at InsightsBookingService

* feat: convert InsightsBookingService to use Prisma.sql raw queries

- Convert auth conditions from Prisma object notation to Prisma.sql
- Convert filter conditions from Prisma object notation to Prisma.sql
- Update return types from Prisma.BookingTimeStatusDenormalizedWhereInput to Prisma.Sql
- Fix type error in isOrgOwnerOrAdmin method
- Follow same pattern as InsightsRoutingService conversion

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

* feat: convert InsightsBookingService to use Prisma.sql raw queries

- Convert auth conditions from Prisma object notation to Prisma.sql
- Convert filter conditions from Prisma object notation to Prisma.sql
- Update return types from Prisma.BookingTimeStatusDenormalizedWhereInput to Prisma.Sql
- Fix type error in isOrgOwnerOrAdmin method
- Follow same pattern as InsightsRoutingService conversion

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

* fix: update InsightsBookingService integration tests for Prisma.sql format

- Replace Prisma object notation expectations with Prisma.sql template literals
- Add NOTHING_CONDITION constant for consistency with InsightsRoutingService
- Update all test cases to use direct Prisma.sql comparisons
- Use $queryRaw for actual database integration testing
- Follow same testing patterns as InsightsRoutingService

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

* fix: exclude intentionally skipped jobs from required CI check failure

- Remove 'skipped' from failure condition in pr.yml and all-checks.yml
- Allow E2E jobs to be skipped without failing the required check
- Only actual failures and cancelled jobs will cause required check to fail

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

* fix tests

* Revert "fix: exclude intentionally skipped jobs from required CI check failure"

This reverts commit 6ff44fc9a8f14ad657f7bba7c2e454e192b66c8f.

* clean up tests

* address feedback

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@re-entry-ai re-entry-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ re-entry.ai Code Review

@CodingKylo — Please review the following assessment:

🚨 Risk Score: 91/100 · CRITICAL

Dimension Level
Likelihood 🔴 Critical
Impact 🔴 Critical
Detectability 🟠 High

Summary

Code Review Summary

Risk Level: CRITICAL (Score: 91/100)
Files Changed: 0
High Risk Areas: 2

Key Concerns

  • SECURITY (critical): Authorization conditions are rebuilt from structured Prisma where-objects into raw Prisma.sql expressions, which can subtly change semantics and therefore access control behavior.
  • CONFIGURATION (critical): The service’s condition types and composition logic are fundamentally changed (where-input objects -> Prisma.sql fragments), which can alter operator precedence/parentheses and null/empty behavior.
  • TESTCOVERAGE (medium): The integration test file is updated to assert Prisma.sql fragments, but the diff truncation suggests some test coverage (e.g., caching tests) may have been removed, reducing confidence in behavior beyond condition equality.

Posted by re-entry.ai · Risk governance for autonomous engineering teams

@re-entry-ai re-entry-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ re-entry.ai Code Review

@CodingKylo — Please review the following assessment:

🟡 Risk Score: 29/100 · MEDIUM

Dimension Level
Likelihood 🟡 Medium
Impact 🟡 Medium
Detectability 🟢 Low

Intent

Convert InsightsBookingService authorization/filtering logic from Prisma WhereInput objects to Prisma.sql raw query fragments.

Summary

The PR replaces structured Prisma where conditions with Prisma.sql fragments for authorization and filtering, and updates integration tests to assert SQL output. The highest risk is a correctness/security crash path in SQL composition when the authorization disjunction set is empty (leading to an exception before returning a safe “no rows” condition). There are also semantic regressions from collapsing “no auth restriction” vs “always-false” into a single truthy SQL object, plus a numeric truthiness bug that can broaden results when eventTypeId is 0. Verify that empty authorization inputs deterministically produce 1=0, that all numeric filters use explicit null/undefined checks, and that all call sites/tests align with the new public options contract.

🎯 Review Focus

The authorization SQL composition in getAuthorizationConditions()—specifically ensure empty principal sets deterministically return the deny-all sentinel without throwing, and that subsequent condition-building does not rely on truthiness of Prisma.Sql fragments.

✅ Action Checklist

  • [ ] SUGGESTION — Centralize the “deny all” sentinel to avoid drift between service and tests. Right now both files define their own NOTHING_CONDITION (packages/lib/server/service/insightsBooking.ts defines const NOTHING_CONDITION = Prisma.sql t1=0 and packages/lib/server/service/__tests__/insightsBooking.integration-test.ts defines const NOTHING_CONDITION = Prisma.sql t1=0 ). Fix: export it from the service module (e.g., export const NOTHING_CONDITION = Prisma.sql t1=0 ;) and import it in the test.
  • [ ] SUGGESTION — Decouple tests from Prisma’s exact SQL rendering/parenthesization. The test now asserts exact SQL strings like expect(conditions).toEqual(Prisma.sql t("userId" = ${...}) AND ("teamId" IS NULL) ); (packages/lib/server/service/__tests__/insightsBooking.integration-test.ts). Fix: assert semantic equivalence by normalizing toString() (strip whitespace/parentheses) or assert stable fragments (e.g., contains "userId" = and "teamId" IS NULL) rather than full equality, to reduce brittleness across Prisma versions.
  • [ ] SUGGESTION — Verify all call sites compile and behave with the new constructor options contract. The service constructor now expects InsightsBookingServicePublicOptions (packages/lib/server/service/insightsBooking.ts), but only this test file appears updated. Fix: search for all new InsightsBookingService({ options: ... }) instantiations and update them; add a type-level test or compile-time assertion to prevent old shapes from slipping in.

Suggestions

  • Centralize the “deny all” sentinel to avoid drift between service and tests. Right now both files define their own NOTHING_CONDITION (packages/lib/server/service/insightsBooking.ts defines const NOTHING_CONDITION = Prisma.sql t1=0 and packages/lib/server/service/__tests__/insightsBooking.integration-test.ts defines const NOTHING_CONDITION = Prisma.sql t1=0 ). Fix: export it from the service module (e.g., export const NOTHING_CONDITION = Prisma.sql t1=0 ;) and import it in the test.
  • Decouple tests from Prisma’s exact SQL rendering/parenthesization. The test now asserts exact SQL strings like expect(conditions).toEqual(Prisma.sql t("userId" = ${...}) AND ("teamId" IS NULL) ); (packages/lib/server/service/__tests__/insightsBooking.integration-test.ts). Fix: assert semantic equivalence by normalizing toString() (strip whitespace/parentheses) or assert stable fragments (e.g., contains "userId" = and "teamId" IS NULL) rather than full equality, to reduce brittleness across Prisma versions.
  • Verify all call sites compile and behave with the new constructor options contract. The service constructor now expects InsightsBookingServicePublicOptions (packages/lib/server/service/insightsBooking.ts), but only this test file appears updated. Fix: search for all new InsightsBookingService({ options: ... }) instantiations and update them; add a type-level test or compile-time assertion to prevent old shapes from slipping in.

📝 This review includes 3 inline comments (1 critical, 2 warnings)


Posted by re-entry.ai · Risk governance for autonomous engineering teams

): Promise<Prisma.BookingTimeStatusDenormalizedWhereInput> {
): Promise<Prisma.Sql> {
const teamRepo = new TeamRepository(this.prisma);
const childTeamOfOrg = await teamRepo.findByIdAndParentId({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 CRITICAL

This is a real crash path: conditions.reduce(...) has no initial value, so when both teamIds and userIdsFromOrg are empty the authorization builder throws before returning 1=0. The root cause is that the new raw-SQL composition assumes at least one disjunct, but the empty-set case is not handled defensively.


re-entry.ai

@@ -60,89 +65,86 @@ export class InsightsBookingService {
this.filters = filters;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

authConditions is always truthy here because getAuthorizationConditions() now returns a Prisma.Sql object, and getBaseConditions() no longer has a way to represent a missing auth clause. That means the else if branches are effectively dead code, and the method can no longer distinguish between "no auth restriction" and "always-false". The underlying issue is that the previous nullable/structured condition model was collapsed into a single truthy SQL object, which changes the control flow semantics.


re-entry.ai

return conditions.length > 0 ? { AND: conditions } : null;
if (conditions.length === 0) {
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

This guard still treats 0 as absent. If eventTypeId is a valid identifier in the data model, the filter will silently skip it and return broader results than intended. The bug comes from using truthiness checks on numeric IDs instead of explicit null/undefined checks.


re-entry.ai

@re-entry-ai re-entry-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ re-entry.ai Code Review

@CodingKylo — Please review the following assessment:

🚨 Risk Score: 91/100 · CRITICAL

Dimension Level
Likelihood 🔴 Critical
Impact 🔴 Critical
Detectability 🟠 High

Intent

Refactor InsightsBookingService to build authorization/filter constraints using Prisma.sql fragments instead of typed Prisma where-input objects.

Summary

The service now represents “no results” and authorization/filter predicates as Prisma.sql fragments (e.g., 1=0 and SQL expressions) and updates integration tests to assert SQL fragments rather than where-input objects. This is a contract-breaking change: any caller expecting Prisma where-inputs (or any code path that still passes these conditions into a Prisma where: clause) will fail or behave differently at runtime. The highest risk is authorization semantics drifting due to manual SQL composition and empty/NULL handling differences, plus correctness issues from truthiness checks (e.g., skipping eventTypeId = 0). The reviewer must verify all call sites and ensure the service’s new return types are only used in contexts that execute SQL fragments correctly, and that the SQL predicates are semantically equivalent to the previous where-input logic.

🎯 Review Focus

Authorization predicate construction and contract correctness: confirm that every place using the new Prisma.Sql conditions executes them in the right Prisma API context (not as where: objects) and that the SQL predicates are semantically equivalent to the previous where-input logic for all combinations of auth scope, NULL teamId, and empty/no-filter cases.

✅ Action Checklist

  • [ ] SUGGESTION — Verify and update all consumers of getAuthorizationConditions() / getFilterConditions() to match the new return type (Prisma.Sql). Add TypeScript compile-time enforcement by changing method signatures and removing any implicit any/structural typing that could allow passing Prisma.Sql into a Prisma where: clause. [packages/lib/server/service/insightsBooking.ts:L60-L90]
  • [ ] SUGGESTION — Stop asserting exact SQL string templates in integration tests; assert semantic equivalence by executing the query and checking returned rows. Current tests are coupled to rendering details (parameter quoting, parentheses, identifier casing), which can cause false failures and also miss real semantic drift. Example: replace expect(conditions).toEqual(Prisma.sql"...") with a test that runs service.findMany(...) and asserts the resulting bookings. [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L267-L310]
  • [ ] SUGGESTION — Check for instance lifecycle/caching correctness: cached auth/filter conditions are now Prisma.Sql. If the service instance can be reused across requests with different options/filters, cached SQL could be stale. Fix by either making the service immutable per request (no reuse) or keying the cache by (scope, userId, orgId, teamId, filters.eventTypeId, filters.memberUserId) or removing caching. [packages/lib/server/service/insightsBooking.ts:L60-L75]
  • [ ] SUGGESTION — If you keep the NOTHING_CONDITION = Prisma.sql1=0`` approach, centralize it in a shared helper/constant and ensure all services use the same sentinel semantics. This avoids inconsistent “no results” behavior across the codebase. [packages/lib/server/service/insightsBooking.ts:L60] and [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L11-L15]

Suggestions

  • Verify and update all consumers of getAuthorizationConditions() / getFilterConditions() to match the new return type (Prisma.Sql). Add TypeScript compile-time enforcement by changing method signatures and removing any implicit any/structural typing that could allow passing Prisma.Sql into a Prisma where: clause. [packages/lib/server/service/insightsBooking.ts:L60-L90]
  • Stop asserting exact SQL string templates in integration tests; assert semantic equivalence by executing the query and checking returned rows. Current tests are coupled to rendering details (parameter quoting, parentheses, identifier casing), which can cause false failures and also miss real semantic drift. Example: replace expect(conditions).toEqual(Prisma.sql"...") with a test that runs service.findMany(...) and asserts the resulting bookings. [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L267-L310]
  • Check for instance lifecycle/caching correctness: cached auth/filter conditions are now Prisma.Sql. If the service instance can be reused across requests with different options/filters, cached SQL could be stale. Fix by either making the service immutable per request (no reuse) or keying the cache by (scope, userId, orgId, teamId, filters.eventTypeId, filters.memberUserId) or removing caching. [packages/lib/server/service/insightsBooking.ts:L60-L75]
  • If you keep the NOTHING_CONDITION = Prisma.sql1=0`` approach, centralize it in a shared helper/constant and ensure all services use the same sentinel semantics. This avoids inconsistent “no results” behavior across the codebase. [packages/lib/server/service/insightsBooking.ts:L60] and [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L11-L15]

📝 This review includes 4 inline comments (4 warnings)


Posted by re-entry.ai · Risk governance for autonomous engineering teams

scope: "user" | "org" | "team";
userId: number;
orgId: number;
teamId?: number;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

Valid concern: the refactor replaces the old typed sentinel { id: -1 } with a raw SQL fragment 1=0. That is still a correct “match nothing” predicate, but it changes the contract from a Prisma where-input to a SQL fragment, so any caller or test that expects a structured filter object will break. The root cause is the API boundary shift from Prisma query objects to raw SQL without a compatibility layer.


re-entry.ai

});
if (authConditions && filterConditions) {
return Prisma.sql`(${authConditions}) AND (${filterConditions})`;
} else if (authConditions) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

This is a real behavioral change: the old implementation always merged auth and filter constraints into a Prisma AND array, which naturally tolerated null entries and preserved object semantics. The new raw-SQL branch logic changes how empty conditions are represented and combined, so downstream code that relied on the previous shape/merge behavior can no longer treat these as interchangeable. The underlying issue is that the service now returns SQL fragments instead of a composable query AST.


re-entry.ai

return null;
}

if (this.filters.eventTypeId) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

Valid bug: eventTypeId is checked with a truthiness test, so a legitimate 0 value would be skipped and the filter silently omitted. This is a correctness issue introduced by the refactor because the code now manually reconstructs the predicate logic instead of relying on Prisma's typed equality handling. Use an explicit !== undefined/!== null check if 0 is a possible identifier.


re-entry.ai

return await this.buildOrgAuthorizationCondition(this.options);
} else if (this.options.scope === "team") {
conditions.push(await this.buildTeamAuthorizationCondition(this.options));
return await this.buildTeamAuthorizationCondition(this.options);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

The concern is real, but the specific risk is not the absence of an id column. 1=0 is a valid always-false predicate regardless of schema. The actual regression is that getAuthorizationConditions() no longer returns a Prisma where-input, so any code path that still expects to pass this into Prisma's where option will fail or require a separate SQL execution path. This is a contract break caused by the raw-query migration.


re-entry.ai

@re-entry-ai re-entry-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ re-entry.ai Code Review

@CodingKylo — Please review the following assessment:

🚨 Risk Score: 86/100 · CRITICAL

Dimension Level
Likelihood 🔴 Critical
Impact 🔴 Critical
Detectability 🟡 Medium

Intent

Refactor InsightsBookingService to build authorization and filter predicates using Prisma.sql fragments instead of Prisma where-input objects.

Summary

The service now composes query conditions as Prisma.sql fragments (including a “nothing” sentinel 1=0) and updates integration tests to assert SQL fragments rather than structured where-inputs. The highest risk is authorization/filter semantics changing due to SQL composition details (parentheses, empty-array handling, and operator precedence) and the contract change of return types from WhereInput to Prisma.Sql. You must verify that all consumers of getAuthorizationConditions/getFilterConditions/getBaseConditions are updated for the new return types and that the resulting SQL is semantically equivalent to the previous Prisma where-input logic, especially for edge cases like empty teamIds and null teamId handling.

🎯 Review Focus

Verify authorization predicate equivalence: ensure the composed Prisma.sql conditions (including empty teamIds and null teamId cases) produce the same effective access control behavior as the prior Prisma where-input logic, and confirm all callers/tests are updated for the Prisma.Sql return-type contract change.

✅ Action Checklist

  • [ ] SUGGESTION — Contract audit: search for all usages of getAuthorizationConditions(), getFilterConditions(), and getBaseConditions() and update their expected return types from Prisma.BookingTimeStatusDenormalizedWhereInput to Prisma.Sql (or add a compatibility wrapper). This is required because the refactor changes the method return types and can break callers at compile-time or runtime. [packages/lib/server/service/insightsBooking.ts:L60-L86]
  • [ ] SUGGESTION — Test strategy: stop asserting exact SQL string equality for authorization correctness; instead, execute the query end-to-end and assert returned rows/IDs for representative cases (owner/admin, non-owner, null teamId, empty teamIds). String-based tests can pass while semantics differ due to parentheses/precedence changes. [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L267-L310]
  • [ ] SUGGESTION — Sentinel consistency: the test defines its own const NOTHING_CONDITION = Prisma.sql1=0`` while the service defines NOTHING_CONDITION internally; export the sentinel from the service (or a shared test helper) so both sides stay consistent if the sentinel strategy changes. [packages/lib/server/service/insightsBooking.ts:L60] and [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L11-L14]

Suggestions

  • Contract audit: search for all usages of getAuthorizationConditions(), getFilterConditions(), and getBaseConditions() and update their expected return types from Prisma.BookingTimeStatusDenormalizedWhereInput to Prisma.Sql (or add a compatibility wrapper). This is required because the refactor changes the method return types and can break callers at compile-time or runtime. [packages/lib/server/service/insightsBooking.ts:L60-L86]
  • Test strategy: stop asserting exact SQL string equality for authorization correctness; instead, execute the query end-to-end and assert returned rows/IDs for representative cases (owner/admin, non-owner, null teamId, empty teamIds). String-based tests can pass while semantics differ due to parentheses/precedence changes. [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L267-L310]
  • Sentinel consistency: the test defines its own const NOTHING_CONDITION = Prisma.sql1=0`` while the service defines NOTHING_CONDITION internally; export the sentinel from the service (or a shared test helper) so both sides stay consistent if the sentinel strategy changes. [packages/lib/server/service/insightsBooking.ts:L60] and [packages/lib/server/service/tests/insightsBooking.integration-test.ts:L11-L14]

📝 This review includes 2 inline comments (2 warnings)


Posted by re-entry.ai · Risk governance for autonomous engineering teams

@@ -1,4 +1,4 @@
import type { Prisma } from "@prisma/client";
import { Prisma } from "@prisma/client";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

Quote: import { Prisma } from "@prisma/client";

Issue: This file previously imported type { Prisma } (type-only). The diff changes it to a runtime import. If @prisma/client is not intended to be loaded in this context (e.g., serverless edge, test environment, or where prisma client generation differs), this can cause runtime failures or increase bundle size.

Fix: If only Prisma.sql is needed, prefer a type-only import plus a value import from the correct module, or keep it as type-only if your tooling supports Prisma.sql via the existing prisma instance. For example:

  • import type { Prisma } from "@prisma/client";
  • and ensure Prisma.sql is available from the correct namespace (often import { Prisma } from "@prisma/client" is required, but then ensure this file is executed only in environments where the generated client exists).

re-entry.ai

return await this.buildTeamAuthorizationCondition(this.options);
} else {
return NOTHING;
return NOTHING_CONDITION;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING

Quote: const conditions: Prisma.Sql[] = [Prisma.sql("teamId" = ANY(${teamIds})) AND ("isTeamBooking" = true)];

Issue: teamIds is derived from teamsFromOrg.map((t) => t.id) and could be an empty array. In PostgreSQL, = ANY('{}') is valid but always false; however, the surrounding OR logic and the intended authorization semantics might differ from the previous structured Prisma in: teamIds behavior (which also becomes false, but the overall OR composition could differ when combined with other branches).

Fix: Add explicit handling for empty arrays to preserve intended semantics. For example:

  • If teamIds.length === 0, omit the team booking condition entirely and rely on the user-based condition.
  • Similarly, ensure the final predicate matches the previous behavior exactly.

re-entry.ai

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants