diff --git a/superset-frontend/src/showSavedQuery/index.jsx b/superset-frontend/src/showSavedQuery/index.jsx deleted file mode 100644 index 10c09703f7206..0000000000000 --- a/superset-frontend/src/showSavedQuery/index.jsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 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 React from 'react'; -import ReactDom from 'react-dom'; -import Form from 'react-jsonschema-form'; -import { interpolate } from 'src/showSavedQuery/utils'; -import { styled } from '@superset-ui/core'; -import getBootstrapData from 'src/utils/getBootstrapData'; - -const scheduleInfoContainer = document.getElementById('schedule-info'); -const bootstrapData = getBootstrapData(); -const config = bootstrapData.common.conf.SCHEDULED_QUERIES; -const { query } = bootstrapData.common; -const scheduleInfo = query.extra_json.schedule_info; -const linkback = config.linkback ? interpolate(config.linkback, query) : null; - -const StyledSavedQueryContainer = styled.div` - .btn-add { - display: none; - } -`; - -const StyledLinkBack = styled.div` - ${({ theme }) => ` - padding-top: 0; - padding-right: ${theme.gridUnit * 2 + 2}px; - padding-bottom: ${theme.gridUnit * 5}px; - padding-left: ${theme.gridUnit / 2}px; -`} -`; - -if (scheduleInfo && config) { - // hide instructions when showing schedule info - config.JSONSCHEMA.description = ''; - - ReactDom.render( - -
-
-
- {linkback && ( - - - -   Pipeline status - - - )} -
, - scheduleInfoContainer, - ); -} diff --git a/superset-frontend/src/showSavedQuery/utils.js b/superset-frontend/src/showSavedQuery/utils.js deleted file mode 100644 index 9cd712bc893f3..0000000000000 --- a/superset-frontend/src/showSavedQuery/utils.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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. - */ - -export function getNestedValue(obj, id, separator = '.') { - /* - * Given a nested object and an id, return the nested value. - * - * > getNestedValue({a:{b:1}}, 'a.b') - * < 1 - */ - const index = id.indexOf(separator); - if (index === -1) { - return obj[id]; - } - const name = id.slice(0, index); - const rest = id.slice(index + separator.length); - return getNestedValue(obj[name], rest, separator); -} - -export function interpolate(str, obj) { - /* - * Programmatic template string for interpolation. - * - * > interpolate('foo ${a.b}', {a:{b:1}}) - * < "foo 1" - */ - return str.replace(/\$\{(.+?)\}/g, (match, id) => getNestedValue(obj, id)); -} diff --git a/superset-frontend/src/showSavedQuery/utils.test.jsx b/superset-frontend/src/showSavedQuery/utils.test.jsx deleted file mode 100644 index 8d999824812a8..0000000000000 --- a/superset-frontend/src/showSavedQuery/utils.test.jsx +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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 { getNestedValue, interpolate } from 'src/showSavedQuery/utils'; - -describe('getNestedValue', () => { - it('is a function', () => { - expect(typeof getNestedValue).toBe('function'); - }); - - it('works with simple ids', () => { - const obj = { a: '1' }; - const id = 'a'; - expect(getNestedValue(obj, id)).toEqual('1'); - }); - - it('works with complex ids', () => { - const obj = { a: { b: '1' } }; - const id = 'a.b'; - expect(getNestedValue(obj, id)).toEqual('1'); - }); - - it('works with other separators', () => { - const obj = { a: { b: { c: '1' } } }; - const id = 'a__b__c'; - const separator = '__'; - expect(getNestedValue(obj, id, separator)).toEqual('1'); - }); -}); - -describe('interpolate', () => { - it('is a function', () => { - expect(typeof interpolate).toBe('function'); - }); - - it('works with simple ids', () => { - const obj = { a: '1' }; - // eslint-disable-next-line no-template-curly-in-string - const str = 'value: ${a}'; - expect(interpolate(str, obj)).toEqual('value: 1'); - }); - - it('works with complex ids', () => { - const obj = { a: { b: '1' } }; - // eslint-disable-next-line no-template-curly-in-string - const str = 'value: ${a.b}'; - expect(interpolate(str, obj)).toEqual('value: 1'); - }); -}); diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js index a47670d9d4023..7bad2ea875393 100644 --- a/superset-frontend/webpack.config.js +++ b/superset-frontend/webpack.config.js @@ -212,7 +212,6 @@ const config = { spa: addPreamble('/src/views/index.tsx'), embedded: addPreamble('/src/embedded/index.tsx'), sqllab: addPreamble('/src/SqlLab/index.tsx'), - showSavedQuery: [path.join(APP_DIR, '/src/showSavedQuery/index.jsx')], }, output, stats: 'minimal', diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index c390a6f779ec4..f5473ba25e84c 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -186,7 +186,6 @@ def init_views(self) -> None: from superset.views.redirects import R from superset.views.sql_lab.views import ( SavedQueryView, - SavedQueryViewApi, SqlLab, TableSchemaView, TabStateView, @@ -312,7 +311,6 @@ def init_views(self) -> None: appbuilder.add_view_no_menu(R) appbuilder.add_view_no_menu(ProfileView) appbuilder.add_view_no_menu(SavedQueryView) - appbuilder.add_view_no_menu(SavedQueryViewApi) appbuilder.add_view_no_menu(SliceAsync) appbuilder.add_view_no_menu(SqlLab) appbuilder.add_view_no_menu(SqlMetricInlineView) diff --git a/superset/views/sql_lab/views.py b/superset/views/sql_lab/views.py index 0da95612de2e0..62558f5ab524f 100644 --- a/superset/views/sql_lab/views.py +++ b/superset/views/sql_lab/views.py @@ -17,120 +17,31 @@ import logging import simplejson as json -from flask import g, redirect, request, Response +from flask import redirect, request, Response from flask_appbuilder import expose -from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_appbuilder.security.decorators import has_access, has_access_api from flask_babel import lazy_gettext as _ from sqlalchemy import and_ from superset import db -from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod -from superset.models.sql_lab import Query, SavedQuery, TableSchema, TabState +from superset.models.sql_lab import Query, TableSchema, TabState from superset.superset_typing import FlaskResponse from superset.utils import core as utils from superset.utils.core import get_user_id -from superset.views.base import ( - BaseSupersetView, - DeleteMixin, - json_success, - SupersetModelView, -) +from superset.views.base import BaseSupersetView, json_success logger = logging.getLogger(__name__) -class SavedQueryView( # pylint: disable=too-many-ancestors - SupersetModelView, - DeleteMixin, -): - datamodel = SQLAInterface(SavedQuery) - include_route_methods = RouteMethod.CRUD_SET - +class SavedQueryView(BaseSupersetView): + route_base = "/savedqueryview" class_permission_name = "SavedQuery" - method_permission_name = MODEL_VIEW_RW_METHOD_PERMISSION_MAP - list_title = _("List Saved Query") - show_title = _("Show Saved Query") - add_title = _("Add Saved Query") - edit_title = _("Edit Saved Query") - - list_columns = [ - "label", - "user", - "database", - "schema", - "description", - "modified", - "pop_tab_link", - ] - order_columns = ["label", "schema", "description", "modified"] - show_columns = [ - "id", - "label", - "user", - "database", - "description", - "sql", - "pop_tab_link", - ] - search_columns = ("label", "user", "database", "schema", "changed_on") - add_columns = ["label", "database", "description", "sql"] - edit_columns = add_columns - base_order = ("changed_on", "desc") - label_columns = { - "label": _("Label"), - "user": _("User"), - "database": _("Database"), - "description": _("Description"), - "modified": _("Modified"), - "end_time": _("End Time"), - "pop_tab_link": _("Pop Tab Link"), - "changed_on": _("Changed on"), - } @expose("/list/") @has_access def list(self) -> FlaskResponse: return super().render_app_template() - def pre_add(self, item: "SavedQueryView") -> None: - item.user = g.user - - def pre_update(self, item: "SavedQueryView") -> None: - self.pre_add(item) - - -class SavedQueryViewApi(SavedQueryView): # pylint: disable=too-many-ancestors - include_route_methods = { - RouteMethod.API_READ, - RouteMethod.API_CREATE, - RouteMethod.API_UPDATE, - RouteMethod.API_GET, - } - - class_permission_name = "SavedQuery" - method_permission_name = MODEL_VIEW_RW_METHOD_PERMISSION_MAP - - list_columns = [ - "id", - "label", - "sqlalchemy_uri", - "user_email", - "schema", - "description", - "sql", - "extra_json", - "extra", - ] - add_columns = ["label", "db_id", "schema", "description", "sql", "extra_json"] - edit_columns = add_columns - show_columns = add_columns + ["id"] - - @has_access_api - @expose("show/") - def show(self, pk: int) -> FlaskResponse: - return super().show(pk) - def _get_owner_id(tab_state_id: int) -> int: return db.session.query(TabState.user_id).filter_by(id=tab_state_id).scalar()