diff --git a/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.test.ts b/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.test.ts index 883784ce0..15d44fe85 100644 --- a/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.test.ts +++ b/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.test.ts @@ -172,6 +172,82 @@ describe("Gremlin > fetchEdgeConnections", () => { ], }); }); + + it("should handle reversed key order in GraphSON map", async () => { + const reversedKeyOrderResponse = { + requestId: "test-request-id", + status: { message: "", code: 200 }, + result: { + data: { + "@type": "g:List", + "@value": [ + { + "@type": "g:Map", + "@value": ["targetType", "airport", "sourceType", "country"], + }, + ], + }, + }, + }; + const gremlinFetch = vi + .fn() + .mockResolvedValueOnce(reversedKeyOrderResponse); + + const result = await fetchEdgeConnections(gremlinFetch, { + edgeTypes: [createEdgeType("contains")], + }); + + expect(result).toStrictEqual({ + edgeConnections: [ + { + sourceVertexType: createVertexType("country"), + edgeType: createEdgeType("contains"), + targetVertexType: createVertexType("airport"), + }, + ], + }); + }); + + it("should skip entries with missing sourceType or targetType", async () => { + const missingKeysResponse = { + requestId: "test-request-id", + status: { message: "", code: 200 }, + result: { + data: { + "@type": "g:List", + "@value": [ + { + "@type": "g:Map", + "@value": ["sourceType", "airport"], + }, + { + "@type": "g:Map", + "@value": ["targetType", "airport"], + }, + { + "@type": "g:Map", + "@value": ["sourceType", "country", "targetType", "airport"], + }, + ], + }, + }, + }; + const gremlinFetch = vi.fn().mockResolvedValueOnce(missingKeysResponse); + + const result = await fetchEdgeConnections(gremlinFetch, { + edgeTypes: [createEdgeType("contains")], + }); + + expect(result).toStrictEqual({ + edgeConnections: [ + { + sourceVertexType: createVertexType("country"), + edgeType: createEdgeType("contains"), + targetVertexType: createVertexType("airport"), + }, + ], + }); + }); }); const routeResponse = { diff --git a/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.ts b/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.ts index d34fbe2cc..4365970af 100644 --- a/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.ts +++ b/packages/graph-explorer/src/connector/gremlin/fetchEdgeConnections/index.ts @@ -7,15 +7,11 @@ import { createEdgeType, createVertexType, type EdgeConnection } from "@/core"; import batchPromisesSerially from "@/utils/batchPromisesSerially"; import { DEFAULT_CONCURRENT_REQUESTS_LIMIT } from "@/utils/constants"; -import type { GremlinFetch } from "../types"; +import type { GMapWithValue, GremlinFetch } from "../types"; +import { parseGMap } from "../mappers/parseGMap"; import edgeConnectionsTemplate from "./edgeConnectionsTemplate"; -type GEdgeConnectionValue = { - "@type": "g:Map"; - "@value": Array; -}; - type RawEdgeConnectionsResponse = { requestId: string; status: { @@ -25,7 +21,7 @@ type RawEdgeConnectionsResponse = { result: { data: { "@type": "g:List"; - "@value": Array; + "@value": Array>; }; }; }; @@ -49,8 +45,14 @@ export default async function fetchEdgeConnections( for (const { edgeType, values } of results) { for (const item of values) { - // Map format: ['sourceType', value, 'targetType', value] - const [, sourceValue, , targetValue] = item["@value"]; + const map = parseGMap(item); + const sourceValue = map.get("sourceType"); + const targetValue = map.get("targetType"); + + if (!sourceValue || !targetValue) { + continue; + } + // Neptune multi-label vertices use :: delimiter const sourceTypes = sourceValue.split("::").filter(Boolean); const targetTypes = targetValue.split("::").filter(Boolean);