mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 18:36:08 +00:00
Update MyData and Explore with new roles and policy (#7018)
* Update MyData and Explore with new roles and policy * Add placeholder for explore side panel * Fix unit tests * Remove Permission API call from AuthProvider * Fix loading issue * Update global settings left panel * Remove feed check from mydata page * Remove permission check from explore page * Minor change * Add resource permission method to provider * Change global settings left panel * Remove unwanted codes * Add comments * Addressing review comments
This commit is contained in:
parent
a6a4e08af1
commit
106f99f4c8
@ -33,10 +33,7 @@ import React, {
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import appState from '../../AppState';
|
||||
import axiosClient from '../../axiosAPIs';
|
||||
import {
|
||||
fetchAuthenticationConfig,
|
||||
getLoggedInUserPermissions,
|
||||
} from '../../axiosAPIs/miscAPI';
|
||||
import { fetchAuthenticationConfig } from '../../axiosAPIs/miscAPI';
|
||||
import {
|
||||
getLoggedInUser,
|
||||
getUserByName,
|
||||
@ -166,32 +163,14 @@ export const AuthProvider = ({
|
||||
}
|
||||
};
|
||||
|
||||
const getUserPermissions = () => {
|
||||
setLoading(true);
|
||||
getLoggedInUserPermissions()
|
||||
.then((res) => {
|
||||
appState.updateUserPermissions(res.data);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['fetch-user-permission-error']
|
||||
);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const getLoggedInUserDetails = () => {
|
||||
setLoading(true);
|
||||
getLoggedInUser(userAPIQueryFields)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
getUserPermissions();
|
||||
appState.updateUserDetails(res);
|
||||
fetchAllUsers();
|
||||
} else {
|
||||
resetUserDetails();
|
||||
setLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
@ -204,6 +183,9 @@ export const AuthProvider = ({
|
||||
jsonData['api-error-messages']['fetch-logged-in-user-error']
|
||||
);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
@ -368,8 +350,6 @@ export const AuthProvider = ({
|
||||
} else {
|
||||
appState.updateUserDetails(res);
|
||||
}
|
||||
getUserPermissions();
|
||||
fetchAllUsers();
|
||||
handledVerifiedUser();
|
||||
// Start expiry timer on successful login
|
||||
startTokenExpiryTimer();
|
||||
@ -439,8 +419,6 @@ export const AuthProvider = ({
|
||||
storeRedirectPath();
|
||||
showErrorToast(error);
|
||||
resetUserDetails(true);
|
||||
} else if (status === ClientErrors.FORBIDDEN) {
|
||||
showErrorToast(jsonData['api-error-messages']['forbidden-error']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ import { WILD_CARD_CHAR } from '../constants/char.constants';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import { AirflowConfiguration } from '../generated/configuration/airflowConfiguration';
|
||||
import { AuthenticationConfiguration } from '../generated/configuration/authenticationConfiguration';
|
||||
import { ResourcePermission } from '../generated/entity/policies/accessControl/resourcePermission';
|
||||
import { EntitiesCount } from '../generated/entity/utils/entitiesCount';
|
||||
import { Paging } from '../generated/type/paging';
|
||||
import { getURLWithQueryFields } from '../utils/APIUtils';
|
||||
@ -121,18 +120,6 @@ export const deleteLineageEdge: Function = (
|
||||
);
|
||||
};
|
||||
|
||||
export const getLoggedInUserPermissions = async () => {
|
||||
const params = {
|
||||
limit: 100,
|
||||
};
|
||||
const response = await APIClient.get<{
|
||||
data: ResourcePermission[];
|
||||
paging: Paging;
|
||||
}>('/permissions', { params });
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getInitialEntity = (
|
||||
index: SearchIndex,
|
||||
params = {} as AxiosRequestConfig
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed 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 { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { ResourcePermission } from '../generated/entity/policies/accessControl/resourcePermission';
|
||||
import { Paging } from '../generated/type/paging';
|
||||
import APIClient from './index';
|
||||
|
||||
export const getLoggedInUserPermissions = async () => {
|
||||
const params = {
|
||||
limit: 100,
|
||||
};
|
||||
const response = await APIClient.get<{
|
||||
data: ResourcePermission[];
|
||||
paging: Paging;
|
||||
}>('/permissions', { params });
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getEntityPermissionById = async (
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => {
|
||||
const response = await APIClient.get<ResourcePermission>(
|
||||
`/permissions/${resource}/${entityId}`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getResourcePermission = async (resource: ResourceEntity) => {
|
||||
const response = await APIClient.get<ResourcePermission>(
|
||||
`/permissions/${resource}`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
@ -13,11 +13,9 @@
|
||||
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { CreatePolicy } from '../generated/api/policies/createPolicy';
|
||||
import { CreateRole } from '../generated/api/teams/createRole';
|
||||
import { ResourceDescriptor } from '../generated/entity/policies/accessControl/resourceDescriptor';
|
||||
import { ResourcePermission } from '../generated/entity/policies/accessControl/resourcePermission';
|
||||
import { Policy } from '../generated/entity/policies/policy';
|
||||
import { Role } from '../generated/entity/teams/role';
|
||||
import { Function } from '../generated/type/function';
|
||||
@ -156,14 +154,3 @@ export const validateRuleCondition = async (condition: string) => {
|
||||
*/
|
||||
return response;
|
||||
};
|
||||
|
||||
export const getEntityPermissionById = async (
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => {
|
||||
const response = await APIClient.get<ResourcePermission>(
|
||||
`/permissions/${resource}/${entityId}`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
@ -15,13 +15,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Card } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { isNil } from 'lodash';
|
||||
import { isEmpty, isNil } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React, {
|
||||
FC,
|
||||
Fragment,
|
||||
HTMLAttributes,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import Select, { SingleValue } from 'react-select';
|
||||
@ -77,21 +78,32 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
const [generateToken, setGenerateToken] = useState<boolean>(false);
|
||||
const [selectedExpiry, setSelectedExpiry] = useState('7');
|
||||
|
||||
const editAllPermission = checkPermission(
|
||||
Operation.EditAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
const editAllPermission = useMemo(
|
||||
() =>
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(Operation.EditAll, ResourceEntity.BOT, permissions),
|
||||
[permissions]
|
||||
);
|
||||
const displayNamePermission = checkPermission(
|
||||
Operation.EditDisplayName,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
const displayNamePermission = useMemo(
|
||||
() =>
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(
|
||||
Operation.EditDisplayName,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const descriptionPermission = checkPermission(
|
||||
Operation.EditDescription,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
const descriptionPermission = useMemo(
|
||||
() =>
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(
|
||||
Operation.EditDescription,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const getJWTTokenExpiryOptions = () => {
|
||||
|
@ -14,6 +14,7 @@
|
||||
import { Button, Col, Row, Space, Table, Tooltip } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getBots } from '../../axiosAPIs/botsAPI';
|
||||
@ -46,10 +47,11 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentPage, setCurrentPage] = useState<number>(INITIAL_PAGING_VALUE);
|
||||
|
||||
const deletePermission = checkPermission(
|
||||
Operation.Delete,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
const deletePermission = useMemo(
|
||||
() =>
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(Operation.Delete, ResourceEntity.BOT, permissions),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -89,12 +89,8 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
fetchData,
|
||||
showDeleted,
|
||||
onShowDeleted,
|
||||
updateTableCount,
|
||||
updateTopicCount,
|
||||
updateDashboardCount,
|
||||
updatePipelineCount,
|
||||
isFilterSelected,
|
||||
updateMlModelCount,
|
||||
handleTabCounts,
|
||||
}: ExploreProps) => {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
@ -273,23 +269,23 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
const setCount = (count = 0, index = searchIndex) => {
|
||||
switch (index) {
|
||||
case SearchIndex.TABLE:
|
||||
updateTableCount(count);
|
||||
handleTabCounts({ table: count });
|
||||
|
||||
break;
|
||||
case SearchIndex.DASHBOARD:
|
||||
updateDashboardCount(count);
|
||||
handleTabCounts({ dashboard: count });
|
||||
|
||||
break;
|
||||
case SearchIndex.TOPIC:
|
||||
updateTopicCount(count);
|
||||
handleTabCounts({ topic: count });
|
||||
|
||||
break;
|
||||
case SearchIndex.PIPELINE:
|
||||
updatePipelineCount(count);
|
||||
handleTabCounts({ pipeline: count });
|
||||
|
||||
break;
|
||||
case SearchIndex.MLMODEL:
|
||||
updateMlModelCount(count);
|
||||
handleTabCounts({ mlmodel: count });
|
||||
|
||||
break;
|
||||
default:
|
||||
@ -441,7 +437,7 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
case SearchIndex.PIPELINE:
|
||||
return getCountBadge(tabCounts.pipeline, className, isActive);
|
||||
case SearchIndex.MLMODEL:
|
||||
return getCountBadge(tabCounts.mlModel, className, isActive);
|
||||
return getCountBadge(tabCounts.mlmodel, className, isActive);
|
||||
default:
|
||||
return getCountBadge();
|
||||
}
|
||||
@ -469,18 +465,6 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
'tw-flex tw-flex-row tw-justify-between tw-gh-tabs-container'
|
||||
)}>
|
||||
<div className="tw-flex">
|
||||
{/* <div className="tw-w-64 tw-mr-5 tw-flex-shrink-0">
|
||||
<Button
|
||||
className={classNames('tw-underline tw-mt-5', {
|
||||
'tw-invisible': !getFilterCount(filters),
|
||||
})}
|
||||
size="custom"
|
||||
theme="primary"
|
||||
variant="link"
|
||||
onClick={() => resetFilters(true)}>
|
||||
Clear All
|
||||
</Button>
|
||||
</div> */}
|
||||
<div>
|
||||
{tabsInfo.map((tabDetail, index) => (
|
||||
<button
|
||||
|
@ -95,6 +95,7 @@ describe('Test Explore component', () => {
|
||||
handleFilterChange={mockFunction}
|
||||
handlePathChange={mockFunction}
|
||||
handleSearchText={mockFunction}
|
||||
handleTabCounts={mockFunction}
|
||||
searchQuery=""
|
||||
searchResult={mockSearchResult}
|
||||
searchText=""
|
||||
@ -106,15 +107,8 @@ describe('Test Explore component', () => {
|
||||
topic: 2,
|
||||
dashboard: 8,
|
||||
pipeline: 5,
|
||||
dbtModel: 7,
|
||||
mlModel: 2,
|
||||
mlmodel: 2,
|
||||
}}
|
||||
updateDashboardCount={mockFunction}
|
||||
updateDbtModelCount={mockFunction}
|
||||
updateMlModelCount={mockFunction}
|
||||
updatePipelineCount={mockFunction}
|
||||
updateTableCount={mockFunction}
|
||||
updateTopicCount={mockFunction}
|
||||
onShowDeleted={mockFunction}
|
||||
/>,
|
||||
{
|
||||
|
@ -29,14 +29,7 @@ export type ExploreSearchData = {
|
||||
};
|
||||
|
||||
export interface ExploreProps {
|
||||
tabCounts: {
|
||||
table: number;
|
||||
topic: number;
|
||||
dashboard: number;
|
||||
pipeline: number;
|
||||
dbtModel: number;
|
||||
mlModel: number;
|
||||
};
|
||||
tabCounts: TabCounts;
|
||||
searchText: string;
|
||||
initialFilter?: FilterObject;
|
||||
searchFilter?: FilterObject;
|
||||
@ -51,17 +44,20 @@ export interface ExploreProps {
|
||||
handleFilterChange: (data: FilterObject) => void;
|
||||
handlePathChange: (path: string) => void;
|
||||
handleSearchText: (text: string) => void;
|
||||
updateTableCount: (count: number) => void;
|
||||
updateTopicCount: (count: number) => void;
|
||||
updateDashboardCount: (count: number) => void;
|
||||
updatePipelineCount: (count: number) => void;
|
||||
updateDbtModelCount: (count: number) => void;
|
||||
updateMlModelCount: (count: number) => void;
|
||||
fetchData: (value: SearchDataFunctionType[]) => void;
|
||||
onShowDeleted: (checked: boolean) => void;
|
||||
handleTabCounts: (value: { [key: string]: number }) => void;
|
||||
}
|
||||
|
||||
export interface AdvanceField {
|
||||
key: string;
|
||||
value: string | undefined;
|
||||
}
|
||||
|
||||
export interface TabCounts {
|
||||
table: number;
|
||||
topic: number;
|
||||
dashboard: number;
|
||||
pipeline: number;
|
||||
mlmodel: number;
|
||||
}
|
||||
|
@ -13,46 +13,72 @@
|
||||
|
||||
import { Menu, MenuProps } from 'antd';
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { AxiosError } from 'axios';
|
||||
import { camelCase } from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { GLOBAL_SETTINGS_MENU } from '../../constants/globalSettings.constants';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { getGlobalSettingMenus } from '../../utils/GlobalSettingsUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { GLOBAL_SETTING_PERMISSION_RESOURCES } from '../../constants/globalSettings.constants';
|
||||
import {
|
||||
getGlobalSettingMenuItem,
|
||||
getGlobalSettingsMenuWithPermission,
|
||||
MenuList,
|
||||
} from '../../utils/GlobalSettingsUtils';
|
||||
import { getSettingPath } from '../../utils/RouterUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import Loader from '../Loader/Loader';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import {
|
||||
ResourceEntity,
|
||||
UIPermission,
|
||||
} from '../PermissionProvider/PermissionProvider.interface';
|
||||
|
||||
const GlobalSettingLeftPanel = () => {
|
||||
const { tab, settingCategory } = useParams<{ [key: string]: string }>();
|
||||
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
const viewAllPermission = checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ALL,
|
||||
permissions
|
||||
);
|
||||
|
||||
const history = useHistory();
|
||||
const items: ItemType[] = GLOBAL_SETTINGS_MENU.filter(({ isProtected }) => {
|
||||
if (viewAllPermission) {
|
||||
return viewAllPermission;
|
||||
}
|
||||
const { tab, settingCategory } = useParams<{ [key: string]: string }>();
|
||||
const [settingResourcePermission, setSettingResourcePermission] =
|
||||
useState<UIPermission>({} as UIPermission);
|
||||
|
||||
return !isProtected;
|
||||
}).map(({ category, items }) => {
|
||||
return getGlobalSettingMenus(
|
||||
category,
|
||||
camelCase(category),
|
||||
'',
|
||||
'',
|
||||
items,
|
||||
'group',
|
||||
viewAllPermission
|
||||
);
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const { getResourcePermission } = usePermissionProvider();
|
||||
|
||||
const fetchResourcesPermission = async (resource: ResourceEntity) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await getResourcePermission(resource);
|
||||
setSettingResourcePermission((prev) => ({
|
||||
...prev,
|
||||
[resource]: response,
|
||||
}));
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const menuItems: ItemType[] = useMemo(
|
||||
() =>
|
||||
getGlobalSettingsMenuWithPermission(settingResourcePermission).reduce(
|
||||
(acc: ItemType[], curr: MenuList) => {
|
||||
const menuItem = getGlobalSettingMenuItem(
|
||||
curr.category,
|
||||
camelCase(curr.category),
|
||||
'',
|
||||
'',
|
||||
curr.items,
|
||||
'group'
|
||||
);
|
||||
if (menuItem.children?.length) {
|
||||
return [...acc, menuItem];
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
},
|
||||
[] as ItemType[]
|
||||
),
|
||||
[setSettingResourcePermission]
|
||||
);
|
||||
|
||||
const onClick: MenuProps['onClick'] = (e) => {
|
||||
// As we are setting key as "category.option" and extracting here category and option
|
||||
@ -60,14 +86,29 @@ const GlobalSettingLeftPanel = () => {
|
||||
history.push(getSettingPath(category, option));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// TODO: This will make number of API calls, need to think of better solution
|
||||
GLOBAL_SETTING_PERMISSION_RESOURCES.forEach((resource) => {
|
||||
fetchResourcesPermission(resource);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className="global-setting-left-panel"
|
||||
items={items}
|
||||
mode="inline"
|
||||
selectedKeys={[`${settingCategory}.${tab}`]}
|
||||
onClick={onClick}
|
||||
/>
|
||||
<>
|
||||
{menuItems.length ? (
|
||||
<Menu
|
||||
className="global-setting-left-panel"
|
||||
items={menuItems}
|
||||
mode="inline"
|
||||
selectedKeys={[`${settingCategory}.${tab}`]}
|
||||
onClick={onClick}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AxiosError } from 'axios';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/resourcePermission';
|
||||
|
||||
export type UIPermission = {
|
||||
@ -65,7 +64,10 @@ export interface PermissionContextType {
|
||||
getEntityPermission: (
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => Promise<OperationPermission | AxiosError>;
|
||||
) => Promise<OperationPermission>;
|
||||
getResourcePermission: (
|
||||
resource: ResourceEntity
|
||||
) => Promise<OperationPermission>;
|
||||
}
|
||||
|
||||
export interface EntityPermissionMap {
|
||||
|
@ -23,8 +23,11 @@ import React, {
|
||||
useState,
|
||||
} from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import { getLoggedInUserPermissions } from '../../axiosAPIs/miscAPI';
|
||||
import { getEntityPermissionById } from '../../axiosAPIs/rolesAPIV1';
|
||||
import {
|
||||
getEntityPermissionById,
|
||||
getLoggedInUserPermissions,
|
||||
getResourcePermission,
|
||||
} from '../../axiosAPIs/permissionAPI';
|
||||
import {
|
||||
getOperationPermissions,
|
||||
getUIPermission,
|
||||
@ -36,12 +39,12 @@ import {
|
||||
ResourceEntity,
|
||||
UIPermission,
|
||||
} from './PermissionProvider.interface';
|
||||
|
||||
/**
|
||||
* Permission Context
|
||||
* Returns ResourcePermission List for loggedIn User
|
||||
* @returns PermissionMap
|
||||
*/
|
||||
|
||||
export const PermissionContext = createContext<PermissionContextType>(
|
||||
{} as PermissionContextType
|
||||
);
|
||||
@ -63,6 +66,10 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
const [entitiesPermission, setEntitiesPermission] =
|
||||
useState<EntityPermissionMap>({} as EntityPermissionMap);
|
||||
|
||||
const [resourcesPermission, setResourcesPermission] = useState<UIPermission>(
|
||||
{} as UIPermission
|
||||
);
|
||||
|
||||
// Update current user details of AppState change
|
||||
const currentUser = useMemo(() => {
|
||||
return AppState.getCurrentUserDetails();
|
||||
@ -84,22 +91,45 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => {
|
||||
try {
|
||||
const entityPermission = entitiesPermission[entityId];
|
||||
if (entityPermission) {
|
||||
return entityPermission;
|
||||
} else {
|
||||
const response = await getEntityPermissionById(resource, entityId);
|
||||
const operationPermission = getOperationPermissions(response);
|
||||
setEntitiesPermission((prev) => ({
|
||||
...prev,
|
||||
[entityId]: operationPermission,
|
||||
}));
|
||||
const entityPermission = entitiesPermission[entityId];
|
||||
if (entityPermission) {
|
||||
return entityPermission;
|
||||
} else {
|
||||
const response = await getEntityPermissionById(resource, entityId);
|
||||
const operationPermission = getOperationPermissions(response);
|
||||
setEntitiesPermission((prev) => ({
|
||||
...prev,
|
||||
[entityId]: operationPermission,
|
||||
}));
|
||||
|
||||
return operationPermission;
|
||||
}
|
||||
} catch (error) {
|
||||
return error as AxiosError;
|
||||
return operationPermission;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchResourcePermission = async (resource: ResourceEntity) => {
|
||||
const resourcePermission = resourcesPermission[resource];
|
||||
if (resourcePermission) {
|
||||
return resourcePermission;
|
||||
} else {
|
||||
const response = await getResourcePermission(resource);
|
||||
const operationPermission = getOperationPermissions(response);
|
||||
/**
|
||||
* Store resource permission if it's not exits
|
||||
*/
|
||||
setResourcesPermission((prev) => ({
|
||||
...prev,
|
||||
[resource]: operationPermission,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Store updated resource permission
|
||||
*/
|
||||
setPermissions((prev) => ({
|
||||
...prev,
|
||||
[resource]: operationPermission,
|
||||
}));
|
||||
|
||||
return operationPermission;
|
||||
}
|
||||
};
|
||||
|
||||
@ -117,6 +147,7 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
value={{
|
||||
permissions,
|
||||
getEntityPermission: fetchEntityPermission,
|
||||
getResourcePermission: fetchResourcePermission,
|
||||
}}>
|
||||
{children}
|
||||
</PermissionContext.Provider>
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
import { toLower } from 'lodash';
|
||||
import { AggregationType, Bucket, FilterObject } from 'Models';
|
||||
import { TabCounts } from '../components/Explore/explore.interface';
|
||||
import { EntityType } from '../enums/entity.enum';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import { getFilterKey } from '../utils/FilterUtils';
|
||||
import { Icons } from '../utils/SvgUtils';
|
||||
@ -301,3 +303,27 @@ export const tabsInfo = [
|
||||
selectedIcon: '',
|
||||
},
|
||||
];
|
||||
|
||||
export const INITIAL_TAB_COUNTS: TabCounts = {
|
||||
table: 0,
|
||||
topic: 0,
|
||||
dashboard: 0,
|
||||
pipeline: 0,
|
||||
mlmodel: 0,
|
||||
};
|
||||
|
||||
export const getEntityTypeByIndex = (index: SearchIndex) => {
|
||||
switch (index) {
|
||||
case SearchIndex.TOPIC:
|
||||
return EntityType.TOPIC;
|
||||
case SearchIndex.DASHBOARD:
|
||||
return EntityType.DASHBOARD;
|
||||
case SearchIndex.PIPELINE:
|
||||
return EntityType.PIPELINE;
|
||||
case SearchIndex.MLMODEL:
|
||||
return EntityType.MLMODEL;
|
||||
case SearchIndex.TABLE:
|
||||
default:
|
||||
return EntityType.TABLE;
|
||||
}
|
||||
};
|
||||
|
@ -11,148 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { ReactComponent as BotIcon } from '../../src/assets/svg/bot-profile.svg';
|
||||
import { ReactComponent as DashboardIcon } from '../../src/assets/svg/dashboard-grey.svg';
|
||||
import { ReactComponent as RolesIcon } from '../../src/assets/svg/icon-role-grey.svg';
|
||||
import { ReactComponent as MlModelIcon } from '../../src/assets/svg/mlmodal.svg';
|
||||
import { ReactComponent as MSTeams } from '../../src/assets/svg/ms-teams.svg';
|
||||
import { ReactComponent as PipelineIcon } from '../../src/assets/svg/pipeline-grey.svg';
|
||||
import { ReactComponent as PoliciesIcon } from '../../src/assets/svg/policies.svg';
|
||||
import { ReactComponent as SlackIcon } from '../../src/assets/svg/slack.svg';
|
||||
import { ReactComponent as TableIcon } from '../../src/assets/svg/table-grey.svg';
|
||||
import { ReactComponent as TeamsIcon } from '../../src/assets/svg/teams-grey.svg';
|
||||
import { ReactComponent as TopicIcon } from '../../src/assets/svg/topic-grey.svg';
|
||||
import { ReactComponent as UsersIcon } from '../../src/assets/svg/user.svg';
|
||||
import { ReactComponent as WebhookIcon } from '../../src/assets/svg/webhook-grey.svg';
|
||||
|
||||
export const GLOBAL_SETTINGS_MENU = [
|
||||
{
|
||||
category: 'Members',
|
||||
isProtected: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Teams',
|
||||
isProtected: false,
|
||||
icon: <TeamsIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
isProtected: true,
|
||||
icon: <UsersIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Admins',
|
||||
isProtected: true,
|
||||
icon: <UsersIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Access',
|
||||
isProtected: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Roles',
|
||||
isProtected: true,
|
||||
icon: <RolesIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Policies',
|
||||
isProtected: true,
|
||||
icon: <PoliciesIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Services',
|
||||
isProtected: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Databases',
|
||||
isProtected: false,
|
||||
icon: <TableIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Messaging',
|
||||
isProtected: false,
|
||||
icon: <TopicIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Dashboards',
|
||||
isProtected: false,
|
||||
icon: <DashboardIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Pipelines',
|
||||
isProtected: false,
|
||||
icon: <PipelineIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'ML Models',
|
||||
isProtected: false,
|
||||
icon: <MlModelIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Custom Attributes',
|
||||
isProtected: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Tables',
|
||||
isProtected: true,
|
||||
icon: <TableIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Topics',
|
||||
isProtected: true,
|
||||
icon: <TopicIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Dashboards',
|
||||
isProtected: true,
|
||||
icon: <DashboardIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Pipelines',
|
||||
isProtected: true,
|
||||
icon: <PipelineIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'ML Models',
|
||||
isProtected: true,
|
||||
icon: <MlModelIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Integrations',
|
||||
isProtected: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Webhook',
|
||||
isProtected: true,
|
||||
icon: <WebhookIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Slack',
|
||||
isProtected: true,
|
||||
icon: <SlackIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'MS Teams',
|
||||
isProtected: true,
|
||||
icon: <MSTeams className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Bots',
|
||||
isProtected: true,
|
||||
icon: <BotIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
|
||||
export const customAttributesPath = {
|
||||
tables: 'table',
|
||||
@ -187,3 +46,18 @@ export enum GlobalSettingOptions {
|
||||
TABLES = 'tables',
|
||||
MSTEAMS = 'msteams',
|
||||
}
|
||||
|
||||
export const GLOBAL_SETTING_PERMISSION_RESOURCES = [
|
||||
ResourceEntity.TEAM,
|
||||
ResourceEntity.USER,
|
||||
ResourceEntity.ROLE,
|
||||
ResourceEntity.POLICY,
|
||||
ResourceEntity.DATABASE_SERVICE,
|
||||
ResourceEntity.MESSAGING_SERVICE,
|
||||
ResourceEntity.DASHBOARD_SERVICE,
|
||||
ResourceEntity.PIPELINE_SERVICE,
|
||||
ResourceEntity.ML_MODEL_SERVICE,
|
||||
ResourceEntity.TYPE,
|
||||
ResourceEntity.WEBHOOK,
|
||||
ResourceEntity.BOT,
|
||||
];
|
||||
|
@ -32,20 +32,25 @@ import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import GithubStarButton from '../../components/GithubStarButton/GithubStarButton';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import MyData from '../../components/MyData/MyData.component';
|
||||
import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { useWebSocketConnector } from '../../components/web-scoket/web-scoket.provider';
|
||||
import { SOCKET_EVENTS } from '../../constants/constants';
|
||||
import { AssetsType } from '../../enums/entity.enum';
|
||||
import { FeedFilter } from '../../enums/mydata.enum';
|
||||
import { Post, Thread, ThreadType } from '../../generated/entity/feed/thread';
|
||||
import { Operation as RuleOperation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { EntitiesCount } from '../../generated/entity/utils/entitiesCount';
|
||||
import { Paging } from '../../generated/type/paging';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
|
||||
const MyDataPage = () => {
|
||||
const location = useLocation();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const { isAuthDisabled } = useAuth(location.pathname);
|
||||
const [error, setError] = useState<string>('');
|
||||
const [entityCounts, setEntityCounts] = useState<EntitiesCount>(
|
||||
@ -72,6 +77,13 @@ const MyDataPage = () => {
|
||||
[AppState.userDetails, AppState.nonSecureUserDetails]
|
||||
);
|
||||
|
||||
const viewUserPermission = useMemo(() => {
|
||||
return (
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(RuleOperation.ViewAll, ResourceEntity.USER, permissions)
|
||||
);
|
||||
}, [permissions]);
|
||||
|
||||
const fetchEntityCount = () => {
|
||||
getAllEntityCount()
|
||||
.then((res) => {
|
||||
@ -258,7 +270,7 @@ const MyDataPage = () => {
|
||||
!isEmpty(AppState.userDetails)) &&
|
||||
(isNil(ownedData) || isNil(followedData))
|
||||
) {
|
||||
fetchMyData();
|
||||
viewUserPermission && fetchMyData();
|
||||
}
|
||||
}, [AppState.userDetails, AppState.users, isAuthDisabled]);
|
||||
|
||||
|
@ -19,13 +19,7 @@ import {
|
||||
SearchDataFunctionType,
|
||||
SearchResponse,
|
||||
} from 'Models';
|
||||
import React, {
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import AppState from '../../AppState';
|
||||
import { searchData } from '../../axiosAPIs/miscAPI';
|
||||
@ -33,6 +27,7 @@ import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import Explore from '../../components/Explore/Explore.component';
|
||||
import {
|
||||
ExploreSearchData,
|
||||
TabCounts,
|
||||
UrlParams,
|
||||
} from '../../components/Explore/explore.interface';
|
||||
import { getExplorePathWithSearch, PAGE_SIZE } from '../../constants/constants';
|
||||
@ -40,11 +35,13 @@ import {
|
||||
emptyValue,
|
||||
getCurrentIndex,
|
||||
getCurrentTab,
|
||||
getEntityTypeByIndex,
|
||||
getInitialFilter,
|
||||
getQueryParam,
|
||||
getSearchFilter,
|
||||
INITIAL_FROM,
|
||||
INITIAL_SORT_ORDER,
|
||||
INITIAL_TAB_COUNTS,
|
||||
tabsInfo,
|
||||
ZERO_SIZE,
|
||||
} from '../../constants/explore.constants';
|
||||
@ -68,46 +65,21 @@ const ExplorePage: FunctionComponent = () => {
|
||||
const [error, setError] = useState<string>('');
|
||||
const { searchQuery, tab } = useParams<UrlParams>();
|
||||
const [searchText, setSearchText] = useState<string>(searchQuery || '');
|
||||
const [tableCount, setTableCount] = useState<number>(0);
|
||||
const [topicCount, setTopicCount] = useState<number>(0);
|
||||
const [dashboardCount, setDashboardCount] = useState<number>(0);
|
||||
const [pipelineCount, setPipelineCount] = useState<number>(0);
|
||||
const [dbtModelCount, setDbtModelCount] = useState<number>(0);
|
||||
const [mlModelCount, setMlModelCount] = useState<number>(0);
|
||||
const [tabCounts, setTabCounts] = useState<TabCounts>(INITIAL_TAB_COUNTS);
|
||||
const [searchResult, setSearchResult] = useState<ExploreSearchData>();
|
||||
const [showDeleted, setShowDeleted] = useState(false);
|
||||
const [initialSortField] = useState<string>(
|
||||
tabsInfo[getCurrentTab(tab) - 1].sortField
|
||||
);
|
||||
|
||||
const handleTabCounts = (value: { [key: string]: number }) => {
|
||||
setTabCounts((prev) => ({ ...prev, ...value }));
|
||||
};
|
||||
|
||||
const handleSearchText = (text: string) => {
|
||||
setSearchText(text);
|
||||
};
|
||||
|
||||
const handleTableCount = (count: number) => {
|
||||
setTableCount(count);
|
||||
};
|
||||
|
||||
const handleTopicCount = (count: number) => {
|
||||
setTopicCount(count);
|
||||
};
|
||||
|
||||
const handleDashboardCount = (count: number) => {
|
||||
setDashboardCount(count);
|
||||
};
|
||||
|
||||
const handlePipelineCount = (count: number) => {
|
||||
setPipelineCount(count);
|
||||
};
|
||||
|
||||
const handleDbtModelCount = (count: number) => {
|
||||
setDbtModelCount(count);
|
||||
};
|
||||
|
||||
const handleMlModelCount = (count: number) => {
|
||||
setMlModelCount(count);
|
||||
};
|
||||
|
||||
const handlePathChange = (path: string) => {
|
||||
AppState.updateExplorePageTab(path);
|
||||
};
|
||||
@ -127,86 +99,42 @@ const ExplorePage: FunctionComponent = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const fetchCounts = () => {
|
||||
const entities = [
|
||||
SearchIndex.TABLE,
|
||||
SearchIndex.TOPIC,
|
||||
SearchIndex.DASHBOARD,
|
||||
SearchIndex.PIPELINE,
|
||||
SearchIndex.MLMODEL,
|
||||
];
|
||||
|
||||
const entityCounts = entities.map((entity) =>
|
||||
searchData(
|
||||
const fetchEntityCount = async (indexType: SearchIndex) => {
|
||||
const entityType = getEntityTypeByIndex(indexType);
|
||||
try {
|
||||
const { data } = await searchData(
|
||||
searchText,
|
||||
0,
|
||||
0,
|
||||
getFilterString(initialFilter),
|
||||
emptyValue,
|
||||
emptyValue,
|
||||
entity,
|
||||
indexType,
|
||||
showDeleted,
|
||||
true
|
||||
)
|
||||
);
|
||||
);
|
||||
const count = getTotalEntityCountByType(
|
||||
data.aggregations?.['sterms#EntityType']?.buckets as Bucket[]
|
||||
);
|
||||
|
||||
Promise.allSettled(entityCounts)
|
||||
.then(
|
||||
([
|
||||
table,
|
||||
topic,
|
||||
dashboard,
|
||||
pipeline,
|
||||
mlmodel,
|
||||
]: PromiseSettledResult<SearchResponse>[]) => {
|
||||
setTableCount(
|
||||
table.status === 'fulfilled'
|
||||
? getTotalEntityCountByType(
|
||||
table.value.data.aggregations?.['sterms#EntityType']
|
||||
?.buckets as Bucket[]
|
||||
)
|
||||
: 0
|
||||
);
|
||||
setTopicCount(
|
||||
topic.status === 'fulfilled'
|
||||
? getTotalEntityCountByType(
|
||||
topic.value.data.aggregations?.['sterms#EntityType']
|
||||
?.buckets as Bucket[]
|
||||
)
|
||||
: 0
|
||||
);
|
||||
setDashboardCount(
|
||||
dashboard.status === 'fulfilled'
|
||||
? getTotalEntityCountByType(
|
||||
dashboard.value.data.aggregations?.['sterms#EntityType']
|
||||
?.buckets as Bucket[]
|
||||
)
|
||||
: 0
|
||||
);
|
||||
setPipelineCount(
|
||||
pipeline.status === 'fulfilled'
|
||||
? getTotalEntityCountByType(
|
||||
pipeline.value.data.aggregations?.['sterms#EntityType']
|
||||
?.buckets as Bucket[]
|
||||
)
|
||||
: 0
|
||||
);
|
||||
setMlModelCount(
|
||||
mlmodel.status === 'fulfilled'
|
||||
? getTotalEntityCountByType(
|
||||
mlmodel.value.data.aggregations?.['sterms#EntityType']
|
||||
?.buckets as Bucket[]
|
||||
)
|
||||
: 0
|
||||
);
|
||||
}
|
||||
)
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['fetch-entity-count-error']
|
||||
);
|
||||
});
|
||||
setTabCounts((prev) => ({ ...prev, [entityType]: count }));
|
||||
} catch (_error) {
|
||||
showErrorToast(
|
||||
jsonData['api-error-messages']['fetch-entity-count-error']
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCounts = () => {
|
||||
fetchEntityCount(SearchIndex.TABLE);
|
||||
|
||||
fetchEntityCount(SearchIndex.TOPIC);
|
||||
|
||||
fetchEntityCount(SearchIndex.DASHBOARD);
|
||||
|
||||
fetchEntityCount(SearchIndex.PIPELINE);
|
||||
|
||||
fetchEntityCount(SearchIndex.MLMODEL);
|
||||
};
|
||||
|
||||
const fetchData = (value: SearchDataFunctionType[]) => {
|
||||
@ -262,6 +190,7 @@ const ExplorePage: FunctionComponent = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setSearchResult(undefined);
|
||||
|
||||
fetchData([
|
||||
{
|
||||
queryString: searchText,
|
||||
@ -303,42 +232,28 @@ const ExplorePage: FunctionComponent = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageContainerV1>
|
||||
<Explore
|
||||
error={error}
|
||||
fetchCount={fetchCounts}
|
||||
fetchData={fetchData}
|
||||
handleFilterChange={handleFilterChange}
|
||||
handlePathChange={handlePathChange}
|
||||
handleSearchText={handleSearchText}
|
||||
initialFilter={initialFilter}
|
||||
isFilterSelected={!isEmpty(searchFilter) || !isEmpty(initialFilter)}
|
||||
searchFilter={searchFilter}
|
||||
searchQuery={searchQuery}
|
||||
searchResult={searchResult}
|
||||
searchText={searchText}
|
||||
showDeleted={showDeleted}
|
||||
sortValue={initialSortField}
|
||||
tab={tab}
|
||||
tabCounts={{
|
||||
table: tableCount,
|
||||
topic: topicCount,
|
||||
dashboard: dashboardCount,
|
||||
pipeline: pipelineCount,
|
||||
dbtModel: dbtModelCount,
|
||||
mlModel: mlModelCount,
|
||||
}}
|
||||
updateDashboardCount={handleDashboardCount}
|
||||
updateDbtModelCount={handleDbtModelCount}
|
||||
updateMlModelCount={handleMlModelCount}
|
||||
updatePipelineCount={handlePipelineCount}
|
||||
updateTableCount={handleTableCount}
|
||||
updateTopicCount={handleTopicCount}
|
||||
onShowDeleted={(checked) => setShowDeleted(checked)}
|
||||
/>
|
||||
</PageContainerV1>
|
||||
</Fragment>
|
||||
<PageContainerV1>
|
||||
<Explore
|
||||
error={error}
|
||||
fetchCount={fetchCounts}
|
||||
fetchData={fetchData}
|
||||
handleFilterChange={handleFilterChange}
|
||||
handlePathChange={handlePathChange}
|
||||
handleSearchText={handleSearchText}
|
||||
handleTabCounts={handleTabCounts}
|
||||
initialFilter={initialFilter}
|
||||
isFilterSelected={!isEmpty(searchFilter) || !isEmpty(initialFilter)}
|
||||
searchFilter={searchFilter}
|
||||
searchQuery={searchQuery}
|
||||
searchResult={searchResult}
|
||||
searchText={searchText}
|
||||
showDeleted={showDeleted}
|
||||
sortValue={initialSortField}
|
||||
tab={tab}
|
||||
tabCounts={tabCounts}
|
||||
onShowDeleted={(checked) => setShowDeleted(checked)}
|
||||
/>
|
||||
</PageContainerV1>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,6 @@ import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import appState from '../../AppState';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { getLoggedInUserPermissions } from '../../axiosAPIs/miscAPI';
|
||||
import { createUser } from '../../axiosAPIs/userAPI';
|
||||
import { Button } from '../../components/buttons/Button/Button';
|
||||
import PageContainer from '../../components/containers/PageContainer';
|
||||
@ -30,7 +29,6 @@ import { getNameFromEmail } from '../../utils/AuthProvider.util';
|
||||
import { getImages } from '../../utils/CommonUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { fetchAllUsers } from '../../utils/UserDataUtils';
|
||||
|
||||
const cookieStorage = new CookieStorage();
|
||||
|
||||
@ -46,31 +44,12 @@ const Signup = () => {
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const getUserPermissions = () => {
|
||||
getLoggedInUserPermissions()
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
appState.updateUserPermissions(res.data);
|
||||
} else {
|
||||
throw jsonData['api-error-messages']['unexpected-server-response'];
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['fetch-user-permission-error']
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const createNewUser = (details: User | CreateUser) => {
|
||||
setLoading(true);
|
||||
createUser(details as CreateUser)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
appState.updateUserDetails(res);
|
||||
fetchAllUsers();
|
||||
getUserPermissions();
|
||||
cookieStorage.removeItem(REDIRECT_PATHNAME);
|
||||
setIsSigningIn(false);
|
||||
history.push(ROUTES.HOME);
|
||||
|
@ -48,7 +48,7 @@ const exploreCount = {
|
||||
dashboard: 0,
|
||||
pipeline: 0,
|
||||
dbtModel: 0,
|
||||
mlModel: 0,
|
||||
mlmodel: 0,
|
||||
};
|
||||
|
||||
const TourPage = () => {
|
||||
@ -164,6 +164,7 @@ const TourPage = () => {
|
||||
handleFilterChange={handleFilterChange}
|
||||
handlePathChange={handleCountChange}
|
||||
handleSearchText={() => setExploreSearchResult(exploreSearchData)}
|
||||
handleTabCounts={handleCountChange}
|
||||
searchQuery=""
|
||||
searchResult={exploreSearchResult as unknown as ExploreSearchData}
|
||||
searchText=""
|
||||
@ -171,12 +172,6 @@ const TourPage = () => {
|
||||
sortValue=""
|
||||
tab=""
|
||||
tabCounts={explorePageCounts}
|
||||
updateDashboardCount={handleCountChange}
|
||||
updateDbtModelCount={handleCountChange}
|
||||
updateMlModelCount={handleCountChange}
|
||||
updatePipelineCount={handleCountChange}
|
||||
updateTableCount={handleCountChange}
|
||||
updateTopicCount={handleCountChange}
|
||||
onShowDeleted={() => {
|
||||
return;
|
||||
}}
|
||||
|
@ -15,6 +15,7 @@ import { isEmpty, isNil, isUndefined, startCase } from 'lodash';
|
||||
import { Bucket, LeafNodes, LineagePos } from 'Models';
|
||||
import React from 'react';
|
||||
import { EntityData } from '../components/common/PopOverCard/EntityPopOverCard';
|
||||
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import TableProfilerGraph from '../components/TableProfiler/TableProfilerGraph.component';
|
||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
||||
import {
|
||||
@ -23,6 +24,7 @@ import {
|
||||
getTeamAndUserDetailsPath,
|
||||
} from '../constants/constants';
|
||||
import { AssetsType, EntityType, FqnPart } from '../enums/entity.enum';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import { ServiceCategory } from '../enums/service.enum';
|
||||
import { PrimaryTableDataTypes } from '../enums/table.enum';
|
||||
import { Dashboard } from '../generated/entity/data/dashboard';
|
||||
@ -443,3 +445,29 @@ export const filterEntityAssets = (data: EntityReference[]) => {
|
||||
|
||||
return data.filter((d) => includedEntity.includes(d.type as AssetsType));
|
||||
};
|
||||
|
||||
export const getResourceEntityFromEntityType = (entityType: string) => {
|
||||
switch (entityType) {
|
||||
case EntityType.TABLE:
|
||||
case SearchIndex.TABLE:
|
||||
return ResourceEntity.TABLE;
|
||||
|
||||
case EntityType.TOPIC:
|
||||
case SearchIndex.TOPIC:
|
||||
return ResourceEntity.TOPIC;
|
||||
|
||||
case EntityType.DASHBOARD:
|
||||
case SearchIndex.DASHBOARD:
|
||||
return ResourceEntity.DASHBOARD;
|
||||
|
||||
case EntityType.PIPELINE:
|
||||
case SearchIndex.PIPELINE:
|
||||
return ResourceEntity.PIPELINE;
|
||||
|
||||
case EntityType.MLMODEL:
|
||||
case SearchIndex.MLMODEL:
|
||||
return ResourceEntity.ML_MODEL;
|
||||
}
|
||||
|
||||
return ResourceEntity.ALL;
|
||||
};
|
||||
|
@ -13,9 +13,231 @@
|
||||
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { camelCase } from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ReactComponent as BotIcon } from '../../src/assets/svg/bot-profile.svg';
|
||||
import { ReactComponent as DashboardIcon } from '../../src/assets/svg/dashboard-grey.svg';
|
||||
import { ReactComponent as RolesIcon } from '../../src/assets/svg/icon-role-grey.svg';
|
||||
import { ReactComponent as MlModelIcon } from '../../src/assets/svg/mlmodal.svg';
|
||||
import { ReactComponent as PipelineIcon } from '../../src/assets/svg/pipeline-grey.svg';
|
||||
import { ReactComponent as PoliciesIcon } from '../../src/assets/svg/policies.svg';
|
||||
import { ReactComponent as SlackIcon } from '../../src/assets/svg/slack.svg';
|
||||
import { ReactComponent as TableIcon } from '../../src/assets/svg/table-grey.svg';
|
||||
import { ReactComponent as TeamsIcon } from '../../src/assets/svg/teams-grey.svg';
|
||||
import { ReactComponent as TopicIcon } from '../../src/assets/svg/topic-grey.svg';
|
||||
import { ReactComponent as UsersIcon } from '../../src/assets/svg/user.svg';
|
||||
import { ReactComponent as WebhookIcon } from '../../src/assets/svg/webhook-grey.svg';
|
||||
import {
|
||||
ResourceEntity,
|
||||
UIPermission,
|
||||
} from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { Operation } from '../generated/entity/policies/accessControl/rule';
|
||||
import { checkPermission } from '../utils/PermissionsUtils';
|
||||
|
||||
export const getGlobalSettingMenus = (
|
||||
export interface MenuListItem {
|
||||
label: string;
|
||||
isProtected: boolean;
|
||||
icon: ReactNode;
|
||||
}
|
||||
export interface MenuList {
|
||||
category: string;
|
||||
items: MenuListItem[];
|
||||
}
|
||||
|
||||
export const getGlobalSettingsMenuWithPermission = (
|
||||
permissions: UIPermission
|
||||
) => {
|
||||
return [
|
||||
{
|
||||
category: 'Members',
|
||||
items: [
|
||||
{
|
||||
label: 'Teams',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TEAM,
|
||||
permissions
|
||||
),
|
||||
icon: <TeamsIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.USER,
|
||||
permissions
|
||||
),
|
||||
icon: <UsersIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Admins',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.USER,
|
||||
permissions
|
||||
),
|
||||
icon: <UsersIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Access',
|
||||
items: [
|
||||
{
|
||||
label: 'Roles',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
),
|
||||
icon: <RolesIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Policies',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
),
|
||||
icon: <PoliciesIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Services',
|
||||
items: [
|
||||
{
|
||||
label: 'Databases',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.DATABASE_SERVICE,
|
||||
permissions
|
||||
),
|
||||
icon: <TableIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Messaging',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.MESSAGING_SERVICE,
|
||||
permissions
|
||||
),
|
||||
icon: <TopicIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Dashboards',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.DASHBOARD_SERVICE,
|
||||
permissions
|
||||
),
|
||||
icon: <DashboardIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Pipelines',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.PIPELINE_SERVICE,
|
||||
permissions
|
||||
),
|
||||
icon: <PipelineIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'ML Models',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ML_MODEL_SERVICE,
|
||||
permissions
|
||||
),
|
||||
icon: <MlModelIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Custom Attributes',
|
||||
items: [
|
||||
{
|
||||
label: 'Tables',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
),
|
||||
icon: <TableIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Topics',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
),
|
||||
icon: <TopicIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Dashboards',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
),
|
||||
icon: <DashboardIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Pipelines',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
),
|
||||
icon: <PipelineIcon className="side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'ML Models',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
),
|
||||
icon: <MlModelIcon className="side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Integrations',
|
||||
items: [
|
||||
{
|
||||
label: 'Webhook',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
),
|
||||
icon: <WebhookIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Slack',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
),
|
||||
icon: <SlackIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: 'Bots',
|
||||
isProtected: checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
),
|
||||
icon: <BotIcon className="tw-w-4 side-panel-icons" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const getGlobalSettingMenuItem = (
|
||||
label: string,
|
||||
key: string,
|
||||
category?: string,
|
||||
@ -25,8 +247,7 @@ export const getGlobalSettingMenus = (
|
||||
isProtected: boolean;
|
||||
icon: React.ReactNode;
|
||||
}[],
|
||||
type?: string,
|
||||
hasAccess?: boolean
|
||||
type?: string
|
||||
): {
|
||||
key: string;
|
||||
icon: React.ReactNode;
|
||||
@ -34,16 +255,18 @@ export const getGlobalSettingMenus = (
|
||||
label: string;
|
||||
type: string | undefined;
|
||||
} => {
|
||||
const subItems = children
|
||||
? children
|
||||
.filter((menu) => menu.isProtected)
|
||||
.map(({ label, icon }) => {
|
||||
return getGlobalSettingMenuItem(label, camelCase(label), key, icon);
|
||||
})
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
key: `${category}.${key}`,
|
||||
icon,
|
||||
children: children
|
||||
? children
|
||||
.filter((menu) => (hasAccess ? menu : !menu.isProtected))
|
||||
.map(({ label, icon }) => {
|
||||
return getGlobalSettingMenus(label, camelCase(label), key, icon);
|
||||
})
|
||||
: undefined,
|
||||
children: subItems,
|
||||
label,
|
||||
type,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user