From eb91832d3ec92b8d56aafb42f6a442db3c96f010 Mon Sep 17 00:00:00 2001 From: Harish V Date: Mon, 23 Dec 2024 11:53:37 +0800 Subject: [PATCH 1/5] tosp migration scripts --- .../scripts/moh-tosp/backupCollectionById.ts | 74 +++++++++++ .../moh-tosp/createCollectionFromLocal.ts | 125 ++++++++++++++++++ .../scripts/moh-tosp/deleteCollectionById.ts | 101 ++++++++++++++ .../moh-tosp/publishDraftCollection.ts | 80 +++++++++++ 4 files changed, 380 insertions(+) create mode 100644 apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts create mode 100644 apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts create mode 100644 apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts create mode 100644 apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts diff --git a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts new file mode 100644 index 0000000000..ccfa6db1fe --- /dev/null +++ b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts @@ -0,0 +1,74 @@ +import fs from "fs/promises" // Use the promise-based version of fs for async/await +import path from "path" + +import { db } from "~/server/modules/database" + +/** + * Backup a collection and its relevant resources to JSON files. + * @param {string} resourceId - ID of the collection resource to back up. + * @param {string} backupDir - Directory to save the backup files. + */ +export async function backupCollection( + resourceId: string, + backupDir: string, +): Promise { + try { + // Ensure the backup directory exists + await fs.mkdir(backupDir, { recursive: true }) + + // Fetch the collection resource + const collection = await db + .selectFrom("Resource") + .selectAll() + .where("id", "=", resourceId) + .executeTakeFirst() + + if (!collection) { + throw new Error(`Collection with ID ${resourceId} not found.`) + } + + // Fetch all child resources + const children = await db + .selectFrom("Resource") + .selectAll() + .where("parentId", "=", resourceId) + .execute() + + // Write all the children's published version to the backup directory as JSON files + for (const child of children) { + // fetch the blob + const blob = await db + .selectFrom("Blob") + .select("content") + .innerJoin("Version", "Blob.id", "Version.blobId") + .where("Version.id", "=", child.publishedVersionId) + .executeTakeFirst() + + if (!blob) { + throw new Error( + `Published version of child with ID ${child.id} not found.`, + ) + } + + console.log(`Writing backup for child with ID ${child.id}`) + + // Parse blob content and write to a file + const blobBuffer = blob.content // Assuming blob.content is a buffer + const blobJsonPath = path.join(backupDir, `${child.title}.json`) + await fs.writeFile(blobJsonPath, JSON.stringify(blobBuffer, null, 2)) + } + + console.log(`Backup completed successfully in directory: ${backupDir}`) + } catch (error: any) { + console.error(`Error backing up collection: ${error.message}`) + } +} + +// Run the backup +// NOTE: TODO: Put in the collection ID to backup +const collectionId = "0" +const backupDirectory = "" + +await backupCollection(collectionId, backupDirectory).catch((err) => { + console.error("Unhandled error:", err.message) +}) diff --git a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts new file mode 100644 index 0000000000..e81c0b3f5a --- /dev/null +++ b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts @@ -0,0 +1,125 @@ +import fs from "fs/promises" +import path from "path" + +import { db, jsonb } from "~/server/modules/database" + +export const createCollectionFromLocal = async ( + contentDir: string, + siteId: number, +) => { + console.log(`Reading from ${contentDir}`) + const jsonFilePath = path.join(contentDir, "cost-financing.json") + const folderPath = path.join(contentDir, "cost-financing-original") + + try { + await db.transaction().execute(async (tx) => { + // Step 1: Create a new collection with title "cost-financing-new" + const collection = await tx + .insertInto("Resource") + .values({ + title: "cost-financing-new", + permalink: "cost-financing-new", + siteId: siteId, + type: "Collection", + state: "Draft", + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + const collectionId = collection.id + console.log(`Collection created with ID: ${collectionId}`) + + // Step 2: Insert "cost-financing.json" as an IndexPage with permalink "_index"\ + const jsonFileContent = await fs.readFile(jsonFilePath, "utf-8") + const indexPageBlob = await tx + .insertInto("Blob") + .values({ + content: jsonb(JSON.parse(jsonFileContent)), + }) + .returning("id") + .executeTakeFirstOrThrow() + + const indexPage = await tx + .insertInto("Resource") + .values({ + title: "cost-financing-new", + permalink: "_index", + siteId: siteId, + type: "IndexPage", + parentId: collectionId, + draftBlobId: indexPageBlob.id, + state: "Draft", + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + const indexPageId = indexPage.id + + console.log(`Index page created with ID: ${indexPageId}`) + + // Step 3: Insert files from "cost-financing/" into the DB as Blobs + const folderFiles = await fs.readdir(folderPath) + console.log(`Reading from folderPath: ${folderPath}`) + console.log(`Folder files`, folderFiles) + for (const file of folderFiles) { + const filePath = path.join(folderPath, file) + console.log(`Reading file path: ${filePath}`) + + console.log(`Filename: ${file}`) + //Sometimes might have hidden internal files like .DSStore + if (!file.endsWith(".json")) { + continue + } + console.log("File path: ", filePath) + const fileContent = await fs.readFile(filePath, "utf-8") + + const parsedFileContent = JSON.parse(fileContent) + + const blob = await tx + .insertInto("Blob") + .values({ + content: parsedFileContent, + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + const resource = await tx + .insertInto("Resource") + .values({ + title: parsedFileContent.page.title, + permalink: file, + siteId: siteId, // Replace with appropriate site ID + type: "CollectionPage", + parentId: collectionId, + state: "Draft", + draftBlobId: blob.id, + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + const resourceId = resource.id + + console.log( + `Blob created for file ${file} with resource ID: ${resourceId}`, + ) + } + }) + + console.log("All operations completed successfully.") + } catch (error) { + console.error("Error during transaction:", error) + } +} + +// NOTE: TODO: Update the content directory and siteId here before usage! +const contentDir = "" +const siteId = 0 +await createCollectionFromLocal(contentDir, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts new file mode 100644 index 0000000000..53bb2b7068 --- /dev/null +++ b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts @@ -0,0 +1,101 @@ +import { db } from "~/server/modules/database" + +export const deleteCollectionById = async ( + collectionId: string, + siteId: number, +) => { + try { + await db.transaction().execute(async (tx) => { + // Step 1: Find all child resources of the collection + const childResources = await tx + .selectFrom("Resource") + .select(["id", "state", "draftBlobId", "publishedVersionId"]) + .where("parentId", "=", collectionId) + .where("siteId", "=", siteId) + .execute() + + // Step 2: Handle each child resource + for (const resource of childResources) { + // Delete published version and its blob, if applicable + if (resource.publishedVersionId) { + const publishedVersion = await tx + .selectFrom("Version") + .select(["blobId"]) + .where("id", "=", resource.publishedVersionId) + .executeTakeFirst() + + const blobIdToDelete = publishedVersion?.blobId + + await tx + .deleteFrom("Version") + .where("id", "=", resource.publishedVersionId) + .execute() + + if (blobIdToDelete) { + await tx + .deleteFrom("Blob") + .where("id", "=", blobIdToDelete) + .execute() + } + } + + // Delete draft blob, if applicable + if (resource.draftBlobId) { + await tx + .deleteFrom("Blob") + .where("id", "=", resource.draftBlobId) + .execute() + } + + // Delete the resource itself + await tx.deleteFrom("Resource").where("id", "=", resource.id).execute() + + console.log(`Resource with ID ${resource.id} deleted successfully.`) + } + + // Step 3: Delete the collection itself + const collection = await tx + .selectFrom("Resource") + .select(["draftBlobId", "publishedVersionId"]) + .where("id", "=", collectionId) + .executeTakeFirst() + + if (!collection) { + throw new Error(`Collection with ID ${collectionId} not found.`) + } + + // Handle published version and its blob for the collection + if (collection.publishedVersionId) { + const publishedVersion = await tx + .selectFrom("Version") + .select(["blobId"]) + .where("id", "=", collection.publishedVersionId) + .executeTakeFirst() + + const blobIdToDelete = publishedVersion?.blobId + + await tx + .deleteFrom("Version") + .where("id", "=", collection.publishedVersionId) + .execute() + + if (blobIdToDelete) { + await tx.deleteFrom("Blob").where("id", "=", blobIdToDelete).execute() + } + } + + // Delete the collection resource itself + await tx.deleteFrom("Resource").where("id", "=", collectionId).execute() + + console.log( + `Collection with ID ${collectionId} and all related data deleted successfully.`, + ) + }) + } catch (error) { + console.error("Error deleting collection:", error) + } +} + +const collectionIdToDelete = "" +const siteId = 0 +await deleteCollectionById(collectionIdToDelete, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts new file mode 100644 index 0000000000..7b51f5cd28 --- /dev/null +++ b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts @@ -0,0 +1,80 @@ +import { db } from "~/server/modules/database" + +export const publishCollectionById = async ( + publisherId: string, + collectionId: string, +) => { + // First publish the collection + await db.transaction().execute(async (tx) => { + // Get collection + const collection = await tx + .selectFrom("Resource") + .selectAll() + .where("id", "=", collectionId) + .executeTakeFirstOrThrow() + + if (collection.state !== "Draft") { + throw new Error( + `Collection with ID ${collectionId} cannot be published as it is either in Published state or draftBlobId is not present.`, + ) + } + + // Update collection state to Published + await tx + .updateTable("Resource") + .set({ + state: "Published", + draftBlobId: null, + updatedAt: new Date(), + }) + .where("id", "=", collectionId) + .executeTakeFirstOrThrow() + + // Update all child resources to Published + const children = await tx + .selectFrom("Resource") + .selectAll() + .where("parentId", "=", collectionId) + .execute() + + for (const child of children) { + if (child.state === "Published" || child.draftBlobId === null) { + console.log( + `Child resource with ID ${child.id} cannot be published as it is either in Published state or draftBlobId is not present.`, + ) + continue + } + + const childVersion = await tx + .insertInto("Version") + .values({ + blobId: child.draftBlobId, + versionNum: 1, + resourceId: child.id, + publishedAt: new Date(), + publishedBy: publisherId, + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + await tx + .updateTable("Resource") + .set({ + state: "Published", + publishedVersionId: childVersion.id, + draftBlobId: null, + updatedAt: new Date(), + }) + .where("id", "=", child.id) + .executeTakeFirstOrThrow() + + console.log(`Published child resource with ID ${child.id}`) + } + }) +} + +// NOTE: TODO: Put in the publisher ID and collection ID to publish +const publisherId = "0" +const collectionId = "0" +await publishCollectionById(publisherId, collectionId) From e75cfda611b5a9e678ecbbdccc46fe585b774f3e Mon Sep 17 00:00:00 2001 From: Harish V Date: Mon, 23 Dec 2024 12:06:58 +0800 Subject: [PATCH 2/5] add file logger for persistent logs --- apps/studio/prisma/scripts/FileLogger.ts | 46 +++++++++++++++++++ .../scripts/moh-tosp/backupCollectionById.ts | 17 ++++--- .../moh-tosp/createCollectionFromLocal.ts | 31 +++++++------ .../scripts/moh-tosp/deleteCollectionById.ts | 14 ++++-- .../moh-tosp/publishDraftCollection.ts | 18 +++++--- 5 files changed, 95 insertions(+), 31 deletions(-) create mode 100644 apps/studio/prisma/scripts/FileLogger.ts diff --git a/apps/studio/prisma/scripts/FileLogger.ts b/apps/studio/prisma/scripts/FileLogger.ts new file mode 100644 index 0000000000..2abb15c315 --- /dev/null +++ b/apps/studio/prisma/scripts/FileLogger.ts @@ -0,0 +1,46 @@ +import fs from "fs" +import path from "path" + +export class FileLogger { + private logFilePath: string + + constructor(logFilePath: string) { + this.logFilePath = logFilePath + + // Ensure the directory for the log file exists + const logDir = path.dirname(logFilePath) + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }) + } + } + + private formatLog(level: string, message: string): string { + const timestamp = new Date().toISOString() + return `[${timestamp}] [${level.toUpperCase()}] ${message}\n` + } + + private writeLog(logMessage: string): void { + fs.appendFile(this.logFilePath, logMessage, (err) => { + if (err) { + console.error("Failed to write log:", err) + } + }) + } + + log(level: string, message: string): void { + const logMessage = this.formatLog(level, message) + this.writeLog(logMessage) + } + + info(message: string): void { + this.log("info", message) + } + + error(message: string): void { + this.log("error", message) + } + + debug(message: string): void { + this.log("debug", message) + } +} diff --git a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts index ccfa6db1fe..3231770769 100644 --- a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts @@ -2,6 +2,10 @@ import fs from "fs/promises" // Use the promise-based version of fs for async/aw import path from "path" import { db } from "~/server/modules/database" +import { FileLogger } from "../FileLogger" + +// Update the logger path if required +const logger = new FileLogger("./backupCollectionById.log") /** * Backup a collection and its relevant resources to JSON files. @@ -50,7 +54,7 @@ export async function backupCollection( ) } - console.log(`Writing backup for child with ID ${child.id}`) + logger.info(`Writing backup for child with ID ${child.id}`) // Parse blob content and write to a file const blobBuffer = blob.content // Assuming blob.content is a buffer @@ -58,17 +62,18 @@ export async function backupCollection( await fs.writeFile(blobJsonPath, JSON.stringify(blobBuffer, null, 2)) } - console.log(`Backup completed successfully in directory: ${backupDir}`) + logger.info(`Backup completed successfully in directory: ${backupDir}`) } catch (error: any) { - console.error(`Error backing up collection: ${error.message}`) + logger.error(`Error backing up collection: ${error.message}`) } } // Run the backup // NOTE: TODO: Put in the collection ID to backup -const collectionId = "0" -const backupDirectory = "" +const collectionId = "5" +const backupDirectory = + "/Users/harishv/Documents/Code/isomer/isomer-next/test-backup-tosp/backup" await backupCollection(collectionId, backupDirectory).catch((err) => { - console.error("Unhandled error:", err.message) + logger.error("Unhandled error:", err.message) }) diff --git a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts index e81c0b3f5a..65e4d5fb0d 100644 --- a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts +++ b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts @@ -2,12 +2,16 @@ import fs from "fs/promises" import path from "path" import { db, jsonb } from "~/server/modules/database" +import { FileLogger } from "../FileLogger" + +// Update the logger path if required +const logger = new FileLogger("./createCollectionFromLocal.log") export const createCollectionFromLocal = async ( contentDir: string, siteId: number, ) => { - console.log(`Reading from ${contentDir}`) + logger.info(`Reading from ${contentDir}`) const jsonFilePath = path.join(contentDir, "cost-financing.json") const folderPath = path.join(contentDir, "cost-financing-original") @@ -29,7 +33,7 @@ export const createCollectionFromLocal = async ( .executeTakeFirstOrThrow() const collectionId = collection.id - console.log(`Collection created with ID: ${collectionId}`) + logger.info(`Collection created with ID: ${collectionId}`) // Step 2: Insert "cost-financing.json" as an IndexPage with permalink "_index"\ const jsonFileContent = await fs.readFile(jsonFilePath, "utf-8") @@ -59,22 +63,22 @@ export const createCollectionFromLocal = async ( const indexPageId = indexPage.id - console.log(`Index page created with ID: ${indexPageId}`) + logger.info(`Index page created with ID: ${indexPageId}`) // Step 3: Insert files from "cost-financing/" into the DB as Blobs const folderFiles = await fs.readdir(folderPath) - console.log(`Reading from folderPath: ${folderPath}`) - console.log(`Folder files`, folderFiles) + logger.info(`Reading from folderPath: ${folderPath}`) + logger.info(`Folder files`, folderFiles) for (const file of folderFiles) { const filePath = path.join(folderPath, file) - console.log(`Reading file path: ${filePath}`) + logger.info(`Reading file path: ${filePath}`) - console.log(`Filename: ${file}`) + logger.info(`Filename: ${file}`) //Sometimes might have hidden internal files like .DSStore if (!file.endsWith(".json")) { continue } - console.log("File path: ", filePath) + logger.info("File path: ", filePath) const fileContent = await fs.readFile(filePath, "utf-8") const parsedFileContent = JSON.parse(fileContent) @@ -107,19 +111,20 @@ export const createCollectionFromLocal = async ( const resourceId = resource.id - console.log( + logger.info( `Blob created for file ${file} with resource ID: ${resourceId}`, ) } }) - console.log("All operations completed successfully.") + logger.info("All operations completed successfully.") } catch (error) { - console.error("Error during transaction:", error) + logger.error("Error during transaction:", error) } } // NOTE: TODO: Update the content directory and siteId here before usage! -const contentDir = "" -const siteId = 0 +const contentDir = + "/Users/harishv/Documents/Code/isomer/isomer-next/test-backup-tosp/content" +const siteId = 1 await createCollectionFromLocal(contentDir, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts index 53bb2b7068..847a94ad43 100644 --- a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts @@ -1,4 +1,8 @@ import { db } from "~/server/modules/database" +import { FileLogger } from "../FileLogger" + +// Update the logger path if required +const logger = new FileLogger("./deleteCollectionById.log") export const deleteCollectionById = async ( collectionId: string, @@ -50,7 +54,7 @@ export const deleteCollectionById = async ( // Delete the resource itself await tx.deleteFrom("Resource").where("id", "=", resource.id).execute() - console.log(`Resource with ID ${resource.id} deleted successfully.`) + logger.info(`Resource with ID ${resource.id} deleted successfully.`) } // Step 3: Delete the collection itself @@ -87,15 +91,15 @@ export const deleteCollectionById = async ( // Delete the collection resource itself await tx.deleteFrom("Resource").where("id", "=", collectionId).execute() - console.log( + logger.info( `Collection with ID ${collectionId} and all related data deleted successfully.`, ) }) } catch (error) { - console.error("Error deleting collection:", error) + logger.error("Error deleting collection:", error) } } -const collectionIdToDelete = "" -const siteId = 0 +const collectionIdToDelete = "3640" +const siteId = 1 await deleteCollectionById(collectionIdToDelete, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts index 7b51f5cd28..1b17d799cd 100644 --- a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts +++ b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts @@ -1,4 +1,8 @@ import { db } from "~/server/modules/database" +import { FileLogger } from "../FileLogger" + +// Update the logger path if required +const logger = new FileLogger("./publishCollectionById.log") export const publishCollectionById = async ( publisherId: string, @@ -14,9 +18,9 @@ export const publishCollectionById = async ( .executeTakeFirstOrThrow() if (collection.state !== "Draft") { - throw new Error( - `Collection with ID ${collectionId} cannot be published as it is either in Published state or draftBlobId is not present.`, - ) + const errMsg = `Collection with ID ${collectionId} cannot be published as it is either in Published state or draftBlobId is not present.` + logger.error(errMsg) + throw new Error(errMsg) } // Update collection state to Published @@ -39,7 +43,7 @@ export const publishCollectionById = async ( for (const child of children) { if (child.state === "Published" || child.draftBlobId === null) { - console.log( + logger.error( `Child resource with ID ${child.id} cannot be published as it is either in Published state or draftBlobId is not present.`, ) continue @@ -69,12 +73,12 @@ export const publishCollectionById = async ( .where("id", "=", child.id) .executeTakeFirstOrThrow() - console.log(`Published child resource with ID ${child.id}`) + logger.info(`Published child resource with ID ${child.id}`) } }) } // NOTE: TODO: Put in the publisher ID and collection ID to publish -const publisherId = "0" -const collectionId = "0" +const publisherId = "mblrtd177gju657mf20m32jo" +const collectionId = "3640" await publishCollectionById(publisherId, collectionId) From ccbc6116b8335925a8ce5b89e10e6d316fae4770 Mon Sep 17 00:00:00 2001 From: Harish V Date: Tue, 24 Dec 2024 13:58:14 +0800 Subject: [PATCH 3/5] fix lints --- .../scripts/moh-tosp/backupCollectionById.ts | 10 +- .../moh-tosp/createCollectionFromLocal.ts | 7 +- .../scripts/moh-tosp/deleteCollectionById.ts | 6 +- .../moh-tosp/publishDraftCollection.ts | 118 +++++++++--------- 4 files changed, 77 insertions(+), 64 deletions(-) diff --git a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts index 3231770769..7807405f7a 100644 --- a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts @@ -63,8 +63,10 @@ export async function backupCollection( } logger.info(`Backup completed successfully in directory: ${backupDir}`) - } catch (error: any) { - logger.error(`Error backing up collection: ${error.message}`) + } catch (error) { + if (error instanceof Error) { + logger.error(`Error backing up collection: ${error.message}`) + } } } @@ -75,5 +77,7 @@ const backupDirectory = "/Users/harishv/Documents/Code/isomer/isomer-next/test-backup-tosp/backup" await backupCollection(collectionId, backupDirectory).catch((err) => { - logger.error("Unhandled error:", err.message) + if (err instanceof Error) { + logger.error(`Unhandled error: ${err.message}`) + } }) diff --git a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts index 65e4d5fb0d..dd017f14d3 100644 --- a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts +++ b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts @@ -68,7 +68,7 @@ export const createCollectionFromLocal = async ( // Step 3: Insert files from "cost-financing/" into the DB as Blobs const folderFiles = await fs.readdir(folderPath) logger.info(`Reading from folderPath: ${folderPath}`) - logger.info(`Folder files`, folderFiles) + logger.info(`Folder files: ${folderFiles}`) for (const file of folderFiles) { const filePath = path.join(folderPath, file) logger.info(`Reading file path: ${filePath}`) @@ -78,7 +78,6 @@ export const createCollectionFromLocal = async ( if (!file.endsWith(".json")) { continue } - logger.info("File path: ", filePath) const fileContent = await fs.readFile(filePath, "utf-8") const parsedFileContent = JSON.parse(fileContent) @@ -119,7 +118,9 @@ export const createCollectionFromLocal = async ( logger.info("All operations completed successfully.") } catch (error) { - logger.error("Error during transaction:", error) + if (error instanceof Error) { + logger.error(`Error during transaction: ${error.message}`) + } } } diff --git a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts index 847a94ad43..b187d46c33 100644 --- a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts @@ -96,10 +96,12 @@ export const deleteCollectionById = async ( ) }) } catch (error) { - logger.error("Error deleting collection:", error) + if (error instanceof Error) { + logger.error(`Error deleting collection: ${error.message}`) + } } } -const collectionIdToDelete = "3640" +const collectionIdToDelete = "7249" const siteId = 1 await deleteCollectionById(collectionIdToDelete, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts index 1b17d799cd..d07d216dbf 100644 --- a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts +++ b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts @@ -8,77 +8,83 @@ export const publishCollectionById = async ( publisherId: string, collectionId: string, ) => { - // First publish the collection - await db.transaction().execute(async (tx) => { - // Get collection - const collection = await tx - .selectFrom("Resource") - .selectAll() - .where("id", "=", collectionId) - .executeTakeFirstOrThrow() - - if (collection.state !== "Draft") { - const errMsg = `Collection with ID ${collectionId} cannot be published as it is either in Published state or draftBlobId is not present.` - logger.error(errMsg) - throw new Error(errMsg) - } - - // Update collection state to Published - await tx - .updateTable("Resource") - .set({ - state: "Published", - draftBlobId: null, - updatedAt: new Date(), - }) - .where("id", "=", collectionId) - .executeTakeFirstOrThrow() - - // Update all child resources to Published - const children = await tx - .selectFrom("Resource") - .selectAll() - .where("parentId", "=", collectionId) - .execute() + try { + // First publish the collection + await db.transaction().execute(async (tx) => { + // Get collection + const collection = await tx + .selectFrom("Resource") + .selectAll() + .where("id", "=", collectionId) + .executeTakeFirstOrThrow() - for (const child of children) { - if (child.state === "Published" || child.draftBlobId === null) { - logger.error( - `Child resource with ID ${child.id} cannot be published as it is either in Published state or draftBlobId is not present.`, - ) - continue + if (collection.state !== "Draft") { + const errMsg = `Collection with ID ${collectionId} cannot be published as it is either in Published state or draftBlobId is not present.` + logger.error(errMsg) + throw new Error(errMsg) } - const childVersion = await tx - .insertInto("Version") - .values({ - blobId: child.draftBlobId, - versionNum: 1, - resourceId: child.id, - publishedAt: new Date(), - publishedBy: publisherId, - updatedAt: new Date(), - }) - .returning("id") - .executeTakeFirstOrThrow() - + // Update collection state to Published await tx .updateTable("Resource") .set({ state: "Published", - publishedVersionId: childVersion.id, draftBlobId: null, updatedAt: new Date(), }) - .where("id", "=", child.id) + .where("id", "=", collectionId) .executeTakeFirstOrThrow() - logger.info(`Published child resource with ID ${child.id}`) + // Update all child resources to Published + const children = await tx + .selectFrom("Resource") + .selectAll() + .where("parentId", "=", collectionId) + .execute() + + for (const child of children) { + if (child.state === "Published" || child.draftBlobId === null) { + logger.error( + `Child resource with ID ${child.id} cannot be published as it is either in Published state or draftBlobId is not present.`, + ) + continue + } + + const childVersion = await tx + .insertInto("Version") + .values({ + blobId: child.draftBlobId, + versionNum: 1, + resourceId: child.id, + publishedAt: new Date(), + publishedBy: publisherId, + updatedAt: new Date(), + }) + .returning("id") + .executeTakeFirstOrThrow() + + await tx + .updateTable("Resource") + .set({ + state: "Published", + publishedVersionId: childVersion.id, + draftBlobId: null, + updatedAt: new Date(), + }) + .where("id", "=", child.id) + .executeTakeFirstOrThrow() + + logger.info(`Published child resource with ID ${child.id}`) + } + }) + } catch (error) { + if (error instanceof Error) { + logger.error(`Error publishing collection by ID: ${error.message}`) } - }) + } } // NOTE: TODO: Put in the publisher ID and collection ID to publish const publisherId = "mblrtd177gju657mf20m32jo" -const collectionId = "3640" +const collectionId = "7249" await publishCollectionById(publisherId, collectionId) From 539a9c1e28cc2982beaf8537a35b752935d9f643 Mon Sep 17 00:00:00 2001 From: Harish V Date: Tue, 24 Dec 2024 14:18:30 +0800 Subject: [PATCH 4/5] fix lints --- .../moh-tosp/createCollectionFromLocal.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts index dd017f14d3..6c0d2c509f 100644 --- a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts +++ b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts @@ -68,7 +68,7 @@ export const createCollectionFromLocal = async ( // Step 3: Insert files from "cost-financing/" into the DB as Blobs const folderFiles = await fs.readdir(folderPath) logger.info(`Reading from folderPath: ${folderPath}`) - logger.info(`Folder files: ${folderFiles}`) + logger.info(`Folder files: ${JSON.stringify(folderFiles)}`) for (const file of folderFiles) { const filePath = path.join(folderPath, file) logger.info(`Reading file path: ${filePath}`) @@ -80,12 +80,21 @@ export const createCollectionFromLocal = async ( } const fileContent = await fs.readFile(filePath, "utf-8") - const parsedFileContent = JSON.parse(fileContent) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let parsedFileContent: any + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + parsedFileContent = JSON.parse(fileContent) + } catch (error) { + if (error instanceof Error) { + logger.error(`Error parsing JSON file: ${file}`) + } + } const blob = await tx .insertInto("Blob") .values({ - content: parsedFileContent, + content: parsedFileContent as PrismaJson.BlobJsonContent, createdAt: new Date(), updatedAt: new Date(), }) @@ -95,6 +104,7 @@ export const createCollectionFromLocal = async ( const resource = await tx .insertInto("Resource") .values({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access title: parsedFileContent.page.title, permalink: file, siteId: siteId, // Replace with appropriate site ID From 03319e7e2c0a719c5a7ed4991d1227a90865f677 Mon Sep 17 00:00:00 2001 From: Harish V Date: Wed, 25 Dec 2024 00:19:23 +0800 Subject: [PATCH 5/5] script enhancements --- .../scripts/moh-tosp/backupCollectionById.ts | 7 ++-- .../moh-tosp/createCollectionFromLocal.ts | 36 +++++++++++++------ .../scripts/moh-tosp/deleteCollectionById.ts | 4 +-- .../moh-tosp/publishDraftCollection.ts | 4 +-- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts index 7807405f7a..a18c73ae2a 100644 --- a/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/backupCollectionById.ts @@ -58,7 +58,7 @@ export async function backupCollection( // Parse blob content and write to a file const blobBuffer = blob.content // Assuming blob.content is a buffer - const blobJsonPath = path.join(backupDir, `${child.title}.json`) + const blobJsonPath = path.join(backupDir, `${child.permalink}.json`) await fs.writeFile(blobJsonPath, JSON.stringify(blobBuffer, null, 2)) } @@ -72,9 +72,8 @@ export async function backupCollection( // Run the backup // NOTE: TODO: Put in the collection ID to backup -const collectionId = "5" -const backupDirectory = - "/Users/harishv/Documents/Code/isomer/isomer-next/test-backup-tosp/backup" +const collectionId = "0" +const backupDirectory = "/Users/XYZ/" await backupCollection(collectionId, backupDirectory).catch((err) => { if (err instanceof Error) { diff --git a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts index 6c0d2c509f..f8d4eb36c9 100644 --- a/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts +++ b/apps/studio/prisma/scripts/moh-tosp/createCollectionFromLocal.ts @@ -10,10 +10,14 @@ const logger = new FileLogger("./createCollectionFromLocal.log") export const createCollectionFromLocal = async ( contentDir: string, siteId: number, + indexPageName: string, // should be placed outside the folder + indexPageTitle: string, // title of the index page + collectionName: string, + nameOfNewCollectionToCreate: string, ) => { logger.info(`Reading from ${contentDir}`) - const jsonFilePath = path.join(contentDir, "cost-financing.json") - const folderPath = path.join(contentDir, "cost-financing-original") + const jsonFilePath = path.join(contentDir, indexPageName) + const folderPath = path.join(contentDir, collectionName) try { await db.transaction().execute(async (tx) => { @@ -21,8 +25,8 @@ export const createCollectionFromLocal = async ( const collection = await tx .insertInto("Resource") .values({ - title: "cost-financing-new", - permalink: "cost-financing-new", + title: nameOfNewCollectionToCreate, + permalink: nameOfNewCollectionToCreate, siteId: siteId, type: "Collection", state: "Draft", @@ -35,7 +39,7 @@ export const createCollectionFromLocal = async ( const collectionId = collection.id logger.info(`Collection created with ID: ${collectionId}`) - // Step 2: Insert "cost-financing.json" as an IndexPage with permalink "_index"\ + // Step 2: Insert "cost-financing.json" as an IndexPage with permalink "_index" const jsonFileContent = await fs.readFile(jsonFilePath, "utf-8") const indexPageBlob = await tx .insertInto("Blob") @@ -48,7 +52,7 @@ export const createCollectionFromLocal = async ( const indexPage = await tx .insertInto("Resource") .values({ - title: "cost-financing-new", + title: nameOfNewCollectionToCreate, permalink: "_index", siteId: siteId, type: "IndexPage", @@ -106,7 +110,7 @@ export const createCollectionFromLocal = async ( .values({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access title: parsedFileContent.page.title, - permalink: file, + permalink: file.replace(/\.json$/, ""), // remove the .json at the back on permalinks siteId: siteId, // Replace with appropriate site ID type: "CollectionPage", parentId: collectionId, @@ -135,7 +139,17 @@ export const createCollectionFromLocal = async ( } // NOTE: TODO: Update the content directory and siteId here before usage! -const contentDir = - "/Users/harishv/Documents/Code/isomer/isomer-next/test-backup-tosp/content" -const siteId = 1 -await createCollectionFromLocal(contentDir, siteId) +const contentDir = "/Users/XYZ/" +const indexPagePath = "cost-financing.json" +const indexPageTitle = "Cost financing" +const collectionName = "cost-financing" +const nameOfNewCollectionToCreate = "cost-financing-new" // will also be the permalink +const siteId = 0 +await createCollectionFromLocal( + contentDir, + siteId, + indexPagePath, + indexPageTitle, + collectionName, + nameOfNewCollectionToCreate, +) diff --git a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts index b187d46c33..d4f851bdff 100644 --- a/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts +++ b/apps/studio/prisma/scripts/moh-tosp/deleteCollectionById.ts @@ -102,6 +102,6 @@ export const deleteCollectionById = async ( } } -const collectionIdToDelete = "7249" -const siteId = 1 +const collectionIdToDelete = "0" +const siteId = 0 await deleteCollectionById(collectionIdToDelete, siteId) diff --git a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts index d07d216dbf..886fddf677 100644 --- a/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts +++ b/apps/studio/prisma/scripts/moh-tosp/publishDraftCollection.ts @@ -85,6 +85,6 @@ export const publishCollectionById = async ( } // NOTE: TODO: Put in the publisher ID and collection ID to publish -const publisherId = "mblrtd177gju657mf20m32jo" -const collectionId = "7249" +const publisherId = "xyz" +const collectionId = "0" await publishCollectionById(publisherId, collectionId)