Skip to content

Commit

Permalink
Allow renaming of lists (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmintey authored Jan 9, 2025
2 parents d18eca8 + a0880e5 commit b48d4ec
Show file tree
Hide file tree
Showing 61 changed files with 1,769 additions and 1,047 deletions.
1 change: 1 addition & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ caddy start --config /usr/src/app/Caddyfile

pnpm prisma migrate deploy
pnpm prisma db seed
pnpm db:patch
pnpm start
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
"format": "prettier --write .",
"db:patch": "node prisma/patches/index.js"
},
"prisma": {
"seed": "node prisma/seed.js"
Expand Down Expand Up @@ -78,6 +79,7 @@
"prisma": "^6.0.1",
"sharp": "^0.33.5",
"svelte-i18n": "^4.0.1",
"svelte-virtuallists": "^1.4.0",
"zod": "^3.23.8"
},
"engines": {
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
-- CreateTable
CREATE TABLE "list" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT,
"ownerId" TEXT NOT NULL,
"groupId" TEXT NOT NULL,
"public" BOOLEAN NOT NULL DEFAULT false,
"icon" TEXT,
"iconColor" TEXT,
CONSTRAINT "list_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "list_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "group" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "_ItemToList" (
"A" INTEGER NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_ItemToList_A_fkey" FOREIGN KEY ("A") REFERENCES "items" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "_ItemToList_B_fkey" FOREIGN KEY ("B") REFERENCES "list" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);

-- CreateIndex
CREATE UNIQUE INDEX "list_id_key" ON "list"("id");

-- CreateIndex
CREATE INDEX "list_ownerId_idx" ON "list"("ownerId");

-- CreateIndex
CREATE INDEX "list_groupId_idx" ON "list"("groupId");

-- CreateIndex
CREATE INDEX "list_ownerId_groupId_idx" ON "list"("ownerId", "groupId");

-- CreateIndex
CREATE UNIQUE INDEX "_ItemToList_AB_unique" ON "_ItemToList"("A", "B");

-- CreateIndex
CREATE INDEX "_ItemToList_B_index" ON "_ItemToList"("B");

-- Create a new list for each user and group
INSERT INTO "list"
SELECT ugm.userId || ugm.groupId AS id, NULL AS name, ugm.userId AS ownerId, ugm.groupId, FALSE AS public, NULL AS icon, NULL AS iconColor
FROM "user_group_membership" ugm
LEFT JOIN "public_list" pl ON pl.userId = ugm.userId AND pl.groupId = ugm.groupId
WHERE pl.id IS NULL;

-- Copy over public lists
INSERT INTO "list"
SELECT id, NULL as name, userId AS ownerId, groupId, TRUE AS public, NULL AS icon, NULL AS iconColor
FROM "public_list";

-- Add the existing items to the list relationship
INSERT INTO "_ItemToList"
SELECT i.id AS A, l.id AS B
FROM "items" i
JOIN "list" l ON l.ownerId = i.userId AND l.groupId = i.groupId;
8 changes: 8 additions & 0 deletions prisma/migrations/20241209020045_add_patches/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- CreateTable
CREATE TABLE "patch" (
"id" TEXT NOT NULL PRIMARY KEY,
"executed_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- CreateIndex
CREATE UNIQUE INDEX "patch_id_key" ON "patch"("id");
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
Warnings:
- You are about to drop the `public_list` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
PRAGMA foreign_keys=off;
DROP TABLE "public_list";
PRAGMA foreign_keys=on;
2 changes: 2 additions & 0 deletions prisma/patches/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
await import("./item-price.js");
await import("./list-relationship.js");
117 changes: 117 additions & 0 deletions prisma/patches/item-price.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { PrismaClient } from "@prisma/client";

const PATCH_ID = "item-price";
const prisma = new PrismaClient();

const isPatchApplied =
(await prisma.patch.findUnique({
where: {
id: PATCH_ID
}
})) !== null;

if (isPatchApplied) {
console.log("Skipping already applied patch: '%s'", PATCH_ID);
} else {
const symbolToCurrencyMap = {
$: "USD",
"€": "EUR",
"£": "GBP",
"¥": "JPY"
};

console.log("Patching item price model");
const itemsWithPrice = await prisma.item.findMany({
where: {
price: {
not: null
},
itemPriceId: null
}
});

console.log("%d items to update", itemsWithPrice.length);
itemsWithPrice.forEach(async (item) => {
console.log("Patching item %s with price %s", item.name, item.price);
const commaGroupSepRegex = /^(\d{0,3}\\,\d{3}){0,}\d{0,}\.?\d{0,2}$/g;
const dotGroupSepRegex = /^(\d{0,3}\.\d{3}){0,}\d{0,},?\d{0,2}$/g;

const price = item.price;
const priceWithoutSymbols = /[\d\\.\\,]+/g.exec(price)?.[0]?.trim();
const symbol = /[^\\,\\.\d\w\s]/g.exec(price)?.[0];
let currency;
let newPriceValue;
if (symbol && symbolToCurrencyMap[symbol]) {
currency = symbolToCurrencyMap[symbol];
console.log("Detected currency as %s", currency);
} else {
currency = process.env.DEFAULT_CURRENCY || "USD";
console.log("No currency detected, defaulting to %s", currency);
}

if (!priceWithoutSymbols) {
return;
}

if (commaGroupSepRegex.test(priceWithoutSymbols)) {
// price is a number with comma as the group separator
console.log("Price detected as using group separator ',' and decimal separator '.'");
const priceWithoutSep = priceWithoutSymbols.replace(",", "");
console.log("price without separator: %s", priceWithoutSep);
if (priceWithoutSep.includes(".")) {
// has decimal value
const floatVal = parseFloat(priceWithoutSep);
console.log("float value: %s", floatVal);
newPriceValue = floatVal * 100;
} else {
newPriceValue = parseInt(priceWithoutSep) * 100;
}
}
if (dotGroupSepRegex.test(priceWithoutSymbols)) {
// price is a number with a dot as the group seperator
console.log("Price detected as using group separator '.' and decimal separator ','");
const priceWithoutSep = priceWithoutSymbols.replace(".", "");
if (priceWithoutSep.includes(",")) {
// has decimal value
const floatVal = parseFloat(priceWithoutSep.replace(",", "."));
newPriceValue = floatVal * 100;
} else {
newPriceValue = parseInt(priceWithoutSep) * 100;
}
}

if (!newPriceValue) {
console.log("Unable to determine price value\n");
return;
} else {
console.log("New price value: %s\n", newPriceValue);
}

try {
await prisma.item.update({
where: {
id: item.id
},
data: {
itemPrice: {
create: {
currency,
value: newPriceValue
}
}
}
});
} catch (e) {
console.warn("Unable to update item with new price model", e);
}
});

await prisma.patch.create({
data: {
id: PATCH_ID
}
});
console.log("Patch '%s' applied successfully.", PATCH_ID);
}

await prisma.$disconnect();
56 changes: 56 additions & 0 deletions prisma/patches/list-relationship.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { PrismaClient } from "@prisma/client";
import { init, isCuid } from "@paralleldrive/cuid2";

const PATCH_ID = "list-relationship";
const prisma = new PrismaClient();
const createId = init({
length: 10
});

const isPatchApplied =
(await prisma.patch.findUnique({
where: {
id: PATCH_ID
}
})) !== null;

if (isPatchApplied) {
console.log("Skipping already applied patch: '%s'", PATCH_ID);
} else {
const listsToUpdate = await prisma.list
.findMany({
select: {
id: true
}
})
.then((lists) => lists.filter((list) => !isCuid(list.id)))
.then((lists) => lists.map((list) => ({ oldId: list.id, newId: createId() })));

const actions = listsToUpdate.map((list) =>
prisma.list.update({
data: {
id: list.newId
},
where: {
id: list.oldId
}
})
);

await prisma
.$transaction(actions)
.then(() =>
prisma.patch.create({
data: {
id: PATCH_ID
}
})
)
.then(() => console.log("Patch '%s' applied successfully.", PATCH_ID))
.catch((e) => {
console.error("Error applying patch '%s'", PATCH_ID);
console.error(e);
});
}

await prisma.$disconnect();
Loading

0 comments on commit b48d4ec

Please sign in to comment.