fix(api): harden broadcast and assistant fallback errors
This commit is contained in:
@@ -463,11 +463,6 @@ function toAssistantTimelineMutationError(
|
||||
error: unknown,
|
||||
context: "updateInline" | "applyShift" | "quickAssign" | "batchShift",
|
||||
): AssistantToolErrorResult | null {
|
||||
const allocationNotFound = toAssistantAllocationNotFoundError(error);
|
||||
if (allocationNotFound && (context === "updateInline" || context === "batchShift")) {
|
||||
return allocationNotFound;
|
||||
}
|
||||
|
||||
if (error instanceof TRPCError) {
|
||||
if (error.code === "NOT_FOUND") {
|
||||
if (error.message.includes("Resource not found")) {
|
||||
@@ -489,6 +484,11 @@ function toAssistantTimelineMutationError(
|
||||
}
|
||||
}
|
||||
|
||||
const allocationNotFound = toAssistantAllocationNotFoundError(error);
|
||||
if (allocationNotFound && (context === "updateInline" || context === "batchShift")) {
|
||||
return allocationNotFound;
|
||||
}
|
||||
|
||||
const prismaError = getPrismaRequestErrorMetadata(error);
|
||||
if (!prismaError) {
|
||||
return null;
|
||||
@@ -1115,21 +1115,11 @@ function toAssistantEstimateMutationError(
|
||||
return null;
|
||||
}
|
||||
|
||||
const errorText = `${prismaError.message} ${prismaError.metaText}`.toLowerCase();
|
||||
if (errorText.includes("project")) {
|
||||
return { error: "Project not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("estimatedemandline") || errorText.includes("estimate_demand_line") || errorText.includes("estimate demand line")) {
|
||||
return { error: "Estimate demand line not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("estimateversion") || errorText.includes("estimate_version") || errorText.includes("estimate version")) {
|
||||
return { error: "Estimate version not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("estimate")) {
|
||||
return { error: "Estimate not found with the given criteria." };
|
||||
}
|
||||
|
||||
if (prismaError.code === "P2003") {
|
||||
const errorText = `${prismaError.message} ${prismaError.metaText}`.toLowerCase();
|
||||
if (errorText.includes("project")) {
|
||||
return { error: "Project not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("role")) {
|
||||
return { error: "Role not found with the given criteria." };
|
||||
}
|
||||
@@ -1139,9 +1129,20 @@ function toAssistantEstimateMutationError(
|
||||
if (errorText.includes("scopeitem") || errorText.includes("scope_item") || errorText.includes("scope item")) {
|
||||
return { error: "Estimate scope item not found with the given criteria." };
|
||||
}
|
||||
return { error: "One of the referenced project, role, resource, or scope items no longer exists." };
|
||||
}
|
||||
|
||||
if (prismaError.code === "P2025") {
|
||||
const errorText = `${prismaError.message} ${prismaError.metaText}`.toLowerCase();
|
||||
if (errorText.includes("estimatedemandline") || errorText.includes("estimate_demand_line") || errorText.includes("estimate demand line")) {
|
||||
return { error: "Estimate demand line not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("estimateversion") || errorText.includes("estimate_version") || errorText.includes("estimate version")) {
|
||||
return { error: "Estimate version not found with the given criteria." };
|
||||
}
|
||||
if (errorText.includes("estimate")) {
|
||||
return { error: "Estimate not found with the given criteria." };
|
||||
}
|
||||
switch (action) {
|
||||
case "generateWeeklyPhasing":
|
||||
return { error: "Estimate demand line not found with the given criteria." };
|
||||
@@ -1631,19 +1632,97 @@ function getPrismaRequestErrorMetadata(error: unknown): {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getTrpcErrorMetadata(error: unknown): {
|
||||
code: string;
|
||||
message: string;
|
||||
} | null {
|
||||
const queue: unknown[] = [error];
|
||||
const visited = new Set<unknown>();
|
||||
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift();
|
||||
if (current === undefined || current === null || visited.has(current)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(current);
|
||||
|
||||
if (current instanceof TRPCError) {
|
||||
return {
|
||||
code: current.code,
|
||||
message: current.message,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof current !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const candidate = current as {
|
||||
code?: unknown;
|
||||
message?: unknown;
|
||||
cause?: unknown;
|
||||
data?: { code?: unknown };
|
||||
shape?: { code?: unknown; message?: unknown };
|
||||
};
|
||||
|
||||
const candidateCode = typeof candidate.code === "string"
|
||||
? candidate.code
|
||||
: typeof candidate.data?.code === "string"
|
||||
? candidate.data.code
|
||||
: typeof candidate.shape?.code === "string"
|
||||
? candidate.shape.code
|
||||
: null;
|
||||
const candidateMessage = typeof candidate.message === "string"
|
||||
? candidate.message
|
||||
: typeof candidate.shape?.message === "string"
|
||||
? candidate.shape.message
|
||||
: "";
|
||||
|
||||
if (candidateCode && /^[A-Z_]+$/.test(candidateCode)) {
|
||||
return {
|
||||
code: candidateCode,
|
||||
message: candidateMessage,
|
||||
};
|
||||
}
|
||||
|
||||
if ("cause" in candidate) {
|
||||
queue.push(candidate.cause);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function toAssistantNotificationCreationError(
|
||||
error: unknown,
|
||||
context: "notification" | "task" | "broadcast",
|
||||
): AssistantToolErrorResult | null {
|
||||
const trpcError = getTrpcErrorMetadata(error);
|
||||
|
||||
if (
|
||||
context === "broadcast"
|
||||
&& error instanceof TRPCError
|
||||
&& error.code === "BAD_REQUEST"
|
||||
&& error.message === "No recipients matched the broadcast target."
|
||||
&& trpcError?.code === "BAD_REQUEST"
|
||||
&& trpcError.message === "No recipients matched the broadcast target."
|
||||
) {
|
||||
return { error: "No recipients matched the broadcast target." };
|
||||
}
|
||||
|
||||
if (trpcError?.code === "NOT_FOUND") {
|
||||
if (trpcError.message.includes("Sender user not found")) {
|
||||
return { error: "Sender user not found with the given criteria." };
|
||||
}
|
||||
if (trpcError.message.includes("Assignee user not found")) {
|
||||
return { error: "Assignee user not found with the given criteria." };
|
||||
}
|
||||
if (trpcError.message.includes("recipient")) {
|
||||
return context === "broadcast"
|
||||
? { error: "Broadcast recipient user not found with the given criteria." }
|
||||
: context === "task"
|
||||
? { error: "Task recipient user not found with the given criteria." }
|
||||
: { error: "Notification recipient user not found with the given criteria." };
|
||||
}
|
||||
}
|
||||
|
||||
const prismaError = getPrismaRequestErrorMetadata(error);
|
||||
if (!prismaError) {
|
||||
return null;
|
||||
@@ -1681,20 +1760,21 @@ function normalizeAssistantExecutionError(
|
||||
return { error: error.message };
|
||||
}
|
||||
|
||||
if (error instanceof TRPCError) {
|
||||
if (error.code === "INTERNAL_SERVER_ERROR") {
|
||||
const trpcError = getTrpcErrorMetadata(error);
|
||||
if (trpcError) {
|
||||
if (trpcError.code === "INTERNAL_SERVER_ERROR") {
|
||||
return {
|
||||
error: "The tool could not complete due to an internal error.",
|
||||
};
|
||||
}
|
||||
|
||||
if (error.code === "UNAUTHORIZED") {
|
||||
if (trpcError.code === "UNAUTHORIZED") {
|
||||
return {
|
||||
error: "Authentication is required to use this tool.",
|
||||
};
|
||||
}
|
||||
|
||||
if (error.code === "FORBIDDEN") {
|
||||
if (trpcError.code === "FORBIDDEN") {
|
||||
return {
|
||||
error: "You do not have permission to perform this action.",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user