Skip to content

Commit f7406fb

Browse files
committed
Add PatchEntityDetailsError
1 parent 4f8fed3 commit f7406fb

File tree

4 files changed

+132
-6
lines changed

4 files changed

+132
-6
lines changed

packages/graph-explorer/src/connector/queries/executeUserQuery.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ describe("executeUserQuery", () => {
165165
executeUserQuery("query", mockUpdateSchema)
166166
);
167167

168-
await expect(result).rejects.toThrow("Failed to fetch entity details");
168+
await expect(result).rejects.toThrow(
169+
"Failed to fetch the details of 2 vertices and 1 edge."
170+
);
169171
expect(rawQuerySpy).toBeCalledTimes(1);
170172
expect(vertexDetailsSpy).toBeCalledTimes(1);
171173
expect(edgeDetailsSpy).toBeCalledTimes(1);

packages/graph-explorer/src/connector/queries/patchEntityDetails.test.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import {
33
createTestableVertex,
44
FakeExplorer,
55
} from "@/utils/testing";
6-
import { patchEntityDetails } from "./patchEntityDetails";
6+
import {
7+
patchEntityDetails,
8+
PatchEntityDetailsError,
9+
} from "./patchEntityDetails";
710
import { createQueryClient } from "@/core/queryClient";
811
import {
912
createResultScalar,
@@ -222,4 +225,77 @@ describe("patchEntityDetails", () => {
222225

223226
expect(result).toStrictEqual([expectedBundle]);
224227
});
228+
229+
it("should throw when source vertex not found", async () => {
230+
const explorer = new FakeExplorer();
231+
const client = createQueryClient({ explorer });
232+
233+
const edge = createTestableEdge();
234+
explorer.addTestableEdge(edge);
235+
explorer.vertexMap.delete(edge.source.id);
236+
237+
await expect(() =>
238+
patchEntityDetails(client, [edge.asFragmentResult()])
239+
).rejects.toThrow(
240+
new PatchEntityDetailsError("Failed to fetch the details of 1 vertex.")
241+
);
242+
});
243+
244+
it("should throw when target vertex not found", async () => {
245+
const explorer = new FakeExplorer();
246+
const client = createQueryClient({ explorer });
247+
248+
const edge = createTestableEdge();
249+
explorer.addTestableEdge(edge);
250+
explorer.vertexMap.delete(edge.target.id);
251+
252+
await expect(() =>
253+
patchEntityDetails(client, [edge.asFragmentResult()])
254+
).rejects.toThrow(
255+
new PatchEntityDetailsError("Failed to fetch the details of 1 vertex.")
256+
);
257+
});
258+
259+
it("should throw when edge full details not found", async () => {
260+
const explorer = new FakeExplorer();
261+
const client = createQueryClient({ explorer });
262+
263+
const edge = createTestableEdge();
264+
explorer.addTestableEdge(edge);
265+
explorer.edgeMap.delete(edge.id);
266+
267+
await expect(() =>
268+
patchEntityDetails(client, [edge.asFragmentResult()])
269+
).rejects.toThrow(
270+
new PatchEntityDetailsError("Failed to fetch the details of 1 edge.")
271+
);
272+
});
273+
274+
it("should throw when multiple vertices and edges are not found", async () => {
275+
const explorer = new FakeExplorer();
276+
const client = createQueryClient({ explorer });
277+
278+
const vertex = createTestableVertex();
279+
const edge1 = createTestableEdge();
280+
const edge2 = createTestableEdge();
281+
explorer.addTestableVertex(vertex);
282+
explorer.addTestableEdge(edge1);
283+
explorer.addTestableEdge(edge2);
284+
explorer.vertexMap.delete(vertex.id);
285+
explorer.vertexMap.delete(edge1.source.id);
286+
explorer.edgeMap.delete(edge1.id);
287+
explorer.edgeMap.delete(edge2.id);
288+
289+
await expect(() =>
290+
patchEntityDetails(client, [
291+
vertex.asFragmentResult(),
292+
edge1.asFragmentResult(),
293+
edge2.asFragmentResult(),
294+
])
295+
).rejects.toThrow(
296+
new PatchEntityDetailsError(
297+
"Failed to fetch the details of 2 vertices and 2 edges."
298+
)
299+
);
300+
});
225301
});

