Skip to content

Commit

Permalink
chore(sqllab): typescript for getInitialState (apache#25047)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpark authored Aug 24, 2023
1 parent ac62bac commit 2aa200e
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const SouthPane = ({
queries[dataPreviewQueryId],
)
.map(({ name, dataPreviewQueryId }) => ({
...queries[dataPreviewQueryId],
...queries[dataPreviewQueryId || ''],
tableName: name,
}));
const editorQueries = Object.values(queries).filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const SqlEditor = ({
const { database, latestQuery, hideLeftBar } = useSelector(
({ sqlLab: { unsavedQueryEditor, databases, queries } }) => {
let { dbId, latestQueryId, hideLeftBar } = queryEditor;
if (unsavedQueryEditor.id === queryEditor.id) {
if (unsavedQueryEditor?.id === queryEditor.id) {
dbId = unsavedQueryEditor.dbId || dbId;
latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId;
hideLeftBar = unsavedQueryEditor.hideLeftBar || hideLeftBar;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import React, {
import { useDispatch } from 'react-redux';
import querystring from 'query-string';

import { Table } from 'src/SqlLab/types';
import {
queryEditorSetDb,
addTable,
Expand All @@ -52,7 +53,7 @@ import {
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
import TableElement, { Table } from '../TableElement';
import TableElement from '../TableElement';

interface ExtendedTable extends Table {
expanded: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
const qe = useSelector<SqlLabRootState, QueryEditor>(
({ sqlLab: { unsavedQueryEditor } }) => ({
...queryEditor,
...(queryEditor.id === unsavedQueryEditor.id && unsavedQueryEditor),
...(queryEditor.id === unsavedQueryEditor?.id && unsavedQueryEditor),
}),
shallowEqual,
);
Expand Down
11 changes: 1 addition & 10 deletions superset-frontend/src/SqlLab/components/TableElement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
import React, { useState, useRef, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import type { Table } from 'src/SqlLab/types';
import Collapse from 'src/components/Collapse';
import Card from 'src/components/Card';
import ButtonGroup from 'src/components/ButtonGroup';
Expand Down Expand Up @@ -49,16 +50,6 @@ export interface Column {
type: string;
}

export interface Table {
id: string;
dbId: number;
schema: string;
name: string;
dataPreviewQueryId?: string | null;
expanded?: boolean;
initialized?: boolean;
}

export interface TableElementProps {
table: Table;
}
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/SqlLab/hooks/useQueryEditor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function useQueryEditor<T extends keyof QueryEditor>(
pick(
{
...queryEditors.find(({ id }) => id === sqlEditorId),
...(sqlEditorId === unsavedQueryEditor.id && unsavedQueryEditor),
...(sqlEditorId === unsavedQueryEditor?.id && unsavedQueryEditor),
},
['id'].concat(attributes),
) as Pick<QueryEditor, T | 'id'>,
Expand Down
108 changes: 90 additions & 18 deletions superset-frontend/src/SqlLab/reducers/getInitialState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,115 @@
* under the License.
*/

import { DEFAULT_COMMON_BOOTSTRAP_DATA } from 'src/constants';
import getInitialState, { dedupeTabHistory } from './getInitialState';

const apiData = {
common: {
conf: {
DEFAULT_SQLLAB_LIMIT: 1,
},
},
active_tab: null,
common: DEFAULT_COMMON_BOOTSTRAP_DATA,
tab_state_ids: [],
databases: [],
queries: [],
requested_query: null,
queries: {},
user: {
userId: 1,
username: 'some name',
isActive: true,
isAnonymous: false,
firstName: 'first name',
lastName: 'last name',
permissions: {},
roles: {},
},
};
const apiDataWithTabState = {
...apiData,
tab_state_ids: [{ id: 1 }],
active_tab: { id: 1, table_schemas: [] },
tab_state_ids: [{ id: 1, label: 'test' }],
active_tab: {
id: 1,
user_id: 1,
label: 'editor1',
active: true,
database_id: 1,
sql: '',
table_schemas: [],
saved_query: null,
template_params: null,
latest_query: null,
},
};
describe('getInitialState', () => {
it('should output the user that is passed in', () => {
expect(getInitialState(apiData).user.userId).toEqual(1);
expect(getInitialState(apiData).user?.userId).toEqual(1);
});
it('should return undefined instead of null for templateParams', () => {
expect(
getInitialState(apiDataWithTabState).sqlLab.queryEditors[0]
.templateParams,
getInitialState(apiDataWithTabState).sqlLab?.queryEditors?.[0]
?.templateParams,
).toBeUndefined();
});

describe('dedupeTabHistory', () => {
it('should dedupe the tab history', () => {
[
{ value: [], expected: [] },
{ value: [12, 3, 4, 5, 6], expected: [12, 3, 4, 5, 6] },
{ value: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], expected: [1, 2] },
{
value: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3],
expected: [1, 2, 3],
value: ['12', '3', '4', '5', '6'],
expected: ['12', '3', '4', '5', '6'],
},
{
value: [
'1',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
],
expected: ['1', '2'],
},
{
value: [
'1',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'3',
],
expected: ['1', '2', '3'],
},
{
value: [
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'2',
'3',
],
expected: ['2', '3'],
},
{ value: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3], expected: [2, 3] },
].forEach(({ value, expected }) => {
expect(dedupeTabHistory(value)).toEqual(expected);
});
Expand Down Expand Up @@ -92,6 +156,11 @@ describe('getInitialState', () => {
...apiData,
active_tab: {
id: 1,
user_id: 1,
label: 'editor1',
active: true,
database_id: 1,
sql: '',
table_schemas: [
{
id: 1,
Expand All @@ -116,6 +185,9 @@ describe('getInitialState', () => {
},
},
],
saved_query: null,
template_params: null,
latest_query: null,
},
}).sqlLab.tables;
expect(initializedTables.map(({ id }) => id)).toEqual([1, 2, 6]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@
*/
import { t } from '@superset-ui/core';
import getToastsFromPyFlashMessages from 'src/components/MessageToasts/getToastsFromPyFlashMessages';
import type { BootstrapData } from 'src/types/bootstrapTypes';
import type { InitialState } from 'src/hooks/apiResources/sqlLab';
import type {
QueryEditor,
UnsavedQueryEditor,
SqlLabRootState,
Table,
} from 'src/SqlLab/types';

export function dedupeTabHistory(tabHistory) {
return tabHistory.reduce(
export function dedupeTabHistory(tabHistory: string[]) {
return tabHistory.reduce<string[]>(
(result, tabId) =>
result.slice(-1)[0] === tabId ? result : result.concat(tabId),
[],
Expand All @@ -34,7 +42,7 @@ export default function getInitialState({
databases,
queries: queries_,
user,
}) {
}: BootstrapData & Partial<InitialState>) {
/**
* Before YYYY-MM-DD, the state for SQL Lab was stored exclusively in the
* browser's localStorage. The feature flag `SQLLAB_BACKEND_PERSISTENCE`
Expand All @@ -43,54 +51,42 @@ export default function getInitialState({
* To allow for a transparent migration, the initial state is a combination
* of the backend state (if any) with the browser state (if any).
*/
let queryEditors = {};
let queryEditors: Record<string, QueryEditor> = {};
const defaultQueryEditor = {
id: null,
loaded: true,
name: t('Untitled query'),
sql: 'SELECT *\nFROM\nWHERE',
selectedText: null,
latestQueryId: null,
autorun: false,
templateParams: null,
dbId: common.conf.SQLLAB_DEFAULT_DBID,
queryLimit: common.conf.DEFAULT_SQLLAB_LIMIT,
validationResult: {
id: null,
errors: [],
completed: false,
},
hideLeftBar: false,
remoteId: null,
};
let unsavedQueryEditor = {};
let unsavedQueryEditor: UnsavedQueryEditor = {};

/**
* Load state from the backend. This will be empty if the feature flag
* `SQLLAB_BACKEND_PERSISTENCE` is off.
*/
tabStateIds.forEach(({ id, label }) => {
let queryEditor;
let queryEditor: QueryEditor;
if (activeTab && activeTab.id === id) {
queryEditor = {
id: id.toString(),
loaded: true,
name: activeTab.label,
sql: activeTab.sql || undefined,
sql: activeTab.sql || '',
selectedText: undefined,
latestQueryId: activeTab.latest_query
? activeTab.latest_query.id
: null,
remoteId: activeTab.saved_query?.id,
autorun: activeTab.autorun,
remoteId: activeTab.saved_query?.id || null,
autorun: Boolean(activeTab.autorun),
templateParams: activeTab.template_params || undefined,
dbId: activeTab.database_id,
schema: activeTab.schema,
queryLimit: activeTab.query_limit,
validationResult: {
id: null,
errors: [],
completed: false,
},
hideLeftBar: activeTab.hide_left_bar,
};
} else {
Expand All @@ -109,7 +105,8 @@ export default function getInitialState({
});

const tabHistory = activeTab ? [activeTab.id.toString()] : [];
let tables = {};
let tables = {} as Record<string, Table>;
const editorTabLastUpdatedAt = Date.now();
if (activeTab) {
activeTab.table_schemas
.filter(tableSchema => tableSchema.description !== null)
Expand Down Expand Up @@ -140,17 +137,18 @@ export default function getInitialState({
* hasn't used SQL Lab after it has been turned on, the state will be stored
* in the browser's local storage.
*/
if (
localStorage.getItem('redux') &&
JSON.parse(localStorage.getItem('redux')).sqlLab
) {
const { sqlLab } = JSON.parse(localStorage.getItem('redux'));
const localStorageData = localStorage.getItem('redux');
const sqlLabCacheData = localStorageData
? (JSON.parse(localStorageData) as Pick<SqlLabRootState, 'sqlLab'>)
: undefined;
if (localStorageData && sqlLabCacheData?.sqlLab) {
const { sqlLab } = sqlLabCacheData;

if (sqlLab.queryEditors.length === 0) {
// migration was successful
localStorage.removeItem('redux');
} else {
unsavedQueryEditor = sqlLab.unsavedQueryEditor || {};
unsavedQueryEditor = sqlLab.unsavedQueryEditor || unsavedQueryEditor;
// add query editors and tables to state with a special flag so they can
// be migrated if the `SQLLAB_BACKEND_PERSISTENCE` feature flag is on
sqlLab.queryEditors.forEach(qe => {
Expand Down Expand Up @@ -199,11 +197,12 @@ export default function getInitialState({
tabHistory: dedupeTabHistory(tabHistory),
tables: Object.values(tables),
queriesLastUpdate: Date.now(),
unsavedQueryEditor,
editorTabLastUpdatedAt,
queryCostEstimates: {},
unsavedQueryEditor,
},
messageToasts: getToastsFromPyFlashMessages(
(common || {}).flash_messages || [],
(common || {})?.flash_messages || [],
),
localStorageUsageInKilobytes: 0,
common: {
Expand Down
Loading

0 comments on commit 2aa200e

Please sign in to comment.