mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-05 13:27:55 +08:00
✨ feat(query-editor): 扩展 SQL 编辑器对象跳转到视图触发器和存储过程
- 为 QueryEditor 补充视图、物化视图、触发器和函数元数据解析 - 支持 Ctrl/Cmd 点击打开对应对象定义页并同步当前 host/db 上下文 - 扩展 sidebarLocate 对触发器和函数的定位能力 - 补充 QueryEditor 与 sidebarLocate 定向测试覆盖
This commit is contained in:
@@ -96,6 +96,32 @@ describe('sidebarLocate', () => {
|
||||
})).toBeNull();
|
||||
});
|
||||
|
||||
it('builds locate requests from trigger and routine tabs', () => {
|
||||
expect(normalizeSidebarLocateObjectRequestFromTab({
|
||||
id: 'trigger-conn-1-main-audit.users_bi',
|
||||
type: 'trigger',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
triggerName: 'audit.users_bi',
|
||||
})).toMatchObject({
|
||||
tableName: 'audit.users_bi',
|
||||
schemaName: 'audit',
|
||||
objectGroup: 'triggers',
|
||||
});
|
||||
|
||||
expect(normalizeSidebarLocateObjectRequestFromTab({
|
||||
id: 'routine-def-conn-1-main-reporting.refresh_stats',
|
||||
type: 'routine-def',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
routineName: 'reporting.refresh_stats',
|
||||
})).toMatchObject({
|
||||
tableName: 'reporting.refresh_stats',
|
||||
schemaName: 'reporting',
|
||||
objectGroup: 'routines',
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps StarRocks materialized view tabs on the materialized views branch', () => {
|
||||
const request = normalizeSidebarLocateObjectRequestFromTab({
|
||||
id: 'view-def-conn-1-main-sales.mv_daily',
|
||||
@@ -182,4 +208,81 @@ describe('sidebarLocate', () => {
|
||||
'conn-1-main-public.users',
|
||||
]);
|
||||
});
|
||||
|
||||
it('finds trigger and routine paths from loaded tree data', () => {
|
||||
const triggerTarget = resolveSidebarLocateTarget({
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
tableName: 'audit.users_bi',
|
||||
schemaName: 'audit',
|
||||
objectGroup: 'triggers',
|
||||
}, { groupBySchema: true });
|
||||
|
||||
const routineTarget = resolveSidebarLocateTarget({
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
tableName: 'reporting.refresh_stats',
|
||||
schemaName: 'reporting',
|
||||
objectGroup: 'routines',
|
||||
}, { groupBySchema: true });
|
||||
|
||||
const tree = [
|
||||
{
|
||||
key: 'conn-1',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main',
|
||||
dataRef: { id: 'conn-1', dbName: 'main' },
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main-schema-audit',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main-schema-audit-triggers',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main-trigger-audit.users_bi-audit.users',
|
||||
type: 'db-trigger',
|
||||
dataRef: { id: 'conn-1', dbName: 'main', triggerName: 'audit.users_bi', schemaName: 'audit' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'conn-1-main-schema-reporting',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main-schema-reporting-routines',
|
||||
children: [
|
||||
{
|
||||
key: 'conn-1-main-routine-reporting.refresh_stats',
|
||||
type: 'routine',
|
||||
dataRef: { id: 'conn-1', dbName: 'main', routineName: 'reporting.refresh_stats', schemaName: 'reporting' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
expect(findSidebarNodePathForLocate(tree, triggerTarget)).toEqual([
|
||||
'conn-1',
|
||||
'conn-1-main',
|
||||
'conn-1-main-schema-audit',
|
||||
'conn-1-main-schema-audit-triggers',
|
||||
'conn-1-main-trigger-audit.users_bi-audit.users',
|
||||
]);
|
||||
expect(findSidebarNodePathForLocate(tree, routineTarget)).toEqual([
|
||||
'conn-1',
|
||||
'conn-1-main',
|
||||
'conn-1-main-schema-reporting',
|
||||
'conn-1-main-schema-reporting-routines',
|
||||
'conn-1-main-routine-reporting.refresh_stats',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type SidebarLocateObjectGroup = 'tables' | 'views' | 'materializedViews';
|
||||
export type SidebarLocateObjectGroup = 'tables' | 'views' | 'materializedViews' | 'triggers' | 'routines';
|
||||
|
||||
export interface SidebarLocateObjectRequest {
|
||||
tabId?: string;
|
||||
@@ -38,6 +38,8 @@ export interface SidebarLocateTabLike {
|
||||
tableName?: string;
|
||||
viewName?: string;
|
||||
viewKind?: string;
|
||||
triggerName?: string;
|
||||
routineName?: string;
|
||||
}
|
||||
|
||||
const toTrimmedString = (value: unknown): string => String(value ?? '').trim();
|
||||
@@ -57,15 +59,21 @@ const inferObjectGroup = (detail: Record<string, unknown>, connectionId: string,
|
||||
const explicitGroup = toTrimmedString(detail.objectGroup);
|
||||
if (explicitGroup === 'views' || explicitGroup === 'view') return 'views';
|
||||
if (explicitGroup === 'materializedViews' || explicitGroup === 'materialized-view') return 'materializedViews';
|
||||
if (explicitGroup === 'triggers' || explicitGroup === 'trigger') return 'triggers';
|
||||
if (explicitGroup === 'routines' || explicitGroup === 'routine') return 'routines';
|
||||
|
||||
const explicitType = toTrimmedString(detail.objectType);
|
||||
if (explicitType === 'view' || explicitType === 'views') return 'views';
|
||||
if (explicitType === 'materialized' || explicitType === 'materialized-view') return 'materializedViews';
|
||||
if (explicitType === 'trigger' || explicitType === 'triggers') return 'triggers';
|
||||
if (explicitType === 'routine' || explicitType === 'routines') return 'routines';
|
||||
|
||||
const tabId = toTrimmedString(detail.tabId);
|
||||
const dbNodeKey = `${connectionId}-${dbName}`;
|
||||
if (tabId.startsWith(`${dbNodeKey}-materialized-view-`)) return 'materializedViews';
|
||||
if (tabId.startsWith(`${dbNodeKey}-view-`)) return 'views';
|
||||
if (tabId.startsWith(`${dbNodeKey}-trigger-`)) return 'triggers';
|
||||
if (tabId.startsWith(`${dbNodeKey}-routine-`) || tabId.startsWith(`routine-def-${connectionId}-${dbName}-`)) return 'routines';
|
||||
|
||||
return 'tables';
|
||||
};
|
||||
@@ -74,7 +82,7 @@ export const normalizeSidebarLocateObjectRequest = (detail: unknown): SidebarLoc
|
||||
const raw = (detail || {}) as Record<string, unknown>;
|
||||
const connectionId = toTrimmedString(raw.connectionId);
|
||||
const dbName = toTrimmedString(raw.dbName);
|
||||
const tableName = toTrimmedString(raw.tableName || raw.objectName || raw.viewName);
|
||||
const tableName = toTrimmedString(raw.tableName || raw.objectName || raw.viewName || raw.triggerName || raw.routineName);
|
||||
|
||||
if (!connectionId || !dbName || !tableName) {
|
||||
return null;
|
||||
@@ -97,8 +105,12 @@ export const normalizeSidebarLocateObjectRequestFromTab = (tab: SidebarLocateTab
|
||||
if (!tab) return null;
|
||||
const objectName = tab.type === 'view-def'
|
||||
? toTrimmedString(tab.viewName || tab.tableName)
|
||||
: toTrimmedString(tab.tableName || tab.viewName);
|
||||
if (tab.type !== 'table' && tab.type !== 'view-def') {
|
||||
: tab.type === 'trigger'
|
||||
? toTrimmedString(tab.triggerName || tab.tableName)
|
||||
: tab.type === 'routine-def'
|
||||
? toTrimmedString(tab.routineName || tab.tableName)
|
||||
: toTrimmedString(tab.tableName || tab.viewName);
|
||||
if (tab.type !== 'table' && tab.type !== 'view-def' && tab.type !== 'trigger' && tab.type !== 'routine-def') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -109,7 +121,7 @@ export const normalizeSidebarLocateObjectRequestFromTab = (tab: SidebarLocateTab
|
||||
tableName: objectName,
|
||||
objectGroup: tab.type === 'view-def'
|
||||
? (tab.viewKind === 'materialized' ? 'materializedViews' : 'views')
|
||||
: undefined,
|
||||
: (tab.type === 'trigger' ? 'triggers' : (tab.type === 'routine-def' ? 'routines' : undefined)),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -121,7 +133,13 @@ export const resolveSidebarLocateTarget = (
|
||||
const databaseKey = `${request.connectionId}-${request.dbName}`;
|
||||
const fallbackTargetKey = request.objectGroup === 'materializedViews'
|
||||
? `${databaseKey}-materialized-view-${request.tableName}`
|
||||
: (request.objectGroup === 'views' ? `${databaseKey}-view-${request.tableName}` : `${databaseKey}-${request.tableName}`);
|
||||
: request.objectGroup === 'views'
|
||||
? `${databaseKey}-view-${request.tableName}`
|
||||
: request.objectGroup === 'triggers'
|
||||
? `${databaseKey}-trigger-${request.tableName}`
|
||||
: request.objectGroup === 'routines'
|
||||
? `${databaseKey}-routine-${request.tableName}`
|
||||
: `${databaseKey}-${request.tableName}`;
|
||||
const targetKey = request.tabId || fallbackTargetKey;
|
||||
const schemaSegment = request.schemaName || 'default';
|
||||
const schemaKey = options.groupBySchema ? `${databaseKey}-schema-${schemaSegment}` : undefined;
|
||||
@@ -204,6 +222,16 @@ const matchesLocateObjectNode = (node: SidebarLocateTreeNodeLike, target: Sideba
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.viewName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
}
|
||||
|
||||
if (target.objectGroup === 'triggers') {
|
||||
if (node.type !== 'db-trigger') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.triggerName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
}
|
||||
|
||||
if (target.objectGroup === 'routines') {
|
||||
if (node.type !== 'routine') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.routineName || dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
}
|
||||
|
||||
if (node.type !== 'table') return false;
|
||||
return matchesLocateObjectName(target, toTrimmedString(dataRef.tableName), toTrimmedString(dataRef.schemaName));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user