mirror of
https://github.com/apache/superset.git
synced 2026-05-10 18:35:40 +00:00
refactor: Organizes the src/core folder (#35119)
This commit is contained in:
committed by
GitHub
parent
133e686224
commit
bc6859a99d
343
superset-frontend/src/core/sqlLab/index.ts
Normal file
343
superset-frontend/src/core/sqlLab/index.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { sqlLab as sqlLabType } from '@apache-superset/core';
|
||||
import {
|
||||
QUERY_FAILED,
|
||||
QUERY_SUCCESS,
|
||||
QUERY_EDITOR_SETDB,
|
||||
querySuccess,
|
||||
startQuery,
|
||||
START_QUERY,
|
||||
stopQuery,
|
||||
STOP_QUERY,
|
||||
createQueryFailedAction,
|
||||
} from 'src/SqlLab/actions/sqlLab';
|
||||
import { RootState, store } from 'src/views/store';
|
||||
import { AnyListenerPredicate } from '@reduxjs/toolkit';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import type { SqlLabRootState } from 'src/SqlLab/types';
|
||||
import { Disposable } from '../models';
|
||||
import { createActionListener } from '../utils';
|
||||
import {
|
||||
Panel,
|
||||
Editor,
|
||||
Tab,
|
||||
QueryContext,
|
||||
QueryResultContext,
|
||||
QueryErrorResultContext,
|
||||
} from './models';
|
||||
|
||||
const { CTASMethod } = sqlLabType;
|
||||
|
||||
const getSqlLabState = () => {
|
||||
const { sqlLab }: { sqlLab: SqlLabRootState['sqlLab'] } = store.getState();
|
||||
return sqlLab;
|
||||
};
|
||||
|
||||
const activeEditorId = () => {
|
||||
const { tabHistory } = getSqlLabState();
|
||||
return tabHistory[tabHistory.length - 1];
|
||||
};
|
||||
|
||||
const findQueryEditor = (editorId: string) => {
|
||||
const { queryEditors } = getSqlLabState();
|
||||
return queryEditors.find(editor => editor.id === editorId);
|
||||
};
|
||||
|
||||
const createTab = (
|
||||
id: string,
|
||||
name: string,
|
||||
sql: string,
|
||||
dbId: number,
|
||||
catalog?: string,
|
||||
schema?: string,
|
||||
table?: any,
|
||||
) => {
|
||||
const editor = new Editor(sql, dbId, catalog, schema, table);
|
||||
const panels: Panel[] = []; // TODO: Populate panels
|
||||
return new Tab(id, name, editor, panels);
|
||||
};
|
||||
|
||||
const notImplemented = (): never => {
|
||||
throw new Error('Not implemented yet');
|
||||
};
|
||||
|
||||
function extractBaseData(action: any): {
|
||||
baseParams: [string, Tab, boolean, number];
|
||||
options: {
|
||||
ctasMethod?: string;
|
||||
tempTable?: string;
|
||||
templateParams?: string;
|
||||
requestedLimit?: number;
|
||||
};
|
||||
} {
|
||||
const { query } = action;
|
||||
const {
|
||||
id,
|
||||
sql,
|
||||
startDttm,
|
||||
runAsync,
|
||||
dbId,
|
||||
catalog,
|
||||
schema,
|
||||
sqlEditorId,
|
||||
tab: tabName,
|
||||
ctas_method: ctasMethod,
|
||||
tempTable,
|
||||
templateParams,
|
||||
queryLimit,
|
||||
} = query;
|
||||
|
||||
const tab = createTab(sqlEditorId, tabName, sql, dbId, catalog, schema);
|
||||
|
||||
return {
|
||||
baseParams: [id, tab, runAsync ?? false, startDttm ?? 0],
|
||||
options: {
|
||||
ctasMethod,
|
||||
tempTable,
|
||||
templateParams,
|
||||
requestedLimit: queryLimit,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createQueryContext(
|
||||
action: ReturnType<typeof startQuery> | ReturnType<typeof stopQuery>,
|
||||
): QueryContext {
|
||||
const { baseParams, options } = extractBaseData(action);
|
||||
return new QueryContext(...baseParams, options);
|
||||
}
|
||||
|
||||
function createQueryResultContext(
|
||||
action: ReturnType<typeof querySuccess>,
|
||||
): QueryResultContext {
|
||||
const { baseParams, options } = extractBaseData(action);
|
||||
const [_, tab] = baseParams;
|
||||
const { results } = action;
|
||||
const {
|
||||
query_id: queryId,
|
||||
columns,
|
||||
data,
|
||||
query: {
|
||||
endDttm,
|
||||
executedSql,
|
||||
tempTable: resultTempTable,
|
||||
limit,
|
||||
limitingFactor,
|
||||
},
|
||||
} = results;
|
||||
|
||||
return new QueryResultContext(
|
||||
...baseParams,
|
||||
queryId,
|
||||
executedSql ?? tab.editor.content,
|
||||
columns,
|
||||
data,
|
||||
endDttm,
|
||||
{
|
||||
...options,
|
||||
tempTable: resultTempTable || options.tempTable,
|
||||
appliedLimit: limit,
|
||||
appliedLimitingFactor: limitingFactor,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function createQueryErrorContext(
|
||||
action: ReturnType<typeof createQueryFailedAction>,
|
||||
): QueryErrorResultContext {
|
||||
const { baseParams, options } = extractBaseData(action);
|
||||
const { msg: errorMessage, errors, query } = action;
|
||||
const { endDttm, executedSql, query_id: queryId } = query;
|
||||
|
||||
return new QueryErrorResultContext(...baseParams, errorMessage, errors, {
|
||||
...options,
|
||||
queryId,
|
||||
executedSql: executedSql ?? null,
|
||||
endDttm: endDttm ?? Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
const getCurrentTab: typeof sqlLabType.getCurrentTab = () => {
|
||||
const queryEditor = findQueryEditor(activeEditorId());
|
||||
if (queryEditor) {
|
||||
const { id, name, sql, dbId, catalog, schema } = queryEditor;
|
||||
return createTab(
|
||||
id,
|
||||
name,
|
||||
sql,
|
||||
dbId!,
|
||||
catalog ?? undefined,
|
||||
schema ?? undefined,
|
||||
undefined,
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getActiveEditorImmutableId = () => {
|
||||
const { tabHistory } = getSqlLabState();
|
||||
const activeEditorId = tabHistory[tabHistory.length - 1];
|
||||
const activeEditor = findQueryEditor(activeEditorId);
|
||||
return activeEditor?.immutableId;
|
||||
};
|
||||
|
||||
// Memoized version to avoid repeated store lookups when active editor hasn't changed
|
||||
const getActiveEditorId = memoizeOne(getActiveEditorImmutableId);
|
||||
|
||||
const predicate = (actionType: string): AnyListenerPredicate<RootState> => {
|
||||
// Capture the immutable ID of the active editor at the time the listener is created
|
||||
// This ID never changes for a tab, ensuring stable event routing
|
||||
const registrationImmutableId = getActiveEditorId();
|
||||
|
||||
return action => {
|
||||
if (action.type !== actionType) return false;
|
||||
|
||||
// If we don't have a registration ID, don't filter events
|
||||
if (!registrationImmutableId) return true;
|
||||
|
||||
// For query events, use the immutableId directly from the action payload
|
||||
if (action.query?.immutableId) {
|
||||
return action.query.immutableId === registrationImmutableId;
|
||||
}
|
||||
|
||||
// For tab events, we need to find the immutable ID of the affected tab
|
||||
if (action.queryEditor?.id) {
|
||||
const queryEditor = findQueryEditor(action.queryEditor.id);
|
||||
return queryEditor?.immutableId === registrationImmutableId;
|
||||
}
|
||||
|
||||
// Fallback: do not allow the event if we can't determine the source
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
const onDidQueryRun: typeof sqlLabType.onDidQueryRun = (
|
||||
listener: (queryContext: sqlLabType.QueryContext) => void,
|
||||
thisArgs?: any,
|
||||
): Disposable =>
|
||||
createActionListener(
|
||||
predicate(START_QUERY),
|
||||
listener,
|
||||
(action: ReturnType<typeof startQuery>) => createQueryContext(action),
|
||||
thisArgs,
|
||||
);
|
||||
|
||||
const onDidQuerySuccess: typeof sqlLabType.onDidQuerySuccess = (
|
||||
listener: (queryResultContext: sqlLabType.QueryResultContext) => void,
|
||||
thisArgs?: any,
|
||||
): Disposable =>
|
||||
createActionListener(
|
||||
predicate(QUERY_SUCCESS),
|
||||
listener,
|
||||
(action: ReturnType<typeof querySuccess>) =>
|
||||
createQueryResultContext(action),
|
||||
thisArgs,
|
||||
);
|
||||
|
||||
const onDidQueryStop: typeof sqlLabType.onDidQueryStop = (
|
||||
listener: (queryContext: sqlLabType.QueryContext) => void,
|
||||
thisArgs?: any,
|
||||
): Disposable =>
|
||||
createActionListener(
|
||||
predicate(STOP_QUERY),
|
||||
listener,
|
||||
(action: ReturnType<typeof stopQuery>) => createQueryContext(action),
|
||||
thisArgs,
|
||||
);
|
||||
|
||||
const onDidQueryFail: typeof sqlLabType.onDidQueryFail = (
|
||||
listener: (
|
||||
queryErrorResultContext: sqlLabType.QueryErrorResultContext,
|
||||
) => void,
|
||||
thisArgs?: any,
|
||||
): Disposable =>
|
||||
createActionListener(
|
||||
predicate(QUERY_FAILED),
|
||||
listener,
|
||||
(action: ReturnType<typeof createQueryFailedAction>) =>
|
||||
createQueryErrorContext(action),
|
||||
thisArgs,
|
||||
);
|
||||
|
||||
const onDidChangeEditorDatabase: typeof sqlLabType.onDidChangeEditorDatabase = (
|
||||
listener: (e: number) => void,
|
||||
thisArgs?: any,
|
||||
): Disposable =>
|
||||
createActionListener(
|
||||
predicate(QUERY_EDITOR_SETDB),
|
||||
listener,
|
||||
(action: { type: string; dbId?: number; queryEditor: { dbId: number } }) =>
|
||||
action.dbId || action.queryEditor.dbId,
|
||||
thisArgs,
|
||||
);
|
||||
|
||||
const onDidChangeEditorContent: typeof sqlLabType.onDidChangeEditorContent =
|
||||
notImplemented;
|
||||
const onDidChangeEditorCatalog: typeof sqlLabType.onDidChangeEditorCatalog =
|
||||
notImplemented;
|
||||
const onDidChangeEditorSchema: typeof sqlLabType.onDidChangeEditorSchema =
|
||||
notImplemented;
|
||||
const onDidChangeEditorTable: typeof sqlLabType.onDidChangeEditorTable =
|
||||
notImplemented;
|
||||
const onDidClosePanel: typeof sqlLabType.onDidClosePanel = notImplemented;
|
||||
const onDidChangeActivePanel: typeof sqlLabType.onDidChangeActivePanel =
|
||||
notImplemented;
|
||||
const onDidChangeTabTitle: typeof sqlLabType.onDidChangeTabTitle =
|
||||
notImplemented;
|
||||
const getDatabases: typeof sqlLabType.getDatabases = notImplemented;
|
||||
const getTabs: typeof sqlLabType.getTabs = notImplemented;
|
||||
const onDidCloseTab: typeof sqlLabType.onDidCloseTab = notImplemented;
|
||||
const onDidChangeActiveTab: typeof sqlLabType.onDidChangeActiveTab =
|
||||
notImplemented;
|
||||
const onDidRefreshDatabases: typeof sqlLabType.onDidRefreshDatabases =
|
||||
notImplemented;
|
||||
const onDidRefreshCatalogs: typeof sqlLabType.onDidRefreshCatalogs =
|
||||
notImplemented;
|
||||
const onDidRefreshSchemas: typeof sqlLabType.onDidRefreshSchemas =
|
||||
notImplemented;
|
||||
const onDidRefreshTables: typeof sqlLabType.onDidRefreshTables = notImplemented;
|
||||
|
||||
export const sqlLab: typeof sqlLabType = {
|
||||
CTASMethod,
|
||||
getCurrentTab,
|
||||
onDidChangeEditorContent,
|
||||
onDidChangeEditorDatabase,
|
||||
onDidChangeEditorCatalog,
|
||||
onDidChangeEditorSchema,
|
||||
onDidChangeEditorTable,
|
||||
onDidClosePanel,
|
||||
onDidChangeActivePanel,
|
||||
onDidChangeTabTitle,
|
||||
onDidQueryRun,
|
||||
onDidQueryStop,
|
||||
onDidQueryFail,
|
||||
onDidQuerySuccess,
|
||||
getDatabases,
|
||||
getTabs,
|
||||
onDidCloseTab,
|
||||
onDidChangeActiveTab,
|
||||
onDidRefreshDatabases,
|
||||
onDidRefreshCatalogs,
|
||||
onDidRefreshSchemas,
|
||||
onDidRefreshTables,
|
||||
};
|
||||
|
||||
// Export all models
|
||||
export * from './models';
|
||||
235
superset-frontend/src/core/sqlLab/models.ts
Normal file
235
superset-frontend/src/core/sqlLab/models.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { sqlLab as sqlLabType, core as coreType } from '@apache-superset/core';
|
||||
|
||||
const { CTASMethod } = sqlLabType;
|
||||
|
||||
export class Panel implements sqlLabType.Panel {
|
||||
id: string;
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class Editor implements sqlLabType.Editor {
|
||||
content: string;
|
||||
|
||||
databaseId: number;
|
||||
|
||||
schema: string;
|
||||
|
||||
// TODO: Check later if we'll use objects instead of strings.
|
||||
catalog: string | null;
|
||||
|
||||
table: string | null;
|
||||
|
||||
constructor(
|
||||
content: string,
|
||||
databaseId: number,
|
||||
catalog: string | null = null,
|
||||
schema = '',
|
||||
table: string | null = null,
|
||||
) {
|
||||
this.content = content;
|
||||
this.databaseId = databaseId;
|
||||
this.catalog = catalog;
|
||||
this.schema = schema;
|
||||
this.table = table;
|
||||
}
|
||||
}
|
||||
|
||||
export class Tab implements sqlLabType.Tab {
|
||||
id: string;
|
||||
|
||||
title: string;
|
||||
|
||||
editor: Editor;
|
||||
|
||||
panels: Panel[];
|
||||
|
||||
constructor(id: string, title: string, editor: Editor, panels: Panel[] = []) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.editor = editor;
|
||||
this.panels = panels;
|
||||
}
|
||||
}
|
||||
|
||||
export class CTAS implements sqlLabType.CTAS {
|
||||
method: sqlLabType.CTASMethod;
|
||||
|
||||
tempTable: string;
|
||||
|
||||
constructor(asView: boolean, tempTable: string) {
|
||||
this.method = asView ? CTASMethod.View : CTASMethod.Table;
|
||||
this.tempTable = tempTable;
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryContext implements sqlLabType.QueryContext {
|
||||
clientId: string;
|
||||
|
||||
ctas: sqlLabType.CTAS | null;
|
||||
|
||||
editor: Editor;
|
||||
|
||||
requestedLimit: number | null;
|
||||
|
||||
runAsync: boolean;
|
||||
|
||||
startDttm: number;
|
||||
|
||||
tab: Tab;
|
||||
|
||||
private templateParams: string;
|
||||
|
||||
private parsedParams: Record<string, any>;
|
||||
|
||||
constructor(
|
||||
clientId: string,
|
||||
tab: Tab,
|
||||
runAsync: boolean,
|
||||
startDttm: number,
|
||||
options: {
|
||||
templateParams?: string;
|
||||
ctasMethod?: string;
|
||||
tempTable?: string;
|
||||
requestedLimit?: number;
|
||||
} = {},
|
||||
) {
|
||||
this.clientId = clientId;
|
||||
this.tab = tab;
|
||||
this.editor = tab.editor;
|
||||
this.runAsync = runAsync;
|
||||
this.startDttm = startDttm;
|
||||
this.requestedLimit = options.requestedLimit ?? null;
|
||||
this.ctas = options.tempTable
|
||||
? new CTAS(options.ctasMethod === CTASMethod.View, options.tempTable)
|
||||
: null;
|
||||
this.templateParams = options.templateParams ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom accessor is used to process JSON parsing only
|
||||
* when necessary for better performance.
|
||||
*/
|
||||
get templateParameters() {
|
||||
if (this.parsedParams) {
|
||||
return this.parsedParams;
|
||||
}
|
||||
|
||||
let parsed = {};
|
||||
try {
|
||||
parsed = JSON.parse(this.templateParams);
|
||||
} catch (e) {
|
||||
// ignore invalid format string.
|
||||
}
|
||||
this.parsedParams = parsed;
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryResultContext
|
||||
extends QueryContext
|
||||
implements sqlLabType.QueryResultContext
|
||||
{
|
||||
appliedLimit: number;
|
||||
|
||||
appliedLimitingFactor: string;
|
||||
|
||||
endDttm: number;
|
||||
|
||||
executedSql: string;
|
||||
|
||||
remoteId: number;
|
||||
|
||||
result: sqlLabType.QueryResult;
|
||||
|
||||
constructor(
|
||||
clientId: string,
|
||||
tab: Tab,
|
||||
runAsync: boolean,
|
||||
startDttm: number,
|
||||
remoteId: number,
|
||||
executedSql: string,
|
||||
columns: sqlLabType.QueryResult['columns'],
|
||||
data: sqlLabType.QueryResult['data'],
|
||||
endDttm: number,
|
||||
options: {
|
||||
appliedLimit?: number;
|
||||
appliedLimitingFactor?: string;
|
||||
templateParams?: string;
|
||||
ctasMethod?: string;
|
||||
tempTable?: string;
|
||||
requestedLimit?: number;
|
||||
} = {},
|
||||
) {
|
||||
const { appliedLimit, appliedLimitingFactor, ...opt } = options;
|
||||
super(clientId, tab, runAsync, startDttm, opt);
|
||||
this.remoteId = remoteId;
|
||||
this.executedSql = executedSql;
|
||||
this.endDttm = endDttm;
|
||||
this.result = {
|
||||
columns,
|
||||
data,
|
||||
};
|
||||
this.appliedLimit = appliedLimit ?? data.length;
|
||||
this.appliedLimitingFactor = options.appliedLimitingFactor ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryErrorResultContext
|
||||
extends QueryContext
|
||||
implements sqlLabType.QueryErrorResultContext
|
||||
{
|
||||
endDttm: number;
|
||||
|
||||
errorMessage: string;
|
||||
|
||||
errors: coreType.SupersetError[] | null;
|
||||
|
||||
executedSql: string | null;
|
||||
|
||||
constructor(
|
||||
clientId: string,
|
||||
tab: Tab,
|
||||
runAsync: boolean,
|
||||
startDttm: number,
|
||||
errorMessage: string,
|
||||
errors: coreType.SupersetError[],
|
||||
options: {
|
||||
ctasMethod?: string;
|
||||
executedSql?: string;
|
||||
endDttm?: number;
|
||||
templateParams?: string;
|
||||
tempTable?: string;
|
||||
requestedLimit?: number;
|
||||
queryId?: number;
|
||||
} = {},
|
||||
) {
|
||||
const { queryId, executedSql, endDttm, ...opt } = options;
|
||||
super(clientId, tab, runAsync, startDttm, opt);
|
||||
this.executedSql = executedSql ?? null;
|
||||
this.errorMessage = errorMessage;
|
||||
this.errors = errors;
|
||||
this.endDttm = endDttm ?? Date.now();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user