-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve default connection logic (#734)
* Cretae type NeptuneServiceType * Move default config logic in to AppStatusLoader * Update changelog * Add some comments * Add test for invalid URLs * Rename config * Improve formatting of validation errors
- Loading branch information
Showing
9 changed files
with
283 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
packages/graph-explorer/src/core/defaultConnection.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { | ||
createRandomBoolean, | ||
createRandomInteger, | ||
createRandomName, | ||
createRandomUrlString, | ||
} from "@shared/utils/testing"; | ||
import { | ||
DefaultConnectionDataSchema, | ||
mapToConnection, | ||
} from "./defaultConnection"; | ||
import { | ||
createRandomAwsRegion, | ||
createRandomQueryEngine, | ||
createRandomServiceType, | ||
} from "@/utils/testing"; | ||
|
||
describe("mapToConnection", () => { | ||
test("should map default connection data to connection config", () => { | ||
const defaultConnectionData = createRandomDefaultConnectionData(); | ||
const actual = mapToConnection(defaultConnectionData); | ||
expect(actual).toEqual({ | ||
id: "Default Connection", | ||
displayLabel: "Default Connection", | ||
connection: { | ||
graphDbUrl: defaultConnectionData.GRAPH_EXP_CONNECTION_URL, | ||
url: defaultConnectionData.GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT, | ||
proxyConnection: defaultConnectionData.GRAPH_EXP_USING_PROXY_SERVER, | ||
queryEngine: defaultConnectionData.GRAPH_EXP_GRAPH_TYPE, | ||
awsAuthEnabled: defaultConnectionData.GRAPH_EXP_IAM, | ||
awsRegion: defaultConnectionData.GRAPH_EXP_AWS_REGION, | ||
serviceType: defaultConnectionData.GRAPH_EXP_SERVICE_TYPE, | ||
fetchTimeoutMs: defaultConnectionData.GRAPH_EXP_FETCH_REQUEST_TIMEOUT, | ||
nodeExpansionLimit: | ||
defaultConnectionData.GRAPH_EXP_NODE_EXPANSION_LIMIT, | ||
}, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("DefaultConnectionDataSchema", () => { | ||
test("should parse default connection data", () => { | ||
const data = createRandomDefaultConnectionData(); | ||
const actual = DefaultConnectionDataSchema.parse(data); | ||
expect(actual).toEqual(data); | ||
}); | ||
|
||
test("should handle missing values", () => { | ||
const data = {}; | ||
const actual = DefaultConnectionDataSchema.parse(data); | ||
expect(actual).toEqual({ | ||
GRAPH_EXP_USING_PROXY_SERVER: false, | ||
GRAPH_EXP_CONNECTION_URL: "", | ||
GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT: "", | ||
GRAPH_EXP_IAM: false, | ||
GRAPH_EXP_AWS_REGION: "", | ||
GRAPH_EXP_SERVICE_TYPE: "neptune-db", | ||
GRAPH_EXP_FETCH_REQUEST_TIMEOUT: 240000, | ||
}); | ||
}); | ||
|
||
test("should handle invalid service type", () => { | ||
const data: any = createRandomDefaultConnectionData(); | ||
data.GRAPH_EXP_SERVICE_TYPE = createRandomName("serviceType"); | ||
// Make the enum less strict | ||
const actual = DefaultConnectionDataSchema.parse(data); | ||
expect(actual).toEqual({ ...data, GRAPH_EXP_SERVICE_TYPE: "neptune-db" }); | ||
}); | ||
|
||
test("should handle invalid URLs", () => { | ||
const data: any = createRandomDefaultConnectionData(); | ||
data.GRAPH_EXP_CONNECTION_URL = createRandomName("connectionURL"); | ||
data.GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT = createRandomName( | ||
"publicOrProxyEndpoint" | ||
); | ||
// Make the enum less strict | ||
const actual = DefaultConnectionDataSchema.parse(data); | ||
expect(actual).toEqual({ | ||
...data, | ||
GRAPH_EXP_CONNECTION_URL: "", | ||
GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT: "", | ||
}); | ||
}); | ||
}); | ||
|
||
function createRandomDefaultConnectionData() { | ||
return { | ||
GRAPH_EXP_USING_PROXY_SERVER: createRandomBoolean(), | ||
GRAPH_EXP_CONNECTION_URL: createRandomUrlString(), | ||
GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT: createRandomUrlString(), | ||
GRAPH_EXP_GRAPH_TYPE: createRandomQueryEngine(), | ||
GRAPH_EXP_IAM: createRandomBoolean(), | ||
GRAPH_EXP_AWS_REGION: createRandomAwsRegion(), | ||
GRAPH_EXP_SERVICE_TYPE: createRandomServiceType(), | ||
GRAPH_EXP_FETCH_REQUEST_TIMEOUT: createRandomInteger(), | ||
GRAPH_EXP_NODE_EXPANSION_LIMIT: createRandomInteger(), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { logger, DEFAULT_SERVICE_TYPE } from "@/utils"; | ||
import { queryEngineOptions, neptuneServiceTypeOptions } from "@shared/types"; | ||
import { z } from "zod"; | ||
import { RawConfiguration } from "./ConfigurationProvider"; | ||
|
||
export const DefaultConnectionDataSchema = z.object({ | ||
// Connection info | ||
GRAPH_EXP_USING_PROXY_SERVER: z.boolean().default(false), | ||
GRAPH_EXP_CONNECTION_URL: z.string().url().catch(""), | ||
GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT: z.string().url().catch(""), | ||
GRAPH_EXP_GRAPH_TYPE: z.enum(queryEngineOptions).optional(), | ||
// IAM auth info | ||
GRAPH_EXP_IAM: z.boolean().default(false), | ||
GRAPH_EXP_AWS_REGION: z.string().optional().default(""), | ||
GRAPH_EXP_SERVICE_TYPE: z | ||
.enum(neptuneServiceTypeOptions) | ||
.default(DEFAULT_SERVICE_TYPE) | ||
.catch(DEFAULT_SERVICE_TYPE), | ||
// Connection options | ||
GRAPH_EXP_FETCH_REQUEST_TIMEOUT: z.number().default(240000), | ||
GRAPH_EXP_NODE_EXPANSION_LIMIT: z.number().optional(), | ||
}); | ||
|
||
export type DefaultConnectionData = z.infer<typeof DefaultConnectionDataSchema>; | ||
|
||
/** Fetches the default connection from multiple possible locations and returns null on failure. */ | ||
export async function fetchDefaultConnection(): Promise<RawConfiguration | null> { | ||
const defaultConnectionPath = `${location.origin}/defaultConnection`; | ||
const sagemakerConnectionPath = `${location.origin}/proxy/9250/defaultConnection`; | ||
|
||
try { | ||
const defaultConnection = | ||
(await fetchDefaultConnectionFor(defaultConnectionPath)) ?? | ||
(await fetchDefaultConnectionFor(sagemakerConnectionPath)); | ||
if (!defaultConnection) { | ||
logger.debug("No default connection found"); | ||
return null; | ||
} | ||
const config = mapToConnection(defaultConnection); | ||
logger.debug("Default connection created", config); | ||
|
||
return config; | ||
} catch (error) { | ||
logger.error( | ||
`Error when trying to create connection: ${error instanceof Error ? error.message : "Unexpected error"}` | ||
); | ||
return null; | ||
} | ||
} | ||
|
||
/** Attempts to fetch a default connection from the given URL and returns null on a failure. */ | ||
export async function fetchDefaultConnectionFor( | ||
url: string | ||
): Promise<DefaultConnectionData | null> { | ||
try { | ||
logger.debug("Fetching default connection from", url); | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
const responseText = await response.text(); | ||
logger.warn( | ||
`Response status ${response.status} for default connection url`, | ||
url, | ||
responseText | ||
); | ||
return null; | ||
} | ||
const data = await response.json(); | ||
logger.debug("Default connection data for url", url, data); | ||
const result = DefaultConnectionDataSchema.safeParse(data); | ||
if (result.success) { | ||
return result.data; | ||
} else { | ||
logger.warn( | ||
"Failed to parse default connection data", | ||
result.error.flatten() | ||
); | ||
return null; | ||
} | ||
} catch (error) { | ||
logger.warn("Failed to fetch default connection for path", url, error); | ||
return null; | ||
} | ||
} | ||
|
||
export function mapToConnection(data: DefaultConnectionData): RawConfiguration { | ||
const config: RawConfiguration = { | ||
id: "Default Connection", | ||
displayLabel: "Default Connection", | ||
connection: { | ||
url: data.GRAPH_EXP_PUBLIC_OR_PROXY_ENDPOINT, | ||
queryEngine: data.GRAPH_EXP_GRAPH_TYPE, | ||
proxyConnection: data.GRAPH_EXP_USING_PROXY_SERVER, | ||
graphDbUrl: data.GRAPH_EXP_CONNECTION_URL, | ||
awsAuthEnabled: data.GRAPH_EXP_IAM, | ||
awsRegion: data.GRAPH_EXP_AWS_REGION, | ||
serviceType: data.GRAPH_EXP_SERVICE_TYPE, | ||
fetchTimeoutMs: data.GRAPH_EXP_FETCH_REQUEST_TIMEOUT, | ||
nodeExpansionLimit: data.GRAPH_EXP_NODE_EXPANSION_LIMIT, | ||
}, | ||
}; | ||
return config; | ||
} |
Oops, something went wrong.