From ceab1e9a99ed72e75164da549aca69b3e1517b0a Mon Sep 17 00:00:00 2001 From: Diego Medina Date: Tue, 3 Jan 2023 19:37:55 -0300 Subject: [PATCH] chore: Migrate /superset/search_queries to API v1 --- .../QuerySearch/QuerySearch.test.jsx | 139 --------- .../SqlLab/components/QuerySearch/index.tsx | 289 ------------------ .../translations/de/LC_MESSAGES/messages.po | 30 -- .../translations/en/LC_MESSAGES/messages.po | 30 -- .../translations/es/LC_MESSAGES/messages.po | 30 -- .../translations/fr/LC_MESSAGES/messages.po | 30 -- .../translations/it/LC_MESSAGES/messages.po | 30 -- .../translations/ja/LC_MESSAGES/messages.po | 30 -- .../translations/ko/LC_MESSAGES/messages.po | 30 -- superset/translations/messages.pot | 30 -- .../translations/nl/LC_MESSAGES/messages.po | 30 -- .../translations/pt/LC_MESSAGES/message.po | 30 -- .../pt_BR/LC_MESSAGES/messages.po | 30 -- .../translations/ru/LC_MESSAGES/messages.po | 30 -- .../translations/sk/LC_MESSAGES/messages.po | 30 -- .../translations/sl/LC_MESSAGES/messages.po | 33 -- .../translations/zh/LC_MESSAGES/messages.po | 30 -- superset/views/core.py | 1 + 18 files changed, 1 insertion(+), 881 deletions(-) delete mode 100644 superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx delete mode 100644 superset-frontend/src/SqlLab/components/QuerySearch/index.tsx diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx b/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx deleted file mode 100644 index 2a891d34af84c..0000000000000 --- a/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx +++ /dev/null @@ -1,139 +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 thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import QuerySearch from 'src/SqlLab/components/QuerySearch'; -import { Provider } from 'react-redux'; -import { supersetTheme, ThemeProvider } from '@superset-ui/core'; -import { fireEvent, render, screen, act } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import userEvent from '@testing-library/user-event'; -import { user } from 'src/SqlLab/fixtures'; - -const mockStore = configureStore([thunk]); -const store = mockStore({ - sqlLab: user, -}); - -const SEARCH_ENDPOINT = 'glob:*/superset/search_queries?*'; -const USER_ENDPOINT = 'glob:*/api/v1/query/related/user'; -const DATABASE_ENDPOINT = 'glob:*/api/v1/database/?*'; - -fetchMock.get(SEARCH_ENDPOINT, []); -fetchMock.get(USER_ENDPOINT, []); -fetchMock.get(DATABASE_ENDPOINT, []); - -describe('QuerySearch', () => { - const mockedProps = { - displayLimit: 50, - }; - - it('is valid', () => { - expect( - React.isValidElement( - - - - - , - ), - ).toBe(true); - }); - - beforeEach(async () => { - // You need this await function in order to change state in the app. In fact you need it everytime you re-render. - await act(async () => { - render( - - - - - , - ); - }); - }); - - it('should have three Selects', () => { - expect(screen.getByText(/28 days ago/i)).toBeInTheDocument(); - expect(screen.getByText(/now/i)).toBeInTheDocument(); - expect(screen.getByText(/success/i)).toBeInTheDocument(); - }); - - it('updates fromTime on user selects from time', () => { - const role = screen.getByText(/28 days ago/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/1 hour ago/i)); - expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument(); - }); - - it('updates toTime on user selects on time', () => { - const role = screen.getByText(/now/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/1 hour ago/i)); - expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument(); - }); - - it('updates status on user selects status', () => { - const role = screen.getByText(/success/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/failed/i)); - expect(screen.getByText(/failed/i)).toBeInTheDocument(); - }); - - it('should have one input for searchText', () => { - expect( - screen.getByPlaceholderText(/Query search string/i), - ).toBeInTheDocument(); - }); - - it('updates search text on user inputs search text', () => { - const search = screen.getByPlaceholderText(/Query search string/i); - userEvent.type(search, 'text'); - expect(search.value).toBe('text'); - }); - - it('should have one Button', () => { - const button = screen.getAllByRole('button'); - expect(button.length).toEqual(1); - }); - - it('should call API when search button is pressed', async () => { - fetchMock.resetHistory(); - const button = screen.getByRole('button'); - await act(async () => { - userEvent.click(button); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1); - }); - - it('should call API when (only)enter key is pressed', async () => { - fetchMock.resetHistory(); - const search = screen.getByPlaceholderText(/Query search string/i); - await act(async () => { - userEvent.type(search, 'a'); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(0); - await act(async () => { - userEvent.type(search, '{enter}'); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1); - }); -}); diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx deleted file mode 100644 index 3018ff1924586..0000000000000 --- a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx +++ /dev/null @@ -1,289 +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, { useState, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; - -import { setDatabases, addDangerToast } from 'src/SqlLab/actions/sqlLab'; -import Button from 'src/components/Button'; -import Select from 'src/components/DeprecatedSelect'; -import { styled, t, SupersetClient, QueryResponse } from '@superset-ui/core'; -import { debounce } from 'lodash'; -import Loading from 'src/components/Loading'; -import { - now, - epochTimeXHoursAgo, - epochTimeXDaysAgo, - epochTimeXYearsAgo, -} from 'src/utils/dates'; -import AsyncSelect from 'src/components/AsyncSelect'; -import { STATUS_OPTIONS, TIME_OPTIONS } from 'src/SqlLab/constants'; -import QueryTable from '../QueryTable'; - -interface QuerySearchProps { - displayLimit: number; -} - -interface UserMutatorProps { - value: number; - text: string; -} - -interface DbMutatorProps { - id: number; - database_name: string; -} - -const TableWrapper = styled.div` - display: flex; - flex-direction: column; - flex: 1; - height: 100%; -`; - -const TableStyles = styled.div` - table { - background-color: ${({ theme }) => theme.colors.grayscale.light4}; - } - - .table > thead > tr > th { - border-bottom: ${({ theme }) => theme.gridUnit / 2}px solid - ${({ theme }) => theme.colors.grayscale.light2}; - background: ${({ theme }) => theme.colors.grayscale.light4}; - } -`; - -const StyledTableStylesContainer = styled.div` - overflow: auto; -`; - -const QuerySearch = ({ displayLimit }: QuerySearchProps) => { - const dispatch = useDispatch(); - - const [databaseId, setDatabaseId] = useState(''); - const [userId, setUserId] = useState(''); - const [searchText, setSearchText] = useState(''); - const [from, setFrom] = useState('28 days ago'); - const [to, setTo] = useState('now'); - const [status, setStatus] = useState('success'); - const [queriesArray, setQueriesArray] = useState([]); - const [queriesLoading, setQueriesLoading] = useState(true); - - const getTimeFromSelection = (selection: string) => { - switch (selection) { - case 'now': - return now(); - case '1 hour ago': - return epochTimeXHoursAgo(1); - case '1 day ago': - return epochTimeXDaysAgo(1); - case '7 days ago': - return epochTimeXDaysAgo(7); - case '28 days ago': - return epochTimeXDaysAgo(28); - case '90 days ago': - return epochTimeXDaysAgo(90); - case '1 year ago': - return epochTimeXYearsAgo(1); - default: - return null; - } - }; - - const insertParams = (baseUrl: string, params: string[]) => { - const validParams = params.filter(function (p) { - return p !== ''; - }); - return `${baseUrl}?${validParams.join('&')}`; - }; - - const refreshQueries = async () => { - setQueriesLoading(true); - const params = [ - userId && `user_id=${userId}`, - databaseId && `database_id=${databaseId}`, - searchText && `search_text=${searchText}`, - status && `status=${status}`, - from && `from=${getTimeFromSelection(from)}`, - to && `to=${getTimeFromSelection(to)}`, - ]; - - try { - const response = await SupersetClient.get({ - endpoint: insertParams('/superset/search_queries', params), - }); - const queries = Object.values(response.json); - setQueriesArray(queries); - } catch (err) { - dispatch(addDangerToast(t('An error occurred when refreshing queries'))); - } finally { - setQueriesLoading(false); - } - }; - useEffect(() => { - refreshQueries(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onUserClicked = (userId: string) => { - setUserId(userId); - refreshQueries(); - }; - - const onDbClicked = (dbId: string) => { - setDatabaseId(dbId); - refreshQueries(); - }; - - const onKeyDown = (event: React.KeyboardEvent) => { - if (event.keyCode === 13) { - refreshQueries(); - } - }; - - const onChange = (e: React.ChangeEvent) => { - e.persist(); - const handleChange = debounce(e => { - setSearchText(e.target.value); - }, 200); - handleChange(e); - }; - - const userMutator = ({ result }: { result: UserMutatorProps[] }) => - result.map(({ value, text }: UserMutatorProps) => ({ - label: text, - value, - })); - - const dbMutator = ({ result }: { result: DbMutatorProps[] }) => { - const options = result.map(({ id, database_name }: DbMutatorProps) => ({ - value: id, - label: database_name, - })); - dispatch(setDatabases(result)); - if (result.length === 0) { - dispatch( - addDangerToast(t("It seems you don't have access to any database")), - ); - } - return options; - }; - - return ( - -
-
- setUserId(selected?.value)} - placeholder={t('Filter by user')} - /> -
-
- setDatabaseId(db?.value)} - dataEndpoint="/api/v1/database/?q=(filters:!((col:expose_in_sqllab,opr:eq,value:!t)))" - value={databaseId} - mutator={dbMutator} - placeholder={t('Filter by database')} - /> -
-
- -
-
- ({ value: xt, label: xt }))} - value={{ value: to, label: to }} - autosize={false} - onChange={(selected: any) => setTo(selected?.value)} - /> - -