From e7e06758b90c76063a4bf199af6c6937cebb307c Mon Sep 17 00:00:00 2001 From: Adam Laycock Date: Tue, 27 Aug 2024 20:22:37 +0100 Subject: [PATCH] feat: implement typedsql for raw queries --- app/routes/app.$assetslug.$entry._index.tsx | 68 +++++------------- package-lock.json | 78 ++++++++++++--------- package.json | 4 +- prisma/schema.prisma | 1 + prisma/sql/getEntryRelations.sql | 18 +++++ prisma/sql/getEntryRevisions.sql | 7 ++ prisma/sql/getEntryValues.sql | 8 +++ 7 files changed, 96 insertions(+), 88 deletions(-) create mode 100644 prisma/sql/getEntryRelations.sql create mode 100644 prisma/sql/getEntryRevisions.sql create mode 100644 prisma/sql/getEntryValues.sql diff --git a/app/routes/app.$assetslug.$entry._index.tsx b/app/routes/app.$assetslug.$entry._index.tsx index 4f10ba2..032f96c 100644 --- a/app/routes/app.$assetslug.$entry._index.tsx +++ b/app/routes/app.$assetslug.$entry._index.tsx @@ -5,7 +5,11 @@ import { json } from '@remix-run/node' import {Link, useLoaderData} from '@remix-run/react' -import {type Entry} from '@prisma/client' +import { + getEntryValues, + getEntryRevisions, + getEntryRelations +} from '@prisma/client/sql' import {ensureUser} from '~/lib/utils/ensure-user' import {getPrisma} from '~/lib/prisma.server' @@ -39,39 +43,14 @@ export const loader = async ({request, params}: LoaderFunctionArgs) => { }) ) - const values = await time( - 'getValues', - 'Get Values', - () => prisma.$queryRaw< - Array<{ - id: string - value: string - order: number - type: string - meta: string - fieldId: string - fieldName: string - }> - >`SELECT Value.id, Value.value, AssetField."order", Field.type, Field.meta, Value.fieldId, Field.name as fieldName FROM Value - INNER JOIN Entry ON Entry.Id = Value.entryId - INNER JOIN Asset on Asset.Id = Entry.assetId - INNER JOIN AssetField on AssetField.assetId = Asset.id AND AssetField.fieldId = Value.fieldId - INNER JOIN Field on Field.id = Value.fieldId - WHERE entryId = ${entry.id} - ORDER BY AssetField."order" ASC` + const values = await time('getValues', 'Get Values', () => + prisma.$queryRawTyped(getEntryValues(entry.id)) ) - const relations = await time( - 'getRelations', - 'Get Relations', - () => prisma.$queryRaw< - Array< - Entry & {value: string; slug: string; entryId: string; icon: string} - > - >`SELECT * FROM Entry - INNER JOIN Value value ON fieldId = (SELECT nameFieldId FROM Asset WHERE Asset.id = entry.assetId) AND entryId = Entry.id - INNER JOIN Asset ON Entry.assetId = Asset.id - WHERE Entry.id IN (SELECT entryId FROM Value WHERE value LIKE ${`%${entry.id}%`}) AND deleted = false` + const relations = await time('getRelations', 'Get Relations', () => + prisma.$queryRawTyped( + getEntryRelations(`%${entry.id}%`, user.role, user.id) + ) ) const documents = await time('getDocuments', 'Get Documents', () => @@ -80,23 +59,8 @@ export const loader = async ({request, params}: LoaderFunctionArgs) => { }) ) - const revisions = await time( - 'getRevisions', - 'Get Revisions', - () => prisma.$queryRaw< - Array<{ - id: string - createdAt: string - changeNote: string - fieldName: string - userName: string - }> - >`SELECT ValueHistory.id, ValueHistory.createdAt, ValueHistory.changeNote, Field.name as fieldName, User.name as userName FROM ValueHistory - INNER JOIN Value on Value.id = ValueHistory.valueId - INNER JOIN Field on Field.id = Value.fieldId - INNER JOIN User on User.id = ValueHistory.editedById - WHERE Value.entryId = ${params.entry} - ORDER BY ValueHistory.createdAt DESC` + const revisions = await time('getRevisions', 'Get Revisions', () => + prisma.$queryRawTyped(getEntryRevisions(entry.id)) ) const name = values.reduce((n, v) => { @@ -164,11 +128,11 @@ const AssetEntry = () => {
{relations.length === 0 ? 'No Linked Entries' - : relations.map(({entryId, value, slug, icon}) => { + : relations.map(({id, value, slug, icon}) => { return ( {icon} {value} diff --git a/package-lock.json b/package-lock.json index 24b0ce2..401f7de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "@arcath/utils": "^3.15.0", "@epic-web/totp": "^1.1.2", - "@prisma/client": "^5.18.0", + "@prisma/client": "^5.19.0", "@remix-run/node": "^2.11.1", "@remix-run/react": "^2.11.1", "@remix-run/serve": "^2.11.1", @@ -54,7 +54,7 @@ "eslint-plugin-react-hooks": "^4.6.2", "postcss": "^8.4.41", "prettier": "^3.3.3", - "prisma": "^5.18.0", + "prisma": "^5.19.0", "tailwindcss": "^3.4.10", "typescript": "^5.5.4", "vite-tsconfig-paths": "^4.3.2", @@ -2786,10 +2786,11 @@ } }, "node_modules/@prisma/client": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.18.0.tgz", - "integrity": "sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.0.tgz", + "integrity": "sha512-CzOpau+q1kEWQyoQMvlnXIHqPvwmWbh48xZ4n8KWbAql0p8PC0BIgSTYW5ncxXa4JSEff0tcoxSZB874wDstdg==", "hasInstallScript": true, + "license": "Apache-2.0", "engines": { "node": ">=16.13" }, @@ -2803,48 +2804,53 @@ } }, "node_modules/@prisma/debug": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.18.0.tgz", - "integrity": "sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw==", - "devOptional": true + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.0.tgz", + "integrity": "sha512-+b/G0ubAZlrS+JSiDhXnYV5DF/aTJ3pinktkiV/L4TtLRLZO6SVGyFELgxBsicCTWJ2ZMu5vEV/jTtYCdjFTRA==", + "devOptional": true, + "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.18.0.tgz", - "integrity": "sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.0.tgz", + "integrity": "sha512-UtW+0m4HYoRSSR3LoDGKF3Ud4BSMWYlLEt4slTnuP1mI+vrV3zaDoiAPmejdAT76vCN5UqnWURbkXxf66nSylQ==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.18.0", - "@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", - "@prisma/fetch-engine": "5.18.0", - "@prisma/get-platform": "5.18.0" + "@prisma/debug": "5.19.0", + "@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f", + "@prisma/fetch-engine": "5.19.0", + "@prisma/get-platform": "5.19.0" } }, "node_modules/@prisma/engines-version": { - "version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz", - "integrity": "sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg==", - "devOptional": true + "version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f.tgz", + "integrity": "sha512-GimI9aZIFy/yvvR11KfXRn3pliFn1QAkdebVlsXlnoh5uk0YhLblVmeYiHfsu+wDA7BeKqYT4sFfzg8mutzuWw==", + "devOptional": true, + "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz", - "integrity": "sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.0.tgz", + "integrity": "sha512-oOiPNtmJX0cP/ebu7BBEouJvCw8T84/MFD/Hf2zlqjxkK4ojl38bB9i9J5LAxotL6WlYVThKdxc7HqoWnPOhqQ==", "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.18.0", - "@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", - "@prisma/get-platform": "5.18.0" + "@prisma/debug": "5.19.0", + "@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f", + "@prisma/get-platform": "5.19.0" } }, "node_modules/@prisma/get-platform": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.18.0.tgz", - "integrity": "sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.0.tgz", + "integrity": "sha512-s9DWkZKnuP4Y8uy6yZfvqQ/9X3/+2KYf3IZUVZz5OstJdGBJrBlbmIuMl81917wp5TuK/1k2TpHNCEdpYLPKmg==", "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.18.0" + "@prisma/debug": "5.19.0" } }, "node_modules/@remix-run/dev": { @@ -16910,19 +16916,23 @@ } }, "node_modules/prisma": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.18.0.tgz", - "integrity": "sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.0.tgz", + "integrity": "sha512-Pu7lUKpVyTx8cVwM26dYh8NdvMOkMnJXzE8L6cikFuR4JwyMU5NKofQkWyxJKlTT4fNjmcnibTvklV8oVMrn+g==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "5.18.0" + "@prisma/engines": "5.19.0" }, "bin": { "prisma": "build/index.js" }, "engines": { "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" } }, "node_modules/proc-log": { diff --git a/package.json b/package.json index 398a792..138d927 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dependencies": { "@arcath/utils": "^3.15.0", "@epic-web/totp": "^1.1.2", - "@prisma/client": "^5.18.0", + "@prisma/client": "^5.19.0", "@remix-run/node": "^2.11.1", "@remix-run/react": "^2.11.1", "@remix-run/serve": "^2.11.1", @@ -68,7 +68,7 @@ "eslint-plugin-react-hooks": "^4.6.2", "postcss": "^8.4.41", "prettier": "^3.3.3", - "prisma": "^5.18.0", + "prisma": "^5.19.0", "tailwindcss": "^3.4.10", "typescript": "^5.5.4", "vite-tsconfig-paths": "^4.3.2", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2f072fa..546e919 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -6,6 +6,7 @@ generator client { provider = "prisma-client-js" + previewFeatures = ["typedSql"] } datasource db { diff --git a/prisma/sql/getEntryRelations.sql b/prisma/sql/getEntryRelations.sql new file mode 100644 index 0000000..2abb08e --- /dev/null +++ b/prisma/sql/getEntryRelations.sql @@ -0,0 +1,18 @@ +-- @param {String} $1:entryId The ID of the entry to collect relations for +-- @param {String} $2:userRole The role of the current user +-- @param {String} $3:userId The ID of the current user +SELECT Entry.id, Value.value, Asset.icon, Asset.slug FROM Entry + INNER JOIN Value value ON fieldId = (SELECT nameFieldId FROM Asset WHERE Asset.id = entry.assetId) AND entryId = Entry.id + INNER JOIN Asset ON Entry.assetId = Asset.id + WHERE + Entry.id IN (SELECT entryId FROM Value WHERE value LIKE $1) + AND + deleted = false + AND + Entry.aclId IN (SELECT aclId FROM ACLEntry + WHERE read = true AND ( + (type = "role" AND target = $2) + OR + (type = "user" AND target = $3) + ) + ) \ No newline at end of file diff --git a/prisma/sql/getEntryRevisions.sql b/prisma/sql/getEntryRevisions.sql new file mode 100644 index 0000000..dae0f74 --- /dev/null +++ b/prisma/sql/getEntryRevisions.sql @@ -0,0 +1,7 @@ +-- @param {String} $1:entryId The ID of the entry to collect revisions for +SELECT ValueHistory.id, ValueHistory.createdAt, ValueHistory.changeNote, Field.name as fieldName, User.name as userName FROM ValueHistory + INNER JOIN Value on Value.id = ValueHistory.valueId + INNER JOIN Field on Field.id = Value.fieldId + INNER JOIN User on User.id = ValueHistory.editedById + WHERE Value.entryId = $1 + ORDER BY ValueHistory.createdAt DESC \ No newline at end of file diff --git a/prisma/sql/getEntryValues.sql b/prisma/sql/getEntryValues.sql new file mode 100644 index 0000000..4f4e2d5 --- /dev/null +++ b/prisma/sql/getEntryValues.sql @@ -0,0 +1,8 @@ +-- @param {String} $1:entryId The ID of the entry to collect values for +SELECT Value.id, Value.value, AssetField."order", Field.type, Field.meta, Value.fieldId, Field.name as fieldName FROM Value + INNER JOIN Entry ON Entry.Id = Value.entryId + INNER JOIN Asset on Asset.Id = Entry.assetId + INNER JOIN AssetField on AssetField.assetId = Asset.id AND AssetField.fieldId = Value.fieldId + INNER JOIN Field on Field.id = Value.fieldId + WHERE entryId = $1 + ORDER BY AssetField."order" ASC \ No newline at end of file