fixing delete

This commit is contained in:
Khalim Conn-Kowlessar 2024-11-07 11:55:33 +00:00
parent cdcc546bd7
commit a1432788cd
3 changed files with 231 additions and 60 deletions

View file

@ -0,0 +1,80 @@
import { db } from "@/app/db/db";
import { NextRequest, NextResponse } from "next/server";
import { portfolioUsers } from "@/app/db/schema/portfolio";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
const PermissionsBodySchema = z.object({
userId: z.string(),
action: z.enum(["delete", "update"]),
});
export async function POST(
request: NextRequest,
{ params }: { params: { portfolioId: string } }
) {
// This endpoint lives at portfolio/{portfolioId}/permissions and will return the permissions level for a given portfolio
// Call this endpoint with a) userId, b) portfolioId, c) an action and this api will tell you if that person can do that thing
const body = await request.json();
let validatedBody;
try {
validatedBody = PermissionsBodySchema.parse(body);
} catch (error) {
console.error("Invalid input: ", error);
return new NextResponse(JSON.stringify({ msg: "Invalid input" }), {
status: 400,
});
}
const action = validatedBody.action;
const userId = validatedBody.userId;
const portfolioId = params.portfolioId;
const existingPortfolioPermissions = await db.query.portfolioUsers.findFirst({
where: and(
eq(portfolioUsers.portfolioId, BigInt(portfolioId)),
eq(portfolioUsers.userId, BigInt(userId))
),
});
if (!existingPortfolioPermissions) {
return new NextResponse(
JSON.stringify({ error: "Portfolio not found or unauthorized" }),
{ status: 404 }
);
}
const role = existingPortfolioPermissions.role;
let permitted;
// If the action is a delete, we only allow an admin or creator to delete
if (action === "delete") {
if (role === "admin" || role === "creator") {
permitted = true;
} else {
permitted = false;
}
}
// If the action is an update, we allow an individual admin, creator or write role to update
if (action === "update") {
if (role === "admin" || role === "creator" || role === "write") {
permitted = true;
} else {
permitted = false;
}
}
// Returning a successful response
return new NextResponse(
JSON.stringify({
permitted: permitted,
}),
{
status: 200,
}
);
}

View file

