mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat(docs): add auto-generated troubleshooting section to database pages (#37345)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
KeyOutlined,
|
||||
SearchOutlined,
|
||||
LinkOutlined,
|
||||
BugOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { DatabaseData, DatabaseInfo, TimeGrains } from './types';
|
||||
|
||||
@@ -44,6 +45,8 @@ interface TableEntry {
|
||||
hasDrivers: boolean;
|
||||
hasAuthMethods: boolean;
|
||||
hasConnectionString: boolean;
|
||||
hasCustomErrors: boolean;
|
||||
customErrorCount: number;
|
||||
joins?: boolean;
|
||||
subqueries?: boolean;
|
||||
supports_dynamic_schema?: boolean;
|
||||
@@ -223,6 +226,8 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
|
||||
db.documentation?.connection_string ||
|
||||
(db.documentation?.drivers?.length ?? 0) > 0
|
||||
),
|
||||
hasCustomErrors: (db.documentation?.custom_errors?.length ?? 0) > 0,
|
||||
customErrorCount: db.documentation?.custom_errors?.length ?? 0,
|
||||
isCompatible: false,
|
||||
});
|
||||
|
||||
@@ -246,6 +251,8 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
|
||||
hasDrivers: false,
|
||||
hasAuthMethods: false,
|
||||
hasConnectionString: Boolean(compat.connection_string),
|
||||
hasCustomErrors: false,
|
||||
customErrorCount: 0,
|
||||
joins: db.joins,
|
||||
subqueries: db.subqueries,
|
||||
supports_dynamic_schema: db.supports_dynamic_schema,
|
||||
@@ -457,7 +464,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
|
||||
{
|
||||
title: 'Documentation',
|
||||
key: 'docs',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (_: unknown, record: TableEntry) => (
|
||||
<div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
|
||||
{record.hasConnectionString && (
|
||||
@@ -475,6 +482,13 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
|
||||
Auth
|
||||
</Tag>
|
||||
)}
|
||||
{record.hasCustomErrors && (
|
||||
<Tooltip title={`${record.customErrorCount} troubleshooting tips`}>
|
||||
<Tag icon={<BugOutlined />} color="volcano">
|
||||
Errors
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
BookOutlined,
|
||||
EditOutlined,
|
||||
GithubOutlined,
|
||||
BugOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { DatabaseInfo } from './types';
|
||||
|
||||
@@ -414,6 +415,132 @@ const DatabasePage: React.FC<DatabasePageProps> = ({ database, name }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// Render troubleshooting / custom errors section
|
||||
const renderTroubleshooting = () => {
|
||||
if (!docs?.custom_errors?.length) return null;
|
||||
|
||||
// Group errors by category
|
||||
const errorsByCategory: Record<string, typeof docs.custom_errors> = {};
|
||||
for (const error of docs.custom_errors) {
|
||||
const category = error.category || 'General';
|
||||
if (!errorsByCategory[category]) {
|
||||
errorsByCategory[category] = [];
|
||||
}
|
||||
errorsByCategory[category].push(error);
|
||||
}
|
||||
|
||||
// Define category order for consistent display
|
||||
const categoryOrder = [
|
||||
'Authentication',
|
||||
'Connection',
|
||||
'Permissions',
|
||||
'Query',
|
||||
'Configuration',
|
||||
'General',
|
||||
];
|
||||
|
||||
const sortedCategories = Object.keys(errorsByCategory).sort((a, b) => {
|
||||
const aIdx = categoryOrder.indexOf(a);
|
||||
const bIdx = categoryOrder.indexOf(b);
|
||||
if (aIdx === -1 && bIdx === -1) return a.localeCompare(b);
|
||||
if (aIdx === -1) return 1;
|
||||
if (bIdx === -1) return -1;
|
||||
return aIdx - bIdx;
|
||||
});
|
||||
|
||||
// Category colors
|
||||
const categoryColors: Record<string, string> = {
|
||||
Authentication: 'orange',
|
||||
Connection: 'red',
|
||||
Permissions: 'purple',
|
||||
Query: 'blue',
|
||||
Configuration: 'cyan',
|
||||
General: 'default',
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={
|
||||
<>
|
||||
<BugOutlined /> Troubleshooting
|
||||
</>
|
||||
}
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
<Paragraph type="secondary">
|
||||
Common error messages you may encounter when connecting to or querying{' '}
|
||||
{name}, along with their causes and solutions.
|
||||
</Paragraph>
|
||||
<Collapse accordion>
|
||||
{sortedCategories.map((category) => (
|
||||
<Panel
|
||||
header={
|
||||
<span>
|
||||
<Tag color={categoryColors[category] || 'default'}>
|
||||
{category}
|
||||
</Tag>
|
||||
{errorsByCategory[category].length} error
|
||||
{errorsByCategory[category].length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
}
|
||||
key={category}
|
||||
>
|
||||
{errorsByCategory[category].map((error, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
marginBottom:
|
||||
idx < errorsByCategory[category].length - 1 ? 16 : 0,
|
||||
paddingBottom:
|
||||
idx < errorsByCategory[category].length - 1 ? 16 : 0,
|
||||
borderBottom:
|
||||
idx < errorsByCategory[category].length - 1
|
||||
? '1px solid var(--ifm-color-emphasis-200)'
|
||||
: 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Text strong>{error.description || error.error_type}</Text>
|
||||
</div>
|
||||
<Alert
|
||||
message={error.message_template}
|
||||
type="error"
|
||||
style={{ marginBottom: 8 }}
|
||||
/>
|
||||
{error.invalid_fields && error.invalid_fields.length > 0 && (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Text type="secondary">Check these fields: </Text>
|
||||
{error.invalid_fields.map((field) => (
|
||||
<Tag key={field} color="warning">
|
||||
{field}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{error.issue_codes && error.issue_codes.length > 0 && (
|
||||
<div>
|
||||
<Text type="secondary">Related issue codes: </Text>
|
||||
{error.issue_codes.map((code) => (
|
||||
<Tag key={code}>
|
||||
<a
|
||||
href={`/docs/using-superset/issue-codes#issue-${code}`}
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
Issue {code}
|
||||
</a>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Panel>
|
||||
))}
|
||||
</Collapse>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="database-page"
|
||||
@@ -556,6 +683,9 @@ const DatabasePage: React.FC<DatabasePageProps> = ({ database, name }) => {
|
||||
{/* Time Grains */}
|
||||
{renderTimeGrains()}
|
||||
|
||||
{/* Troubleshooting / Custom Errors */}
|
||||
{renderTroubleshooting()}
|
||||
|
||||
{/* Compatible Databases */}
|
||||
{renderCompatibleDatabases()}
|
||||
|
||||
|
||||
@@ -86,6 +86,17 @@ export interface CompatibleDatabase {
|
||||
docs_url?: string;
|
||||
}
|
||||
|
||||
export interface CustomError {
|
||||
error_type: string; // e.g., "CONNECTION_INVALID_USERNAME_ERROR"
|
||||
message_template: string; // e.g., 'The username "%(username)s" does not exist.'
|
||||
regex_pattern?: string; // The regex pattern that matches this error (optional, for reference)
|
||||
regex_name?: string; // The name of the regex constant (e.g., "CONNECTION_INVALID_USERNAME_REGEX")
|
||||
invalid_fields?: string[]; // Fields that are invalid, e.g., ["username", "password"]
|
||||
issue_codes?: number[]; // Related issue codes from ISSUE_CODES mapping
|
||||
category?: string; // Error category: "Authentication", "Connection", "Query", etc.
|
||||
description?: string; // Human-readable short description of the error type
|
||||
}
|
||||
|
||||
export interface DatabaseDocumentation {
|
||||
description?: string;
|
||||
logo?: string;
|
||||
@@ -111,6 +122,7 @@ export interface DatabaseDocumentation {
|
||||
sqlalchemy_docs_url?: string;
|
||||
advanced_features?: Record<string, string>;
|
||||
compatible_databases?: CompatibleDatabase[];
|
||||
custom_errors?: CustomError[]; // Database-specific error messages and troubleshooting info
|
||||
}
|
||||
|
||||
export interface TimeGrains {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user