Skip to content
This repository was archived by the owner on Oct 30, 2024. It is now read-only.

feat(prisma): add typedsql #1

Open
wants to merge 11 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
2 changes: 1 addition & 1 deletion .github/actions/node-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ runs:
shell: bash
run: |
npm ci --no-audit
npm run prisma:generate
npm run prisma:generate-sql
env:
HUSKY: '0'
322 changes: 268 additions & 54 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
"dx:down": "docker compose -f docker/development/compose.yml down",
"ci": "turbo run test:e2e",
"prisma:generate": "npm run with:env -- npm run prisma:generate -w @documenso/prisma",
"prisma:generate-sql": "npm run with:env -- npm run prisma:generate-sql -w @documenso/prisma",
"prisma:migrate-dev": "npm run with:env -- npm run prisma:migrate-dev -w @documenso/prisma",
"prisma:migrate-deploy": "npm run with:env -- npm run prisma:migrate-deploy -w @documenso/prisma",
"prisma:migrate-reset": "npm run with:env -- npm run prisma:migrate-reset -w @documenso/prisma",
"prisma:seed": "npm run with:env -- npm run prisma:seed -w @documenso/prisma",
"prisma:studio": "npm run with:env -- npm run prisma:studio -w @documenso/prisma",
"with:env": "dotenv -e .env -e .env.local --",
"reset:hard": "npm run clean && npm i && npm run prisma:generate",
"reset:hard": "npm run clean && npm i && npm run prisma:generate-sql",
"precommit": "npm install && git add package.json package-lock.json",
"trigger:dev": "npm run with:env -- npx trigger-cli dev --handler-path=\"/api/jobs\"",
"inngest:dev": "inngest dev -u http://localhost:3000/api/jobs",
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"clean": "rimraf node_modules"
},
"dependencies": {
"@auth/kysely-adapter": "^0.6.0",
"@auth/kysely-adapter": "^0.6.2",
"@aws-sdk/client-s3": "^3.410.0",
"@aws-sdk/cloudfront-signer": "^3.410.0",
"@aws-sdk/s3-request-presigner": "^3.410.0",
Expand Down
19 changes: 2 additions & 17 deletions packages/lib/server-only/admin/get-users-stats.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DateTime } from 'luxon';

import { prisma } from '@documenso/prisma';
import { SQL, prisma } from '@documenso/prisma';
import { DocumentStatus, SubscriptionStatus } from '@documenso/prisma/client';

