rhombus-node-mcp
Version:
MCP server for Rhombus API
207 lines (206 loc) • 7.96 kB
JavaScript
import z from "zod";
import { postApi, throwIfApiError } from "../network/network.js";
import createUuidMap from "../network/postApiMap.js";
import { getAccessControlledDoors } from "./get-entity-tool-api.js";
export const DateRangeFilter = z.object({
localStartDateRangeStart: z.iso.datetime({ offset: true }).optional(),
localStartDateRangeEnd: z.iso.datetime({ offset: true }).optional(),
localEndDateRangeStart: z.iso.datetime({ offset: true }).optional(),
localEndDateRangeEnd: z.iso.datetime({ offset: true }).optional(),
});
function mapException(exception) {
const doorUuids = exception?.doorUuids?.filter((value) => value !== null) ??
[];
const intervalCount = Array.isArray(exception?.intervals)
? exception.intervals.length
: 0;
return {
uuid: exception?.uuid ?? undefined,
name: exception?.name ?? undefined,
description: exception?.description ?? undefined,
locationUuid: exception?.locationUuid ?? undefined,
localStartDate: exception?.localStartDate ?? undefined,
localEndDate: exception?.localEndDate ?? undefined,
defaultState: exception?.defaultState ?? undefined,
doorUuids,
intervalCount,
createdAtMillis: exception?.createdAtMillis ?? undefined,
updatedAtMillis: exception?.updatedAtMillis ?? undefined,
};
}
function buildDateRangeFilter(filter) {
if (!filter)
return undefined;
const { localStartDateRangeStart, localStartDateRangeEnd, localEndDateRangeStart, localEndDateRangeEnd, } = filter;
const hasAnyFilter = Boolean(localStartDateRangeStart ||
localStartDateRangeEnd ||
localEndDateRangeStart ||
localEndDateRangeEnd);
if (!hasAnyFilter)
return undefined;
return {
localStartDateRangeStart,
localStartDateRangeEnd,
localEndDateRangeStart,
localEndDateRangeEnd,
};
}
async function verifyExceptionConfig(exception, options, requestModifiers, sessionId) {
const verified = {
...exception,
};
const doorUuidsProvided = Array.isArray(exception.doorUuids);
const cleanedDoorUuids = doorUuidsProvided
? (exception.doorUuids?.filter((doorUuid) => !!doorUuid) ?? [])
: undefined;
if (cleanedDoorUuids !== undefined) {
verified.doorUuids = cleanedDoorUuids;
}
if ((!Array.isArray(exception.intervals) || exception.intervals.length === 0) &&
exception.defaultState &&
exception.localStartDate &&
exception.localEndDate) {
verified.intervals = [
{
localStartDateTime: `${exception.localStartDate}T00:00:00`,
localEndDateTime: `${exception.localEndDate}T23:59:59`,
state: exception.defaultState,
},
];
}
// construct locationToDoorsMap only when doors are explicitly provided.
// For update, omitting doorUuids should preserve the existing map.
if (cleanedDoorUuids !== undefined) {
const locationToDoorsMap = {};
if (cleanedDoorUuids.length > 0) {
const doors = await getAccessControlledDoors(requestModifiers, sessionId);
const doorsMap = await createUuidMap(doors.accessControlledDoors ?? [], "uuid");
for (const doorUuid of cleanedDoorUuids) {
const door = doorsMap.get(doorUuid);
const locationUuid = door?.locationUuid;
if (locationUuid) {
if (!locationToDoorsMap[locationUuid]) {
locationToDoorsMap[locationUuid] = [];
}
locationToDoorsMap[locationUuid].push(doorUuid);
}
}
}
verified.locationToDoorsMap = locationToDoorsMap;
}
else if (!options.isUpdate) {
verified.locationToDoorsMap = {};
}
return verified;
}
export async function createDoorScheduleException(exception, requestModifiers, sessionId) {
const normalizedException = await verifyExceptionConfig(exception, { isUpdate: false }, requestModifiers, sessionId);
const res = await postApi({
route: "/accesscontrol/doorScheduleException/createExceptionV2",
body: {
exception: normalizedException,
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exception: res.exception ? mapException(res.exception) : undefined,
warningMsg: res.warningMsg ?? undefined,
};
}
export async function updateDoorScheduleException(exception, requestModifiers, sessionId) {
const normalizedException = await verifyExceptionConfig(exception, { isUpdate: true }, requestModifiers, sessionId);
const res = await postApi({
route: "/accesscontrol/doorScheduleException/updateExceptionV2",
body: {
exception: normalizedException,
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exception: res.exception ? mapException(res.exception) : undefined,
expiredACDLicensesDoorUuids: res.expiredACDLicensesDoorUuids?.filter((value) => value !== null) ?? [],
unassignedACDLicensesDoorUuids: res.unassignedACDLicensesDoorUuids?.filter((value) => value !== null) ?? [],
warningMsg: res.warningMsg ?? undefined,
};
}
export async function deleteDoorScheduleException(exceptionUuid, requestModifiers, sessionId) {
const res = await postApi({
route: "/accesscontrol/doorScheduleException/deleteExceptionV2",
body: {
exceptionUuid,
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
deleted: { success: true, exceptionUuid },
warningMsg: res.warningMsg ?? undefined,
};
}
export async function getDoorScheduleException(exceptionUuid, requestModifiers, sessionId) {
const res = await postApi({
route: "/accesscontrol/doorScheduleException/getExceptionV2",
body: {
exceptionUuid,
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exception: res.exception ? mapException(res.exception) : undefined,
warningMsg: res.warningMsg ?? undefined,
};
}
export async function findDoorScheduleExceptions(filter, requestModifiers, sessionId) {
const res = await postApi({
route: "/accesscontrol/doorScheduleException/findExceptionsV2",
body: {
dateRangeFilter: buildDateRangeFilter(filter),
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exceptions: res.exceptions?.map(mapException) ?? [],
warningMsg: res.warningMsg ?? undefined,
};
}
export async function findDoorScheduleExceptionsForLocation(locationUuid, filter, requestModifiers, sessionId) {
const res = await postApi({
route: "/accesscontrol/doorScheduleException/findExceptionsForLocationV2",
body: {
locationUuid,
dateRangeFilter: buildDateRangeFilter(filter),
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exceptions: res.exceptions?.map(mapException) ?? [],
warningMsg: res.warningMsg ?? undefined,
};
}
export async function findDoorScheduleExceptionsForDoor(doorUuid, filter, requestModifiers, sessionId) {
const res = await postApi({
route: "/accesscontrol/doorScheduleException/findExceptionsForDoor",
body: {
doorUuid,
dateRangeFilter: buildDateRangeFilter(filter),
},
modifiers: requestModifiers,
sessionId,
});
throwIfApiError(res);
return {
exceptions: res.exceptions?.map(mapException) ?? [],
warningMsg: res.warningMsg ?? undefined,
};
}