Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: convert to raw sql queries #1

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"typescript.preferences.importModuleSpecifier": "non-relative",
"spellright.language": ["en"],
"spellright.documentTypes": ["markdown", "typescript", "typescriptreact"],
"tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]]
"spellright.documentTypes": ["markdown"],
"tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]],
"prisma.fileWatcher": false
}
894 changes: 894 additions & 0 deletions .yarn/releases/yarn-4.3.1.cjs

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions apps/web/test/lib/checkDurationLimits.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import prismaMock from "../../../../tests/libs/__mocks__/prismaMock";
import prisma from "../../../../tests/libs/__mocks__/prismaMock";

import { describe, expect, it } from "vitest";

Expand All @@ -19,19 +19,19 @@ const MOCK_DATA: MockData = {
// Path: apps/web/test/lib/checkDurationLimits.ts
describe("Check Duration Limits Tests", () => {
it("Should return no errors if limit is not reached", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 0 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 0 }]);
await expect(
checkDurationLimits({ PER_DAY: 60 }, MOCK_DATA.startDate, MOCK_DATA.id)
).resolves.toBeTruthy();
});
it("Should throw an error if limit is reached", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 60 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 60 }]);
await expect(
checkDurationLimits({ PER_DAY: 60 }, MOCK_DATA.startDate, MOCK_DATA.id)
).rejects.toThrowError();
});
it("Should pass with multiple duration limits", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 30 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 30 }]);
await expect(
checkDurationLimits(
{
Expand All @@ -44,7 +44,7 @@ describe("Check Duration Limits Tests", () => {
).resolves.toBeTruthy();
});
it("Should pass with multiple duration limits with one undefined", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 30 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 30 }]);
await expect(
checkDurationLimits(
{
Expand All @@ -57,7 +57,7 @@ describe("Check Duration Limits Tests", () => {
).resolves.toBeTruthy();
});
it("Should return no errors if limit is not reached with multiple bookings", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 60 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 60 }]);
await expect(
checkDurationLimits(
{
Expand All @@ -70,7 +70,7 @@ describe("Check Duration Limits Tests", () => {
).resolves.toBeTruthy();
});
it("Should throw an error if one of the limit is reached with multiple bookings", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 90 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 90 }]);
await expect(
checkDurationLimits(
{
Expand All @@ -87,7 +87,7 @@ describe("Check Duration Limits Tests", () => {
// Path: apps/web/test/lib/checkDurationLimits.ts
describe("Check Duration Limit Tests", () => {
it("Should return no busyTimes and no error if limit is not reached", async () => {
prismaMock.$queryRaw.mockResolvedValue([{ totalMinutes: 60 }]);
prisma.$queryRawTyped.mockResolvedValue([{ totalMinutes: 60 }]);
await expect(
checkDurationLimit({
key: "PER_DAY",
Expand Down
4 changes: 2 additions & 2 deletions example-apps/credential-sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@calcom/atoms": "*",
"@prisma/client": "5.4.2",
"@prisma/client": "5.18.0-integration-feat-typed-sql.1",
"next": "14.0.4",
"prisma": "^5.7.1",
"prisma": "^5.18.0-integration-feat-typed-sql.1",
"react": "^18",
"react-dom": "^18"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* see: https://github.com/calcom/cal.com/pull/10480
* https://github.com/calcom/cal.com/pull/10968
*/
import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import prisma from "../../../../../../tests/libs/__mocks__/prismaMock";

import {
TestData,
Expand Down Expand Up @@ -244,7 +244,7 @@ describe("handleNewBooking", () => {
body: mockBookingData,
});

vi.spyOn(prismock, "$queryRaw").mockResolvedValue([{ totalMinutes: yearlyDurationLimit }]);
vi.spyOn(prisma, "$queryRawTyped").mockResolvedValue([{ totalMinutes: yearlyDurationLimit }]);

await expect(async () => await handleNewBooking(req)).rejects.toThrowError(
"duration_limit_reached"
Expand All @@ -270,7 +270,7 @@ describe("handleNewBooking", () => {
body: mockBookingDataFollowingYear,
});

vi.spyOn(prismock, "$queryRaw").mockResolvedValue([{ totalMinutes: 0 }]);
vi.spyOn(prisma, "$queryRawTyped").mockResolvedValue([{ totalMinutes: 0 }]);

const createdBooking = await handleNewBooking(reqFollowingYear);

Expand Down
21 changes: 3 additions & 18 deletions packages/lib/apps/getInstallCountPerApp.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
import { z } from "zod";

import prisma from "@calcom/prisma";
import prisma, { SQL } from "@calcom/prisma";

const getInstallCountPerApp = async () => {
const mostPopularApps = z.array(z.object({ appId: z.string(), installCount: z.number() })).parse(
await prisma.$queryRaw`
SELECT
c."appId",
COUNT(*)::integer AS "installCount"
FROM
"Credential" c
WHERE
c."appId" IS NOT NULL
GROUP BY
c."appId"
ORDER BY
"installCount" DESC
`
);
const mostPopularApps = await prisma.$queryRawTyped(SQL.queryForMostPopularApps());

return mostPopularApps.reduce((acc, { appId, installCount }) => {
acc[appId] = installCount;
return acc;
Expand Down
13 changes: 4 additions & 9 deletions packages/lib/server/queries/booking/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import prisma from "@calcom/prisma";
import prisma, { SQL } from "@calcom/prisma";

export const getTotalBookingDuration = async ({
eventId,
Expand All @@ -11,14 +11,9 @@ export const getTotalBookingDuration = async ({
}) => {
// Aggregates the total booking time for a given event in a given time period
// FIXME: bookings that overlap on one side will never be counted
const [totalBookingTime] = await prisma.$queryRaw<[{ totalMinutes: number | null }]>`
SELECT SUM(EXTRACT(EPOCH FROM ("endTime" - "startTime")) / 60) as "totalMinutes"
FROM "Booking"
WHERE "status" = 'accepted'
AND "eventTypeId" = ${eventId}
AND "startTime" >= ${startDate}
AND "endTime" <= ${endDate};
`;
const [totalBookingTime] = await prisma.$queryRawTyped(
SQL.queryForTotalBookingTime(eventId, startDate, endDate)
);

return totalBookingTime.totalMinutes ?? 0;
};
4 changes: 2 additions & 2 deletions packages/platform/examples/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@calcom/atoms": "*",
"@prisma/client": "5.4.2",
"@prisma/client": "5.19.0",
"next": "14.0.4",
"prisma": "^5.7.1",
"prisma": "5.19.0",
"react": "^18",
"react-dom": "^18",
"react-select": "^5.8.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/examples/base/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ model User {
createdAt DateTime @default(now())
updatedAt DateTime @default(now())

}
}
13 changes: 13 additions & 0 deletions packages/prisma/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ import type { Prisma } from "@prisma/client";
import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client";
import { withAccelerate } from "@prisma/extension-accelerate";

import {
queryForMostPopularApps,
queryForTotalBookingTime,
queryForTotalMembers,
queryHealthCheck,
} from "../../node_modules/.prisma/client/sql";
import { bookingIdempotencyKeyExtension } from "./extensions/booking-idempotency-key";
import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-payment-teams";
import { usageTrackingExtention } from "./extensions/usage-tracking";
import { bookingReferenceMiddleware } from "./middleware";

export const SQL = {
queryForMostPopularApps,
queryForTotalBookingTime,
queryForTotalMembers,
queryHealthCheck,
};

const prismaOptions: Prisma.PrismaClientOptions = {};

const globalForPrisma = global as unknown as {
Expand Down
4 changes: 2 additions & 2 deletions packages/prisma/is-prisma-available-check.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Prisma } from "@prisma/client";

import prisma from ".";
import prisma, { SQL } from "@calcom/prisma";

export async function isPrismaAvailableCheck() {
try {
await prisma.$queryRaw`SELECT 1`;
await prisma.$queryRawTyped(SQL.queryHealthCheck());
return true;
} catch (e: unknown) {
if (e instanceof Prisma.PrismaClientInitializationError) {
Expand Down
13 changes: 9 additions & 4 deletions packages/prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"db-studio": "yarn prisma studio",
"db-up": "docker compose up -d || docker-compose up -d",
"dx": "yarn db-setup",
"generate-schemas": "prisma generate && prisma format",
"generate-schemas": "prisma generate --sql && prisma format",
"post-install": "yarn generate-schemas",
"seed-app-store": "ts-node --transpile-only ./seed-app-store.ts",
"delete-app": "ts-node --transpile-only ./delete-app.ts",
Expand All @@ -25,15 +25,20 @@
},
"dependencies": {
"@calcom/lib": "*",
"@prisma/client": "^5.4.2",
"@prisma/client": "5.19.0",
"@prisma/extension-accelerate": "^0.6.2",
"@prisma/generator-helper": "^5.4.2",
"prisma": "^5.4.2",
"@prisma/generator-helper": "5.19.0",
"prisma": "5.19.0",
"prisma-kysely": "^1.7.1",
"ts-node": "^10.9.1",
"zod": "^3.22.4",
"zod-prisma": "^0.5.4"
},
"overrides": {
"@prisma/client": "5.19.0",
"@prisma/generator-helper": "5.19.0",
"prisma": "5.19.0"
},
"main": "index.ts",
"types": "index.d.ts",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ datasource db {

generator client {
provider = "prisma-client-js"
previewFeatures = ["views"]
previewFeatures = ["views", "typedSql"]
}

generator zod {
Expand Down
11 changes: 11 additions & 0 deletions packages/prisma/sql/queryForMostPopularApps.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SELECT
c."appId",
COUNT(*)::integer AS "installCount"
FROM
"Credential" c
WHERE
c."appId" IS NOT NULL
GROUP BY
c."appId"
ORDER BY
"installCount" DESC;
9 changes: 9 additions & 0 deletions packages/prisma/sql/queryForTotalBookingTime.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT
SUM(EXTRACT(EPOCH FROM ("endTime" - "startTime")) / 60) AS "totalMinutes"
FROM
"Booking"
WHERE
"status" = 'accepted'
AND "eventTypeId" = $1
AND "startTime" >= $2
AND "endTime" <= $3;
3 changes: 3 additions & 0 deletions packages/prisma/sql/queryForTotalMembers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT COUNT(DISTINCT "userId")::integer
FROM "Membership"
WHERE "teamId" = ANY($1::int[])
1 change: 1 addition & 0 deletions packages/prisma/sql/queryHealthCheck.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
select 1 as one;
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Prisma } from "@prisma/client";

import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import type { DateRange } from "@calcom/lib/date-ranges";
import { buildDateRanges } from "@calcom/lib/date-ranges";
import { UserRepository } from "@calcom/lib/server/repository/user";
import { prisma } from "@calcom/prisma";
import prisma, { SQL } from "@calcom/prisma";

import { TRPCError } from "@trpc/server";

Expand Down Expand Up @@ -156,11 +154,7 @@ async function getInfoForAllTeams({ ctx, input }: GetOptions) {

// Get total team count across all teams the user is in (for pagination)

const totalTeamMembers = await prisma.$queryRaw<
{
count: number;
}[]
>`SELECT COUNT(DISTINCT "userId")::integer from "Membership" WHERE "teamId" IN (${Prisma.join(teamIds)})`;
const totalTeamMembers = await prisma.$queryRawTyped(SQL.queryForTotalMembers(teamIds));

return {
teamMembers,
Expand Down
8 changes: 6 additions & 2 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,12 @@
},
"post-install": {
"dependsOn": [],
"outputs": ["../../node_modules/@prisma/client/**", "../../node_modules/@prisma/admin-client/**"],
"inputs": ["./schema.prisma", "./prisma/schema.prisma"],
"outputs": [
"../../node_modules/@prisma/client/**",
"../../node_modules/@prisma/admin-client/**",
"../../node_modules/.prisma/client/sql/**"
],
"inputs": ["./schema.prisma", "./prisma/schema.prisma", "./prisma/sql"],
"env": ["PRISMA_GENERATE_DATAPROXY"]
},
"@calcom/prisma#post-install": {
Expand Down
Loading