Backend: - Remove ConnectionConfig model, CRUD routes (connections.py), and tests - Remove ensure_connection_configs_table from database.py - Migrate MapperPlugin from psycopg2 direct PostgreSQL to SupersetSqlLabExecutor - Add DatasetMapper.get_sqllab_mappings() with default information_schema query - Update MapColumnsRequest: connection_id → database_id + sql_query - Fix pydantic_settings v2 deprecation (Field(env=...) → validation_alias) - Clean up app.py, routes/__init__.py, database.py imports Frontend: - Remove DatabaseConnectionsTab, ConnectionForm, ConnectionList components - Remove /settings/connections route page and Navbar link - Remove connectionService.js, connections i18n files - Update MapperTool.svelte: postgres → sqllab source - Update Map Columns modal: database selector + SQL query instead of connection_id - Clean up settings page tabs, nav links, test mocks - Clean up i18n keys (settings, nav, mapper) Tests: 25/25 pass (14 030-feature + 11 route tests) Build: succeeds
160 lines
6.3 KiB
JavaScript
160 lines
6.3 KiB
JavaScript
/**
|
|
* Shared utilities for the Settings page.
|
|
* Centralises constants, URL helpers, normalisation functions and validation
|
|
* helpers that are consumed by +page.svelte and EnvironmentsTab.svelte.
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Constants
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const SETTINGS_TABS = [
|
|
"environments",
|
|
"logging",
|
|
"git",
|
|
"llm",
|
|
"migration",
|
|
"storage",
|
|
"features",
|
|
"automation",
|
|
];
|
|
|
|
export const DEFAULT_LLM_PROMPTS = {
|
|
dashboard_validation_prompt:
|
|
'Analyze the attached dashboard screenshot and the following execution logs for health and visual issues.\\n\\nLogs:\\n{logs}\\n\\nProvide the analysis in JSON format with the following structure:\\n{\\n \\"status\\": \\"PASS\\" | \\"WARN\\" | \\"FAIL\\",\\n \\"summary\\": \\"Short summary of findings\\",\\n \\"issues\\": [\\n {\\n \\"severity\\": \\"WARN\\" | \\"FAIL\\",\\n \\"message\\": \\"Description of the issue\\",\\n \\"location\\": \\"Optional location info (e.g. chart name)\\"\\n }\\n ]\\n}',
|
|
documentation_prompt:
|
|
'Generate professional documentation for the following dataset and its columns.\\nDataset: {dataset_name}\\nColumns: {columns_json}\\n\\nProvide the documentation in JSON format:\\n{\\n \\"dataset_description\\": \\"General description of the dataset\\",\\n \\"column_descriptions\\": [\\n {\\n \\"name\\": \\"column_name\\",\\n \\"description\\": \\"Generated description\\"\\n }\\n ]\\n}',
|
|
git_commit_prompt:
|
|
'Generate a concise and professional git commit message based on the following diff and recent history.\\nUse Conventional Commits format (e.g., feat: ..., fix: ..., docs: ...).\\n\\nRecent History:\\n{history}\\n\\nDiff:\\n{diff}\\n\\nCommit Message:',
|
|
};
|
|
|
|
export const DEFAULT_LLM_PROVIDER_BINDINGS = {
|
|
dashboard_validation: "",
|
|
documentation: "",
|
|
git_commit: "",
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// URL tab helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Read the current settings tab from the URL hash fragment.
|
|
* Falls back to "environments" when the hash is missing or invalid.
|
|
*/
|
|
export function readTabFromUrl() {
|
|
const hash = typeof window !== "undefined" ? window.location.hash : "";
|
|
const tab = hash.replace(/^#\/?settings\//, "") || "";
|
|
return normalizeTab(tab) || "environments";
|
|
}
|
|
|
|
/**
|
|
* Write the active settings tab to the URL hash fragment so it survives
|
|
* browser refreshes and can be linked to directly.
|
|
*/
|
|
export function writeTabToUrl(tab) {
|
|
if (typeof window === "undefined") return;
|
|
const normalized = normalizeTab(tab);
|
|
if (normalized) {
|
|
window.location.hash = `/settings/${normalized}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Normalise an arbitrary tab identifier to a valid tab key, or return null
|
|
* when the identifier does not match any known tab.
|
|
*/
|
|
export function normalizeTab(tab) {
|
|
if (!tab || typeof tab !== "string") return null;
|
|
const candidate = tab.trim().toLowerCase();
|
|
return SETTINGS_TABS.includes(candidate) ? candidate : null;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LLM helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Merge incoming LLM settings with the default prompts and bindings so that
|
|
* individual fields are never accidentally set to `undefined`.
|
|
*/
|
|
export function normalizeLlmSettings(llm) {
|
|
if (!llm || typeof llm !== "object") {
|
|
return {
|
|
prompts: { ...DEFAULT_LLM_PROMPTS },
|
|
provider_bindings: { ...DEFAULT_LLM_PROVIDER_BINDINGS },
|
|
};
|
|
}
|
|
return {
|
|
...llm,
|
|
prompts: { ...DEFAULT_LLM_PROMPTS, ...llm.prompts },
|
|
provider_bindings: {
|
|
...DEFAULT_LLM_PROVIDER_BINDINGS,
|
|
...llm.provider_bindings,
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Check whether the `dashboard_validation` provider binding resolves to a
|
|
* multimodal model that can process screenshots.
|
|
*
|
|
* `settings` is the full consolidated settings object, expected to contain
|
|
* `llm.provider_bindings` and the providers list at `settings.providers` or
|
|
* `settings.llm_providers`.
|
|
*/
|
|
export function isDashboardValidationBindingValid(settings) {
|
|
if (!settings?.llm?.provider_bindings?.dashboard_validation) return false;
|
|
const providerId = settings.llm.provider_bindings.dashboard_validation;
|
|
const providers = settings.providers || settings.llm_providers || [];
|
|
const provider = providers.find((p) => p.id === providerId);
|
|
return !!provider && Boolean(provider.is_multimodal);
|
|
}
|
|
|
|
/**
|
|
* Look up a provider by its id from the providers list on the settings object.
|
|
*/
|
|
export function getProviderById(settings, providerId) {
|
|
if (!providerId || !settings) return null;
|
|
const providers = settings.providers || settings.llm_providers || [];
|
|
return providers.find((p) => p.id === providerId) || null;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Environment helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Strip trailing slashes and the `/api/v1` suffix from a Superset base URL,
|
|
* auto-prepend https:// if no scheme is present,
|
|
* returning a clean base URL suitable for constructing dashboard links.
|
|
*/
|
|
export function normalizeSupersetBaseUrl(rawUrl) {
|
|
let baseUrl = String(rawUrl || "").trim().replace(/\/+$/, "");
|
|
if (!baseUrl) return null;
|
|
if (baseUrl.endsWith("/api/v1")) {
|
|
baseUrl = baseUrl.slice(0, -"/api/v1".length);
|
|
}
|
|
// Auto-prepend https:// if no scheme is present
|
|
if (baseUrl && !/^https?:\/\//i.test(baseUrl)) {
|
|
baseUrl = `https://${baseUrl}`;
|
|
}
|
|
return baseUrl;
|
|
}
|
|
|
|
/**
|
|
* Classify an environment's deployment stage from its `name` or `url`.
|
|
* Returns one of: `"PROD"`, `"PREPROD"`, or `"DEV"`.
|
|
*/
|
|
export function resolveEnvStage(env) {
|
|
if (!env) return "DEV";
|
|
const needle = ((env.name || "") + " " + (env.url || "")).toLowerCase();
|
|
if (/prod(uction)?/.test(needle) && !/preprod/.test(needle)) return "PROD";
|
|
if (/preprod|staging|stage/.test(needle)) return "PREPROD";
|
|
return "DEV";
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Connection draft helper
|
|
// ---------------------------------------------------------------------------
|