export const getUsersCount = async () => {
Expand Down Expand Up @@ -56,23 +56,8 @@ export type GetUserWithDocumentMonthlyGrowth = Array<{
signed_count: number;
}>;

type GetUserWithDocumentMonthlyGrowthQueryResult = Array<{
month: Date;
count: bigint;
signed_count: bigint;
}>;

export const getUserWithSignedDocumentMonthlyGrowth = async () => {
const result = await prisma.$queryRaw<GetUserWithDocumentMonthlyGrowthQueryResult>`
SELECT
DATE_TRUNC('month', "Document"."createdAt") AS "month",
COUNT(DISTINCT "Document"."userId") as "count",
COUNT(DISTINCT CASE WHEN "Document"."status" = 'COMPLETED' THEN "Document"."userId" END) as "signed_count"
FROM "Document"
GROUP BY "month"
ORDER BY "month" DESC
LIMIT 12
`;
const result = await prisma.$queryRawTyped(SQL.userWithSignedDocumentMonthlyGrowth());

return result.map((row) => ({
month: DateTime.fromJSDate(row.month).toFormat('yyyy-MM'),
Expand Down
22 changes: 2 additions & 20 deletions packages/lib/server-only/user/get-monthly-completed-document.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import { DateTime } from 'luxon';

import { kyselyPrisma, sql } from '@documenso/prisma';
import { DocumentStatus } from '@documenso/prisma/client';
import { SQL, prisma } from '@documenso/prisma';

export const getCompletedDocumentsMonthly = async () => {
const qb = kyselyPrisma.$kysely
.selectFrom('Document')
.select(({ fn }) => [
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']).as('month'),
fn.count('id').as('count'),
fn
.sum(fn.count('id'))
// Feels like a bug in the Kysely extension but I just can not do this orderBy in a type-safe manner
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']) as any))
.as('cume_count'),
])
.where(() => sql`"Document"."status" = ${DocumentStatus.COMPLETED}::"DocumentStatus"`)
.groupBy('month')
.orderBy('month', 'desc')
.limit(12);

const result = await qb.execute();
const result = await prisma.$queryRawTyped(SQL.completedDocumentsMonthly());

return result.map((row) => ({
month: DateTime.fromJSDate(row.month).toFormat('yyyy-MM'),
Expand Down
21 changes: 2 additions & 19 deletions packages/lib/server-only/user/get-signer-conversion.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
import { DateTime } from 'luxon';

import { kyselyPrisma, sql } from '@documenso/prisma';
import { SQL, prisma } from '@documenso/prisma';

export const getSignerConversionMonthly = async () => {
const qb = kyselyPrisma.$kysely
.selectFrom('Recipient')
.innerJoin('User', 'Recipient.email', 'User.email')
.select(({ fn }) => [
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']).as('month'),
fn.count('Recipient.email').distinct().as('count'),
fn
.sum(fn.count('Recipient.email').distinct())
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']) as any))
.as('cume_count'),
])
.where('Recipient.signedAt', 'is not', null)
.where('Recipient.signedAt', '<', (eb) => eb.ref('User.createdAt'))
.groupBy(({ fn }) => fn('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']))
.orderBy('month', 'desc');

const result = await qb.execute();
const result = await prisma.$queryRawTyped(SQL.signerConversionMonthly());

return result.map((row) => ({
month: DateTime.fromJSDate(row.month).toFormat('yyyy-MM'),
Expand Down
20 changes: 2 additions & 18 deletions packages/lib/server-only/user/get-user-monthly-growth.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
import { DateTime } from 'luxon';

import { kyselyPrisma, sql } from '@documenso/prisma';
import { SQL, prisma } from '@documenso/prisma';

export const getUserMonthlyGrowth = async () => {
const qb = kyselyPrisma.$kysely
.selectFrom('User')
.select(({ fn }) => [
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']).as('month'),
fn.count('id').as('count'),
fn
.sum(fn.count('id'))
// Feels like a bug in the Kysely extension but I just can not do this orderBy in a type-safe manner
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']) as any))
.as('cume_count'),
])
.groupBy('month')
.orderBy('month', 'desc')
.limit(12);

const result = await qb.execute();
const result = await prisma.$queryRawTyped(SQL.userMonthlyGrowth());

return result.map((row) => ({
month: DateTime.fromJSDate(row.month).toFormat('yyyy-MM'),
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/translations/de/marketing.js

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions packages/prisma/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { PrismaClient } from '@prisma/client';
import {
completedDocumentsMonthly,
signerConversionMonthly,
userMonthlyGrowth,
userWithSignedDocumentMonthlyGrowth,
} from '@prisma/client/sql';
import { Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely';
import kyselyExtension from 'prisma-extension-kysely';

Expand All @@ -14,6 +20,13 @@ export const prisma = remember(
}),
);

export const SQL = {
completedDocumentsMonthly,
signerConversionMonthly,
userMonthlyGrowth,
userWithSignedDocumentMonthlyGrowth,
};

export const kyselyPrisma = remember('kyselyPrisma', () =>
prisma.$extends(
kyselyExtension({
Expand Down
13 changes: 7 additions & 6 deletions packages/prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
"types": "./index.ts",
"license": "MIT",
"scripts": {
"build": "prisma generate",
"prebuild": "prisma generate",
"build": "npm run prisma:generate-sql",
"prebuild": "npm run prisma:generate-sql",
"format": "prisma format",
"clean": "rimraf node_modules",
"post-install": "prisma generate",
"post-install": "npm run prisma:generate-sql",
"prisma:generate": "prisma generate",
"prisma:generate-sql": "prisma generate --sql",
"prisma:migrate-dev": "prisma migrate dev --skip-seed",
"prisma:migrate-deploy": "prisma migrate deploy",
"prisma:migrate-reset": "prisma migrate reset",
Expand All @@ -21,9 +22,9 @@
"seed": "tsx ./seed-database.ts"
},
"dependencies": {
"@prisma/client": "5.4.2",
"kysely": "^0.27.3",
"prisma": "5.4.2",
"@prisma/client": "^5.19.0",
"kysely": "^0.27.4",
"prisma": "^5.19.0",
"prisma-extension-kysely": "^2.1.0",
"ts-pattern": "^5.0.6"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ generator kysely {
}

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

datasource db {
Expand Down
9 changes: 9 additions & 0 deletions packages/prisma/sql/completedDocumentsMonthly.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT
DATE_TRUNC('month', "updatedAt") AS "month",
COUNT("id") as "count",
SUM(COUNT("id")) OVER (ORDER BY DATE_TRUNC('month', "updatedAt")) as "cume_count"
FROM "Document"
WHERE "status" = 'COMPLETED'
GROUP BY "month"
ORDER BY "month" DESC
LIMIT 12
10 changes: 10 additions & 0 deletions packages/prisma/sql/signerConversionMonthly.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SELECT
DATE_TRUNC('month', "User"."createdAt") AS "month",
COUNT(DISTINCT "Recipient"."email") as "count",
SUM(COUNT(DISTINCT "Recipient"."email")) OVER (ORDER BY DATE_TRUNC('month', "User"."createdAt")) as "cume_count"
FROM "Recipient"
INNER JOIN "User" ON "Recipient"."email" = "User"."email"
WHERE "Recipient"."signedAt" IS NOT NULL
AND "Recipient"."signedAt" < "User"."createdAt"
GROUP BY DATE_TRUNC('month', "User"."createdAt")
ORDER BY "month" DESC
8 changes: 8 additions & 0 deletions packages/prisma/sql/userMonthlyGrowth.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SELECT
DATE_TRUNC('MONTH', "User"."createdAt") AS "month",
COUNT("id") AS "count",
SUM(COUNT("id")) OVER (ORDER BY DATE_TRUNC('MONTH', "User"."createdAt")) AS "cume_count"
FROM "User"
GROUP BY "month"
ORDER BY "month" DESC
LIMIT 12;
8 changes: 8 additions & 0 deletions packages/prisma/sql/userWithSignedDocumentMonthlyGrowth.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SELECT
DATE_TRUNC('month', "Document"."createdAt") AS "month",
COUNT(DISTINCT "Document"."userId") as "count",
COUNT(DISTINCT CASE WHEN "Document"."status" = 'COMPLETED' THEN "Document"."userId" END) as "signed_count"
FROM "Document"
GROUP BY "month"
ORDER BY "month" DESC
LIMIT 12
4 changes: 2 additions & 2 deletions scripts/vercel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function build_webapp() {
remap_webapp_env
remap_database_integration

npm run prisma:generate --workspace=@documenso/prisma
npm run prisma:generate-sql --workspace=@documenso/prisma
npm run prisma:migrate-deploy --workspace=@documenso/prisma

if [[ "$VERCEL_ENV" != "production" ]]; then
Expand All @@ -43,7 +43,7 @@ function build_marketing() {
remap_marketing_env
remap_database_integration

npm run prisma:generate --workspace=@documenso/prisma
npm run prisma:generate-sql --workspace=@documenso/prisma
npm run build -- --filter @documenso/marketing
}

Expand Down
Loading