From 6bc7c70c02cec4f1fccc1348faace3ce95e44d60 Mon Sep 17 00:00:00 2001 From: mikkancso Date: Thu, 17 Aug 2023 14:37:39 +0200 Subject: [PATCH] Datasources: Cleanup unused code from datasources (#73360) * remove unused datasource pages * move useDataSourceSettingsNav from datasources to connections without any modification in the code yet The reason for this is that its only usage is in this file, so we are going to merge it with the other useDataSourceSettingsNav hook in the next commit. * merge two useDataSourceSettingsNav's and rename consts and remove duplicated calls * simplify useDataSourceSettingsNav --- .../hooks/useDataSourceSettingsNav.ts | 74 +++++++-- .../pages/DataSourceDashboardsPage.test.tsx | 82 ---------- .../pages/DataSourceDashboardsPage.tsx | 24 --- .../pages/DataSourcesListPage.test.tsx | 143 ------------------ .../datasources/pages/DataSourcesListPage.tsx | 25 --- .../pages/EditDataSourcePage.test.tsx | 123 --------------- .../datasources/pages/EditDataSourcePage.tsx | 34 ----- .../datasources/pages/NewDataSourcePage.tsx | 27 ---- .../app/features/datasources/state/hooks.ts | 65 +------- 9 files changed, 61 insertions(+), 536 deletions(-) delete mode 100644 public/app/features/datasources/pages/DataSourceDashboardsPage.test.tsx delete mode 100644 public/app/features/datasources/pages/DataSourceDashboardsPage.tsx delete mode 100644 public/app/features/datasources/pages/DataSourcesListPage.test.tsx delete mode 100644 public/app/features/datasources/pages/DataSourcesListPage.tsx delete mode 100644 public/app/features/datasources/pages/EditDataSourcePage.test.tsx delete mode 100644 public/app/features/datasources/pages/EditDataSourcePage.tsx delete mode 100644 public/app/features/datasources/pages/NewDataSourcePage.tsx diff --git a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts index 9b9383c0205..6d165be812d 100644 --- a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts +++ b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts @@ -1,26 +1,70 @@ import { useLocation, useParams } from 'react-router-dom'; -import { - useDataSource, - useDataSourceSettingsNav as useDataSourceSettingsNavOriginal, -} from 'app/features/datasources/state/hooks'; +import { NavModel, NavModelItem } from '@grafana/data'; +import { getDataSourceSrv } from '@grafana/runtime'; +import { getNavModel } from 'app/core/selectors/navModel'; +import { useDataSource, useDataSourceSettings } from 'app/features/datasources/state/hooks'; +import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from 'app/features/datasources/state/navModel'; import { useGetSingle } from 'app/features/plugins/admin/state/hooks'; +import { useSelector } from 'app/types'; -// We are extending the original useDataSourceSettingsNav in the following ways: -// - changing the URL of the nav items to point to Connections -// - setting the parent nav item -export function useDataSourceSettingsNav(pageId?: string) { +export function useDataSourceSettingsNav(pageIdParam?: string) { const { uid } = useParams<{ uid: string }>(); const location = useLocation(); const datasource = useDataSource(uid); const datasourcePlugin = useGetSingle(datasource.type); const params = new URLSearchParams(location.search); - const nav = useDataSourceSettingsNavOriginal(uid, pageId || params.get('page')); - const pageNav = { - ...nav.main, + const pageId = pageIdParam || params.get('page'); + + const { plugin, loadError, loading } = useDataSourceSettings(); + const dsi = getDataSourceSrv()?.getInstanceSettings(uid); + const hasAlertingEnabled = Boolean(dsi?.meta?.alerting ?? false); + const isAlertManagerDatasource = dsi?.type === 'alertmanager'; + const alertingSupported = hasAlertingEnabled || isAlertManagerDatasource; + + const navIndex = useSelector((state) => state.navIndex); + const navIndexId = pageId ? `datasource-${pageId}-${uid}` : `datasource-settings-${uid}`; + let pageNav: NavModel = { + node: { + text: 'Data Source Nav Node', + }, + main: { + text: 'Data Source Nav Node', + }, + }; + + if (loadError) { + const node: NavModelItem = { + text: loadError, + subTitle: 'Data Source Error', + icon: 'exclamation-triangle', + }; + + pageNav = { + node: node, + main: node, + }; + } + + if (loading || !plugin) { + pageNav = getNavModel(navIndex, navIndexId, getDataSourceLoadingNav('settings')); + } + + if (plugin) { + pageNav = getNavModel( + navIndex, + navIndexId, + getDataSourceNav(buildNavModel(datasource, plugin), pageId || 'settings') + ); + } + + const connectionsPageNav = { + ...pageNav.main, + dataSourcePluginName: datasourcePlugin?.name || plugin?.meta.name || '', + active: true, text: datasource.name, subTitle: `Type: ${datasourcePlugin?.name}`, - children: (nav.main.children || []).map((navModelItem) => ({ + children: (pageNav.main.children || []).map((navModelItem) => ({ ...navModelItem, url: navModelItem.url?.replace('datasources/edit/', '/connections/datasources/edit/'), })), @@ -28,7 +72,9 @@ export function useDataSourceSettingsNav(pageId?: string) { return { navId: 'connections-datasources', - pageNav, - dataSourceHeader: nav.dataSourceHeader, + pageNav: connectionsPageNav, + dataSourceHeader: { + alertingSupported, + }, }; } diff --git a/public/app/features/datasources/pages/DataSourceDashboardsPage.test.tsx b/public/app/features/datasources/pages/DataSourceDashboardsPage.test.tsx deleted file mode 100644 index 3aa51ab03b5..00000000000 --- a/public/app/features/datasources/pages/DataSourceDashboardsPage.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; -import { Store } from 'redux'; -import { TestProvider } from 'test/helpers/TestProvider'; - -import { setAngularLoader } from '@grafana/runtime'; -import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps'; -import { configureStore } from 'app/store/configureStore'; - -import { navIndex, getMockDataSource } from '../__mocks__'; -import * as api from '../api'; -import { initialState as dataSourcesInitialState } from '../state'; - -import DataSourceDashboardsPage from './DataSourceDashboardsPage'; - -jest.mock('../api'); -jest.mock('app/core/services/context_srv', () => ({ - contextSrv: { - hasPermission: () => true, - hasPermissionInMetadata: () => true, - }, -})); - -const setup = (uid: string, store: Store) => - render( - - - - ); - -describe('', () => { - const uid = 'foo'; - const dataSourceName = 'My DataSource'; - const dataSource = getMockDataSource<{}>({ uid, name: dataSourceName }); - let store: Store; - - beforeAll(() => { - setAngularLoader({ - load: () => ({ - destroy: jest.fn(), - digest: jest.fn(), - getScope: () => ({ $watch: () => {} }), - }), - }); - }); - - beforeEach(() => { - // @ts-ignore - api.getDataSourceByIdOrUid = jest.fn().mockResolvedValue(dataSource); - - store = configureStore({ - dataSources: { - ...dataSourcesInitialState, - dataSource: dataSource, - }, - navIndex: { - ...navIndex, - [`datasource-dashboards-${uid}`]: { - id: `datasource-dashboards-${uid}`, - text: dataSourceName, - icon: 'list-ul', - url: `/datasources/edit/${uid}/dashboards`, - }, - }, - }); - }); - - it('should render the dashboards page without an issue', async () => { - setup(uid, store); - - expect(await screen.findByText(dataSourceName)).toBeVisible(); - }); -}); diff --git a/public/app/features/datasources/pages/DataSourceDashboardsPage.tsx b/public/app/features/datasources/pages/DataSourceDashboardsPage.tsx deleted file mode 100644 index 9f8fc7d00ab..00000000000 --- a/public/app/features/datasources/pages/DataSourceDashboardsPage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import { Page } from 'app/core/components/Page/Page'; -import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; - -import { DataSourceDashboards } from '../components/DataSourceDashboards'; -import { useDataSourceSettingsNav } from '../state'; - -export interface Props extends GrafanaRouteComponentProps<{ uid: string }> {} - -export function DataSourceDashboardsPage(props: Props) { - const uid = props.match.params.uid; - const nav = useDataSourceSettingsNav(uid, 'dashboards'); - - return ( - - - - - - ); -} - -export default DataSourceDashboardsPage; diff --git a/public/app/features/datasources/pages/DataSourcesListPage.test.tsx b/public/app/features/datasources/pages/DataSourcesListPage.test.tsx deleted file mode 100644 index de0a4b9670b..00000000000 --- a/public/app/features/datasources/pages/DataSourcesListPage.test.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; -import { TestProvider } from 'test/helpers/TestProvider'; - -import { LayoutModes } from '@grafana/data'; -import { contextSrv } from 'app/core/services/context_srv'; - -import { navIndex, getMockDataSources } from '../__mocks__'; -import { getDataSources } from '../api'; -import { initialState } from '../state'; - -import { DataSourcesListPage } from './DataSourcesListPage'; - -jest.mock('app/core/services/context_srv'); -jest.mock('../api', () => ({ - ...jest.requireActual('../api'), - getDataSources: jest.fn().mockResolvedValue([]), -})); - -const getDataSourcesMock = getDataSources as jest.Mock; - -const setup = (options: { isSortAscending: boolean }) => { - const storeState = { - dataSources: { - ...initialState, - layoutMode: LayoutModes.Grid, - isSortAscending: options.isSortAscending, - }, - navIndex, - }; - - return render( - - - - ); -}; - -describe('Render', () => { - beforeEach(() => { - (contextSrv.hasPermission as jest.Mock) = jest.fn().mockReturnValue(true); - }); - - it('should render component', async () => { - setup({ isSortAscending: true }); - - expect(await screen.findByRole('heading', { name: 'Data sources' })).toBeInTheDocument(); - expect(await screen.findByRole('link', { name: 'Add data source' })).toBeInTheDocument(); - - // Should not show button in page header when the list is empty - expect(await screen.queryByRole('link', { name: 'Add new data source' })).toBeNull(); - }); - - describe('when user has no permissions', () => { - beforeEach(() => { - (contextSrv.hasPermission as jest.Mock) = jest.fn().mockReturnValue(false); - }); - - it('should disable the "Add data source" button if user has no permissions', async () => { - setup({ isSortAscending: true }); - - expect(await screen.findByRole('heading', { name: 'Data sources' })).toBeInTheDocument(); - expect(await screen.findByRole('link', { name: 'Add data source' })).toHaveStyle('pointer-events: none'); - }); - - it('should not show the Explore button', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(3)); - setup({ isSortAscending: true }); - - expect(await screen.findAllByRole('link', { name: /Build a dashboard/i })).toHaveLength(3); - expect(screen.queryAllByRole('link', { name: 'Explore' })).toHaveLength(0); - }); - - it('should not link cards to edit pages', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(1)); - setup({ isSortAscending: true }); - - expect(await screen.findByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument(); - expect(await screen.queryByRole('link', { name: 'dataSource-0' })).toBeNull(); - }); - }); - - it('should show the Explore button', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(3)); - setup({ isSortAscending: true }); - - expect(await screen.findAllByRole('link', { name: /Build a dashboard/i })).toHaveLength(3); - expect(screen.queryAllByRole('link', { name: 'Explore' })).toHaveLength(3); - }); - - it('should link cards to edit pages', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(1)); - setup({ isSortAscending: true }); - - expect(await screen.findByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument(); - expect(await screen.findByRole('link', { name: 'dataSource-0' })).toBeInTheDocument(); - }); - - it('should render action bar and datasources', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(5)); - - setup({ isSortAscending: true }); - - expect(await screen.findByPlaceholderText('Search by name or type')).toBeInTheDocument(); - expect(await screen.findByRole('combobox', { name: 'Sort' })).toBeInTheDocument(); - expect(await screen.findByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument(); - expect(await screen.findByRole('heading', { name: 'dataSource-1' })).toBeInTheDocument(); - expect(await screen.findByRole('heading', { name: 'dataSource-2' })).toBeInTheDocument(); - expect(await screen.findByRole('heading', { name: 'dataSource-3' })).toBeInTheDocument(); - expect(await screen.findByRole('heading', { name: 'dataSource-4' })).toBeInTheDocument(); - expect(await screen.findAllByRole('img')).toHaveLength(5); - - // Should show button in page header when the list is not empty - expect(await screen.findByRole('link', { name: 'Add new data source' })).toBeInTheDocument(); - }); - - describe('should render elements in sort order', () => { - it('ascending', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(5)); - setup({ isSortAscending: true }); - - expect(await screen.findByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument(); - const dataSourceItems = await screen.findAllByRole('heading'); - - expect(dataSourceItems).toHaveLength(6); - expect(dataSourceItems[0]).toHaveTextContent('Data sources'); - expect(dataSourceItems[1]).toHaveTextContent('dataSource-0'); - expect(dataSourceItems[2]).toHaveTextContent('dataSource-1'); - }); - it('descending', async () => { - getDataSourcesMock.mockResolvedValue(getMockDataSources(5)); - setup({ isSortAscending: false }); - - expect(await screen.findByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument(); - const dataSourceItems = await screen.findAllByRole('heading'); - - expect(dataSourceItems).toHaveLength(6); - expect(dataSourceItems[0]).toHaveTextContent('Data sources'); - expect(dataSourceItems[1]).toHaveTextContent('dataSource-4'); - expect(dataSourceItems[2]).toHaveTextContent('dataSource-3'); - }); - }); -}); diff --git a/public/app/features/datasources/pages/DataSourcesListPage.tsx b/public/app/features/datasources/pages/DataSourcesListPage.tsx deleted file mode 100644 index ce4d8769450..00000000000 --- a/public/app/features/datasources/pages/DataSourcesListPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -import { Page } from 'app/core/components/Page/Page'; -import { ConnectionsRedirectNotice } from 'app/features/connections/components/ConnectionsRedirectNotice'; -import { StoreState, useSelector } from 'app/types'; - -import { DataSourceAddButton } from '../components/DataSourceAddButton'; -import { DataSourcesList } from '../components/DataSourcesList'; -import { getDataSourcesCount } from '../state'; - -export function DataSourcesListPage() { - const dataSourcesCount = useSelector(({ dataSources }: StoreState) => getDataSourcesCount(dataSources)); - - const actions = dataSourcesCount > 0 ? : undefined; - return ( - - - - - - - ); -} - -export default DataSourcesListPage; diff --git a/public/app/features/datasources/pages/EditDataSourcePage.test.tsx b/public/app/features/datasources/pages/EditDataSourcePage.test.tsx deleted file mode 100644 index 4ad10721056..00000000000 --- a/public/app/features/datasources/pages/EditDataSourcePage.test.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { screen, render, waitFor } from '@testing-library/react'; -import React from 'react'; -import { Store } from 'redux'; -import { TestProvider } from 'test/helpers/TestProvider'; - -import { LayoutModes } from '@grafana/data'; -import { setAngularLoader, setPluginExtensionGetter } from '@grafana/runtime'; -import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps'; -import { configureStore } from 'app/store/configureStore'; - -import { navIndex, getMockDataSource, getMockDataSourceMeta, getMockDataSourceSettingsState } from '../__mocks__'; -import * as api from '../api'; -import { initialState } from '../state'; - -import { EditDataSourcePage } from './EditDataSourcePage'; - -jest.mock('../api'); -jest.mock('app/core/services/context_srv', () => ({ - contextSrv: { - hasPermission: () => true, - hasPermissionInMetadata: () => true, - }, -})); -jest.mock('@grafana/runtime', () => { - const original = jest.requireActual('@grafana/runtime'); - return { - ...original, - getDataSourceSrv: jest.fn(() => ({ - getInstanceSettings: (uid: string) => ({ - uid, - meta: getMockDataSourceMeta(), - }), - })), - }; -}); - -const setup = (uid: string, store: Store) => - render( - - - - ); - -describe('', () => { - const uid = 'foo'; - const name = 'My DataSource'; - const dataSource = getMockDataSource<{}>({ uid, name }); - const dataSourceMeta = getMockDataSourceMeta(); - const dataSourceSettings = getMockDataSourceSettingsState(); - let store: Store; - - beforeAll(() => { - setAngularLoader({ - load: () => ({ - destroy: jest.fn(), - digest: jest.fn(), - getScope: () => ({ $watch: () => {} }), - }), - }); - }); - - beforeEach(() => { - // @ts-ignore - api.getDataSourceByIdOrUid = jest.fn().mockResolvedValue(dataSource); - setPluginExtensionGetter(jest.fn().mockReturnValue({ extensions: [] })); - - store = configureStore({ - dataSourceSettings, - dataSources: { - ...initialState, - dataSources: [dataSource], - dataSource: dataSource, - dataSourceMeta: dataSourceMeta, - layoutMode: LayoutModes.Grid, - isLoadingDataSources: false, - }, - navIndex: { - ...navIndex, - [`datasource-settings-${uid}`]: { - id: `datasource-settings-${uid}`, - text: name, - icon: 'list-ul', - url: `/datasources/edit/${uid}`, - }, - }, - }); - }); - - it('should render the edit page without an issue', async () => { - setup(uid, store); - - expect(screen.queryByText('Loading ...')).not.toBeInTheDocument(); - - // Title - expect(screen.queryByText(name)).toBeVisible(); - - // Buttons - expect(screen.queryByRole('button', { name: /Save (.*) test/i })).toBeVisible(); - - // wait for the rest of the async processes to finish - expect(await screen.findByText(name)).toBeVisible(); - }); - - it('should show updated action buttons when topnav is on', async () => { - setup(uid, store); - - await waitFor(() => { - // Buttons - expect(screen.queryByRole('button', { name: /Delete/i })).toBeVisible(); - expect(screen.queryByRole('link', { name: /Build a dashboard/i })).toBeVisible(); - expect(screen.queryAllByRole('link', { name: /Explore data/i })).toHaveLength(1); - }); - }); -}); diff --git a/public/app/features/datasources/pages/EditDataSourcePage.tsx b/public/app/features/datasources/pages/EditDataSourcePage.tsx deleted file mode 100644 index cd8d52ba85d..00000000000 --- a/public/app/features/datasources/pages/EditDataSourcePage.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -import { config } from '@grafana/runtime'; -import { Page } from 'app/core/components/Page/Page'; -import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; - -import DataSourceTabPage from '../components/DataSourceTabPage'; -import { EditDataSource } from '../components/EditDataSource'; -import { EditDataSourceActions } from '../components/EditDataSourceActions'; -import { useDataSourceSettingsNav } from '../state'; - -export interface Props extends GrafanaRouteComponentProps<{ uid: string }> {} - -export function EditDataSourcePage(props: Props) { - const uid = props.match.params.uid; - const params = new URLSearchParams(props.location.search); - const pageId = params.get('page'); - const dataSourcePageHeader = config.featureToggles.dataSourcePageHeader; - const nav = useDataSourceSettingsNav(uid, pageId); - - if (dataSourcePageHeader) { - return ; - } - - return ( - }> - - - - - ); -} - -export default EditDataSourcePage; diff --git a/public/app/features/datasources/pages/NewDataSourcePage.tsx b/public/app/features/datasources/pages/NewDataSourcePage.tsx deleted file mode 100644 index 498cae88f10..00000000000 --- a/public/app/features/datasources/pages/NewDataSourcePage.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; - -import { NavModelItem } from '@grafana/data'; -import { Page } from 'app/core/components/Page/Page'; - -import { NewDataSource } from '../components/NewDataSource'; -import { DATASOURCES_ROUTES } from '../constants'; - -export function NewDataSourcePage() { - const pageNav: NavModelItem = { - icon: 'database', - id: 'datasource-new', - text: 'Add data source', - url: DATASOURCES_ROUTES.New, - subTitle: 'Choose a data source type', - }; - - return ( - - - - - - ); -} - -export default NewDataSourcePage; diff --git a/public/app/features/datasources/state/hooks.ts b/public/app/features/datasources/state/hooks.ts index 92222cd5c85..11587a60d42 100644 --- a/public/app/features/datasources/state/hooks.ts +++ b/public/app/features/datasources/state/hooks.ts @@ -1,12 +1,9 @@ import { useContext, useEffect } from 'react'; -import { DataSourcePluginMeta, DataSourceSettings, NavModel, NavModelItem } from '@grafana/data'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data'; import { cleanUpAction } from 'app/core/actions/cleanUp'; import appEvents from 'app/core/app_events'; import { contextSrv } from 'app/core/core'; -import { getNavModel } from 'app/core/selectors/navModel'; -import { useGetSingle } from 'app/features/plugins/admin/state/hooks'; import { AccessControlAction, useDispatch, useSelector } from 'app/types'; import { ShowConfirmModalEvent } from 'app/types/events'; @@ -24,7 +21,6 @@ import { deleteLoadedDataSource, } from './actions'; import { DataSourcesRoutesContext } from './contexts'; -import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from './navModel'; import { initialDataSourceSettingsState } from './reducers'; import { getDataSource, getDataSourceMeta } from './selectors'; @@ -128,65 +124,6 @@ export const useDataSourceSettings = () => { return useSelector((state) => state.dataSourceSettings); }; -export const useDataSourceSettingsNav = (dataSourceId: string, pageId: string | null) => { - const { plugin, loadError, loading } = useDataSourceSettings(); - const dataSource = useDataSource(dataSourceId); - const dsi = getDataSourceSrv()?.getInstanceSettings(dataSourceId); - const hasAlertingEnabled = Boolean(dsi?.meta?.alerting ?? false); - const isAlertManagerDatasource = dsi?.type === 'alertmanager'; - const alertingSupported = hasAlertingEnabled || isAlertManagerDatasource; - - const datasourcePlugin = useGetSingle(dataSource.type); - const navIndex = useSelector((state) => state.navIndex); - const navIndexId = pageId ? `datasource-${pageId}-${dataSourceId}` : `datasource-settings-${dataSourceId}`; - let pageNav: NavModel = { - node: { - text: 'Data Source Nav Node', - }, - main: { - text: 'Data Source Nav Node', - }, - }; - - if (loadError) { - const node: NavModelItem = { - text: loadError, - subTitle: 'Data Source Error', - icon: 'exclamation-triangle', - }; - - pageNav = { - node: node, - main: node, - }; - } - - if (loading || !plugin) { - pageNav = getNavModel(navIndex, navIndexId, getDataSourceLoadingNav('settings')); - } - - if (plugin) { - pageNav = getNavModel( - navIndex, - navIndexId, - getDataSourceNav(buildNavModel(dataSource, plugin), pageId || 'settings') - ); - } - - return { - node: pageNav.node, - main: { - ...pageNav.main, - text: dataSource.name, - dataSourcePluginName: datasourcePlugin?.name || plugin?.meta.name || '', - active: true, - }, - dataSourceHeader: { - alertingSupported, - }, - }; -}; - export const useDataSourceRights = (uid: string): DataSourceRights => { const dataSource = useDataSource(uid); const readOnly = dataSource.readOnly === true;