diff --git a/backend/src/controllers/DeviceController.ts b/backend/src/controllers/DeviceController.ts index e572438..fb57a9a 100644 --- a/backend/src/controllers/DeviceController.ts +++ b/backend/src/controllers/DeviceController.ts @@ -148,9 +148,13 @@ export class DeviceController { bookedDevice: device }); } + res.status(200).json(device); } catch (error) { - if (error instanceof Error && error.message === "ALREADY_BOOKED") { + if (error instanceof Error && error.message === "DEVICE_NOT_FOUND") { + // bypass as a success becuase it should just delete the topology + res.status(200); + } else if (error instanceof Error && error.message === "ALREADY_BOOKED") { res.status(409).json({ error: "Device already booked" }); } else { next(error); @@ -172,7 +176,10 @@ export class DeviceController { } res.status(200).json(device); } catch (error) { - if (error instanceof Error && error.message === "UNAUTHORIZED") { + if (error instanceof Error && error.message === "DEVICE_NOT_FOUND") { + // bypass as a success becuase it should just delete the topology + res.status(200); + } else if (error instanceof Error && error.message === "UNAUTHORIZED") { res.status(401).json({ error: "You are not authorized to unbook this device." }); } else { next(error); diff --git a/backend/src/repositories/PrismaDeviceRepository.ts b/backend/src/repositories/PrismaDeviceRepository.ts index d96e198..934e7d1 100644 --- a/backend/src/repositories/PrismaDeviceRepository.ts +++ b/backend/src/repositories/PrismaDeviceRepository.ts @@ -93,7 +93,10 @@ export class PrismaDeviceRepository implements IDeviceRepository { where: { id: deviceId }, }); - if (current?.userId && current.userId !== userId) { + if (!current) { + // device not found + throw new Error("DEVICE_NOT_FOUND"); + } else if (current?.userId && current.userId !== userId) { throw new Error("ALREADY_BOOKED"); } else if (current?.userId === userId) { // device is already booked by the same user, return current device @@ -116,6 +119,11 @@ export class PrismaDeviceRepository implements IDeviceRepository { select: { userId: true } }); + // device not found + if (!current) { + throw new Error("DEVICE_NOT_FOUND"); + } + // only allow unbooking of device if userIds match AND account type is not admin or owner if (accountType !== 'ADMIN' && accountType !== 'OWNER' && current?.userId !== userId) { throw new Error("UNAUTHORIZED"); diff --git a/frontend/src/hooks/useLinkOperations.ts b/frontend/src/hooks/useLinkOperations.ts index 211450e..98c7361 100644 --- a/frontend/src/hooks/useLinkOperations.ts +++ b/frontend/src/hooks/useLinkOperations.ts @@ -23,7 +23,7 @@ export function useLinkOperationsBase() { const fetchConnectionDetails = async (deviceName: string, devicePort: string) => { const conns = await authenticatedApiClient.getConnectionsByDeviceName(deviceName); return conns.data?.find(c => c.labDevicePort === devicePort); - } + }; // get interconnect information const fetchInterconnectDevice = async (connectionInfo: Connection) => { @@ -69,6 +69,21 @@ export function useLinkOperationsBase() { return devicePort; }; + const checkDevicesExist = async (firstDeviceName: string, secondDeviceName: string): Promise => { + try { + const devices = await authenticatedApiClient.getAllDevices(); + const firstDeviceMatches = devices.data?.filter(d => d.name === firstDeviceName) || []; + const secondDeviceMatches = devices.data?.filter(d => d.name === secondDeviceName) || []; + + // Return true if one or both devices don't exist + return firstDeviceMatches.length === 0 || secondDeviceMatches.length === 0; + } catch (error) { + // In case of API error, assume devices don't exist for safety + console.error("Error checking device existence:", error); + return true; + } + }; + // API operations without ReactFlow dependencies const performLinkOperation = async (params: LinkOperationParams, operation: 'create' | 'delete', createToastPerLink: boolean = true) => { const { firstDeviceName, firstDevicePort, secondDeviceName, secondDevicePort } = params; @@ -103,6 +118,13 @@ export function useLinkOperationsBase() { return false; } + // Check to see if the devices exist, if one or both don't anymore + // just show the Toast as a success and do not perform the interconnect configs + const devicesNotFound = await checkDevicesExist(firstDeviceName, secondDeviceName); + if (devicesNotFound) { + return true; // return true to indicate success + } + // Prepare link payload // Get the correct interconnect information based on the device number for the interconnect const [interconnect1, interconnect2] = firstInterconnectInfo?.deviceNumber === 1