packages/graph-explorer/src/connector/queries/patchEntityDetails.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,30 @@ export async function patchEntityDetails(
3535

3636
// Ensure all details are fetched
3737
if (missingVertices.length > 0 || missingEdges.length > 0) {
38+
const vertexCount =
39+
missingVertices.length === 0
40+
? null
41+
: missingVertices.length === 1
42+
? "1 vertex"
43+
: `${missingVertices.length} vertices`;
44+
const edgeCount =
45+
missingEdges.length === 0
46+
? null
47+
: missingEdges.length === 1
48+
? "1 edge"
49+
: `${missingEdges.length} edges`;
50+
51+
const missingCount =
52+
vertexCount && edgeCount
53+
? `${vertexCount} and ${edgeCount}`
54+
: vertexCount || edgeCount || "";
3855
logger.error("Failed to fetch fragment entity details", {
3956
missingVertices,
4057
missingEdges,
4158
});
42-
throw new Error("Failed to fetch entity details");
59+
throw new PatchEntityDetailsError(
60+
`Failed to fetch the details of ${missingCount}.`
61+
);
4362
}
4463

4564
// Create a mapping from vertex ID to full vertex details
@@ -76,7 +95,7 @@ function patchVertex(
7695
const fullVertex = vertexDetailsMap.get(vertex.id);
7796

7897
if (!fullVertex) {
79-
throw new Error("Failed to fetch vertex details");
98+
throw new PatchEntityDetailsError("Failed to fetch vertex details");
8099
}
81100

82101
return createPatchedResultVertex({
@@ -94,8 +113,21 @@ function patchEdge(
94113
const fullSource = vertexDetailsMap.get(edge.sourceId);
95114
const fullTarget = vertexDetailsMap.get(edge.targetId);
96115

97-
if (!fullEdge || !fullSource || !fullTarget) {
98-
throw new Error("Failed to fetch edge details");
116+
if (!fullEdge) {
117+
throw new PatchEntityDetailsError(
118+
"Could not find the full details of the edge"
119+
);
120+
}
121+
122+
if (!fullSource) {
123+
throw new PatchEntityDetailsError(
124+
"Could not find the full details of the source vertex"
125+
);
126+
}
127+
if (!fullTarget) {
128+
throw new PatchEntityDetailsError(
129+
"Could not find the full details of the target vertex"
130+
);
99131
}
100132

101133
return createPatchedResultEdge({
@@ -118,3 +150,11 @@ function patchBundle(
118150
),
119151
};
120152
}
153+
154+
export class PatchEntityDetailsError extends Error {
155+
constructor(message: string) {
156+
super(message);
157+
this.name = "PatchEntityDetailsError";
158+
Object.setPrototypeOf(this, PatchEntityDetailsError.prototype);
159+
}
160+
}

packages/graph-explorer/src/utils/createDisplayError.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ZodError } from "zod";
22
import { NetworkError } from "./NetworkError";
33
import { isCancellationError } from "./isCancellationError";
4+
import { PatchEntityDetailsError } from "@/connector/queries/patchEntityDetails";
45

56
export type DisplayError = {
67
title: string;
@@ -118,6 +119,13 @@ export function createDisplayError(error: any): DisplayError {
118119
};
119120
}
120121

122+
if (error instanceof PatchEntityDetailsError) {
123+
return {
124+
title: "Failed to update entity details",
125+
message: error.message,
126+
};
127+
}
128+
121129
if (error instanceof ZodError) {
122130
return {
123131
title: "Unrecognized Result Format",

0 commit comments

Comments
 (0)