@ -1,24 +1,37 @@
import { db } from "@/app/db/db";
import { NextRequest, NextResponse } from "next/server";
import { portfolio, portfolioUsers } from "@/app/db/schema/portfolio";
import { and, eq, inArray } from "drizzle-orm";
import { user } from "@/app/db/schema/users";
import {
recommendation,
recommendationMaterials,
planRecommendations,
plan,
scenario,
} from "@/app/db/schema/recommendations";
import {
propertyTargets,
propertyDetailsEpc,
property,
} from "@/app/db/schema/property";
import { eq, inArray } from "drizzle-orm";
export async function PUT(request: NextRequest) {
export async function PUT(
request: NextRequest,
{ params }: { params: { portfolioId: string } }
) {
const body = await request.json();
console.log("HI WE MADE IT!!");
console.log(body);
const portfolioId = params.portfolioId;
const portfolioId = body.portfolioId;
// We'll eventually veryify the user is authorized to update this portfolio
const userId = body.userId;
delete body.userId;
delete body.portfolioId;
console.log(body);
// Update the database
await db.update(portfolio).set(body).where(eq(portfolio.id, portfolioId));
await db
.update(portfolio)
.set(body)
.where(eq(portfolio.id, BigInt(portfolioId)));
// Returning a successful response
return new NextResponse(JSON.stringify({}), {
@ -26,46 +39,110 @@ export async function PUT(request: NextRequest) {
});
}
export async function DELETE(request: NextRequest) {
console.log("Incoming DELETE request:", request.method);
export async function DELETE(
request: NextRequest,
{ params }: { params: { portfolioId: string } }
) {
try {
// Parse the request body
const body = await request.json();
console.log("DELETE Request Received", body);
const portfolioId = params.portfolioId;
const portfolioId = body.portfolioId;
const userId = body.userId;
// 1) Fetch the portfolio ids
// 2) Fetch the recommendation ids
// 3) Delete all entries from RecommendationMaterials for these recommendations
// 4) Delete all entries from PlanRecommendations that reference plans in the portfolio
// 5) Delete all Plans associated with the portfolio
// 6) Delete all Scenarios associated with the portfolio
// 7) Delete all Recommendations associated with the properties
// 8) Delete PropertyTargetsModel, PropertyDetailsEpcModel, and PropertyModel
// 9) Delete portfolioUsers
// 10) Then, we finally delete the portfolio!!!
// First verify the portfolio exists and belongs to this user
const existingPortfolio = await db.query.portfolio.findFirst({
where: and(
eq(portfolioUsers.portfolioId, portfolioId),
eq(portfolioUsers.userId, userId)
)
// Step 1) Fetch the property ids for the portfolio
const propertyIdsResponse = await db.query.property.findMany({
columns: { id: true },
where: eq(property.portfolioId, BigInt(portfolioId)),
});
const propertyIds = propertyIdsResponse.map((property) => property.id);
if (!existingPortfolio) {
return new NextResponse(
JSON.stringify({ error: "Portfolio not found or unauthorized" }),
{ status: 404 }
// Step 2) Fetch the recommendation ids - filter the recommendation table, where propertyId is in the propertyIds
// if there are no prpoperty Ids, we make recommendationIds an empty array
let recommendationIds: bigint[] = [];
if (propertyIds.length) {
const recommendations = await db.query.recommendation.findMany({
columns: { id: true },
where: inArray(recommendation.propertyId, propertyIds),
});
recommendationIds = recommendations.map(
(recommendation) => recommendation.id
);
}
// Delete the portfolio
// Step 3) Delete all entries from RecommendationMaterials for these recommendations
if (recommendationIds.length) {
await db
.delete(recommendationMaterials)
.where(
inArray(recommendationMaterials.recommendationId, recommendationIds)
);
}
// Step 4) Delete all entries from PlanRecommendations that reference plans in the portfolio
// Get the plan ids
const plans = await db.query.plan.findMany({
columns: { id: true },
where: eq(plan.portfolioId, BigInt(portfolioId)),
});
const planIds = plans.map((plan) => plan.id);
if (planIds.length) {
await db
.delete(planRecommendations)
.where(inArray(planRecommendations.planId, planIds));
}
// Step 5) Delete all Plans associated with the portfolio
await db.delete(plan).where(eq(plan.portfolioId, BigInt(portfolioId)));
// Step 6) Delete all Scenarios associated with the portfolio
await db
.delete(portfolio)
.where(eq(portfolio.id, portfolioId));
.delete(scenario)
.where(eq(scenario.portfolioId, BigInt(portfolioId)));
// Step 7) Delete all Recommendations associated with the properties
if (propertyIds.length) {
await db
.delete(recommendation)
.where(inArray(recommendation.propertyId, propertyIds));
}
// Step 8) Delete PropertyTargets, PropertyDetailsEpc, and Property
await db
.delete(propertyTargets)
.where(eq(propertyTargets.portfolioId, BigInt(portfolioId)));
await db
.delete(propertyDetailsEpc)
.where(eq(propertyDetailsEpc.portfolioId, BigInt(portfolioId)));
await db
.delete(property)
.where(eq(property.portfolioId, BigInt(portfolioId)));
await db
.delete(portfolioUsers)
.where(eq(portfolioUsers.portfolioId, BigInt(portfolioId)));
await db.delete(portfolio).where(eq(portfolio.id, BigInt(portfolioId)));
// Return success response
return new NextResponse(
JSON.stringify({ message: "Portfolio successfully deleted" }),
JSON.stringify({ message: "Portfolio successfully deleted" }),
{ status: 200 }
);
} catch (error) {
console.error("Error deleting portfolio:", error);
return new NextResponse(
JSON.stringify({ error: "Your API has Failed to delete the portfolio" }),
JSON.stringify({ error: "Your API has Failed to delete the portfolio" }),
{ status: 500 }
);
}

View file

@ -70,7 +70,6 @@ type updateSettingsArgs = {
type bodyType = {
userId: string;
portfolioId: string;
name?: string;
budget?: number | string;
goal?: string;
@ -92,7 +91,6 @@ const updateSettings = async ({
const body: bodyType = {
userId: userId.toString(),
portfolioId: portfolioId,
};
if (name) {
@ -128,25 +126,53 @@ const updateSettings = async ({
return response.json();
};
async function deletePortfolio({ userId, portfolioId }: {
async function deletePortfolio({
userId,
portfolioId,
}: {
userId: bigint;
portfolioId: string;
}) {
try {
console.log("Attempting to DELETE portfolio by calling API:", { userId, portfolioId });
console.log("Attempting to DELETE portfolio by calling API:", {
userId,
portfolioId,
});
// We'll check if the user is authorized to delete this portfolio
const permissionsReponse = await fetch(
`/api/portfolio/${portfolioId}/permissions`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: userId.toString(),
action: "delete",
}),
}
);
const permissionsData = await permissionsReponse.json();
const permitted = permissionsData.permitted;
// If the user is not permitted to delete the portfolio, we'll throw an error
if (!permitted) {
throw new Error("User is not permitted to delete this portfolio");
}
const response = await fetch(`/api/portfolio/${portfolioId}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: userId.toString(),
portfolioId: portfolioId,
}),
});
if (!response.ok) {
throw new Error("deletePortfolio has been called into action but utterly failed to do the API handoff");
throw new Error(
"deletePortfolio has been called into action but utterly failed to do the API handoff"
);
}
return await response.json();
@ -165,7 +191,6 @@ export default function PortfolioSettings({
}) {
// This is a client component so we can access the session directly
const session = useSession();
const router = useRouter();
const { mutate, isLoading } = useMutation(updateSettings, {
@ -181,11 +206,13 @@ export default function PortfolioSettings({
const { mutate: mutateDelete } = useMutation(deletePortfolio, {
onSuccess: () => {
setIsDeleteModalOpen(false);
console.log("Succesfully Deleted")
router.push('/home');
router.push("/home");
},
onError: (error) => {
console.error("Because the API hand off failed, we're right back here at the mutation station", error);
console.error(
"Because the API hand off failed, we're right back here at the mutation station",
error
);
},
});
@ -230,7 +257,7 @@ export default function PortfolioSettings({
userId,
portfolioId,
});
console.log("succesfully called the mututate function")
console.log("succesfully called the mututate function");
}
}
@ -303,19 +330,6 @@ export default function PortfolioSettings({
status: portfolioStatus,
});
}
// Delete function
function handleDelete() {
mutate({
userId,
portfolioId,
name: portfolioName,
budget: portfolioBudget,
goal: portfolioGoal,
status: portfolioStatus,
});
}
// HTML to render the page