From 9f83ad2360f27dfa09278d1101d1a61ad7eeab62 Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:36:40 -0300 Subject: [PATCH] refactor: Removes the Profile feature (#26462) --- RESOURCES/STANDARD_ROLES.md | 1 - UPDATING.md | 1 + superset-frontend/package-lock.json | 116 ------------- superset-frontend/package.json | 1 - .../TableLoader/TableLoader.test.tsx | 122 ------------- .../src/components/TableLoader/index.tsx | 98 ----------- superset-frontend/src/constants.ts | 1 - .../src/features/home/Menu.test.tsx | 22 +-- .../src/features/home/RightMenu.test.tsx | 1 - .../src/features/home/RightMenu.tsx | 5 - .../features/profile/CreatedContent.test.tsx | 50 ------ .../src/features/profile/CreatedContent.tsx | 111 ------------ .../src/features/profile/Favorites.test.tsx | 50 ------ .../src/features/profile/Favorites.tsx | 107 ------------ .../features/profile/RecentActivity.test.tsx | 40 ----- .../src/features/profile/RecentActivity.tsx | 55 ------ .../src/features/profile/Security.test.tsx | 49 ------ .../src/features/profile/Security.tsx | 75 -------- .../src/features/profile/UserInfo.test.tsx | 53 ------ .../src/features/profile/UserInfo.tsx | 82 --------- .../src/features/profile/fixtures.tsx | 48 ----- .../src/features/profile/types.ts | 46 ----- .../src/pages/Profile/Profile.test.tsx | 43 ----- superset-frontend/src/pages/Profile/index.tsx | 93 ---------- superset-frontend/src/types/bootstrapTypes.ts | 1 - superset-frontend/src/views/routes.tsx | 8 - superset/config.py | 4 +- superset/initialization/__init__.py | 2 - superset/models/helpers.py | 9 +- superset/templates/appbuilder/navbar.html | 2 +- superset/views/base.py | 3 - superset/views/core.py | 7 - superset/views/profile.py | 40 ----- tests/integration_tests/core_tests.py | 6 - tests/integration_tests/profile_tests.py | 164 ------------------ 35 files changed, 8 insertions(+), 1508 deletions(-) delete mode 100644 superset-frontend/src/components/TableLoader/TableLoader.test.tsx delete mode 100644 superset-frontend/src/components/TableLoader/index.tsx delete mode 100644 superset-frontend/src/features/profile/CreatedContent.test.tsx delete mode 100644 superset-frontend/src/features/profile/CreatedContent.tsx delete mode 100644 superset-frontend/src/features/profile/Favorites.test.tsx delete mode 100644 superset-frontend/src/features/profile/Favorites.tsx delete mode 100644 superset-frontend/src/features/profile/RecentActivity.test.tsx delete mode 100644 superset-frontend/src/features/profile/RecentActivity.tsx delete mode 100644 superset-frontend/src/features/profile/Security.test.tsx delete mode 100644 superset-frontend/src/features/profile/Security.tsx delete mode 100644 superset-frontend/src/features/profile/UserInfo.test.tsx delete mode 100644 superset-frontend/src/features/profile/UserInfo.tsx delete mode 100644 superset-frontend/src/features/profile/fixtures.tsx delete mode 100644 superset-frontend/src/features/profile/types.ts delete mode 100644 superset-frontend/src/pages/Profile/Profile.test.tsx delete mode 100644 superset-frontend/src/pages/Profile/index.tsx delete mode 100644 superset/views/profile.py delete mode 100644 tests/integration_tests/profile_tests.py diff --git a/RESOURCES/STANDARD_ROLES.md b/RESOURCES/STANDARD_ROLES.md index 7516ed2868a99..51fcc6479b6c4 100644 --- a/RESOURCES/STANDARD_ROLES.md +++ b/RESOURCES/STANDARD_ROLES.md @@ -77,7 +77,6 @@ | can select star on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | | can warm up cache on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | | can sqllab table viz on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| can profile on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | | can available domains on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | | can request access on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | | can dashboard on Superset | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | O | diff --git a/UPDATING.md b/UPDATING.md index 5a4f1bc1210c0..84893e9080d2e 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -39,6 +39,7 @@ assists people when migrating to a new version. - [26331](https://github.com/apache/superset/issues/26331): Removes the deprecated `DISABLE_DATASET_SOURCE_EDIT` feature flag. The previous value of the feature flag was `False` and now the feature is permanently removed. - [26636](https://github.com/apache/superset/issues/26636): Sets the `DASHBOARD_VIRTUALIZATION` feature flag to `True` by default. This feature was introduced by [21438](https://github.com/apache/superset/pull/21438) and will enable virtualization when rendering a dashboard's charts in an attempt to reduce the number of elements (DOM nodes) rendered at once. This is especially useful for large dashboards. - [26637](https://github.com/apache/superset/issues/26637): Sets the `DRILL_BY` feature flag to `True` by default given that the feature has been tested for a while and reached a stable state. +- [26462](https://github.com/apache/superset/issues/26462): Removes the Profile feature given that it's not actively maintained and not widely used. ### Potential Downtime diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 27145dacce467..6e9867ecb0a05 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -112,7 +112,6 @@ "react-dnd-html5-backend": "^11.1.3", "react-dom": "^16.13.0", "react-draggable": "^4.4.6", - "react-gravatar": "^2.6.1", "react-hot-loader": "^4.13.1", "react-intersection-observer": "^9.4.1", "react-js-cron": "^1.2.0", @@ -25235,14 +25234,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "devOptional": true }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "engines": { - "node": "*" - } - }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -27066,14 +27057,6 @@ "node": ">=4.8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "engines": { - "node": "*" - } - }, "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -36753,11 +36736,6 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, - "node_modules/is-retina": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-retina/-/is-retina-1.0.3.tgz", - "integrity": "sha1-10AbKGvqKuN/Ykd1iN5QTQuGR+M=" - }, "node_modules/is-scoped": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz", @@ -44884,16 +44862,6 @@ "integrity": "sha512-QBcepxkFxuGk12q4G0KuNbuU3UCXhDROxWZllaNZSpBivkHl2z8qNvi7UGE/WLJt+c7GTC4jigYtur+JDL+40A==", "deprecated": "Use cephes instead, for a more complete and well-tested module" }, - "node_modules/md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "dependencies": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -51757,39 +51725,6 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "node_modules/react-gravatar": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-gravatar/-/react-gravatar-2.6.3.tgz", - "integrity": "sha1-VAfrash+gw4qNN63YNKkxATrHaw=", - "dependencies": { - "is-retina": "^1.0.3", - "md5": "^2.1.0", - "query-string": "^4.2.2" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-gravatar/node_modules/query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dependencies": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-gravatar/node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-helmet-async": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", @@ -84150,11 +84085,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "devOptional": true }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -85609,11 +85539,6 @@ "which": "^1.2.9" } }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -92968,11 +92893,6 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, - "is-retina": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-retina/-/is-retina-1.0.3.tgz", - "integrity": "sha1-10AbKGvqKuN/Ykd1iN5QTQuGR+M=" - }, "is-scoped": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz", @@ -99270,16 +99190,6 @@ "resolved": "https://registry.npmjs.org/mathfn/-/mathfn-1.2.0.tgz", "integrity": "sha512-QBcepxkFxuGk12q4G0KuNbuU3UCXhDROxWZllaNZSpBivkHl2z8qNvi7UGE/WLJt+c7GTC4jigYtur+JDL+40A==" }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -104452,32 +104362,6 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "react-gravatar": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-gravatar/-/react-gravatar-2.6.3.tgz", - "integrity": "sha1-VAfrash+gw4qNN63YNKkxATrHaw=", - "requires": { - "is-retina": "^1.0.3", - "md5": "^2.1.0", - "query-string": "^4.2.2" - }, - "dependencies": { - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - } - } - }, "react-helmet-async": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 4cbff6e5e982a..cc243754fc598 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -178,7 +178,6 @@ "react-dnd-html5-backend": "^11.1.3", "react-dom": "^16.13.0", "react-draggable": "^4.4.6", - "react-gravatar": "^2.6.1", "react-hot-loader": "^4.13.1", "react-intersection-observer": "^9.4.1", "react-js-cron": "^1.2.0", diff --git a/superset-frontend/src/components/TableLoader/TableLoader.test.tsx b/superset-frontend/src/components/TableLoader/TableLoader.test.tsx deleted file mode 100644 index b91a6c490aaad..0000000000000 --- a/superset-frontend/src/components/TableLoader/TableLoader.test.tsx +++ /dev/null @@ -1,122 +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 { render, screen } from 'spec/helpers/testing-library'; -import { Provider } from 'react-redux'; -import fetchMock from 'fetch-mock'; -import { storeWithState } from 'spec/fixtures/mockStore'; -import ToastContainer from 'src/components/MessageToasts/ToastContainer'; -import TableLoader, { TableLoaderProps } from '.'; - -const NO_DATA_TEXT = 'No data available'; -const MOCK_GLOB = 'glob:*/api/v1/mock'; - -fetchMock.get(MOCK_GLOB, [ - { id: 1, name: 'John Doe' }, - { id: 2, name: 'Jane Doe' }, -]); - -const defaultProps: TableLoaderProps = { - dataEndpoint: '/api/v1/mock', - addDangerToast: jest.fn(), - noDataText: NO_DATA_TEXT, -}; - -function renderWithProps(props: TableLoaderProps = defaultProps) { - return render( - - - - , - ); -} - -test('renders loading and table', async () => { - renderWithProps(); - - expect(screen.getByRole('status')).toBeInTheDocument(); - expect(await screen.findByRole('table')).toBeInTheDocument(); -}); - -test('renders with column names', async () => { - renderWithProps({ - ...defaultProps, - columns: ['id_modified', 'name_modified'], - }); - - const columnHeaders = await screen.findAllByRole('columnheader'); - - expect(columnHeaders[0]).toHaveTextContent('id_modified'); - expect(columnHeaders[1]).toHaveTextContent('name_modified'); -}); - -test('renders without mutator', async () => { - renderWithProps(); - - expect(await screen.findAllByRole('row')).toHaveLength(3); - expect(await screen.findAllByRole('columnheader')).toHaveLength(2); - expect(await screen.findAllByRole('cell')).toHaveLength(4); -}); - -test('renders with mutator', async () => { - const mutator = function (data: { id: number; name: string }[]) { - return data.map(row => ({ - id: row.id, - name:

{row.name}

, - })); - }; - - renderWithProps({ ...defaultProps, mutator }); - - expect(await screen.findAllByRole('heading', { level: 4 })).toHaveLength(2); -}); - -test('renders empty message', async () => { - fetchMock.mock(MOCK_GLOB, [], { - overwriteRoutes: true, - }); - - renderWithProps(); - - expect(await screen.findByText('No data available')).toBeInTheDocument(); -}); - -test('renders blocked message', async () => { - fetchMock.mock(MOCK_GLOB, 403, { - overwriteRoutes: true, - }); - - renderWithProps(); - - expect( - await screen.findByText('Access to user activity data is restricted'), - ).toBeInTheDocument(); - expect(screen.queryByRole('alert')).not.toBeInTheDocument(); -}); - -test('renders error message', async () => { - fetchMock.mock(MOCK_GLOB, 500, { - overwriteRoutes: true, - }); - - renderWithProps(); - - expect(await screen.findByText(NO_DATA_TEXT)).toBeInTheDocument(); - expect(await screen.findByRole('alert')).toBeInTheDocument(); -}); diff --git a/superset-frontend/src/components/TableLoader/index.tsx b/superset-frontend/src/components/TableLoader/index.tsx deleted file mode 100644 index 9d89991f6b7a0..0000000000000 --- a/superset-frontend/src/components/TableLoader/index.tsx +++ /dev/null @@ -1,98 +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, useMemo } from 'react'; -import { t, SupersetClient, JsonObject } from '@superset-ui/core'; -import TableView, { EmptyWrapperType } from 'src/components/TableView'; -import withToasts from 'src/components/MessageToasts/withToasts'; -import Loading from 'src/components/Loading'; -import '../../assets/stylesheets/reactable-pagination.less'; - -export interface TableLoaderProps { - dataEndpoint?: string; - mutator?: (data: JsonObject) => any[]; - columns?: string[]; - noDataText?: string; - addDangerToast(text: string): any; -} - -const TableLoader = (props: TableLoaderProps) => { - const [data, setData] = useState>([]); - const [isLoading, setIsLoading] = useState(true); - const [isBlocked, setIsBlocked] = useState(false); - - useEffect(() => { - const { dataEndpoint, mutator } = props; - if (dataEndpoint) { - SupersetClient.get({ endpoint: dataEndpoint }) - .then(({ json }) => { - const data = (mutator ? mutator(json) : json) as Array; - setData(data); - setIsBlocked(false); - setIsLoading(false); - }) - .catch(response => { - setIsLoading(false); - if (response.status === 403) { - setIsBlocked(true); - } else { - setIsBlocked(false); - props.addDangerToast(t('An error occurred')); - } - }); - } - }, [props]); - - const { columns, noDataText, ...tableProps } = props; - - const memoizedColumns = useMemo(() => { - let tableColumns = columns; - if (!columns && data.length > 0) { - tableColumns = Object.keys(data[0]).filter(col => col[0] !== '_'); - } - return tableColumns - ? tableColumns.map((column: string) => ({ - accessor: column, - Header: column, - })) - : []; - }, [columns, data]); - - delete tableProps.dataEndpoint; - delete tableProps.mutator; - - if (isLoading) { - return ; - } - - return ( - - ); -}; - -export default withToasts(TableLoader); diff --git a/superset-frontend/src/constants.ts b/superset-frontend/src/constants.ts index a5d10b93e3396..b64cf913755b6 100644 --- a/superset-frontend/src/constants.ts +++ b/superset-frontend/src/constants.ts @@ -175,7 +175,6 @@ export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = { user_info_url: '', user_login_url: '', user_logout_url: '', - user_profile_url: '', locale: '', }, settings: [], diff --git a/superset-frontend/src/features/home/Menu.test.tsx b/superset-frontend/src/features/home/Menu.test.tsx index bcb10dfbbe33f..28ef85aad5b90 100644 --- a/superset-frontend/src/features/home/Menu.test.tsx +++ b/superset-frontend/src/features/home/Menu.test.tsx @@ -171,7 +171,7 @@ const mockedProps = { }, ], brand: { - path: '/superset/profile/admin/', + path: '/superset/welcome/', icon: '/static/assets/images/superset-logo-horiz.png', alt: 'Superset', width: '126', @@ -203,7 +203,6 @@ const mockedProps = { user_info_url: '/users/userinfo/', user_logout_url: '/logout/', user_login_url: '/login/', - user_profile_url: '/profile/', locale: 'en', version_string: '1.0.0', version_sha: 'randomSHA', @@ -464,25 +463,6 @@ test('should NOT render the user actions when user is anonymous', async () => { expect(screen.queryByText('User')).not.toBeInTheDocument(); }); -test('should render the Profile link when available', async () => { - useSelectorMock.mockReturnValue({ roles: user.roles }); - const { - data: { - navbar_right: { user_profile_url }, - }, - } = mockedProps; - - render(, { - useRedux: true, - useQueryParams: true, - useRouter: true, - }); - - userEvent.hover(screen.getByText('Settings')); - const profile = await screen.findByText('Profile'); - expect(profile).toHaveAttribute('href', user_profile_url); -}); - test('should render the About section and version_string, sha or build_number when available', async () => { useSelectorMock.mockReturnValue({ roles: user.roles }); const { diff --git a/superset-frontend/src/features/home/RightMenu.test.tsx b/superset-frontend/src/features/home/RightMenu.test.tsx index 97b9fb20bd593..d823b378954cd 100644 --- a/superset-frontend/src/features/home/RightMenu.test.tsx +++ b/superset-frontend/src/features/home/RightMenu.test.tsx @@ -117,7 +117,6 @@ const createProps = (): RightMenuProps => ({ user_info_url: '/users/userinfo/', user_logout_url: '/logout/', user_login_url: '/login/', - user_profile_url: '/profile/', locale: 'en', version_string: '1.0.0', version_sha: 'randomSHA', diff --git a/superset-frontend/src/features/home/RightMenu.tsx b/superset-frontend/src/features/home/RightMenu.tsx index a37214b66bb00..20e1d525b3d46 100644 --- a/superset-frontend/src/features/home/RightMenu.tsx +++ b/superset-frontend/src/features/home/RightMenu.tsx @@ -473,11 +473,6 @@ const RightMenu = ({ {!navbarRight.user_is_anonymous && [ , - {navbarRight.user_profile_url && ( - - {t('Profile')} - - )} {navbarRight.user_info_url && ( {t('Info')} diff --git a/superset-frontend/src/features/profile/CreatedContent.test.tsx b/superset-frontend/src/features/profile/CreatedContent.test.tsx deleted file mode 100644 index df49e98aff90d..0000000000000 --- a/superset-frontend/src/features/profile/CreatedContent.test.tsx +++ /dev/null @@ -1,50 +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 { shallow } from 'enzyme'; -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import TableLoader from 'src/components/TableLoader'; -import CreatedContent from './CreatedContent'; - -import { user } from './fixtures'; - -// store needed for withToasts(TableLoader) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -describe('CreatedContent', () => { - const mockedProps = { - user, - }; - - it('renders 2 TableLoader', () => { - const wrapper = shallow(, { - context: { store }, - }); - expect(wrapper.find(TableLoader)).toHaveLength(2); - }); - - it('renders 2 titles', () => { - const wrapper = shallow(, { - context: { store }, - }); - expect(wrapper.find('h3')).toHaveLength(2); - }); -}); diff --git a/superset-frontend/src/features/profile/CreatedContent.tsx b/superset-frontend/src/features/profile/CreatedContent.tsx deleted file mode 100644 index a972ea5db90f5..0000000000000 --- a/superset-frontend/src/features/profile/CreatedContent.tsx +++ /dev/null @@ -1,111 +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 rison from 'rison'; -import React from 'react'; -import { t } from '@superset-ui/core'; - -import TableLoader from 'src/components/TableLoader'; -import { - BootstrapUser, - ChartResponse, - DashboardResponse, -} from 'src/types/bootstrapTypes'; - -interface CreatedContentProps { - user: BootstrapUser; -} - -class CreatedContent extends React.PureComponent { - renderSliceTable() { - const search = [ - { col: 'created_by', opr: 'chart_created_by_me', value: 'me' }, - ]; - const query = rison.encode({ - keys: ['none'], - columns: ['created_on_delta_humanized', 'slice_name', 'url'], - filters: search, - order_column: 'changed_on_delta_humanized', - order_direction: 'desc', - page: 0, - page_size: 100, - }); - - const mutator = (data: ChartResponse) => - data.result.map(chart => ({ - chart: {chart.slice_name}, - created: chart.created_on_delta_humanized, - _created: chart.created_on_delta_humanized, - })); - return ( - - ); - } - - renderDashboardTable() { - const search = [ - { col: 'created_by', opr: 'dashboard_created_by_me', value: 'me' }, - ]; - const query = rison.encode({ - keys: ['none'], - columns: ['created_on_delta_humanized', 'dashboard_title', 'url'], - filters: search, - order_column: 'changed_on', - order_direction: 'desc', - page: 0, - page_size: 100, - }); - const mutator = (data: DashboardResponse) => - data.result.map(dash => ({ - dashboard: {dash.dashboard_title}, - created: dash.created_on_delta_humanized, - _created: dash.created_on_delta_humanized, - })); - return ( - - ); - } - - render() { - return ( -
-

{t('Dashboards')}

- {this.renderDashboardTable()} -
-

{t('Charts')}

- {this.renderSliceTable()} -
- ); - } -} - -export default CreatedContent; diff --git a/superset-frontend/src/features/profile/Favorites.test.tsx b/superset-frontend/src/features/profile/Favorites.test.tsx deleted file mode 100644 index e21967f3bf170..0000000000000 --- a/superset-frontend/src/features/profile/Favorites.test.tsx +++ /dev/null @@ -1,50 +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 { shallow } from 'enzyme'; -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import TableLoader from 'src/components/TableLoader'; -import Favorites from './Favorites'; - -import { user } from './fixtures'; - -// store needed for withToasts(TableLoader) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -describe('Favorites', () => { - const mockedProps = { - user, - }; - - it('renders 2 TableLoader', () => { - const wrapper = shallow(, { - context: { store }, - }); - expect(wrapper.find(TableLoader)).toHaveLength(2); - }); - - it('renders 2 titles', () => { - const wrapper = shallow(, { - context: { store }, - }); - expect(wrapper.find('h3')).toHaveLength(2); - }); -}); diff --git a/superset-frontend/src/features/profile/Favorites.tsx b/superset-frontend/src/features/profile/Favorites.tsx deleted file mode 100644 index f38f174779689..0000000000000 --- a/superset-frontend/src/features/profile/Favorites.tsx +++ /dev/null @@ -1,107 +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 rison from 'rison'; -import moment from 'moment'; -import { t } from '@superset-ui/core'; -import { DashboardResponse, BootstrapUser } from 'src/types/bootstrapTypes'; -import TableLoader from '../../components/TableLoader'; -import { Chart } from './types'; - -interface FavoritesProps { - user: BootstrapUser; -} - -export default class Favorites extends React.PureComponent { - renderSliceTable() { - const mutator = (payload: { result: Chart[] }) => - payload.result.map(slice => ({ - slice: {slice.slice_name}, - creator: slice.created_by_name, - favorited: moment.utc(slice.changed_on_dttm).fromNow(), - _favorited: slice.changed_on_dttm, - })); - - const query = rison.encode({ - filters: [ - { - col: 'id', - opr: 'chart_is_favorite', - value: true, - }, - ], - order_column: 'slice_name', - order_direction: 'asc', - page: 0, - page_size: 25, - }); - - return ( - - ); - } - - renderDashboardTable() { - const search = [{ col: 'id', opr: 'dashboard_is_favorite', value: true }]; - const query = rison.encode({ - keys: ['none'], - columns: ['created_on_delta_humanized', 'dashboard_title', 'url'], - filters: search, - order_column: 'changed_on', - order_direction: 'desc', - page: 0, - page_size: 100, - }); - const mutator = (data: DashboardResponse) => - data.result.map(dash => ({ - dashboard: {dash.dashboard_title}, - created: dash.created_on_delta_humanized, - _created: dash.created_on_delta_humanized, - })); - return ( - - ); - } - - render() { - return ( -
-

{t('Dashboards')}

- {this.renderDashboardTable()} -
-

{t('Charts')}

- {this.renderSliceTable()} -
- ); - } -} diff --git a/superset-frontend/src/features/profile/RecentActivity.test.tsx b/superset-frontend/src/features/profile/RecentActivity.test.tsx deleted file mode 100644 index a64c209296743..0000000000000 --- a/superset-frontend/src/features/profile/RecentActivity.test.tsx +++ /dev/null @@ -1,40 +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 { shallow } from 'enzyme'; -import TableLoader from 'src/components/TableLoader'; -import RecentActivity from './RecentActivity'; - -import { user } from './fixtures'; - -describe('RecentActivity', () => { - const mockedProps = { - user, - }; - it('is valid', () => { - expect(React.isValidElement()).toBe( - true, - ); - }); - - it('renders a TableLoader', () => { - const wrapper = shallow(); - expect(wrapper.find(TableLoader)).toExist(); - }); -}); diff --git a/superset-frontend/src/features/profile/RecentActivity.tsx b/superset-frontend/src/features/profile/RecentActivity.tsx deleted file mode 100644 index d550d2a9531c4..0000000000000 --- a/superset-frontend/src/features/profile/RecentActivity.tsx +++ /dev/null @@ -1,55 +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 moment from 'moment'; -import { t } from '@superset-ui/core'; -import rison from 'rison'; -import TableLoader from 'src/components/TableLoader'; -import { BootstrapUser } from 'src/types/bootstrapTypes'; -import { ActivityResult } from './types'; - -interface RecentActivityProps { - user: BootstrapUser; -} - -export default function RecentActivity({ user }: RecentActivityProps) { - const rowLimit = 50; - const mutator = function (data: ActivityResult) { - return data.result - .filter(row => row.action === 'dashboard' || row.action === 'explore') - .map(row => ({ - name: {row.item_title}, - type: row.action, - time: moment.utc(row.time).fromNow(), - _time: row.time, - })); - }; - const params = rison.encode({ page_size: rowLimit }); - return ( -
- -
- ); -} diff --git a/superset-frontend/src/features/profile/Security.test.tsx b/superset-frontend/src/features/profile/Security.test.tsx deleted file mode 100644 index af4706375c848..0000000000000 --- a/superset-frontend/src/features/profile/Security.test.tsx +++ /dev/null @@ -1,49 +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 { styledMount as mount } from 'spec/helpers/theming'; -import Label from 'src/components/Label'; -import { user, userNoPerms } from './fixtures'; -import Security from './Security'; - -describe('Security', () => { - const mockedProps = { - user, - }; - it('is valid', () => { - expect(React.isValidElement()).toBe(true); - }); - it('renders 2 role labels', () => { - const wrapper = mount(); - expect(wrapper.find('.roles').find(Label)).toHaveLength(2); - }); - it('renders 2 datasource labels', () => { - const wrapper = mount(); - expect(wrapper.find('.datasources').find(Label)).toHaveLength(2); - }); - it('renders 3 database labels', () => { - const wrapper = mount(); - expect(wrapper.find('.databases').find(Label)).toHaveLength(3); - }); - it('renders no permission label when empty', () => { - const wrapper = mount(); - expect(wrapper.find('.datasources').find(Label)).not.toExist(); - expect(wrapper.find('.databases').find(Label)).not.toExist(); - }); -}); diff --git a/superset-frontend/src/features/profile/Security.tsx b/superset-frontend/src/features/profile/Security.tsx deleted file mode 100644 index a102e55992878..0000000000000 --- a/superset-frontend/src/features/profile/Security.tsx +++ /dev/null @@ -1,75 +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 Badge from 'src/components/Badge'; -import { t } from '@superset-ui/core'; - -import Label from 'src/components/Label'; -import { BootstrapUser } from 'src/types/bootstrapTypes'; - -interface SecurityProps { - user: BootstrapUser; -} - -export default function Security({ user }: SecurityProps) { - return ( -
-
-

- {t('Roles')}{' '} - -

- {Object.keys(user?.roles || {}).map(role => ( - - ))} -
-
-
- {user?.permissions.database_access && ( -
-

- {t('Databases')}{' '} - -

- {user.permissions.database_access.map(role => ( - - ))} -
-
- )} -
-
- {user?.permissions.datasource_access && ( -
-

- {t('Datasets')}{' '} - -

- {user.permissions.datasource_access.map(role => ( - - ))} -
- )} -
-
- ); -} diff --git a/superset-frontend/src/features/profile/UserInfo.test.tsx b/superset-frontend/src/features/profile/UserInfo.test.tsx deleted file mode 100644 index 6bbc2b52d1e8e..0000000000000 --- a/superset-frontend/src/features/profile/UserInfo.test.tsx +++ /dev/null @@ -1,53 +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 Gravatar from 'react-gravatar'; -import { mount } from 'enzyme'; -import UserInfo from './UserInfo'; - -import { user } from './fixtures'; - -describe('UserInfo', () => { - const mockedProps = { - user, - }; - it('is valid', () => { - expect(React.isValidElement()).toBe(true); - }); - it('renders a Gravatar', () => { - const wrapper = mount(); - expect(wrapper.find(Gravatar)).toExist(); - }); - it('renders a Panel', () => { - const wrapper = mount(); - expect(wrapper.find('.panel')).toExist(); - }); - it('renders 5 icons', () => { - const wrapper = mount(); - expect(wrapper.find('i')).toHaveLength(5); - }); - it('renders roles information', () => { - const wrapper = mount(); - expect(wrapper.find('.roles').text()).toBe(' Alpha, sql_lab'); - }); - it('shows the right user-id', () => { - const wrapper = mount(); - expect(wrapper.find('.user-id').text()).toBe('5'); - }); -}); diff --git a/superset-frontend/src/features/profile/UserInfo.tsx b/superset-frontend/src/features/profile/UserInfo.tsx deleted file mode 100644 index b79f1fb97cb68..0000000000000 --- a/superset-frontend/src/features/profile/UserInfo.tsx +++ /dev/null @@ -1,82 +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 Gravatar from 'react-gravatar'; -import moment from 'moment'; -import { t, styled } from '@superset-ui/core'; -import { BootstrapUser } from 'src/types/bootstrapTypes'; - -interface UserInfoProps { - user: BootstrapUser; -} - -const StyledContainer = styled.div` - .panel { - padding: ${({ theme }) => theme.gridUnit * 6}px; - } -`; - -export default function UserInfo({ user }: UserInfoProps) { - return ( - - - - -
-
-
-

- - {user?.firstName} {user?.lastName} - -

-

- {user?.username} -

-
-
-

- {' '} - {t('joined')} {moment(user?.createdOn, 'YYYYMMDD').fromNow()} -

-

- {user?.email} -

-

- {' '} - {Object.keys(user?.roles || {}).join(', ')} -

-

- -   - {t('id')}:  - {user?.userId} -

-
-
- ); -} diff --git a/superset-frontend/src/features/profile/fixtures.tsx b/superset-frontend/src/features/profile/fixtures.tsx deleted file mode 100644 index d4a3612df4f41..0000000000000 --- a/superset-frontend/src/features/profile/fixtures.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; - -/** - * 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 const user: UserWithPermissionsAndRoles = { - username: 'alpha', - roles: { - Alpha: [ - ['can_this_form_post', 'ResetMyPasswordView'], - ['can_this_form_get', 'ResetMyPasswordView'], - ['can_this_form_post', 'UserInfoEditView'], - ['can_this_form_get', 'UserInfoEditView'], - ], - sql_lab: [ - ['menu_access', 'SQL Lab'], - ['can_search_queries', 'Superset'], - ['can_csv', 'Superset'], - ], - }, - firstName: 'alpha', - lastName: 'alpha', - createdOn: '2016-11-11T12:34:17', - userId: 5, - email: 'alpha@alpha.com', - isActive: true, - isAnonymous: false, - permissions: { - datasource_access: ['table1', 'table2'], - database_access: ['db1', 'db2', 'db3'], - }, -}; -export const userNoPerms = { ...user, permissions: {} }; diff --git a/superset-frontend/src/features/profile/types.ts b/superset-frontend/src/features/profile/types.ts deleted file mode 100644 index 370434dfe817d..0000000000000 --- a/superset-frontend/src/features/profile/types.ts +++ /dev/null @@ -1,46 +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 type Slice = { - dttm: number; - id: number; - url: string; - title: string; - creator?: string; - creator_url?: string; - viz_type: string; -}; - -export type Chart = { - id: number; - slice_name: string; - slice_url: string; - created_by_name?: string; - changed_on_dttm: number; -}; - -export type Activity = { - action: string; - item_title: string; - item_url: string; - time: number; -}; - -export type ActivityResult = { - result: Activity[]; -}; diff --git a/superset-frontend/src/pages/Profile/Profile.test.tsx b/superset-frontend/src/pages/Profile/Profile.test.tsx deleted file mode 100644 index cf1446c29ac62..0000000000000 --- a/superset-frontend/src/pages/Profile/Profile.test.tsx +++ /dev/null @@ -1,43 +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 { Row, Col } from 'src/components'; -import { shallow } from 'enzyme'; -import Profile from 'src/pages/Profile'; -import { user } from 'src/features/profile/fixtures'; - -describe('Profile', () => { - const mockedProps = { - user, - }; - it('is valid', () => { - expect(React.isValidElement()).toBe(true); - }); - - it('renders 2 Col', () => { - const wrapper = shallow(); - expect(wrapper.find(Row)).toExist(); - expect(wrapper.find(Col)).toHaveLength(2); - }); - - it('renders 4 Tabs', () => { - const wrapper = shallow(); - expect(wrapper.find('[tab]')).toHaveLength(4); - }); -}); diff --git a/superset-frontend/src/pages/Profile/index.tsx b/superset-frontend/src/pages/Profile/index.tsx deleted file mode 100644 index 511017941a85b..0000000000000 --- a/superset-frontend/src/pages/Profile/index.tsx +++ /dev/null @@ -1,93 +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 { t, styled } from '@superset-ui/core'; -import { Row, Col } from 'src/components'; -import Tabs from 'src/components/Tabs'; -import { BootstrapUser } from 'src/types/bootstrapTypes'; -import Favorites from 'src/features/profile/Favorites'; -import UserInfo from 'src/features/profile/UserInfo'; -import Security from 'src/features/profile/Security'; -import RecentActivity from 'src/features/profile/RecentActivity'; -import CreatedContent from 'src/features/profile/CreatedContent'; - -interface AppProps { - user: BootstrapUser; -} - -const StyledTabPane = styled(Tabs.TabPane)` - background-color: ${({ theme }) => theme.colors.grayscale.light5}; - padding: ${({ theme }) => theme.gridUnit * 4}px; -`; - -export default function App({ user }: AppProps) { - return ( -
- - - - - - - - {t('Favorites')} -
- } - > - - - - {t('Created content')} - - } - > - - - - {t('Recent activity')} - - } - > - - - - {t('Security & Access')} - - } - > - - - - - - - ); -} diff --git a/superset-frontend/src/types/bootstrapTypes.ts b/superset-frontend/src/types/bootstrapTypes.ts index 2f54c4b426902..80570f5d22541 100644 --- a/superset-frontend/src/types/bootstrapTypes.ts +++ b/superset-frontend/src/types/bootstrapTypes.ts @@ -109,7 +109,6 @@ export interface NavBarProps { user_info_url: string; user_login_url: string; user_logout_url: string; - user_profile_url: string | null; locale: string; } diff --git a/superset-frontend/src/views/routes.tsx b/superset-frontend/src/views/routes.tsx index 2e5b987e2e230..63d502fed08da 100644 --- a/superset-frontend/src/views/routes.tsx +++ b/superset-frontend/src/views/routes.tsx @@ -123,10 +123,6 @@ const RowLevelSecurityList = lazy( ), ); -const Profile = lazy( - () => import(/* webpackChunkName: "Profile" */ 'src/pages/Profile'), -); - type Routes = { path: string; Component: React.ComponentType; @@ -225,10 +221,6 @@ export const routes: Routes = [ path: '/rowlevelsecurity/list', Component: RowLevelSecurityList, }, - { - path: '/profile', - Component: Profile, - }, { path: '/sqllab/', Component: SqlLab, diff --git a/superset/config.py b/superset/config.py index 7f1d98a3207ac..97278c805d770 100644 --- a/superset/config.py +++ b/superset/config.py @@ -479,9 +479,7 @@ class D3Format(TypedDict, total=False): # otherwise enabling this flag won't have any effect on the DB. "SSH_TUNNELING": False, "AVOID_COLORS_COLLISION": True, - # Set to False to only allow viewing own recent activity - # or to disallow users from viewing other users profile page - # Do not show user info or profile in the menu + # Do not show user info in the menu "MENU_HIDE_USER_INFO": False, # Allows users to add a ``superset://`` DB that can query across databases. This is # an experimental feature with potential security and performance risks, so use with diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index c18cf8a059f74..9da84c1fa323f 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -183,7 +183,6 @@ def init_views(self) -> None: from superset.views.key_value import KV from superset.views.log.api import LogRestApi from superset.views.log.views import LogModelView - from superset.views.profile import ProfileView from superset.views.redirects import R from superset.views.sql_lab.views import ( SavedQueryView, @@ -311,7 +310,6 @@ def init_views(self) -> None: appbuilder.add_view_no_menu(ExplorePermalinkView) appbuilder.add_view_no_menu(KV) 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) diff --git a/superset/models/helpers.py b/superset/models/helpers.py index 2c56c41b904df..a6d879b785e48 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -461,11 +461,10 @@ def template_params_dict(self) -> dict[Any, Any]: return json_to_dict(self.template_params) # type: ignore -def _user_link(user: User) -> Union[Markup, str]: +def _user(user: User) -> str: if not user: return "" - url = f"/superset/profile/{user.username}/" - return Markup(f"{escape(user) or ''}") + return escape(user) class AuditMixinNullable(AuditMixin): @@ -512,11 +511,11 @@ def changed_by_name(self) -> str: @renders("created_by") def creator(self) -> Union[Markup, str]: - return _user_link(self.created_by) + return _user(self.created_by) @property def changed_by_(self) -> Union[Markup, str]: - return _user_link(self.changed_by) + return _user(self.changed_by) @renders("changed_on") def changed_on_(self) -> Markup: diff --git a/superset/templates/appbuilder/navbar.html b/superset/templates/appbuilder/navbar.html index 4ad74ff3c24b0..3db7f5de6583e 100644 --- a/superset/templates/appbuilder/navbar.html +++ b/superset/templates/appbuilder/navbar.html @@ -18,7 +18,7 @@ #} {% set menu = appbuilder.menu %} {% set app_icon_width = appbuilder.app.config['APP_ICON_WIDTH'] %} -{% set logo_target_path = appbuilder.app.config['LOGO_TARGET_PATH'] or '/profile/{}/'.format(current_user.username) %} +{% set logo_target_path = appbuilder.app.config['LOGO_TARGET_PATH'] or '/' %} {% set root_path = logo_target_path if not logo_target_path.startswith('/') else '/superset' + logo_target_path if current_user.username is defined else '#' %} {% block navbar %} diff --git a/superset/views/base.py b/superset/views/base.py index db9ee645afc14..c8b48627103d7 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -370,9 +370,6 @@ def menu_data(user: User) -> dict[str, Any]: else appbuilder.get_url_for_userinfo, "user_logout_url": appbuilder.get_url_for_logout, "user_login_url": appbuilder.get_url_for_login, - "user_profile_url": None - if user.is_anonymous or is_feature_enabled("MENU_HIDE_USER_INFO") - else "/profile/", "locale": session.get("locale", "en"), }, } diff --git a/superset/views/core.py b/superset/views/core.py index febebed34bf9f..330d517a37d4c 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -968,13 +968,6 @@ def welcome(self) -> FlaskResponse: ), ) - @has_access - @event_logger.log_this - @expose("/profile/") - @deprecated(new_target="/profile") - def profile(self) -> FlaskResponse: - return redirect("/profile/") - @has_access @event_logger.log_this @expose( diff --git a/superset/views/profile.py b/superset/views/profile.py deleted file mode 100644 index 3308a0f645edc..0000000000000 --- a/superset/views/profile.py +++ /dev/null @@ -1,40 +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. -from flask import abort, g -from flask_appbuilder import permission_name -from flask_appbuilder.api import expose -from flask_appbuilder.security.decorators import has_access - -from superset import event_logger, security_manager -from superset.superset_typing import FlaskResponse - -from .base import BaseSupersetView - - -class ProfileView(BaseSupersetView): - route_base = "/profile" - class_permission_name = "Profile" - - @expose("/") - @has_access - @permission_name("read") - @event_logger.log_this - def root(self) -> FlaskResponse: - user = g.user if hasattr(g, "user") and g.user else None - if not user or security_manager.is_guest_user(user) or user.is_anonymous: - abort(404) - return super().render_app_template() diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py index 6d1a62c7f2fc8..526a5592e9637 100644 --- a/tests/integration_tests/core_tests.py +++ b/tests/integration_tests/core_tests.py @@ -971,7 +971,6 @@ def test_feature_flag_serialization(self): urls = [ "/superset/welcome", f"/superset/dashboard/{dash_id}/", - "/superset/profile/", f"/explore/?datasource_type=table&datasource_id={tbl_id}", ] for url in urls: @@ -1203,11 +1202,6 @@ def test_has_table_by_name(self): is True ) - def test_redirect_new_profile(self): - self.login(username="admin") - resp = self.client.get("/superset/profile/") - assert resp.status_code == 302 - def test_redirect_new_sqllab(self): self.login(username="admin") resp = self.client.get( diff --git a/tests/integration_tests/profile_tests.py b/tests/integration_tests/profile_tests.py deleted file mode 100644 index 5a0efc7509a5b..0000000000000 --- a/tests/integration_tests/profile_tests.py +++ /dev/null @@ -1,164 +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 prison -import pytest - -from superset import db -from superset.connectors.sqla.models import SqlaTable -from superset.models.dashboard import Dashboard -from superset.models.slice import Slice -from tests.integration_tests.fixtures.birth_names_dashboard import ( - load_birth_names_dashboard_with_slices, - load_birth_names_data, -) -from tests.integration_tests.fixtures.public_role import public_role_like_gamma -from tests.integration_tests.insert_chart_mixin import InsertChartMixin - -from .base_tests import SupersetTestCase - - -class TestProfile(InsertChartMixin, SupersetTestCase): - def insert_dashboard_created_by(self, username: str) -> Dashboard: - user = self.get_user(username) - dashboard = self.insert_dashboard( - f"create_title_test", - f"create_slug_test", - [user.id], - created_by=user, - ) - return dashboard - - @pytest.fixture() - def insert_dashboard_created_by_admin(self): - with self.create_app().app_context(): - dashboard = self.insert_dashboard_created_by("admin") - yield dashboard - db.session.delete(dashboard) - db.session.commit() - - def insert_chart_created_by(self, username: str) -> Slice: - user = self.get_user(username) - dataset = db.session.query(SqlaTable).first() - chart = self.insert_chart( - f"create_title_test", - [user.id], - dataset.id, - created_by=user, - ) - return chart - - @pytest.fixture() - def insert_chart_created_by_admin(self): - with self.create_app().app_context(): - chart = self.insert_chart_created_by("admin") - yield chart - db.session.delete(chart) - db.session.commit() - - @pytest.mark.usefixtures("insert_dashboard_created_by_admin") - @pytest.mark.usefixtures("insert_chart_created_by_admin") - @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices") - def test_user_profile(self, username="admin"): - self.login(username=username) - slc = self.get_slice("Girls", db.session) - dashboard = db.session.query(Dashboard).filter_by(slug="births").first() - # Set a favorite dashboard - self.client.post(f"/api/v1/dashboard/{dashboard.id}/favorites/", json={}) - # Set a favorite chart - self.client.post(f"/api/v1/chart/{slc.id}/favorites/", json={}) - - # Get favorite dashboards: - request_query = { - "columns": ["created_on_delta_humanized", "dashboard_title", "url"], - "filters": [{"col": "id", "opr": "dashboard_is_favorite", "value": True}], - "keys": ["none"], - "order_column": "changed_on", - "order_direction": "desc", - "page": 0, - "page_size": 100, - } - url = f"/api/v1/dashboard/?q={prison.dumps(request_query)}" - resp = self.client.get(url) - assert resp.json["count"] == 1 - assert resp.json["result"][0]["dashboard_title"] == "USA Births Names" - - # Get Favorite Charts - request_query = { - "filters": [{"col": "id", "opr": "chart_is_favorite", "value": True}], - "order_column": "slice_name", - "order_direction": "asc", - "page": 0, - "page_size": 25, - } - url = f"api/v1/chart/?q={prison.dumps(request_query)}" - resp = self.client.get(url) - assert resp.json["count"] == 1 - assert resp.json["result"][0]["id"] == slc.id - - # Get recent activity - url = "/api/v1/log/recent_activity/?q=(page_size:50)" - resp = self.client.get(url) - # TODO data for recent activity varies for sqlite, we should be able to assert - # the returned data - assert resp.status_code == 200 - - # Get dashboards created by the user - request_query = { - "columns": ["created_on_delta_humanized", "dashboard_title", "url"], - "filters": [ - {"col": "created_by", "opr": "dashboard_created_by_me", "value": "me"} - ], - "keys": ["none"], - "order_column": "changed_on", - "order_direction": "desc", - "page": 0, - "page_size": 100, - } - url = f"/api/v1/dashboard/?q={prison.dumps(request_query)}" - resp = self.client.get(url) - assert resp.json["result"][0]["dashboard_title"] == "create_title_test" - - # Get charts created by the user - request_query = { - "columns": ["created_on_delta_humanized", "slice_name", "url"], - "filters": [ - {"col": "created_by", "opr": "chart_created_by_me", "value": "me"} - ], - "keys": ["none"], - "order_column": "changed_on_delta_humanized", - "order_direction": "desc", - "page": 0, - "page_size": 100, - } - url = f"/api/v1/chart/?q={prison.dumps(request_query)}" - resp = self.client.get(url) - assert resp.json["count"] == 1 - assert resp.json["result"][0]["slice_name"] == "create_title_test" - - resp = self.get_resp(f"/profile/") - self.assertIn('"app"', resp) - - def test_user_profile_gamma(self): - self.login(username="gamma") - resp = self.get_resp(f"/profile/") - self.assertIn('"app"', resp) - - @pytest.mark.usefixtures("public_role_like_gamma") - def test_user_profile_anonymous(self): - self.logout() - resp = self.client.get("/profile/") - assert resp.status_code == 404