Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(sql_lab): SQL Lab Persistent Saved State #17771

Merged
merged 6 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions superset-frontend/src/SqlLab/actions/sqlLab.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,12 +636,13 @@ export function switchQueryEditor(queryEditor, displayLimit) {
title: json.label,
sql: json.sql,
selectedText: null,
latestQueryId: json.latest_query ? json.latest_query.id : null,
latestQueryId: json.latest_query?.id,
autorun: json.autorun,
dbId: json.database_id,
templateParams: json.template_params,
schema: json.schema,
queryLimit: json.query_limit,
remoteId: json.saved_query?.id,
validationResult: {
id: null,
errors: [],
Expand Down Expand Up @@ -864,19 +865,38 @@ export function saveQuery(query) {
stringify: false,
})
.then(result => {
const savedQuery = convertQueryToClient(result.json.item);
dispatch({
type: QUERY_EDITOR_SAVED,
query,
result: convertQueryToClient(result.json.item),
result: savedQuery,
});
dispatch(addSuccessToast(t('Your query was saved')));
dispatch(queryEditorSetTitle(query, query.title));
return savedQuery;
})
.catch(() =>
dispatch(addDangerToast(t('Your query could not be saved'))),
);
}

export const addSavedQueryToTabState =
(queryEditor, savedQuery) => dispatch => {
const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)
? SupersetClient.put({
endpoint: `/tabstateview/${queryEditor.id}`,
postPayload: { saved_query_id: savedQuery.remoteId },
})
: Promise.resolve();

return sync
.catch(() => {
dispatch(addDangerToast(t('Your query was not properly saved')));
})
.then(() => {
dispatch(addSuccessToast(t('Your query was saved')));
});
};

export function updateSavedQuery(query) {
return dispatch =>
SupersetClient.put({
Expand Down
4 changes: 1 addition & 3 deletions superset-frontend/src/SqlLab/actions/sqlLab.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import shortid from 'shortid';
import * as featureFlags from 'src/featureFlags';
import { ADD_TOAST } from 'src/components/MessageToasts/actions';
import * as actions from 'src/SqlLab/actions/sqlLab';
import { defaultQueryEditor, query } from '../fixtures';

Expand Down Expand Up @@ -93,7 +92,7 @@ describe('async actions', () => {
expect.assertions(1);

return makeRequest().then(() => {
expect(dispatch.callCount).toBe(3);
expect(dispatch.callCount).toBe(2);
});
});

Expand All @@ -111,7 +110,6 @@ describe('async actions', () => {
const store = mockStore({});
const expectedActionTypes = [
actions.QUERY_EDITOR_SAVED,
ADD_TOAST,
actions.QUERY_EDITOR_SET_TITLE,
];
return store.dispatch(actions.saveQuery(query)).then(() => {
Expand Down
7 changes: 6 additions & 1 deletion superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Row, Col, Input, TextArea } from 'src/common/components';
import { t, styled } from '@superset-ui/core';
import Button from 'src/components/Button';
Expand Down Expand Up @@ -90,6 +90,11 @@ export default function SaveQuery({
description,
});

useEffect(() => {
if (!isSaved) {
setLabel(defaultLabel);
}
}, [defaultLabel]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was added to change the title of the modal

const close = () => {
setShowSave(false);
};
Expand Down
12 changes: 11 additions & 1 deletion superset-frontend/src/SqlLab/components/SqlEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
queryEditorSetTemplateParams,
runQuery,
saveQuery,
addSavedQueryToTabState,
scheduleQuery,
setActiveSouthPaneTab,
updateSavedQuery,
Expand Down Expand Up @@ -192,6 +193,7 @@ class SqlEditor extends React.PureComponent {
this.canValidateQuery = this.canValidateQuery.bind(this);
this.runQuery = this.runQuery.bind(this);
this.stopQuery = this.stopQuery.bind(this);
this.saveQuery = this.saveQuery.bind(this);
this.onSqlChanged = this.onSqlChanged.bind(this);
this.setQueryEditorSql = this.setQueryEditorSql.bind(this);
this.setQueryEditorSqlWithDebounce = debounce(
Expand Down Expand Up @@ -592,6 +594,12 @@ class SqlEditor extends React.PureComponent {
);
}

async saveQuery(query) {
const { queryEditor: qe, actions } = this.props;
const savedQuery = await actions.saveQuery(query);
actions.addSavedQueryToTabState(qe, savedQuery);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@betodealmeida we need to return it so that we can used the savedQuery context here, to pass it into the next action.

}

renderEditorBottomBar() {
const { queryEditor: qe } = this.props;

Expand Down Expand Up @@ -630,6 +638,7 @@ class SqlEditor extends React.PureComponent {
)}
</Menu>
);

return (
<StyledToolbar className="sql-toolbar" id="js-sql-toolbar">
<div className="leftItems">
Expand Down Expand Up @@ -693,7 +702,7 @@ class SqlEditor extends React.PureComponent {
<SaveQuery
query={qe}
defaultLabel={qe.title || qe.description}
onSave={this.props.actions.saveQuery}
onSave={this.saveQuery}
onUpdate={this.props.actions.updateSavedQuery}
saveQueryWarning={this.props.saveQueryWarning}
/>
Expand Down Expand Up @@ -809,6 +818,7 @@ function mapDispatchToProps(dispatch) {
queryEditorSetTemplateParams,
runQuery,
saveQuery,
addSavedQueryToTabState,
scheduleQuery,
setActiveSouthPaneTab,
updateSavedQuery,
Expand Down
1 change: 1 addition & 0 deletions superset-frontend/src/SqlLab/reducers/getInitialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default function getInitialState({
latestQueryId: activeTab.latest_query
? activeTab.latest_query.id
: null,
remoteId: activeTab.saved_query?.id,
autorun: activeTab.autorun,
templateParams: activeTab.template_params || undefined,
dbId: activeTab.database_id,
Expand Down
10 changes: 10 additions & 0 deletions superset/models/sql_lab.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ class SavedQuery(Model, AuditMixinNullable, ExtraJSONMixin, ImportExportMixin):
def __repr__(self) -> str:
return str(self.label)

def to_dict(self) -> Dict[str, Any]:
return {
"id": self.id,
}

@property
def pop_tab_link(self) -> Markup:
return Markup(
Expand Down Expand Up @@ -285,6 +290,10 @@ class TabState(Model, AuditMixinNullable, ExtraJSONMixin):
template_params = Column(Text)
hide_left_bar = Column(Boolean, default=False)

# any saved queries that are associated with the Tab State
saved_query_id = Column(Integer, ForeignKey("saved_query.id"), nullable=True)
saved_query = relationship("SavedQuery", foreign_keys=[saved_query_id])

def to_dict(self) -> Dict[str, Any]:
return {
"id": self.id,
Expand All @@ -300,6 +309,7 @@ def to_dict(self) -> Dict[str, Any]:
"autorun": self.autorun,
"template_params": self.template_params,
"hide_left_bar": self.hide_left_bar,
"saved_query": self.saved_query.to_dict() if self.saved_query else None,
}


Expand Down