Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions backend/src/controllers/DeviceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion backend/src/repositories/PrismaDeviceRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
Expand Down
24 changes: 23 additions & 1 deletion frontend/src/hooks/useLinkOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -69,6 +69,21 @@ export function useLinkOperationsBase() {
return devicePort;
};

const checkDevicesExist = async (firstDeviceName: string, secondDeviceName: string): Promise<boolean> => {
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;
Expand Down Expand Up @@ -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
Expand Down