Connect delete frontend route to delete backend

This commit is contained in:
pmoharana-cmd 2025-01-07 16:32:24 -05:00
parent 9c970ca40e
commit ccf2f1f83f
14 changed files with 229 additions and 32 deletions

View File

@ -60,12 +60,12 @@ def update(
return resource_svc.update(subject, resource)
@api.delete("", response_model=None, tags=["Resource"])
@api.delete("", response_model=dict, tags=["Resource"])
def delete(
uuid: str,
resource: Resource,
id: int,
user_svc: UserService = Depends(),
resource_svc: ResourceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
resource_svc.delete(subject, resource)
return resource_svc.delete(subject, id)

View File

@ -19,7 +19,10 @@ openapi_tags = {
# TODO: Create custom exceptions
@api.post("", response_model=Service, tags=["Service"])
def create(
uuid: str, service: Service, user_svc: UserService = Depends(), service_svc: ServiceService = Depends()
uuid: str,
service: Service,
user_svc: UserService = Depends(),
service_svc: ServiceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
return service_svc.create(subject, service)
@ -27,28 +30,42 @@ def create(
@api.get("", response_model=List[Service], tags=["Service"])
def get_all(
uuid: str, user_svc: UserService = Depends(), service_svc: ServiceService = Depends()
uuid: str,
user_svc: UserService = Depends(),
service_svc: ServiceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
return service_svc.get_service_by_user(subject)
@api.get("/{name}", response_model=Service, tags=["Service"])
def get_by_name(
name: str, uuid: str, user_svc: UserService = Depends(), service_svc: ServiceService = Depends()
name: str,
uuid: str,
user_svc: UserService = Depends(),
service_svc: ServiceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
return service_svc.get_service_by_name(name, subject)
@api.put("", response_model=Service, tags=["Service"])
def update(
uuid: str, service: Service, user_svc: UserService = Depends(), service_svc: ServiceService = Depends()
uuid: str,
service: Service,
user_svc: UserService = Depends(),
service_svc: ServiceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
return service_svc.update(subject, service)
@api.delete("", response_model=None, tags=["Service"])
@api.delete("", response_model=dict, tags=["Service"])
def delete(
uuid: str, service: Service, user_svc: UserService = Depends(), service_svc: ServiceService = Depends()
uuid: str,
id: int,
user_svc: UserService = Depends(),
service_svc: ServiceService = Depends(),
):
subject = user_svc.get_user_by_uuid(uuid)
service_svc.delete(subject, service)
return service_svc.delete(subject, id)

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from ..services import UserService
from ..models.user_model import User, UserTypeEnum
@ -46,3 +46,14 @@ def update_user(uuid: str, user: User, user_svc: UserService = Depends()):
raise Exception(f"Insufficient permissions for user {subject.uuid}")
return user_svc.update(user)
@api.delete("/", response_model=dict, tags=["Users"])
def delete_user(uuid: str, id: int, user_svc: UserService = Depends()):
subject = user_svc.get_user_by_uuid(uuid)
try:
user_svc.delete_by_id(id, subject)
return {"message": "User deleted successfully"}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

View File

@ -93,7 +93,7 @@ class ResourceService:
self._session.commit()
return entity.to_model()
def delete(self, subject: User, resource: Resource) -> None:
def delete(self, subject: User, id: int) -> None:
"""
Delete resource based on id that the user has access to
Parameters:
@ -106,15 +106,16 @@ class ResourceService:
raise ProgramNotAssignedException(
f"User is not {UserTypeEnum.ADMIN}, cannot update service"
)
query = select(ResourceEntity).where(ResourceEntity.id == resource.id)
query = select(ResourceEntity).where(ResourceEntity.id == id)
entity = self._session.scalars(query).one_or_none()
if entity is None:
raise ResourceNotFoundException(
f"No resource found with matching id: {resource.id}"
)
raise ResourceNotFoundException(f"No resource found with matching id: {id}")
self._session.delete(entity)
self._session.commit()
return {"message": "Resource deleted successfully"}
def get_by_slug(self, user: User, search_string: str) -> list[Resource]:
"""
Get a list of resources given a search string that the user has access to

View File

@ -90,12 +90,12 @@ class ServiceService:
return entity.to_model()
def delete(self, subject: User, service: Service) -> None:
def delete(self, subject: User, id: int) -> None:
"""Deletes a service from the table."""
if subject.role != UserTypeEnum.ADMIN:
raise ProgramNotAssignedException(f"User is not {UserTypeEnum.ADMIN}")
query = select(ServiceEntity).where(ServiceEntity.id == service.id)
query = select(ServiceEntity).where(ServiceEntity.id == id)
entity = self._session.scalars(query).one_or_none()
if entity is None:
@ -105,3 +105,5 @@ class ServiceService:
self._session.delete(entity)
self._session.commit()
return {"message": "Service deleted successfully"}

View File

@ -4,6 +4,7 @@ from sqlalchemy.orm import Session
from ..entities.user_entity import UserEntity
from ..models.user_model import User
from sqlalchemy import select
from ..models.enum_for_models import UserTypeEnum
class UserService:
@ -89,6 +90,22 @@ class UserService:
self._session.delete(obj)
self._session.commit()
def delete_by_id(self, id: int, user: User) -> None:
"""
Delete a user by id
Args: the id of the user to delete
Returns: none
"""
if user.role != UserTypeEnum.ADMIN:
raise Exception(f"Insufficient permissions for user {user.uuid}")
obj = self._session.get(UserEntity, id)
self._session.delete(obj)
self._session.commit()
def update(self, user: User) -> User:
"""
Updates a user

View File

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
export async function DELETE(request: Request) {
const apiEndpoint = `${process.env.NEXT_PUBLIC_API_HOST}/api/resource`;
try {
const { searchParams } = new URL(request.url);
const uuid = searchParams.get("uuid");
const resource_id = searchParams.get("id");
console.log("Resource to be deleted", resource_id);
// Send the POST request to the backend
const response = await fetch(
`${apiEndpoint}?uuid=${uuid}&id=${resource_id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return NextResponse.json(
{ message: "Resource deleted successfully" },
{ status: response.status }
);
} catch (error) {
console.error("Error deleting resource:", error);
return NextResponse.json(
{ error: "Failed to delete resource" },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
export async function DELETE(request: Request) {
const apiEndpoint = `${process.env.NEXT_PUBLIC_API_HOST}/api/service`;
try {
const { searchParams } = new URL(request.url);
const uuid = searchParams.get("uuid");
const service_id = searchParams.get("id");
console.log("Service to be deleted", service_id);
// Send the POST request to the backend
const response = await fetch(
`${apiEndpoint}?uuid=${uuid}&id=${service_id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return NextResponse.json(
{ message: "Service deleted successfully" },
{ status: response.status }
);
} catch (error) {
console.error("Error deleting service:", error);
return NextResponse.json(
{ error: "Failed to delete service" },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
export async function DELETE(request: Request) {
const apiEndpoint = `${process.env.NEXT_PUBLIC_API_HOST}/api/user`;
try {
const { searchParams } = new URL(request.url);
const uuid = searchParams.get("uuid");
const user_id = searchParams.get("id");
console.log("User to be deleted", user_id);
// Send the POST request to the backend
const response = await fetch(
`${apiEndpoint}?uuid=${uuid}&id=${user_id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return NextResponse.json(
{ message: "User deleted successfully" },
{ status: response.status }
);
} catch (error) {
console.error("Error deleting user:", error);
return NextResponse.json(
{ error: "Failed to delete user" },
{ status: 500 }
);
}
}

View File

@ -143,6 +143,7 @@ export default function ResourceTable({
columns={columns}
details={resourceDetails}
createEndpoint={`/api/resource/create?uuid=${user?.uuid}`}
deleteEndpoint={`/api/resource/delete?uuid=${user?.uuid}`}
isAdmin={user?.role === "ADMIN"}
/>
);

View File

@ -12,7 +12,7 @@ import { useState, useEffect, useRef } from "react";
import { RowOption } from "./RowOption";
interface RowOptionMenuProps {
onDelete?: () => void;
onDelete: () => void;
onHide: () => void;
visible: boolean;
}
@ -26,6 +26,13 @@ export const RowOptionMenu = ({
const menuRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const handleDelete = () => {
if (window.confirm("Are you sure you want to delete this item?")) {
onDelete();
setMenuOpen(false);
}
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
@ -60,18 +67,11 @@ export const RowOptionMenu = ({
(!menuOpen ? " invisible" : "")
}
>
{onDelete && (
<RowOption
icon={TrashIcon}
label="Delete"
onClick={onDelete}
/>
)}
{/* <RowOption
icon={ArrowUpRightIcon}
label="Open"
onClick={() => {}}
/> */}
<RowOption
icon={TrashIcon}
label="Delete"
onClick={handleDelete}
/>
<RowOption
icon={visible ? EyeSlashIcon : EyeIcon}
label={visible ? "Hide" : "Show"}

View File

@ -195,6 +195,7 @@ export default function ServiceTable({
columns={columns}
details={serviceDetails}
createEndpoint={`/api/service/create?uuid=${user?.uuid}`}
deleteEndpoint={`/api/service/delete?uuid=${user?.uuid}`}
isAdmin={user?.role === "ADMIN"}
/>
);

View File

@ -28,6 +28,7 @@ type TableProps<T extends DataPoint> = {
columns: ColumnDef<T, any>[];
details: Details[];
createEndpoint: string;
deleteEndpoint: string;
isAdmin?: boolean;
};
@ -79,12 +80,24 @@ export default function Table<T extends DataPoint>({
columns,
details,
createEndpoint,
deleteEndpoint,
isAdmin = false,
}: TableProps<T>) {
console.log(data);
const columnHelper = createColumnHelper<T>();
const deleteRow = async (id: number) => {
const response = await fetch(`${deleteEndpoint}&id=${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
return response.ok;
};
const createRow = async (newItem: any) => {
const response = await fetch(createEndpoint, {
method: "POST",
@ -144,7 +157,23 @@ export default function Table<T extends DataPoint>({
id: "options",
cell: (props) => (
<RowOptionMenu
onDelete={() => console.log("delete")}
onDelete={() => {
deleteRow(props.row.original.id).then(
(response) => {
if (response) {
setData((prev) =>
prev.filter(
(data) =>
data.id !==
props.row.original.id
)
);
} else {
alert("Failed to delete row!");
}
}
);
}}
onHide={() => hideData(props.row.original.id)}
visible={props.row.original.visible}
/>

View File

@ -143,6 +143,7 @@ export default function UserTable({ data, setData, user }: UserTableProps) {
columns={columns}
details={userDetails}
createEndpoint={`/api/user/create?uuid=${user?.uuid}`}
deleteEndpoint={`/api/user/delete?uuid=${user?.uuid}`}
isAdmin={user?.role === "ADMIN"}
/>
);