From faea621b2fe338829c31e9c008b37108ed103e21 Mon Sep 17 00:00:00 2001 From: Duane Nykamp Date: Sun, 8 Dec 2024 22:29:06 -0600 Subject: [PATCH] upgrade to prisma 6 (#2486) --- server/package-lock.json | 82 ++++--- server/package.json | 4 +- server/prisma/schema.prisma | 3 +- server/src/index.ts | 31 +-- server/src/model.test.ts | 167 +++++++------- server/src/model.ts | 381 +++++++++++++++++--------------- server/src/types.ts | 18 +- server/src/utils/binary-uuid.ts | 5 +- server/src/utils/uuid.ts | 38 +++- 9 files changed, 395 insertions(+), 334 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 076a52a14..b03d05aad 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-ses": "^3.629.0", - "@prisma/client": "^5.18.0", + "@prisma/client": "^6.0.1", "@quixo3/prisma-session-store": "^3.1.13", "cookie-parser": "^1.4.6", "dotenv": "^16.4.5", @@ -39,7 +39,7 @@ "globals": "^15.9.0", "nodemon": "^3.1.4", "prettier": "^3.3.3", - "prisma": "^5.18.0", + "prisma": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.5.4", "typescript-eslint": "^8.1.0", @@ -1391,12 +1391,13 @@ } }, "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": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.0.1.tgz", + "integrity": "sha512-60w7kL6bUxz7M6Gs/V+OWMhwy94FshpngVmOY05TmGD0Lhk+Ac0ZgtjlL6Wll9TD4G03t4Sq1wZekNVy+Xdlbg==", "hasInstallScript": true, + "license": "Apache-2.0", "engines": { - "node": ">=16.13" + "node": ">=18.18" }, "peerDependencies": { "prisma": "*" @@ -1408,48 +1409,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": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.0.1.tgz", + "integrity": "sha512-jQylgSOf7ibTVxqBacnAlVGvek6fQxJIYCQOeX2KexsfypNzXjJQSS2o5s+Mjj2Np93iSOQUaw6TvPj8syhG4w==", + "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": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.0.1.tgz", + "integrity": "sha512-4hxzI+YQIR2uuDyVsDooFZGu5AtixbvM2psp+iayDZ4hRrAHo/YwgA17N23UWq7G6gRu18NvuNMb48qjP3DPQw==", "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": "6.0.1", + "@prisma/engines-version": "5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "@prisma/fetch-engine": "6.0.1", + "@prisma/get-platform": "6.0.1" } }, "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.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e.tgz", + "integrity": "sha512-JmIds0Q2/vsOmnuTJYxY4LE+sajqjYKhLtdOT6y4imojqv5d/aeVEfbBGC74t8Be1uSp0OP8lxIj2OqoKbLsfQ==", + "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": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.0.1.tgz", + "integrity": "sha512-T36bWFVGeGYYSyYOj9d+O9G3sBC+pAyMC+jc45iSL63/Haq1GrYjQPgPMxrEj9m739taXrupoysRedQ+VyvM/Q==", "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": "6.0.1", + "@prisma/engines-version": "5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e", + "@prisma/get-platform": "6.0.1" } }, "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": "6.0.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.0.1.tgz", + "integrity": "sha512-zspC9vlxAqx4E6epMPMLLBMED2VD8axDe8sPnquZ8GOsn6tiacWK0oxrGK4UAHYzYUVuMVUApJbdXB2dFpLhvg==", "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.18.0" + "@prisma/debug": "6.0.1" } }, "node_modules/@quixo3/prisma-session-store": { @@ -5054,19 +5060,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": "6.0.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.0.1.tgz", + "integrity": "sha512-CaMNFHkf+DDq8zq3X/JJsQ4Koy7dyWwwtOKibkT/Am9j/tDxcfbg7+lB1Dzhx18G/+RQCMgjPYB61bhRqteNBQ==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "5.18.0" + "@prisma/engines": "6.0.1" }, "bin": { "prisma": "build/index.js" }, "engines": { - "node": ">=16.13" + "node": ">=18.18" + }, + "optionalDependencies": { + "fsevents": "2.3.3" } }, "node_modules/proxy-addr": { diff --git a/server/package.json b/server/package.json index de83d9ee2..d1f9061c7 100644 --- a/server/package.json +++ b/server/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@aws-sdk/client-ses": "^3.629.0", - "@prisma/client": "^5.18.0", + "@prisma/client": "^6.0.1", "@quixo3/prisma-session-store": "^3.1.13", "cookie-parser": "^1.4.6", "dotenv": "^16.4.5", @@ -46,7 +46,7 @@ "globals": "^15.9.0", "nodemon": "^3.1.4", "prettier": "^3.3.3", - "prisma": "^5.18.0", + "prisma": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.5.4", "typescript-eslint": "^8.1.0", diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 7e55a11d3..c5d76afb1 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -1,6 +1,5 @@ generator client { - provider = "prisma-client-js" - previewFeatures = ["fullTextIndex"] + provider = "prisma-client-js" } datasource db { diff --git a/server/src/index.ts b/server/src/index.ts index f42c7228e..fd6baa0c0 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -105,6 +105,7 @@ import { assignmentStudentDataConvertUUID, allAssignmentScoresConvertUUID, studentDataConvertUUID, + isEqualUUID, } from "./utils/uuid"; import { LicenseCode, UserInfo } from "./types"; @@ -1058,7 +1059,7 @@ app.get( ); app.get("/api/searchSharedContent", async (req: Request, res: Response) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const query = req.query.q as string; res.send({ users: (await searchUsersWithSharedContent(query, loggedInUserId)).map( @@ -1105,7 +1106,7 @@ app.post( app.get( "/api/loadPromotedContent", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); try { const content = await loadPromotedContent(loggedInUserId); @@ -1304,7 +1305,7 @@ app.post( app.get( "/api/getActivityEditorData/:activityId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const activityId = toUUID(req.params.activityId); try { const editorData = await getActivityEditorData( @@ -1331,7 +1332,7 @@ app.get( app.get( "/api/getSharedEditorData/:activityId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const activityId = toUUID(req.params.activityId); try { const editorData = await getSharedEditorData(activityId, loggedInUserId); @@ -1352,7 +1353,7 @@ app.get( app.get( "/api/getDocumentSource/:docId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const docId = toUUID(req.params.docId); try { const sourceData = await getDocumentSource(docId, loggedInUserId); @@ -1383,7 +1384,7 @@ app.get("/api/getAllLicenses", async (_req: Request, res: Response) => { app.get( "/api/getActivityViewerData/:activityId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const activityId = toUUID(req.params.activityId); try { @@ -1409,7 +1410,7 @@ app.get( app.get( "/api/getContributorHistory/:activityId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const activityId = toUUID(req.params.activityId); try { @@ -1435,7 +1436,7 @@ app.get( app.get( "/api/getRemixes/:activityId", async (req: Request, res: Response, next: NextFunction) => { - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const activityId = toUUID(req.params.activityId); try { @@ -2166,7 +2167,7 @@ app.get( const ownerId = toUUID(req.params.ownerId); const loggedInUserId = req.user?.userId; - if (!loggedInUserId || !ownerId.equals(loggedInUserId)) { + if (!loggedInUserId || !isEqualUUID(ownerId, loggedInUserId)) { return res.send({ notMe: true }); } @@ -2203,7 +2204,7 @@ app.get( const folderId = toUUID(req.params.folderId); const loggedInUserId = req.user?.userId; - if (!loggedInUserId || !ownerId.equals(loggedInUserId)) { + if (!loggedInUserId || !isEqualUUID(ownerId, loggedInUserId)) { return res.send({ notMe: true }); } @@ -2243,7 +2244,7 @@ app.get( const ownerId = toUUID(req.params.ownerId); const query = req.query.q as string; - if (!loggedInUserId || !ownerId.equals(loggedInUserId)) { + if (!loggedInUserId || !isEqualUUID(ownerId, loggedInUserId)) { return res.send({ notMe: true }); } @@ -2289,7 +2290,7 @@ app.get( const folderId = toUUID(req.params.folderId); const query = req.query.q as string; - if (!loggedInUserId || !ownerId.equals(loggedInUserId)) { + if (!loggedInUserId || !isEqualUUID(ownerId, loggedInUserId)) { return res.send({ notMe: true }); } @@ -2323,7 +2324,7 @@ app.get( "/api/getSharedFolderContent/:ownerId", async (req: Request, res: Response, next: NextFunction) => { const ownerId = toUUID(req.params.ownerId); - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); try { const contentData = await getSharedFolderContent({ ownerId, @@ -2352,7 +2353,7 @@ app.get( async (req: Request, res: Response, next: NextFunction) => { const ownerId = toUUID(req.params.ownerId); const folderId = toUUID(req.params.folderId); - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); try { const contentData = await getSharedFolderContent({ ownerId, @@ -2444,7 +2445,7 @@ app.get( async (req: Request, res: Response, next: NextFunction) => { try { const activityId = toUUID(req.body.activityId); - const loggedInUserId = req.user?.userId ?? Buffer.alloc(16); + const loggedInUserId = req.user?.userId ?? new Uint8Array(16); const classifications = await getClassifications( activityId, loggedInUserId, diff --git a/server/src/model.test.ts b/server/src/model.test.ts index 62608b9dc..a72bfc11b 100644 --- a/server/src/model.test.ts +++ b/server/src/model.test.ts @@ -78,11 +78,14 @@ import { DateTime } from "luxon"; import { ClassificationSystemTree, ContentStructure } from "./types"; import { allAssignmentScoresConvertUUID, + compareUUID, fromUUID, + isEqualUUID, newUUID, studentDataConvertUUID, userConvertUUID, } from "./utils/uuid"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; // const EMPTY_DOC_CID = // "bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku"; @@ -200,7 +203,7 @@ test("New activity starts out private, then delete it", async () => { await deleteActivity(activityId, userId); await expect(getActivityEditorData(activityId, userId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); const dataAfterDelete = await getMyFolderContent({ @@ -445,7 +448,7 @@ test("getMyFolderContent returns both public and private content, getSharedFolde loggedInUserId: userId, folderId: publicFolder1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); ownerContent = await getMyFolderContent({ loggedInUserId: ownerId, @@ -509,7 +512,7 @@ test("getMyFolderContent returns both public and private content, getSharedFolde folderId: privateFolder1Id, loggedInUserId: userId, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // If other user tries to access folder, throws error await expect( @@ -517,7 +520,7 @@ test("getMyFolderContent returns both public and private content, getSharedFolde loggedInUserId: userId, folderId: privateFolder1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); ownerContent = await getMyFolderContent({ loggedInUserId: ownerId, @@ -542,7 +545,7 @@ test("getMyFolderContent returns both public and private content, getSharedFolde loggedInUserId: userId, folderId: publicFolder3Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); }); test( @@ -887,7 +890,7 @@ test( folderId: sharedFolder1Id, loggedInUserId: user3Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // If other user tries to access folder via my folder, throws error await expect( @@ -895,7 +898,7 @@ test( loggedInUserId: user1Id, folderId: sharedFolder1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); ownerContent = await getMyFolderContent({ loggedInUserId: ownerId, @@ -963,7 +966,7 @@ test( folderId: privateFolder1Id, loggedInUserId: user1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // If other user tries to access folder, throws error await expect( @@ -971,7 +974,7 @@ test( loggedInUserId: user1Id, folderId: privateFolder1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); ownerContent = await getMyFolderContent({ loggedInUserId: ownerId, @@ -996,7 +999,7 @@ test( loggedInUserId: user1Id, folderId: sharedFolder3Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); }, ); @@ -1811,16 +1814,18 @@ test("deleteActivity marks a activity and document as deleted and prevents its r expect(deleteResult.isDeleted).toBe(true); // cannot retrieve activity - await expect(getActivity(activityId)).rejects.toThrow("No content found"); + await expect(getActivity(activityId)).rejects.toThrow( + PrismaClientKnownRequestError, + ); await expect(getActivityViewerData(activityId, userId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getActivityEditorData(activityId, userId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); - await expect(getDoc(docId)).rejects.toThrow("No documents found"); + await expect(getDoc(docId)).rejects.toThrow(PrismaClientKnownRequestError); await expect(getDocumentSource(docId, userId)).rejects.toThrow( - "No documents found", + PrismaClientKnownRequestError, ); }); @@ -1942,19 +1947,19 @@ test( loggedInUserId: userId, folderId: folder1Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await expect( getMyFolderContent({ loggedInUserId: userId, folderId: folder2Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await expect( getMyFolderContent({ loggedInUserId: userId, folderId: folder3Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); folder4Content = await getMyFolderContent({ loggedInUserId: userId, folderId: folder4Id, @@ -1971,15 +1976,21 @@ test( }); expect(folder6Content.content.length).eq(1); - await expect(getActivity(activity1Id)).rejects.toThrow("No content found"); - await expect(getActivity(activity2Id)).rejects.toThrow("No content found"); - await expect(getActivity(activity3Id)).rejects.toThrow("No content found"); + await expect(getActivity(activity1Id)).rejects.toThrow( + PrismaClientKnownRequestError, + ); + await expect(getActivity(activity2Id)).rejects.toThrow( + PrismaClientKnownRequestError, + ); + await expect(getActivity(activity3Id)).rejects.toThrow( + PrismaClientKnownRequestError, + ); await getActivity(activity4Id); await getActivity(activity5Id); await getActivity(activity6Id); - await expect(getDoc(doc1Id)).rejects.toThrow("No documents found"); - await expect(getDoc(doc2Id)).rejects.toThrow("No documents found"); - await expect(getDoc(doc3Id)).rejects.toThrow("No documents found"); + await expect(getDoc(doc1Id)).rejects.toThrow(PrismaClientKnownRequestError); + await expect(getDoc(doc2Id)).rejects.toThrow(PrismaClientKnownRequestError); + await expect(getDoc(doc3Id)).rejects.toThrow(PrismaClientKnownRequestError); await getDoc(doc4Id); await getDoc(doc5Id); await getDoc(doc6Id); @@ -2002,20 +2013,24 @@ test( loggedInUserId: userId, folderId: folder5Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await expect( getMyFolderContent({ loggedInUserId: userId, folderId: folder6Id, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await getActivity(activity4Id); - await expect(getActivity(activity5Id)).rejects.toThrow("No content found"); - await expect(getActivity(activity6Id)).rejects.toThrow("No content found"); + await expect(getActivity(activity5Id)).rejects.toThrow( + PrismaClientKnownRequestError, + ); + await expect(getActivity(activity6Id)).rejects.toThrow( + PrismaClientKnownRequestError, + ); await getDoc(doc4Id); - await expect(getDoc(doc5Id)).rejects.toThrow("No documents found"); - await expect(getDoc(doc6Id)).rejects.toThrow("No documents found"); + await expect(getDoc(doc5Id)).rejects.toThrow(PrismaClientKnownRequestError); + await expect(getDoc(doc6Id)).rejects.toThrow(PrismaClientKnownRequestError); }, ); @@ -2027,7 +2042,7 @@ test("non-owner cannot delete folder", async () => { const { folderId } = await createFolder(ownerId, null); await expect(deleteFolder(folderId, userId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); // folder is still around @@ -2430,7 +2445,7 @@ test("copyActivityToFolder copies a public document to a new owner", async () => // cannot copy if not yet public await expect( copyActivityToFolder(activityId, newOwnerId, null), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // Make the activity public before copying await makeActivityPublic({ @@ -2463,7 +2478,7 @@ test("copyActivityToFolder copies a shared document to a new owner", async () => // cannot copy if not yet shared await expect( copyActivityToFolder(activityId, newOwnerId, null), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // Make the activity public before copying await shareActivity({ @@ -2846,7 +2861,7 @@ test("searchSharedContent returns public/shared activities and folders matching expect(searchResults.length).eq(4); const namesInOrder = searchResults - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => c.name); expect(namesInOrder).eqls([ @@ -2962,7 +2977,7 @@ test("searchSharedContent returns public/shared activities and folders even in a expect(searchResults.length).eq(4); const namesInOrder = searchResults - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => c.name); expect(namesInOrder).eqls([ @@ -2990,15 +3005,15 @@ test("searchSharedContent, document source matches", async () => { // apple doesn't hit let results = await searchSharedContent("apple", userId); - expect(results.filter((r) => r.id.equals(activityId))).toHaveLength(0); + expect(results.filter((r) => isEqualUUID(r.id, activityId))).toHaveLength(0); // first part of a word hits results = await searchSharedContent(`b${code}ana`, userId); - expect(results.filter((r) => r.id.equals(activityId))).toHaveLength(1); + expect(results.filter((r) => isEqualUUID(r.id, activityId))).toHaveLength(1); // full word hits results = await searchSharedContent(`b${code}ananas`, userId); - expect(results.filter((r) => r.id.equals(activityId))).toHaveLength(1); + expect(results.filter((r) => isEqualUUID(r.id, activityId))).toHaveLength(1); }); test("searchSharedContent, owner name matches", async () => { @@ -3016,10 +3031,10 @@ test("searchSharedContent, owner name matches", async () => { }); let results = await searchSharedContent("Arya", userId); - expect(results.filter((r) => r.id.equals(activityId))).toHaveLength(1); + expect(results.filter((r) => isEqualUUID(r.id, activityId))).toHaveLength(1); results = await searchSharedContent("Arya Abbas", userId); - expect(results.filter((r) => r.id.equals(activityId))).toHaveLength(1); + expect(results.filter((r) => isEqualUUID(r.id, activityId))).toHaveLength(1); }); test("searchSharedContent, document source is more relevant than classification", async () => { @@ -3475,7 +3490,9 @@ test("deleteActivity prevents a document from being retrieved", async () => { const ownerId = owner.userId; const { activityId } = await createActivity(ownerId, null); await deleteActivity(activityId, ownerId); - await expect(getActivity(activityId)).rejects.toThrow("No content found"); + await expect(getActivity(activityId)).rejects.toThrow( + PrismaClientKnownRequestError, + ); }); test("updateContent does not update properties when passed undefined values", async () => { @@ -3566,7 +3583,7 @@ test("add and remove promoted content", async () => { const fakeGroupId = Math.random() * 1e8; await expect( addPromotedContent(fakeGroupId, activityId, userId), - ).rejects.toThrowError("Foreign key constraint failed"); + ).rejects.toThrowError("Foreign key constraint violated"); { const promotedContent = await loadPromotedContent(userId); const myContent = promotedContent.find( @@ -3884,7 +3901,7 @@ test("cannot assign other user's activity", async () => { }); await expect(assignActivity(activityId, ownerId2)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); // still cannot create assignment even if activity is made public @@ -3895,7 +3912,7 @@ test("cannot assign other user's activity", async () => { }); await expect(assignActivity(activityId, ownerId2)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); // still cannot create assignment even if activity is shared @@ -3907,7 +3924,7 @@ test("cannot assign other user's activity", async () => { }); await expect(assignActivity(activityId, ownerId2)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); }); @@ -3944,7 +3961,7 @@ test("open and close assignment with code", async () => { // close assignment completely unassigns since there is no data await closeAssignmentWithCode(activityId, ownerId); await expect(getAssignment(activityId, ownerId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); assignmentData = await getAssignmentDataFromCode(classCode); @@ -4039,7 +4056,7 @@ test("open and unassign assignment with code", async () => { // unassign activity await unassignActivity(activityId, ownerId); await expect(getAssignment(activityId, ownerId)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); // Getting deleted assignment by code fails @@ -4070,7 +4087,7 @@ test("only owner can open, close, modify, or unassign assignment", async () => { source: "Some content", ownerId: userId2, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await updateContent({ id: activityId, name: "Activity 1", ownerId }); await updateDoc({ @@ -4080,13 +4097,13 @@ test("only owner can open, close, modify, or unassign assignment", async () => { }); await expect(assignActivity(activityId, userId2)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await assignActivity(activityId, ownerId); await expect(getAssignment(activityId, userId2)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); let assignment = await getAssignment(activityId, ownerId); @@ -4104,7 +4121,7 @@ test("only owner can open, close, modify, or unassign assignment", async () => { await expect( openAssignmentWithCode(activityId, closeAt, userId2), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); const { codeValidUntil } = await openAssignmentWithCode( activityId, @@ -4467,7 +4484,7 @@ test("can't get assignment data if other user, but student can get their own dat activityId, ownerId: otherUserId, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // student cannot get score data on all of assignment await expect( @@ -4475,7 +4492,7 @@ test("can't get assignment data if other user, but student can get their own dat activityId, ownerId: newUser1.userId, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // assignment owner can get data on student const studentData = await getAssignmentStudentData({ @@ -4491,7 +4508,7 @@ test("can't get assignment data if other user, but student can get their own dat loggedInUserId: otherUserId, studentId: newUser1.userId, }), - ).rejects.toThrow("No assignmentScores found"); + ).rejects.toThrow(PrismaClientKnownRequestError); // student can get own data expect( @@ -4558,13 +4575,13 @@ test("get activity/document data only if owner or limited data for public/shared await getDocumentSource(docId, ownerId); await expect(getActivityEditorData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getActivityViewerData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getDocumentSource(docId, user1Id)).rejects.toThrow( - "No documents found", + PrismaClientKnownRequestError, ); await makeActivityPublic({ id: activityId, ownerId, licenseCode: "CCDUAL" }); @@ -4601,13 +4618,13 @@ test("get activity/document data only if owner or limited data for public/shared await makeActivityPrivate({ id: activityId, ownerId }); await expect(getActivityEditorData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getActivityViewerData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getDocumentSource(docId, user1Id)).rejects.toThrow( - "No documents found", + PrismaClientKnownRequestError, ); await shareActivity({ @@ -4640,13 +4657,13 @@ test("get activity/document data only if owner or limited data for public/shared await getDocumentSource(docId, user1Id); await expect(getActivityEditorData(activityId, user2Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getActivityViewerData(activityId, user2Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await expect(getDocumentSource(docId, user2Id)).rejects.toThrow( - "No documents found", + PrismaClientKnownRequestError, ); }); @@ -4664,7 +4681,7 @@ test("get public activity editor data only if public or shared", async () => { await updateDoc({ id: docId, source: doenetML, ownerId }); await expect(getSharedEditorData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await updateContent({ @@ -4688,7 +4705,7 @@ test("get public activity editor data only if public or shared", async () => { }); await expect(getSharedEditorData(activityId, user1Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); await shareActivity({ @@ -4703,7 +4720,7 @@ test("get public activity editor data only if public or shared", async () => { expect(sharedData.documents[0].source).eq(doenetML); await expect(getSharedEditorData(activityId, user2Id)).rejects.toThrow( - "No content found", + PrismaClientKnownRequestError, ); }); @@ -5136,7 +5153,7 @@ test("only user and assignment owner can load document state", async () => { userId: user2.userId, withMaxScore: false, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); }); test("load document state based on withMaxScore", async () => { @@ -5967,7 +5984,7 @@ test("only owner can get submitted responses", async () => { ownerId: userId2, answerId: answerId1, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); await expect( getDocumentSubmittedResponseHistory({ @@ -5978,7 +5995,7 @@ test("only owner can get submitted responses", async () => { answerId: answerId1, userId: newUser!.userId, }), - ).rejects.toThrow("No content found"); + ).rejects.toThrow(PrismaClientKnownRequestError); }); test("list assigned and get assigned scores get student assignments and scores", async () => { @@ -7120,7 +7137,7 @@ test( expect(content.length).eq(3); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, @@ -7141,7 +7158,7 @@ test( expect(content.length).eq(2); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, @@ -7161,7 +7178,7 @@ test( expect(content.length).eq(1); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, @@ -7189,7 +7206,7 @@ test( expect(content.length).eq(3); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, @@ -7210,7 +7227,7 @@ test( expect(content.length).eq(2); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, @@ -7230,7 +7247,7 @@ test( expect(content.length).eq(1); expect( content - .sort((a, b) => a.id.compare(b.id)) + .sort((a, b) => compareUUID(a.id, b.id)) .map((c) => ({ id: fromUUID(c.id), parentFolderId: c.parentFolder ? fromUUID(c.parentFolder.id) : null, diff --git a/server/src/model.ts b/server/src/model.ts index 1fb40983e..3a75a81a8 100644 --- a/server/src/model.ts +++ b/server/src/model.ts @@ -1,7 +1,7 @@ import { PrismaClient, Prisma } from "@prisma/client"; import { cidFromText } from "./utils/cid"; import { DateTime } from "luxon"; -import { fromUUID } from "./utils/uuid"; +import { fromUUID, isEqualUUID } from "./utils/uuid"; import { AssignmentStatus, ClassificationSystemTree, @@ -25,7 +25,7 @@ export class InvalidRequestError extends Error { export const prisma = new PrismaClient(); async function mustBeAdmin( - userId: Buffer, + userId: Uint8Array, message = "You must be an community admin to take this action", ) { const isAdmin = await getIsAdmin(userId); @@ -53,8 +53,8 @@ type ShiftIndicesCallbackFunction = ({ * @param folderId */ export async function createActivity( - ownerId: Buffer, - parentFolderId: Buffer | null, + ownerId: Uint8Array, + parentFolderId: Uint8Array | null, ) { const sortIndex = await getNextSortIndexForFolder(ownerId, parentFolderId); @@ -66,7 +66,7 @@ export async function createActivity( let isPublic = false; let licenseCode = undefined; - let sharedWith: Buffer[] = []; + let sharedWith: Uint8Array[] = []; // If parent folder isn't null, check if it is shared and get its license if (parentFolderId !== null) { @@ -131,14 +131,14 @@ export async function createActivity( } export async function createFolder( - ownerId: Buffer, - parentFolderId: Buffer | null, + ownerId: Uint8Array, + parentFolderId: Uint8Array | null, ) { const sortIndex = await getNextSortIndexForFolder(ownerId, parentFolderId); let isPublic = false; let licenseCode = undefined; - let sharedWith: Buffer[] = []; + let sharedWith: Uint8Array[] = []; // If parent folder isn't null, check if it is shared and get its license if (parentFolderId !== null) { @@ -194,8 +194,8 @@ export async function createFolder( * @param folderId */ async function getNextSortIndexForFolder( - ownerId: Buffer, - folderId: Buffer | null, + ownerId: Uint8Array, + folderId: Uint8Array | null, ) { if (folderId !== null) { // if a folderId is present, verify that it is a folder is owned by ownerId @@ -222,7 +222,7 @@ function getNextSortIndex(lastIndex: bigint | null) { : Math.ceil(Number(lastIndex) / SORT_INCREMENT + 1) * SORT_INCREMENT; } -export async function deleteActivity(id: Buffer, ownerId: Buffer) { +export async function deleteActivity(id: Uint8Array, ownerId: Uint8Array) { const deleted = await prisma.content.update({ where: { id, ownerId, isFolder: false }, data: { @@ -241,7 +241,7 @@ export async function deleteActivity(id: Buffer, ownerId: Buffer) { return { id: deleted.id, isDeleted: deleted.isDeleted }; } -export async function deleteFolder(id: Buffer, ownerId: Buffer) { +export async function deleteFolder(id: Uint8Array, ownerId: Uint8Array) { // Delete the folder `id` along with all the content inside it, // recursing to subfolders, and including the documents of activities. @@ -268,7 +268,7 @@ export async function deleteFolder(id: Buffer, ownerId: Buffer) { } // Note: currently (June 4, 2024) unused and untested -export async function deleteDocument(id: Buffer, ownerId: Buffer) { +export async function deleteDocument(id: Uint8Array, ownerId: Uint8Array) { await prisma.documents.update({ where: { id, activity: { ownerId } }, data: { isDeleted: true }, @@ -281,10 +281,10 @@ export async function updateContent({ imagePath, ownerId, }: { - id: Buffer; + id: Uint8Array; name?: string; imagePath?: string; - ownerId: Buffer; + ownerId: Uint8Array; }) { const updated = await prisma.content.update({ where: { id, ownerId, isDeleted: false }, @@ -308,11 +308,11 @@ export async function updateDoc({ doenetmlVersionId, ownerId, }: { - id: Buffer; + id: Uint8Array; source?: string; name?: string; doenetmlVersionId?: number; - ownerId: Buffer; + ownerId: Uint8Array; }) { // check if activity is assigned const isAssigned = ( @@ -351,7 +351,7 @@ export async function updateDoc({ // by relies on calling functions to determine access. // Also, the results of getActivity shouldn't be sent unchanged to the response, // as the sortIndex (bigint) cannot be serialized -export async function getActivity(id: Buffer) { +export async function getActivity(id: Uint8Array) { return await prisma.content.findUniqueOrThrow({ where: { id, isDeleted: false, isFolder: false }, include: { @@ -362,7 +362,7 @@ export async function getActivity(id: Buffer) { }); } -export async function getActivityName(id: Buffer) { +export async function getActivityName(id: Uint8Array) { return await prisma.content.findUniqueOrThrow({ where: { id, isDeleted: false, isFolder: false }, select: { @@ -374,7 +374,7 @@ export async function getActivityName(id: Buffer) { // Note: getDoc does not currently incorporate access control, // by relies on calling functions to determine access -export async function getDoc(id: Buffer) { +export async function getDoc(id: Uint8Array) { return await prisma.documents.findUniqueOrThrow({ where: { id, isDeleted: false }, }); @@ -393,10 +393,10 @@ export async function moveContent({ desiredPosition, ownerId, }: { - id: Buffer; - desiredParentFolderId: Buffer | null; + id: Uint8Array; + desiredParentFolderId: Uint8Array | null; desiredPosition: number; - ownerId: Buffer; + ownerId: Uint8Array; }) { if (!Number.isInteger(desiredPosition)) { throw Error("desiredPosition must be an integer"); @@ -414,7 +414,7 @@ export async function moveContent({ let desiredFolderIsPublic = false; let desiredFolderLicenseCode: LicenseCode = "CCDUAL"; - let desiredFolderShares: Buffer[] = []; + let desiredFolderShares: Uint8Array[] = []; if (desiredParentFolderId !== null) { // if desired parent folder is specified, make sure it exists and is owned by `ownerId` @@ -451,13 +451,13 @@ export async function moveContent({ // if content is a folder and moving it to another folder, // make sure that folder is not itself or a subfolder of itself - if (desiredParentFolderId.equals(content.id)) { + if (isEqualUUID(desiredParentFolderId, content.id)) { throw Error("Cannot move folder into itself"); } const subfolders = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; }[] >(Prisma.sql` WITH RECURSIVE folder_tree(id) AS ( @@ -474,7 +474,9 @@ export async function moveContent({ `); if ( - subfolders.findIndex((sf) => sf.id.equals(desiredParentFolderId)) !== -1 + subfolders.findIndex((sf) => + isEqualUUID(sf.id, desiredParentFolderId), + ) !== -1 ) { throw Error("Cannot move folder into a subfolder of itself"); } @@ -662,9 +664,9 @@ async function calculateNewSortIndex( * @param folderId */ export async function copyActivityToFolder( - origActivityId: Buffer, - userId: Buffer, - folderId: Buffer | null, + origActivityId: Uint8Array, + userId: Uint8Array, + folderId: Uint8Array | null, ) { const origActivity = await prisma.content.findUniqueOrThrow({ where: { @@ -796,9 +798,9 @@ export async function copyActivityToFolder( // Note: createDocumentVersion does not currently incorporate access control, // by relies on calling functions to determine access -async function createDocumentVersion(docId: Buffer): Promise<{ +async function createDocumentVersion(docId: Uint8Array): Promise<{ versionNum: number; - docId: Buffer; + docId: Uint8Array; cid: string | null; source: string | null; createdAt: Date | null; @@ -858,8 +860,8 @@ async function createDocumentVersion(docId: Buffer): Promise<{ * @param loggedInUserId */ export async function getActivityEditorData( - activityId: Buffer, - loggedInUserId: Buffer, + activityId: Uint8Array, + loggedInUserId: Uint8Array, ) { // TODO: is there a way to combine these queries and avoid any race condition? @@ -877,7 +879,7 @@ export async function getActivityEditorData( select: { isAssigned: true, ownerId: true }, }); - if (!contentCheck.ownerId.equals(loggedInUserId)) { + if (!isEqualUUID(contentCheck.ownerId, loggedInUserId)) { // activity is public or shared but not owned by the logged in user const activity: ContentStructure = { @@ -1151,8 +1153,8 @@ export async function getActivityEditorData( * @param activityId */ export async function getSharedEditorData( - activityId: Buffer, - loggedInUserId: Buffer, + activityId: Uint8Array, + loggedInUserId: Uint8Array, ) { // TODO: add pagination or a hard limit in the number of documents one can add to an activity @@ -1242,8 +1244,8 @@ export async function getSharedEditorData( // TODO: generalize this to multi-document activities export async function getActivityViewerData( - activityId: Buffer, - loggedInUserId: Buffer, + activityId: Uint8Array, + loggedInUserId: Uint8Array, ) { const preliminaryActivity = await prisma.content.findUniqueOrThrow({ where: { @@ -1346,7 +1348,10 @@ export async function getActivityViewerData( }; } -export async function getDocumentSource(docId: Buffer, loggedInUserId: Buffer) { +export async function getDocumentSource( + docId: Uint8Array, + loggedInUserId: Uint8Array, +) { const document = await prisma.documents.findUniqueOrThrow({ where: { id: docId, @@ -1369,8 +1374,8 @@ export async function getActivityContributorHistory({ activityId, loggedInUserId, }: { - activityId: Buffer; - loggedInUserId: Buffer; + activityId: Uint8Array; + loggedInUserId: Uint8Array; }) { const activity = await prisma.content.findUniqueOrThrow({ where: { @@ -1398,8 +1403,8 @@ export async function getDocumentContributorHistories({ docIds, loggedInUserId, }: { - docIds: Buffer[]; - loggedInUserId: Buffer; + docIds: Uint8Array[]; + loggedInUserId: Uint8Array; }) { const docHistories: DocHistory[] = await prisma.documents.findMany({ where: { @@ -1468,8 +1473,8 @@ export async function getActivityRemixes({ activityId, loggedInUserId, }: { - activityId: Buffer; - loggedInUserId: Buffer; + activityId: Uint8Array; + loggedInUserId: Uint8Array; }) { const activity = await prisma.content.findUniqueOrThrow({ where: { @@ -1502,8 +1507,8 @@ export async function getDocumentDirectRemixes({ docIds, loggedInUserId, }: { - docIds: Buffer[]; - loggedInUserId: Buffer; + docIds: Uint8Array[]; + loggedInUserId: Uint8Array; }) { const docRemixes = await prisma.documents.findMany({ where: { @@ -1571,8 +1576,8 @@ export async function getDocumentRemixes({ docIds, loggedInUserId, }: { - docIds: Buffer[]; - loggedInUserId: Buffer; + docIds: Uint8Array[]; + loggedInUserId: Uint8Array; }) { const docRemixes = await prisma.documents.findMany({ where: { @@ -1685,7 +1690,7 @@ export async function getAssignmentDataFromCode(code: string) { export async function searchSharedContent( query: string, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { // remove operators that break MySQL BOOLEAN search // and add * at the end of every word so that match beginning of words @@ -1698,7 +1703,7 @@ export async function searchSharedContent( const matches = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; relevance: number; }[] >(Prisma.sql` @@ -1834,7 +1839,7 @@ export async function searchSharedContent( export async function searchUsersWithSharedContent( query: string, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { // remove operators that break MySQL BOOLEAN search // and add * at the end of every word so that match beginning of words @@ -1847,7 +1852,7 @@ export async function searchUsersWithSharedContent( const usersWithPublic = await prisma.$queryRaw< { - userId: Buffer; + userId: Uint8Array; firstNames: string | null; lastNames: string; }[] @@ -1881,7 +1886,7 @@ export async function searchUsersWithSharedContent( return usersWithPublic2; } -export async function listUserAssigned(userId: Buffer) { +export async function listUserAssigned(userId: Uint8Array) { const preliminaryAssignments = await prisma.content.findMany({ where: { isDeleted: false, @@ -1968,7 +1973,7 @@ export async function findOrCreateUser({ return user; } -export async function getUserInfo(userId: Buffer) { +export async function getUserInfo(userId: Uint8Array) { const user = await prisma.users.findUniqueOrThrow({ where: { userId }, select: { @@ -2002,7 +2007,7 @@ export async function upgradeAnonymousUser({ userId, email, }: { - userId: Buffer; + userId: Uint8Array; email: string; }) { const user = await prisma.users.update({ @@ -2018,7 +2023,7 @@ export async function updateUser({ firstNames, lastNames, }: { - userId: Buffer; + userId: Uint8Array; firstNames: string; lastNames: string; }) { @@ -2041,7 +2046,7 @@ export async function getAllDoenetmlVersions() { return allDoenetmlVersions; } -export async function getIsAdmin(userId: Buffer) { +export async function getIsAdmin(userId: Uint8Array) { const user = await prisma.users.findUnique({ where: { userId } }); let isAdmin = false; if (user) { @@ -2072,7 +2077,7 @@ export async function getAllRecentPublicActivities() { export async function addPromotedContentGroup( groupName: string, - userId: Buffer, + userId: Uint8Array, ) { await mustBeAdmin( userId, @@ -2101,7 +2106,7 @@ export async function updatePromotedContentGroup( newGroupName: string, homepage: boolean, currentlyFeatured: boolean, - userId: Buffer, + userId: Uint8Array, ) { await mustBeAdmin( userId, @@ -2122,7 +2127,7 @@ export async function updatePromotedContentGroup( export async function deletePromotedContentGroup( groupId: number, - userId: Buffer, + userId: Uint8Array, ) { await mustBeAdmin( userId, @@ -2150,7 +2155,7 @@ export async function deletePromotedContentGroup( */ export async function movePromotedContentGroup( groupId: number, - userId: Buffer, + userId: Uint8Array, desiredPosition: number, ) { await mustBeAdmin( @@ -2212,7 +2217,7 @@ export async function movePromotedContentGroup( }); } -export async function loadPromotedContent(userId: Buffer) { +export async function loadPromotedContent(userId: Uint8Array) { const isAdmin = userId ? await getIsAdmin(userId) : false; const content = await prisma.promotedContentGroups.findMany({ where: { @@ -2276,8 +2281,8 @@ export async function loadPromotedContent(userId: Buffer) { export async function addPromotedContent( groupId: number, - activityId: Buffer, - userId: Buffer, + activityId: Uint8Array, + userId: Uint8Array, ) { await mustBeAdmin( userId, @@ -2320,8 +2325,8 @@ export async function addPromotedContent( export async function removePromotedContent( groupId: number, - activityId: Buffer, - userId: Buffer, + activityId: Uint8Array, + userId: Uint8Array, ) { await mustBeAdmin( userId, @@ -2363,8 +2368,8 @@ export async function removePromotedContent( */ export async function movePromotedContent( groupId: number, - activityId: Buffer, - userId: Buffer, + activityId: Uint8Array, + userId: Uint8Array, desiredPosition: number, ) { await mustBeAdmin( @@ -2445,7 +2450,10 @@ export async function movePromotedContent( }); } -export async function assignActivity(activityId: Buffer, userId: Buffer) { +export async function assignActivity( + activityId: Uint8Array, + userId: Uint8Array, +) { const origActivity = await prisma.content.findUniqueOrThrow({ where: { id: activityId, @@ -2482,9 +2490,9 @@ function generateClassCode() { } export async function openAssignmentWithCode( - activityId: Buffer, + activityId: Uint8Array, closeAt: DateTime, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { const initialActivity = await prisma.content.findUniqueOrThrow({ where: { id: activityId, ownerId: loggedInUserId, isFolder: false }, @@ -2514,9 +2522,9 @@ export async function openAssignmentWithCode( } export async function updateAssignmentSettings( - activityId: Buffer, + activityId: Uint8Array, closeAt: DateTime, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { const codeValidUntil = closeAt.toJSDate(); @@ -2536,8 +2544,8 @@ export async function updateAssignmentSettings( } export async function closeAssignmentWithCode( - activityId: Buffer, - userId: Buffer, + activityId: Uint8Array, + userId: Uint8Array, ) { await prisma.content.update({ where: { @@ -2568,7 +2576,10 @@ export async function closeAssignmentWithCode( } } -export async function unassignActivity(activityId: Buffer, userId: Buffer) { +export async function unassignActivity( + activityId: Uint8Array, + userId: Uint8Array, +) { await prisma.content.update({ where: { id: activityId, @@ -2591,7 +2602,10 @@ export async function unassignActivity(activityId: Buffer, userId: Buffer) { // Note: this function returns `sortIndex` (which is a bigint) // so the data shouldn't be sent unchanged to the response -export async function getAssignment(activityId: Buffer, ownerId: Buffer) { +export async function getAssignment( + activityId: Uint8Array, + ownerId: Uint8Array, +) { const assignment = await prisma.content.findUniqueOrThrow({ where: { id: activityId, @@ -2622,10 +2636,10 @@ export async function saveScoreAndState({ onSubmission, state, }: { - activityId: Buffer; - docId: Buffer; + activityId: Uint8Array; + docId: Uint8Array; docVersionNum: number; - userId: Buffer; + userId: Uint8Array; score: number; onSubmission: boolean; state: string; @@ -2795,14 +2809,14 @@ export async function loadState({ userId, withMaxScore, }: { - activityId: Buffer; - docId: Buffer; + activityId: Uint8Array; + docId: Uint8Array; docVersionNum: number; - requestedUserId: Buffer; - userId: Buffer; + requestedUserId: Uint8Array; + userId: Uint8Array; withMaxScore: boolean; }) { - if (!requestedUserId.equals(userId)) { + if (!isEqualUUID(requestedUserId, userId)) { // If user isn't the requested user, then user is allowed to load requested users state // only if they are the owner of the assignment. // If not user is not owner, then it will throw an error. @@ -2852,8 +2866,8 @@ export async function getAssignmentScoreData({ activityId, ownerId, }: { - activityId: Buffer; - ownerId: Buffer; + activityId: Uint8Array; + ownerId: Uint8Array; }) { const assignment: { name: string; @@ -2899,16 +2913,18 @@ export async function getAssignmentStudentData({ loggedInUserId, studentId, }: { - activityId: Buffer; - loggedInUserId: Buffer; - studentId: Buffer; + activityId: Uint8Array; + loggedInUserId: Uint8Array; + studentId: Uint8Array; }) { const assignmentData = await prisma.assignmentScores.findUniqueOrThrow({ where: { activityId_userId: { activityId, userId: studentId }, activity: { // allow access if logged in user is the student or the owner - ownerId: studentId.equals(loggedInUserId) ? undefined : loggedInUserId, + ownerId: isEqualUUID(studentId, loggedInUserId) + ? undefined + : loggedInUserId, isDeleted: false, isFolder: false, isAssigned: true, @@ -2976,13 +2992,13 @@ export async function getAllAssignmentScores({ ownerId, parentFolderId, }: { - ownerId: Buffer; - parentFolderId: Buffer | null; + ownerId: Uint8Array; + parentFolderId: Uint8Array | null; }) { let orderedActivities; let folder: { - id: Buffer; + id: Uint8Array; name: string; } | null = null; @@ -2996,7 +3012,7 @@ export async function getAllAssignmentScores({ if (parentFolderId === null) { orderedActivities = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; name: string; }[] >(Prisma.sql` @@ -3020,7 +3036,7 @@ export async function getAllAssignmentScores({ } else { orderedActivities = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; name: string; }[] >(Prisma.sql` @@ -3086,9 +3102,9 @@ export async function getStudentData({ ownerId, parentFolderId, }: { - userId: Buffer; - ownerId: Buffer; - parentFolderId: Buffer | null; + userId: Uint8Array; + ownerId: Uint8Array; + parentFolderId: Uint8Array | null; }) { const userData = await prisma.users.findUniqueOrThrow({ where: { @@ -3105,7 +3121,7 @@ export async function getStudentData({ let orderedActivityScores; let folder: { - id: Buffer; + id: Uint8Array; name: string; } | null = null; @@ -3119,7 +3135,7 @@ export async function getStudentData({ if (parentFolderId === null) { orderedActivityScores = await prisma.$queryRaw< { - activityId: Buffer; + activityId: Uint8Array; activityName: string; score: number | null; }[] @@ -3148,7 +3164,7 @@ export async function getStudentData({ } else { orderedActivityScores = await prisma.$queryRaw< { - activityId: Buffer; + activityId: Uint8Array; activityName: string; score: number | null; }[] @@ -3184,7 +3200,7 @@ export async function getStudentData({ return { userData, orderedActivityScores, folder }; } -export async function getAssignedScores(loggedInUserId: Buffer) { +export async function getAssignedScores(loggedInUserId: Uint8Array) { const scores = await prisma.assignmentScores.findMany({ where: { userId: loggedInUserId, @@ -3215,8 +3231,8 @@ export async function getAssignmentContent({ activityId, ownerId, }: { - activityId: Buffer; - ownerId: Buffer; + activityId: Uint8Array; + ownerId: Uint8Array; }) { const assignmentData = await prisma.documents.findMany({ where: { @@ -3258,10 +3274,10 @@ export async function recordSubmittedEvent({ itemCreditAchieved, documentCreditAchieved, }: { - activityId: Buffer; - docId: Buffer; + activityId: Uint8Array; + docId: Uint8Array; docVersionNum: number; - userId: Buffer; + userId: Uint8Array; answerId: string; response: string; answerNumber?: number; @@ -3291,15 +3307,15 @@ export async function getAnswersThatHaveSubmittedResponses({ activityId, ownerId, }: { - activityId: Buffer; - ownerId: Buffer; + activityId: Uint8Array; + ownerId: Uint8Array; }) { // Using raw query as it seems prisma does not support distinct in count. // https://github.com/prisma/prisma/issues/4228 let submittedResponses = await prisma.$queryRaw< { - docId: Buffer; + docId: Uint8Array; docVersionNum: number; answerId: string; answerNumber: number | null; @@ -3338,10 +3354,10 @@ export async function getDocumentSubmittedResponses({ ownerId, answerId, }: { - activityId: Buffer; - docId: Buffer; + activityId: Uint8Array; + docId: Uint8Array; docVersionNum: number; - ownerId: Buffer; + ownerId: Uint8Array; answerId: string; }) { // get activity name and make sure that owner is the owner @@ -3362,7 +3378,7 @@ export async function getDocumentSubmittedResponses({ // Can we come up with a better solution? const rawResponses = await prisma.$queryRaw< { - userId: Buffer; + userId: Uint8Array; firstNames: string | null; lastNames: string; email: string; @@ -3386,7 +3402,7 @@ select dsr.userId, users.firstNames, users.lastNames, users.email, response, cre const submittedResponses = []; let newResponse; - let lastUserId = Buffer.alloc(16); + let lastUserId = new Uint8Array(16); for (const respObj of rawResponses) { if (respObj.userId > lastUserId) { @@ -3431,12 +3447,12 @@ export async function getDocumentSubmittedResponseHistory({ answerId, userId, }: { - activityId: Buffer; - docId: Buffer; + activityId: Uint8Array; + docId: Uint8Array; docVersionNum: number; - ownerId: Buffer; + ownerId: Uint8Array; answerId: string; - userId: Buffer; + userId: Uint8Array; }) { // get activity name and make sure that owner is the owner const activityName = ( @@ -3493,8 +3509,8 @@ export async function getMyFolderContent({ folderId, loggedInUserId, }: { - folderId: Buffer | null; - loggedInUserId: Buffer; + folderId: Uint8Array | null; + loggedInUserId: Uint8Array; }) { let folder: ContentStructure | null = null; @@ -3701,8 +3717,8 @@ export async function searchMyFolderContent({ loggedInUserId, query, }: { - folderId: Buffer | null; - loggedInUserId: Buffer; + folderId: Uint8Array | null; + loggedInUserId: Uint8Array; query: string; }) { let folder: ContentStructure | null = null; @@ -3789,7 +3805,7 @@ export async function searchMyFolderContent({ } let matches: { - id: Buffer; + id: Uint8Array; relevance: number; }[]; @@ -3805,7 +3821,7 @@ export async function searchMyFolderContent({ if (folderId === null) { matches = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; relevance: number; }[] >(Prisma.sql` @@ -3843,7 +3859,7 @@ export async function searchMyFolderContent({ } else { matches = await prisma.$queryRaw< { - id: Buffer; + id: Uint8Array; relevance: number; }[] >( @@ -4018,9 +4034,9 @@ export async function getSharedFolderContent({ folderId, loggedInUserId, }: { - ownerId: Buffer; - folderId: Buffer | null; - loggedInUserId: Buffer; + ownerId: Uint8Array; + folderId: Uint8Array | null; + loggedInUserId: Uint8Array; }) { let folder: ContentStructure | null = null; @@ -4077,7 +4093,7 @@ export async function getSharedFolderContent({ preliminaryFolder.parentFolder && (preliminaryFolder.parentFolder.isPublic || preliminaryFolder.parentFolder.sharedWith.findIndex((cs) => - cs.userId.equals(loggedInUserId), + isEqualUUID(cs.userId, loggedInUserId), ) !== -1) ) ) { @@ -4369,9 +4385,9 @@ export async function searchPossibleClassifications(query: string) { * @param loggedInUserId */ export async function addClassification( - activityId: Buffer, + activityId: Uint8Array, classificationId: number, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { const activity = await prisma.content.findUnique({ where: { @@ -4406,9 +4422,9 @@ export async function addClassification( * @param loggedInUserId */ export async function removeClassification( - activityId: Buffer, + activityId: Uint8Array, classificationId: number, - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, ) { const activity = await prisma.content.findUnique({ where: { @@ -4441,8 +4457,8 @@ export async function removeClassification( * @param loggedInUserId */ export async function getClassifications( - activityId: Buffer, - loggedInUserId: Buffer, + activityId: Uint8Array, + loggedInUserId: Uint8Array, ) { const activity = await prisma.content.findUnique({ where: { @@ -4571,19 +4587,20 @@ function processSharedWith( * @returns */ function processSharedWithForUser( - sharedWithIds: { userId: Buffer }[] | null | undefined, - determineSharedToUser: Buffer, + sharedWithIds: { userId: Uint8Array }[] | null | undefined, + determineSharedToUser: Uint8Array, ): { isShared: boolean; sharedWith: UserInfo[] } { if (sharedWithIds === null || sharedWithIds === undefined) { return { isShared: false, sharedWith: [] }; } const isShared = - sharedWithIds.findIndex((cs) => cs.userId.equals(determineSharedToUser)) !== - -1; + sharedWithIds.findIndex((cs) => + isEqualUUID(cs.userId, determineSharedToUser), + ) !== -1; const sharedWith: { - userId: Buffer; + userId: Uint8Array; email: string; firstNames: string | null; lastNames: string; @@ -4597,7 +4614,7 @@ function processSharedWithForUser( */ function processParentFolder( parentFolder: { - id: Buffer; + id: Uint8Array; name: string; isPublic: boolean; sharedWith?: { @@ -4626,12 +4643,12 @@ function processParentFolder( */ function processParentFolderForUser( parentFolder: { - id: Buffer; + id: Uint8Array; name: string; isPublic: boolean; - sharedWith: { userId: Buffer }[]; + sharedWith: { userId: Uint8Array }[]; } | null, - determineSharedToUser: Buffer, + determineSharedToUser: Uint8Array, ) { if (parentFolder === null) { return null; @@ -4711,8 +4728,8 @@ export async function setActivityLicense({ ownerId, licenseCode, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; }) { await prisma.content.update({ @@ -4726,8 +4743,8 @@ export async function makeActivityPublic({ ownerId, licenseCode, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; }) { await prisma.content.update({ @@ -4740,8 +4757,8 @@ export async function makeActivityPrivate({ id, ownerId, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; }) { await prisma.content.update({ where: { id, isDeleted: false, ownerId, isFolder: false }, @@ -4755,10 +4772,10 @@ export async function shareActivity({ licenseCode, users, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; - users: Buffer[]; + users: Uint8Array[]; }) { await prisma.content.update({ where: { id, isDeleted: false, ownerId: ownerId, isFolder: false }, @@ -4780,8 +4797,8 @@ export async function shareActivityWithEmail({ licenseCode, email, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; email: string; }) { @@ -4805,7 +4822,7 @@ export async function shareActivityWithEmail({ } } - if (userId.equals(ownerId)) { + if (isEqualUUID(userId, ownerId)) { // cannot share with self throw Error("Cannot share with self"); } @@ -4818,9 +4835,9 @@ export async function unshareActivity({ ownerId, users, }: { - id: Buffer; - ownerId: Buffer; - users: Buffer[]; + id: Uint8Array; + ownerId: Uint8Array; + users: Uint8Array[]; }) { await prisma.contentShares.deleteMany({ where: { @@ -4835,8 +4852,8 @@ export async function setFolderLicense({ ownerId, licenseCode, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; }) { // Set license for the folder `id` along with all the content inside it, @@ -4870,8 +4887,8 @@ export async function makeFolderPublic({ ownerId, licenseCode, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; }) { // Make the folder `id` public along with all the content inside it, @@ -4904,8 +4921,8 @@ export async function makeFolderPrivate({ id, ownerId, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; }) { // Make the folder `id` private along with all the content inside it, // recursing to subfolders. @@ -4939,10 +4956,10 @@ export async function shareFolder({ licenseCode, users, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; - users: Buffer[]; + users: Uint8Array[]; }) { // Share the folder `id` to users along with all the content inside it, // recursing to subfolders. @@ -4954,7 +4971,7 @@ export async function shareFolder({ }); const contentIds = ( - await prisma.$queryRaw<{ id: Buffer }[]>(Prisma.sql` + await prisma.$queryRaw<{ id: Uint8Array }[]>(Prisma.sql` WITH RECURSIVE content_tree(id) AS ( SELECT id FROM content WHERE id = ${id} AND ownerId = ${ownerId} AND isDeleted = FALSE @@ -4992,8 +5009,8 @@ export async function shareFolderWithEmail({ licenseCode, email, }: { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; licenseCode: LicenseCode; email: string; }) { @@ -5017,7 +5034,7 @@ export async function shareFolderWithEmail({ } } - if (userId.equals(ownerId)) { + if (isEqualUUID(userId, ownerId)) { // cannot share with self throw Error("Cannot share with self"); } @@ -5030,9 +5047,9 @@ export async function unshareFolder({ ownerId, users, }: { - id: Buffer; - ownerId: Buffer; - users: Buffer[]; + id: Uint8Array; + ownerId: Uint8Array; + users: Uint8Array[]; }) { // Stop the folder `id` along with all the content inside it, // recursing to subfolders. @@ -5044,7 +5061,7 @@ export async function unshareFolder({ }); const contentIds = ( - await prisma.$queryRaw<{ id: Buffer }[]>(Prisma.sql` + await prisma.$queryRaw<{ id: Uint8Array }[]>(Prisma.sql` WITH RECURSIVE content_tree(id) AS ( SELECT id FROM content WHERE id = ${id} AND ownerId = ${ownerId} AND isDeleted = FALSE @@ -5071,7 +5088,7 @@ export async function unshareFolder({ } export async function setPreferredFolderView( - loggedInUserId: Buffer, + loggedInUserId: Uint8Array, cardView: boolean, ) { return await prisma.users.update({ @@ -5081,7 +5098,7 @@ export async function setPreferredFolderView( }); } -export async function getPreferredFolderView(loggedInUserId: Buffer) { +export async function getPreferredFolderView(loggedInUserId: Uint8Array) { return await prisma.users.findUniqueOrThrow({ where: { userId: loggedInUserId }, select: { cardView: true }, diff --git a/server/src/types.ts b/server/src/types.ts index 41df76bca..e90af8be6 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -11,7 +11,7 @@ export type DoenetmlVersion = { export type AssignmentStatus = "Unassigned" | "Closed" | "Open"; export type UserInfo = { - userId: Buffer; + userId: Uint8Array; firstNames: string | null; lastNames: string; email: string; @@ -38,8 +38,8 @@ export type ContentClassification = { }; export type ContentStructure = { - id: Buffer; - ownerId: Buffer; + id: Uint8Array; + ownerId: Uint8Array; owner?: UserInfo; name: string; imagePath: string | null; @@ -53,7 +53,7 @@ export type ContentStructure = { license: License | null; classifications: ContentClassification[]; documents: { - id: Buffer; + id: Uint8Array; versionNum?: number; name?: string; source?: string; @@ -61,7 +61,7 @@ export type ContentStructure = { }[]; hasScoreData: boolean; parentFolder: { - id: Buffer; + id: Uint8Array; name: string; isPublic: boolean; isShared: boolean; @@ -90,10 +90,10 @@ export type License = { }; export type DocHistory = { - id: Buffer; + id: Uint8Array; contributorHistory: { - docId: Buffer; - prevDocId: Buffer; + docId: Uint8Array; + prevDocId: Uint8Array; prevDocVersionNum: number; withLicenseCode: string | null; timestampDoc: Date; @@ -103,7 +103,7 @@ export type DocHistory = { source: string; activity: { name: string; - id: Buffer; + id: Uint8Array; owner: UserInfo; }; }; diff --git a/server/src/utils/binary-uuid.ts b/server/src/utils/binary-uuid.ts index c019fcc6f..78012d19a 100644 --- a/server/src/utils/binary-uuid.ts +++ b/server/src/utils/binary-uuid.ts @@ -23,7 +23,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export function fromBinaryUUID(buf: Buffer): string { +export function fromBinaryUUID(uint8: Uint8Array): string { + const buf = Buffer.from(uint8); return [ buf.toString("hex", 4, 8), buf.toString("hex", 2, 4), @@ -33,7 +34,7 @@ export function fromBinaryUUID(buf: Buffer): string { ].join("-"); } -export function toBinaryUUID(uuid: string): Buffer { +export function toBinaryUUID(uuid: string): Uint8Array { const buf = Buffer.from(uuid.replace(/-/g, ""), "hex"); return Buffer.concat([ buf.subarray(6, 8), diff --git a/server/src/utils/uuid.ts b/server/src/utils/uuid.ts index a1b463fb8..7aba58130 100644 --- a/server/src/utils/uuid.ts +++ b/server/src/utils/uuid.ts @@ -8,7 +8,7 @@ export function toUUID(id: string) { return toBinaryUUID(translator.toUUID(id)); } -export function fromUUID(UUID: Buffer) { +export function fromUUID(UUID: Uint8Array) { return translator.fromUUID(fromBinaryUUID(UUID)); } @@ -16,6 +16,22 @@ export function newUUID() { return toBinaryUUID(translator.new()); } +export function isEqualUUID(UUID1: Uint8Array, UUID2: Uint8Array) { + if (UUID1.length !== UUID2.length) { + return false; + } + for (let i = 0; i < UUID1.length; i++) { + if (UUID1[i] !== UUID2[i]) { + return false; + } + } + return true; +} + +export function compareUUID(UUID1: Uint8Array, UUID2: Uint8Array) { + return Buffer.from(UUID1).compare(UUID2); +} + export function contentStructureConvertUUID(content: ContentStructure) { const parentFolder = content.parentFolder ? { @@ -75,9 +91,9 @@ export function docHistoryConvertUUID(docHistory: DocHistory) { } export function assignmentConvertUUID(assignment: { - id: Buffer; + id: Uint8Array; documents: { - id: Buffer; + id: Uint8Array; assignedVersionNum: number | null; assignedVersion: { source: string; @@ -104,7 +120,7 @@ export function assignmentStudentDataConvertUUID({ }: { score: number; documentScores: { - docId: Buffer; + docId: Uint8Array; score: number; docVersionNum: number; hasMaxScore: boolean; @@ -115,11 +131,11 @@ export function assignmentStudentDataConvertUUID({ assignedVersion: { source: string; doenetmlVersion: { fullVersion: string }; - docId: Buffer; + docId: Uint8Array; versionNum: number; } | null; }[]; - id: Buffer; + id: Uint8Array; name: string; }; }) { @@ -151,16 +167,16 @@ export function allAssignmentScoresConvertUUID({ folder, }: { orderedActivities: { - id: Buffer; + id: Uint8Array; name: string; }[]; assignmentScores: { score: number; user: UserInfo; - activityId: Buffer; + activityId: Uint8Array; }[]; folder: { - id: Buffer; + id: Uint8Array; name: string; } | null; }) { @@ -185,12 +201,12 @@ export function studentDataConvertUUID({ }: { userData: UserInfo; orderedActivityScores: { - activityId: Buffer; + activityId: Uint8Array; activityName: string; score: number | null; }[]; folder: { - id: Buffer; + id: Uint8Array; name: string; } | null; }) {