feat(jdbc): add managed Apache Phoenix profile
This commit is contained in:
1 parent
c835deb3f9
commit
69f4450aee
38 files changed
+1651
-77
No files matched your search
@@ -0,0 +1,232 @@
|
||||
import type { ConnectionConfig, DatabaseType, JdbcMavenBundleInfo } from "@/types/database";
|
||||
import type { ManagedJdbcDriverDefinition } from "@/lib/database/managedJdbcDriver";
|
||||
|
||||
export type JdbcProductConnectionFields = {
|
||||
connectionString: string;
|
||||
driverClass: string;
|
||||
};
|
||||
|
||||
export type JdbcProductConnectionFieldsByMode = Record<string, JdbcProductConnectionFields>;
|
||||
|
||||
export type JdbcProductModeDefinition = {
|
||||
id: string;
|
||||
labelKey: string;
|
||||
hintKey: string;
|
||||
defaultConnectionString: string;
|
||||
defaultDriverClass: string;
|
||||
managedCoordinates: readonly string[];
|
||||
};
|
||||
|
||||
export type JdbcProductProfileDefinition = {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
category: string;
|
||||
port: number;
|
||||
user: string;
|
||||
match: {
|
||||
dbType: DatabaseType;
|
||||
driverProfile?: string;
|
||||
};
|
||||
managedDriverId: string;
|
||||
runtimeLabelKey: string;
|
||||
driverManagerHintPrefixKey: string;
|
||||
driverManagerHintSuffixKey: string;
|
||||
docsLabelKey: string;
|
||||
docsUrl: string;
|
||||
missingDriverError: string;
|
||||
missingPluginError: string;
|
||||
modes: readonly JdbcProductModeDefinition[];
|
||||
detectMode: (config: Partial<Pick<ConnectionConfig, "connection_string" | "jdbc_driver_class">>) => string;
|
||||
isCompatibleRuntimePath: (path: string, mode: string) => boolean;
|
||||
};
|
||||
|
||||
export type JdbcProductRuntimeApi = {
|
||||
jdbcPluginStatus: () => Promise<{ installed: boolean; compatible: boolean }>;
|
||||
listJdbcMavenBundles: () => Promise<JdbcMavenBundleInfo[]>;
|
||||
};
|
||||
|
||||
export type JdbcProductRuntimeResult = {
|
||||
profile: JdbcProductProfileDefinition;
|
||||
mode: JdbcProductModeDefinition;
|
||||
bundles: JdbcMavenBundleInfo[];
|
||||
paths: string[];
|
||||
runtimeSelectionId?: string;
|
||||
};
|
||||
|
||||
export type JdbcProductRuntimeErrorKind = "plugin-missing" | "driver-missing";
|
||||
|
||||
export class JdbcProductRuntimeError extends Error {
|
||||
readonly kind: JdbcProductRuntimeErrorKind;
|
||||
readonly profileId: string;
|
||||
readonly managedDriverId: string;
|
||||
|
||||
constructor(kind: JdbcProductRuntimeErrorKind, profile: JdbcProductProfileDefinition) {
|
||||
super(kind === "plugin-missing" ? profile.missingPluginError : profile.missingDriverError);
|
||||
this.name = "JdbcProductRuntimeError";
|
||||
this.kind = kind;
|
||||
this.profileId = profile.id;
|
||||
this.managedDriverId = profile.managedDriverId;
|
||||
}
|
||||
}
|
||||
|
||||
export type JdbcProductProfileRegistry = {
|
||||
profiles: readonly JdbcProductProfileDefinition[];
|
||||
byId: ReadonlyMap<string, JdbcProductProfileDefinition>;
|
||||
};
|
||||
|
||||
export function createJdbcProductProfileRegistry(profiles: readonly JdbcProductProfileDefinition[]): JdbcProductProfileRegistry {
|
||||
const byId = new Map<string, JdbcProductProfileDefinition>();
|
||||
const matcherKeys = new Set<string>();
|
||||
for (const profile of profiles) {
|
||||
if (!profile.id.trim()) throw new Error("JDBC product profile id must not be empty");
|
||||
if (byId.has(profile.id)) throw new Error(`Duplicate JDBC product profile id: ${profile.id}`);
|
||||
const matcherKey = `${profile.match.dbType}:${profile.match.driverProfile ?? "*"}`;
|
||||
if (matcherKeys.has(matcherKey)) throw new Error(`Duplicate JDBC product profile matcher: ${matcherKey}`);
|
||||
if (profile.modes.length === 0) throw new Error(`JDBC product profile must define at least one mode: ${profile.id}`);
|
||||
byId.set(profile.id, profile);
|
||||
matcherKeys.add(matcherKey);
|
||||
}
|
||||
return { profiles: [...profiles], byId };
|
||||
}
|
||||
|
||||
export function jdbcProductProfileMatches(profile: JdbcProductProfileDefinition, config: Pick<ConnectionConfig, "db_type" | "driver_profile">): boolean {
|
||||
return config.db_type === profile.match.dbType && (profile.match.driverProfile === undefined || config.driver_profile === profile.match.driverProfile);
|
||||
}
|
||||
|
||||
export function jdbcProductMode(profile: JdbcProductProfileDefinition, modeId: string): JdbcProductModeDefinition {
|
||||
const mode = profile.modes.find((candidate) => candidate.id === modeId) ?? profile.modes[0];
|
||||
if (!mode) throw new Error(`JDBC product profile has no modes: ${profile.id}`);
|
||||
return mode;
|
||||
}
|
||||
|
||||
export function jdbcProductConnectionDefaults(profile: JdbcProductProfileDefinition, modeId: string): JdbcProductConnectionFields {
|
||||
const mode = jdbcProductMode(profile, modeId);
|
||||
return {
|
||||
connectionString: mode.defaultConnectionString,
|
||||
driverClass: mode.defaultDriverClass,
|
||||
};
|
||||
}
|
||||
|
||||
export function createJdbcProductConnectionFieldsByMode(profile: JdbcProductProfileDefinition, config?: Partial<Pick<ConnectionConfig, "connection_string" | "jdbc_driver_class">>): JdbcProductConnectionFieldsByMode {
|
||||
const fields = Object.fromEntries(profile.modes.map((mode) => [mode.id, jdbcProductConnectionDefaults(profile, mode.id)]));
|
||||
if (!config) return fields;
|
||||
|
||||
const modeId = profile.detectMode(config);
|
||||
const defaults = jdbcProductConnectionDefaults(profile, modeId);
|
||||
fields[modeId] = {
|
||||
connectionString: config.connection_string?.trim() || defaults.connectionString,
|
||||
driverClass: config.jdbc_driver_class?.trim() || defaults.driverClass,
|
||||
};
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function rememberJdbcProductConnectionFields(profile: JdbcProductProfileDefinition, fields: JdbcProductConnectionFieldsByMode, modeId: string, current: Partial<JdbcProductConnectionFields>): JdbcProductConnectionFieldsByMode {
|
||||
const defaults = jdbcProductConnectionDefaults(profile, modeId);
|
||||
return {
|
||||
...fields,
|
||||
[modeId]: {
|
||||
connectionString: current.connectionString?.trim() || defaults.connectionString,
|
||||
driverClass: current.driverClass?.trim() || defaults.driverClass,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function isJdbcProductDefaultDriverClass(profile: JdbcProductProfileDefinition, value: string | undefined): boolean {
|
||||
const normalized = value?.trim() || "";
|
||||
return !normalized || profile.modes.some((mode) => mode.defaultDriverClass === normalized);
|
||||
}
|
||||
|
||||
export function jdbcProductManagedCoordinates(profile: JdbcProductProfileDefinition): string[] {
|
||||
return Array.from(new Set(profile.modes.flatMap((mode) => [...mode.managedCoordinates])));
|
||||
}
|
||||
|
||||
export function validateJdbcProductManagedDriver(profile: JdbcProductProfileDefinition, driver: ManagedJdbcDriverDefinition | undefined): void {
|
||||
if (!driver || driver.id !== profile.managedDriverId) {
|
||||
throw new Error(`JDBC product profile ${profile.id} references unknown managed driver: ${profile.managedDriverId}`);
|
||||
}
|
||||
const ownedCoordinates = new Set(driver.bundles.map(({ coordinate }) => coordinate));
|
||||
const unownedCoordinate = jdbcProductManagedCoordinates(profile).find((coordinate) => !ownedCoordinates.has(coordinate));
|
||||
if (unownedCoordinate) {
|
||||
throw new Error(`JDBC product profile ${profile.id} requires coordinate not owned by ${driver.id}: ${unownedCoordinate}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function isJdbcProductManagedMavenCoordinate(profile: JdbcProductProfileDefinition, coordinate: string): boolean {
|
||||
return jdbcProductManagedCoordinates(profile).includes(coordinate);
|
||||
}
|
||||
|
||||
export function jdbcProductManagedRuntimePaths(profile: JdbcProductProfileDefinition, bundles: readonly JdbcMavenBundleInfo[], modeId: string): string[] {
|
||||
const bundleByCoordinate = new Map(bundles.map((bundle) => [bundle.coordinate, bundle]));
|
||||
const requiredBundles = jdbcProductMode(profile, modeId).managedCoordinates.map((coordinate) => bundleByCoordinate.get(coordinate));
|
||||
if (requiredBundles.some((bundle) => !bundle)) return [];
|
||||
return requiredBundles.flatMap((bundle) => (bundle?.artifacts ?? []).map((artifact) => artifact.path).filter(Boolean));
|
||||
}
|
||||
|
||||
export function jdbcProductRuntimeSelectionId(profile: JdbcProductProfileDefinition, modeId: string): string {
|
||||
return `${profile.id}:${modeId}`;
|
||||
}
|
||||
|
||||
function mavenBundleId(coordinate: string): string {
|
||||
return coordinate
|
||||
.trim()
|
||||
.split("")
|
||||
.map((character) => (/^[A-Za-z0-9.-]$/.test(character) ? character : "_"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function isJdbcProductManagedMavenPath(profile: JdbcProductProfileDefinition, path: string): boolean {
|
||||
const normalized = path.replaceAll("\\", "/");
|
||||
return jdbcProductManagedCoordinates(profile).some((coordinate) => normalized.split("/").includes(mavenBundleId(coordinate)));
|
||||
}
|
||||
|
||||
export function isJdbcProductRuntimeInstallError(profile: JdbcProductProfileDefinition, message: string): boolean {
|
||||
return message.includes(profile.missingDriverError) || message.includes(profile.missingPluginError);
|
||||
}
|
||||
|
||||
export async function ensureJdbcProductRuntimeDrivers(profile: JdbcProductProfileDefinition, config: ConnectionConfig, api: JdbcProductRuntimeApi): Promise<JdbcProductRuntimeResult | undefined> {
|
||||
if (!jdbcProductProfileMatches(profile, config)) return undefined;
|
||||
|
||||
const modeId = profile.detectMode(config);
|
||||
const mode = jdbcProductMode(profile, modeId);
|
||||
const defaults = jdbcProductConnectionDefaults(profile, modeId);
|
||||
config.connection_string = config.connection_string?.trim() || defaults.connectionString;
|
||||
config.jdbc_driver_class = config.jdbc_driver_class?.trim() || defaults.driverClass;
|
||||
const configuredPaths = (config.jdbc_driver_paths ?? []).map((path) => path.trim()).filter(Boolean);
|
||||
|
||||
const pluginStatus = await api.jdbcPluginStatus();
|
||||
if (!pluginStatus.installed || !pluginStatus.compatible) {
|
||||
throw new JdbcProductRuntimeError("plugin-missing", profile);
|
||||
}
|
||||
|
||||
const bundles = await api.listJdbcMavenBundles();
|
||||
const managedCoordinates = jdbcProductManagedCoordinates(profile);
|
||||
const managedBundles = bundles.filter((bundle) => managedCoordinates.includes(bundle.coordinate));
|
||||
const managedPaths = new Set(managedBundles.flatMap((bundle) => bundle.artifacts.map((artifact) => artifact.path).filter(Boolean)));
|
||||
const hasCustomRuntime = configuredPaths.some((path) => profile.isCompatibleRuntimePath(path, modeId) && !managedPaths.has(path) && !isJdbcProductManagedMavenPath(profile, path));
|
||||
if (hasCustomRuntime) {
|
||||
config.jdbc_driver_paths = configuredPaths;
|
||||
return { profile, mode, bundles, paths: configuredPaths };
|
||||
}
|
||||
|
||||
const installedCoordinates = new Set(bundles.map((bundle) => bundle.coordinate));
|
||||
if (mode.managedCoordinates.some((coordinate) => !installedCoordinates.has(coordinate))) {
|
||||
throw new JdbcProductRuntimeError("driver-missing", profile);
|
||||
}
|
||||
|
||||
const customPaths = configuredPaths.filter((path) => !managedPaths.has(path) && !isJdbcProductManagedMavenPath(profile, path));
|
||||
const runtimePaths = jdbcProductManagedRuntimePaths(profile, bundles, modeId);
|
||||
if (runtimePaths.length === 0) {
|
||||
throw new JdbcProductRuntimeError("driver-missing", profile);
|
||||
}
|
||||
|
||||
const paths = Array.from(new Set([...customPaths, ...runtimePaths]));
|
||||
config.jdbc_driver_paths = paths;
|
||||
return {
|
||||
profile,
|
||||
mode,
|
||||
bundles,
|
||||
paths,
|
||||
runtimeSelectionId: jdbcProductRuntimeSelectionId(profile, modeId),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { ConnectionConfig } from "@/types/database";
|
||||
import { PHOENIX_JDBC_PRODUCT_PROFILE } from "@/lib/database/phoenixConnection";
|
||||
import { createJdbcProductProfileRegistry, ensureJdbcProductRuntimeDrivers, isJdbcProductRuntimeInstallError, jdbcProductProfileMatches, validateJdbcProductManagedDriver, type JdbcProductProfileDefinition, type JdbcProductRuntimeApi } from "@/lib/database/jdbcProductProfile";
|
||||
import { managedJdbcDriverDefinition } from "@/lib/database/managedJdbcDrivers";
|
||||
|
||||
export const JDBC_PRODUCT_PROFILES = [PHOENIX_JDBC_PRODUCT_PROFILE] as const satisfies readonly JdbcProductProfileDefinition[];
|
||||
|
||||
const jdbcProductProfileRegistry = createJdbcProductProfileRegistry(JDBC_PRODUCT_PROFILES);
|
||||
for (const profile of JDBC_PRODUCT_PROFILES) {
|
||||
validateJdbcProductManagedDriver(profile, managedJdbcDriverDefinition(profile.managedDriverId));
|
||||
}
|
||||
|
||||
export function jdbcProductProfileDefinition(id: string): JdbcProductProfileDefinition | undefined {
|
||||
return jdbcProductProfileRegistry.byId.get(id);
|
||||
}
|
||||
|
||||
export function jdbcProductProfileForConfig(config: Pick<ConnectionConfig, "db_type" | "driver_profile">): JdbcProductProfileDefinition | undefined {
|
||||
return JDBC_PRODUCT_PROFILES.find((profile) => jdbcProductProfileMatches(profile, config));
|
||||
}
|
||||
|
||||
export function jdbcProductDriverProfiles() {
|
||||
return Object.fromEntries(
|
||||
JDBC_PRODUCT_PROFILES.map((profile) => [
|
||||
profile.id,
|
||||
{
|
||||
type: profile.match.dbType,
|
||||
port: profile.port,
|
||||
user: profile.user,
|
||||
label: profile.label,
|
||||
icon: profile.icon,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
export function jdbcProductPickerOptions() {
|
||||
return JDBC_PRODUCT_PROFILES.map((profile) => ({ value: profile.id, label: profile.label }));
|
||||
}
|
||||
|
||||
export function jdbcProductIconTypes(): Record<string, string> {
|
||||
return Object.fromEntries(JDBC_PRODUCT_PROFILES.map((profile) => [profile.id, profile.icon]));
|
||||
}
|
||||
|
||||
export function jdbcProductProfileIdsForCategory(category: string): string[] {
|
||||
return JDBC_PRODUCT_PROFILES.filter((profile) => profile.category === category).map((profile) => profile.id);
|
||||
}
|
||||
|
||||
export function isRegisteredJdbcProductRuntimeInstallError(config: Pick<ConnectionConfig, "db_type" | "driver_profile">, message: string): boolean {
|
||||
const profile = jdbcProductProfileForConfig(config);
|
||||
return Boolean(profile && isJdbcProductRuntimeInstallError(profile, message));
|
||||
}
|
||||
|
||||
export async function ensureRegisteredJdbcProductRuntimeDrivers(config: ConnectionConfig, api: JdbcProductRuntimeApi) {
|
||||
const profile = jdbcProductProfileForConfig(config);
|
||||
if (!profile) return undefined;
|
||||
return ensureJdbcProductRuntimeDrivers(profile, config, api);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { AgentDriverInfo } from "@/lib/backend/api";
|
||||
import type { JdbcDriverInfo, JdbcMavenBundleInfo, JdbcPluginStatus } from "@/types/database";
|
||||
|
||||
export type ManagedJdbcBundleDefinition = {
|
||||
coordinate: string;
|
||||
repositories: readonly string[];
|
||||
};
|
||||
|
||||
export type ManagedJdbcDriverDefinition = {
|
||||
id: string;
|
||||
label: string;
|
||||
version: string;
|
||||
jre: string;
|
||||
bundles: readonly ManagedJdbcBundleDefinition[];
|
||||
};
|
||||
|
||||
export type ManagedJdbcDriverApi = {
|
||||
installJdbcPlugin: () => Promise<JdbcPluginStatus>;
|
||||
installJdbcDriverFromMaven: (coordinate: string, repositories?: string[]) => Promise<JdbcDriverInfo[]>;
|
||||
listJdbcMavenBundles: () => Promise<JdbcMavenBundleInfo[]>;
|
||||
deleteJdbcMavenBundle: (bundleId: string) => Promise<JdbcDriverInfo[]>;
|
||||
};
|
||||
|
||||
export type ManagedJdbcDriverMutationResult = {
|
||||
drivers?: JdbcDriverInfo[];
|
||||
bundles: JdbcMavenBundleInfo[];
|
||||
pluginStatus?: JdbcPluginStatus;
|
||||
};
|
||||
|
||||
export type ManagedJdbcDriverRegistry = {
|
||||
definitions: readonly ManagedJdbcDriverDefinition[];
|
||||
byId: ReadonlyMap<string, ManagedJdbcDriverDefinition>;
|
||||
};
|
||||
|
||||
export function createManagedJdbcDriverRegistry(definitions: readonly ManagedJdbcDriverDefinition[]): ManagedJdbcDriverRegistry {
|
||||
const byId = new Map<string, ManagedJdbcDriverDefinition>();
|
||||
const coordinateOwners = new Map<string, string>();
|
||||
for (const definition of definitions) {
|
||||
if (!definition.id.trim()) throw new Error("Managed JDBC driver id must not be empty");
|
||||
if (byId.has(definition.id)) throw new Error(`Duplicate managed JDBC driver id: ${definition.id}`);
|
||||
if (definition.bundles.length === 0) throw new Error(`Managed JDBC driver must own at least one bundle: ${definition.id}`);
|
||||
for (const bundle of definition.bundles) {
|
||||
const owner = coordinateOwners.get(bundle.coordinate);
|
||||
if (owner) throw new Error(`Managed JDBC coordinate ${bundle.coordinate} is owned by both ${owner} and ${definition.id}`);
|
||||
coordinateOwners.set(bundle.coordinate, definition.id);
|
||||
}
|
||||
byId.set(definition.id, definition);
|
||||
}
|
||||
return { definitions: [...definitions], byId };
|
||||
}
|
||||
|
||||
export function managedJdbcDriverBundles(definition: ManagedJdbcDriverDefinition, bundles: readonly JdbcMavenBundleInfo[]): JdbcMavenBundleInfo[] {
|
||||
const bundleByCoordinate = new Map(bundles.map((bundle) => [bundle.coordinate, bundle]));
|
||||
return definition.bundles.map(({ coordinate }) => bundleByCoordinate.get(coordinate)).filter((bundle): bundle is JdbcMavenBundleInfo => Boolean(bundle));
|
||||
}
|
||||
|
||||
export function missingManagedJdbcDriverBundles(definition: ManagedJdbcDriverDefinition, bundles: readonly JdbcMavenBundleInfo[]): ManagedJdbcBundleDefinition[] {
|
||||
const installedCoordinates = new Set(bundles.map((bundle) => bundle.coordinate));
|
||||
return definition.bundles.filter(({ coordinate }) => !installedCoordinates.has(coordinate));
|
||||
}
|
||||
|
||||
export function managedJdbcDriverPaths(definition: ManagedJdbcDriverDefinition, bundles: readonly JdbcMavenBundleInfo[], coordinates: readonly string[] = definition.bundles.map(({ coordinate }) => coordinate)): string[] {
|
||||
const bundleByCoordinate = new Map(managedJdbcDriverBundles(definition, bundles).map((bundle) => [bundle.coordinate, bundle]));
|
||||
const requiredBundles = coordinates.map((coordinate) => bundleByCoordinate.get(coordinate));
|
||||
if (requiredBundles.some((bundle) => !bundle)) return [];
|
||||
return requiredBundles.flatMap((bundle) => (bundle?.artifacts ?? []).map((artifact) => artifact.path).filter(Boolean));
|
||||
}
|
||||
|
||||
export function managedJdbcDriverRow(definition: ManagedJdbcDriverDefinition, bundles: readonly JdbcMavenBundleInfo[], pluginStatus?: Pick<JdbcPluginStatus, "installed" | "compatible"> | null): AgentDriverInfo {
|
||||
const installedBundles = managedJdbcDriverBundles(definition, bundles);
|
||||
const pluginReady = pluginStatus?.installed === true && pluginStatus.compatible;
|
||||
const installed = pluginReady && installedBundles.length === definition.bundles.length;
|
||||
return {
|
||||
db_type: definition.id,
|
||||
label: definition.label,
|
||||
version: definition.version,
|
||||
size: installedBundles.reduce((bundleTotal, bundle) => bundleTotal + bundle.artifacts.reduce((artifactTotal, artifact) => artifactTotal + Number(artifact.size || 0), 0), 0),
|
||||
installed,
|
||||
installed_version: installed ? definition.version : null,
|
||||
update_available: false,
|
||||
requires_java_runtime: true,
|
||||
jre: definition.jre,
|
||||
jre_installed: true,
|
||||
};
|
||||
}
|
||||
|
||||
export async function installManagedJdbcDriver(definition: ManagedJdbcDriverDefinition, currentBundles: readonly JdbcMavenBundleInfo[], currentPluginStatus: Pick<JdbcPluginStatus, "installed" | "compatible"> | null | undefined, api: ManagedJdbcDriverApi): Promise<ManagedJdbcDriverMutationResult> {
|
||||
let pluginStatus: JdbcPluginStatus | undefined;
|
||||
if (!currentPluginStatus?.installed || !currentPluginStatus.compatible) {
|
||||
pluginStatus = await api.installJdbcPlugin();
|
||||
}
|
||||
|
||||
let bundles = [...currentBundles];
|
||||
let drivers: JdbcDriverInfo[] | undefined;
|
||||
for (const bundle of definition.bundles) {
|
||||
if (bundles.some(({ coordinate }) => coordinate === bundle.coordinate)) continue;
|
||||
drivers = await api.installJdbcDriverFromMaven(bundle.coordinate, [...bundle.repositories]);
|
||||
bundles = await api.listJdbcMavenBundles();
|
||||
}
|
||||
return { drivers, bundles, pluginStatus };
|
||||
}
|
||||
|
||||
export async function uninstallManagedJdbcDriver(definition: ManagedJdbcDriverDefinition, currentBundles: readonly JdbcMavenBundleInfo[], api: ManagedJdbcDriverApi): Promise<ManagedJdbcDriverMutationResult> {
|
||||
let drivers: JdbcDriverInfo[] | undefined;
|
||||
for (const bundle of managedJdbcDriverBundles(definition, currentBundles)) {
|
||||
drivers = await api.deleteJdbcMavenBundle(bundle.id);
|
||||
}
|
||||
const bundles = await api.listJdbcMavenBundles();
|
||||
return { drivers, bundles };
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { JdbcMavenBundleInfo, JdbcPluginStatus } from "@/types/database";
|
||||
import { PHOENIX_MANAGED_JDBC_DRIVER } from "@/lib/database/phoenixBuiltinDriver";
|
||||
import { PRESTOSQL_MANAGED_JDBC_DRIVER } from "@/lib/database/prestoSqlBuiltinDriver";
|
||||
import { createManagedJdbcDriverRegistry, installManagedJdbcDriver, managedJdbcDriverRow, uninstallManagedJdbcDriver, type ManagedJdbcDriverApi, type ManagedJdbcDriverDefinition, type ManagedJdbcDriverMutationResult } from "@/lib/database/managedJdbcDriver";
|
||||
|
||||
export const MANAGED_JDBC_DRIVERS = [PRESTOSQL_MANAGED_JDBC_DRIVER, PHOENIX_MANAGED_JDBC_DRIVER] as const satisfies readonly ManagedJdbcDriverDefinition[];
|
||||
|
||||
const managedJdbcDriverRegistry = createManagedJdbcDriverRegistry(MANAGED_JDBC_DRIVERS);
|
||||
|
||||
export function managedJdbcDriverDefinition(id: string): ManagedJdbcDriverDefinition | undefined {
|
||||
return managedJdbcDriverRegistry.byId.get(id);
|
||||
}
|
||||
|
||||
export function isManagedJdbcDriver(id: string): boolean {
|
||||
return managedJdbcDriverRegistry.byId.has(id);
|
||||
}
|
||||
|
||||
export function managedJdbcDriverRows(bundles: readonly JdbcMavenBundleInfo[], pluginStatus?: Pick<JdbcPluginStatus, "installed" | "compatible"> | null) {
|
||||
return MANAGED_JDBC_DRIVERS.map((definition) => managedJdbcDriverRow(definition, bundles, pluginStatus));
|
||||
}
|
||||
|
||||
export async function installRegisteredManagedJdbcDriver(id: string, bundles: readonly JdbcMavenBundleInfo[], pluginStatus: Pick<JdbcPluginStatus, "installed" | "compatible"> | null | undefined, api: ManagedJdbcDriverApi): Promise<ManagedJdbcDriverMutationResult | undefined> {
|
||||
const definition = managedJdbcDriverDefinition(id);
|
||||
if (!definition) return undefined;
|
||||
return installManagedJdbcDriver(definition, bundles, pluginStatus, api);
|
||||
}
|
||||
|
||||
export async function uninstallRegisteredManagedJdbcDriver(id: string, bundles: readonly JdbcMavenBundleInfo[], api: ManagedJdbcDriverApi): Promise<ManagedJdbcDriverMutationResult | undefined> {
|
||||
const definition = managedJdbcDriverDefinition(id);
|
||||
if (!definition) return undefined;
|
||||
return uninstallManagedJdbcDriver(definition, bundles, api);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { PHOENIX_DIRECT_LOGGING_MAVEN_COORDINATE, PHOENIX_DIRECT_MAVEN_COORDINATE, PHOENIX_DRIVER_PROFILE, PHOENIX_QUERY_SERVER_MAVEN_COORDINATE } from "@/lib/database/phoenixConnection";
|
||||
import type { ManagedJdbcDriverDefinition } from "@/lib/database/managedJdbcDriver";
|
||||
|
||||
export const PHOENIX_JDBC_DRIVER_VERSION = "5.2.1 / 6.0.0";
|
||||
|
||||
export const PHOENIX_MANAGED_MAVEN_COORDINATES = [PHOENIX_DIRECT_MAVEN_COORDINATE, PHOENIX_DIRECT_LOGGING_MAVEN_COORDINATE, PHOENIX_QUERY_SERVER_MAVEN_COORDINATE] as const;
|
||||
export const PHOENIX_MANAGED_JDBC_DRIVER: ManagedJdbcDriverDefinition = {
|
||||
id: PHOENIX_DRIVER_PROFILE,
|
||||
label: "Apache Phoenix JDBC",
|
||||
version: PHOENIX_JDBC_DRIVER_VERSION,
|
||||
jre: "21",
|
||||
bundles: PHOENIX_MANAGED_MAVEN_COORDINATES.map((coordinate) => ({
|
||||
coordinate,
|
||||
repositories: ["https://repo.maven.apache.org/maven2/"],
|
||||
})),
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { ConnectionConfig } from "@/types/database";
|
||||
import type { JdbcProductProfileDefinition } from "@/lib/database/jdbcProductProfile";
|
||||
|
||||
export const PHOENIX_DRIVER_PROFILE = "phoenix";
|
||||
export const PHOENIX_DIRECT_JDBC_URL = "jdbc:phoenix:localhost";
|
||||
export const PHOENIX_DIRECT_JDBC_DRIVER_CLASS = "org.apache.phoenix.jdbc.PhoenixDriver";
|
||||
export const PHOENIX_QUERY_SERVER_JDBC_URL = "jdbc:phoenix:thin:url=http://127.0.0.1:8765;serialization=PROTOBUF";
|
||||
export const PHOENIX_QUERY_SERVER_JDBC_DRIVER_CLASS = "org.apache.phoenix.queryserver.client.Driver";
|
||||
export const PHOENIX_MAVEN_REPOSITORY = "https://repo.maven.apache.org/maven2/";
|
||||
export const PHOENIX_DIRECT_MAVEN_COORDINATE = "org.apache.phoenix:phoenix-client-embedded-hbase-2.5:5.2.1";
|
||||
export const PHOENIX_QUERY_SERVER_MAVEN_COORDINATE = "org.apache.phoenix:phoenix-queryserver-client:6.0.0";
|
||||
export const PHOENIX_DIRECT_LOGGING_MAVEN_COORDINATE = "ch.qos.reload4j:reload4j:1.2.26";
|
||||
export const PHOENIX_DRIVER_NOT_INSTALLED_ERROR = "Apache Phoenix JDBC driver is not installed. Install it from the Driver Manager, then retry.";
|
||||
export const PHOENIX_JDBC_PLUGIN_NOT_INSTALLED_ERROR = "DBX JDBC plugin is not installed. Install Apache Phoenix JDBC from the Driver Manager, then retry.";
|
||||
|
||||
export type PhoenixConnectionMode = "direct" | "query-server";
|
||||
|
||||
type PhoenixConnectionConfig = Partial<Pick<ConnectionConfig, "connection_string" | "jdbc_driver_class">>;
|
||||
|
||||
export const PHOENIX_JDBC_PRODUCT_PROFILE: JdbcProductProfileDefinition = {
|
||||
id: PHOENIX_DRIVER_PROFILE,
|
||||
label: "Apache Phoenix",
|
||||
icon: "phoenix",
|
||||
category: "analytics",
|
||||
port: 0,
|
||||
user: "",
|
||||
match: { dbType: "jdbc", driverProfile: PHOENIX_DRIVER_PROFILE },
|
||||
managedDriverId: PHOENIX_DRIVER_PROFILE,
|
||||
runtimeLabelKey: "connection.phoenixRuntimeOption",
|
||||
driverManagerHintPrefixKey: "connection.phoenixDriverManagerHintPrefix",
|
||||
driverManagerHintSuffixKey: "connection.phoenixDriverManagerHintSuffix",
|
||||
docsLabelKey: "connection.phoenixDocs",
|
||||
docsUrl: "https://phoenix.apache.org/docs/fundamentals/client-classpath-and-jdbc-url/",
|
||||
missingDriverError: PHOENIX_DRIVER_NOT_INSTALLED_ERROR,
|
||||
missingPluginError: PHOENIX_JDBC_PLUGIN_NOT_INSTALLED_ERROR,
|
||||
modes: [
|
||||
{
|
||||
id: "direct",
|
||||
labelKey: "connection.phoenixDirectMode",
|
||||
hintKey: "connection.phoenixDirectModeHint",
|
||||
defaultConnectionString: PHOENIX_DIRECT_JDBC_URL,
|
||||
defaultDriverClass: PHOENIX_DIRECT_JDBC_DRIVER_CLASS,
|
||||
managedCoordinates: [PHOENIX_DIRECT_MAVEN_COORDINATE, PHOENIX_DIRECT_LOGGING_MAVEN_COORDINATE],
|
||||
},
|
||||
{
|
||||
id: "query-server",
|
||||
labelKey: "connection.phoenixQueryServerMode",
|
||||
hintKey: "connection.phoenixQueryServerModeHint",
|
||||
defaultConnectionString: PHOENIX_QUERY_SERVER_JDBC_URL,
|
||||
defaultDriverClass: PHOENIX_QUERY_SERVER_JDBC_DRIVER_CLASS,
|
||||
managedCoordinates: [PHOENIX_QUERY_SERVER_MAVEN_COORDINATE],
|
||||
},
|
||||
],
|
||||
detectMode: (config) => phoenixConnectionModeForConfig(config),
|
||||
isCompatibleRuntimePath: (path, mode) => isPhoenixRuntimePath(path, mode as PhoenixConnectionMode),
|
||||
};
|
||||
|
||||
export function phoenixConnectionModeForConfig(config: PhoenixConnectionConfig): PhoenixConnectionMode {
|
||||
const connectionString = config.connection_string?.trim().toLowerCase() || "";
|
||||
const driverClass = config.jdbc_driver_class?.trim().toLowerCase() || "";
|
||||
return connectionString.startsWith("jdbc:phoenix:thin:") || driverClass === PHOENIX_QUERY_SERVER_JDBC_DRIVER_CLASS.toLowerCase() ? "query-server" : "direct";
|
||||
}
|
||||
|
||||
export function isPhoenixRuntimePath(path: string, mode: PhoenixConnectionMode): boolean {
|
||||
const parts = path.split(/[/\\]/);
|
||||
const fileName = parts[parts.length - 1] || path;
|
||||
return mode === "direct" ? /^phoenix-client-(?:embedded-)?hbase-.+\.jar$/i.test(fileName) : /^phoenix-queryserver-client-.+\.jar$/i.test(fileName);
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
import type { AgentDriverInfo } from "@/lib/backend/api";
|
||||
import type { JdbcMavenBundleInfo } from "@/types/database";
|
||||
import { managedJdbcDriverPaths, type ManagedJdbcDriverDefinition } from "@/lib/database/managedJdbcDriver";
|
||||
|
||||
export const PRESTOSQL_DRIVER_DB_TYPE = "prestosql";
|
||||
export const PRESTOSQL_JDBC_DRIVER_VERSION = "350";
|
||||
export const PRESTOSQL_JDBC_DRIVER_COORDINATE = `io.prestosql:presto-jdbc:${PRESTOSQL_JDBC_DRIVER_VERSION}`;
|
||||
export const PRESTOSQL_JDBC_DRIVER_REPOSITORY = "https://repo.maven.apache.org/maven2/";
|
||||
|
||||
export function prestoSqlMavenBundle(bundles: JdbcMavenBundleInfo[]): JdbcMavenBundleInfo | undefined {
|
||||
return bundles.find((bundle) => bundle.coordinate === PRESTOSQL_JDBC_DRIVER_COORDINATE);
|
||||
}
|
||||
export const PRESTOSQL_MANAGED_JDBC_DRIVER: ManagedJdbcDriverDefinition = {
|
||||
id: PRESTOSQL_DRIVER_DB_TYPE,
|
||||
label: "PrestoSQL",
|
||||
version: PRESTOSQL_JDBC_DRIVER_VERSION,
|
||||
jre: "21",
|
||||
bundles: [
|
||||
{
|
||||
coordinate: PRESTOSQL_JDBC_DRIVER_COORDINATE,
|
||||
repositories: [PRESTOSQL_JDBC_DRIVER_REPOSITORY],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function prestoSqlBuiltinDriverPaths(bundles: JdbcMavenBundleInfo[]): string[] {
|
||||
return (prestoSqlMavenBundle(bundles)?.artifacts ?? []).map((artifact) => artifact.path).filter(Boolean);
|
||||
}
|
||||
|
||||
export function prestoSqlBuiltinDriverRow(bundles: JdbcMavenBundleInfo[]): AgentDriverInfo {
|
||||
const installedBundle = prestoSqlMavenBundle(bundles);
|
||||
return {
|
||||
db_type: PRESTOSQL_DRIVER_DB_TYPE,
|
||||
label: "PrestoSQL",
|
||||
version: PRESTOSQL_JDBC_DRIVER_VERSION,
|
||||
size: installedBundle ? installedBundle.artifacts.reduce((total, artifact) => total + Number(artifact.size || 0), 0) : 0,
|
||||
installed: Boolean(installedBundle),
|
||||
installed_version: installedBundle ? PRESTOSQL_JDBC_DRIVER_VERSION : null,
|
||||
update_available: false,
|
||||
requires_java_runtime: true,
|
||||
jre: "21",
|
||||
jre_installed: true,
|
||||
};
|
||||
return managedJdbcDriverPaths(PRESTOSQL_MANAGED_JDBC_DRIVER, bundles);
|
||||
}
|
||||
Reference in new issue
Block a user