diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/policies.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/policies.svg new file mode 100644 index 00000000000..b330a1a14db --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/policies.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts new file mode 100644 index 00000000000..aa9d9b78227 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts @@ -0,0 +1,88 @@ +/* + * 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 { AxiosResponse } from 'axios'; +import { CreateRole } from '../generated/api/teams/createRole'; +import { Policy } from '../generated/entity/policies/policy'; +import { Role } from '../generated/entity/teams/role'; +import { Paging } from '../generated/type/paging'; +import APIClient from './index'; + +export const getRoles = async ( + fields: string, + after?: string, + before?: string, + defaultRoles = false, + limit = 10 +) => { + const params = { + default: defaultRoles, + limit, + fields, + after, + before, + }; + + const response = await APIClient.get<{ data: Role[]; paging: Paging }>( + '/roles', + { params } + ); + + return response.data; +}; + +export const getPolicies = async ( + fields: string, + after?: string, + before?: string, + limit = 10 +) => { + const params = { + limit, + fields, + after, + before, + }; + + const response = await APIClient.get<{ data: Policy[]; paging: Paging }>( + '/policies', + { params } + ); + + return response.data; +}; + +export const getRoleByName = async (name: string, fields: string) => { + const response = await APIClient.get(`/roles/name/${name}`, { + params: { fields }, + }); + + return response.data; +}; + +export const getPolicyByName = async (name: string, fields: string) => { + const response = await APIClient.get(`/policies/name/${name}`, { + params: { fields }, + }); + + return response.data; +}; + +export const addRole = async (data: CreateRole) => { + const dataResponse = await APIClient.post>( + '/roles', + data + ); + + return dataResponse.data; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index a20606ed36e..7f7fa42f588 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -226,6 +226,7 @@ export const ROUTES = { TASK_DETAIL: `/tasks/${PLACEHOLDER_TASK_ID}`, ACTIVITY_PUSH_FEED: '/api/v1/push/feed', + ADD_ROLE: '/settings/access/roles/add-role', }; export const SOCKET_EVENTS = { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts index d03d19bbc7d..040a1cd5c72 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts @@ -22,6 +22,7 @@ export const GLOBAL_SETTINGS_MENU = [ { label: 'Users', isProtected: true, icon: Icons.USERS }, { label: 'Admins', isProtected: true, icon: Icons.USERS }, { label: 'Roles', isProtected: true, icon: Icons.ROLE_GREY }, + { label: 'Policies', isProtected: true, icon: Icons.POLICIES }, ], }, { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetail.less b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetail.less new file mode 100644 index 00000000000..ae85326cc9a --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetail.less @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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. + */ + +.policies-detail { + .roles-list-table { + .ant-table-thead > tr > th { + font-weight: bold; + background: #fff; + } + + .ant-table-row .ant-table-cell:first-child, + .ant-table-thead .ant-table-cell:first-child { + padding-left: 16px; + } + + table { + border: 1px solid #dde3ea; + box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.12); + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx new file mode 100644 index 00000000000..fe7430659a0 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx @@ -0,0 +1,174 @@ +/* + * 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 { Card, Col, Empty, Row, Table, Tabs } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { AxiosError } from 'axios'; +import { isEmpty, uniqueId } from 'lodash'; +import React, { useEffect, useMemo, useState } from 'react'; +import { Link, useParams } from 'react-router-dom'; +import { getPolicyByName } from '../../../axiosAPIs/rolesAPIV1'; +import Description from '../../../components/common/description/Description'; +import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component'; +import Loader from '../../../components/Loader/Loader'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../../constants/globalSettings.constants'; +import { Policy } from '../../../generated/entity/policies/policy'; +import { EntityReference } from '../../../generated/type/entityReference'; +import { getEntityName } from '../../../utils/CommonUtils'; +import { getRoleWithFqnPath, getSettingPath } from '../../../utils/RouterUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import './PoliciesDetail.less'; + +const { TabPane } = Tabs; + +const RolesList = ({ roles }: { roles: EntityReference[] }) => { + const columns: ColumnsType = useMemo(() => { + return [ + { + title: 'Name', + dataIndex: 'name', + width: '200px', + key: 'name', + render: (_, record) => ( + + {getEntityName(record)} + + ), + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + render: (_, record) => ( + + ), + }, + ]; + }, []); + + return ( + + ); +}; + +const PoliciesDetailPage = () => { + const { fqn } = useParams<{ fqn: string }>(); + + const [policy, setPolicy] = useState({} as Policy); + const [isLoading, setLoading] = useState(false); + + const breadcrumb = useMemo( + () => [ + { + name: 'Policies', + url: getSettingPath( + GlobalSettingsMenuCategory.ACCESS, + GlobalSettingOptions.POLICIES + ), + }, + { + name: fqn, + url: '', + }, + ], + [fqn] + ); + + const fetchPolicy = async () => { + setLoading(true); + try { + const data = await getPolicyByName(fqn, 'owner,location,teams,roles'); + setPolicy(data ?? ({} as Policy)); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchPolicy(); + }, [fqn]); + + if (isLoading) { + return ; + } + + return ( +
+ + {isEmpty(policy) ? ( + + ) : ( +
+
+ +
+ + + + + + {isEmpty(policy.teams) ? ( + + ) : ( + + {policy.teams?.map((team) => ( +
+ + + + + ))} + + )} + + + {isEmpty(policy.rules) ? ( + + ) : ( + + {policy.rules.map((rule) => ( + + + + + + ))} + + )} + + + + )} + + ); +}; + +export default PoliciesDetailPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.less b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.less new file mode 100644 index 00000000000..3c46bf82c4e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.less @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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. + */ + +.policies-list-container { + .ant-btn { + border-radius: 4px; + } +} + +.policies-list-table { + .ant-table-thead > tr > th { + font-weight: bold; + background: #fff; + } + + .ant-table-row .ant-table-cell:first-child, + .ant-table-thead .ant-table-cell:first-child { + padding-left: 16px; + } + + table { + border: 1px solid #dde3ea; + box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.12); + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx new file mode 100644 index 00000000000..7ca4d355df0 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx @@ -0,0 +1,100 @@ +/* + * 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 { Space, Table } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { uniqueId } from 'lodash'; +import React, { FC, useMemo } from 'react'; +import { Link } from 'react-router-dom'; +import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import { Policy } from '../../../generated/entity/policies/policy'; +import { getEntityName } from '../../../utils/CommonUtils'; +import { + getPolicyWithFqnPath, + getRoleWithFqnPath, +} from '../../../utils/RouterUtils'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; + +interface PolicyListProps { + policies: Policy[]; +} + +const PoliciesList: FC = ({ policies }) => { + const columns: ColumnsType = useMemo(() => { + return [ + { + title: 'Name', + dataIndex: 'name', + width: '200px', + key: 'name', + render: (_, record) => ( + + {getEntityName(record)} + + ), + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + render: (_, record) => ( + + ), + }, + { + title: 'Roles', + dataIndex: 'roles', + width: '200px', + key: 'roles', + render: (_, record) => { + return record.roles?.length ? ( + + {record.roles.map((role) => ( + + {getEntityName(role)} + + ))} + + ) : ( + '-- ' + ); + }, + }, + { + title: 'Actions', + dataIndex: 'actions', + width: '80px', + key: 'actions', + render: () => { + return ; + }, + }, + ]; + }, []); + + return ( +
+ ); +}; + +export default PoliciesList; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx new file mode 100644 index 00000000000..c998e2b9ad2 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx @@ -0,0 +1,66 @@ +/* + * 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 { Button, Col, Row, Space } from 'antd'; +import { AxiosError } from 'axios'; +import React, { useEffect, useState } from 'react'; +import { getPolicies } from '../../../axiosAPIs/rolesAPIV1'; +import Loader from '../../../components/Loader/Loader'; +import { Policy } from '../../../generated/entity/policies/policy'; +import { Paging } from '../../../generated/type/paging'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import PoliciesList from './PoliciesList'; +import './PoliciesList.less'; + +const PoliciesListPage = () => { + const [policies, setPolicies] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const fetchPolicies = async (paging?: Paging) => { + setIsLoading(true); + try { + const data = await getPolicies( + 'owner,location,roles,teams', + paging?.after, + paging?.before + ); + + setPolicies(data.data || []); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + fetchPolicies(); + }, []); + + return isLoading ? ( + + ) : ( + + + + + + + + + + + ); +}; + +export default PoliciesListPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx new file mode 100644 index 00000000000..2d675f3e9b7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/AddRolePage/AddRolePage.tsx @@ -0,0 +1,167 @@ +/* + * 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 { Button, Card, Col, Form, Input, Row, Select, Space } from 'antd'; +import { AxiosError } from 'axios'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { addRole, getPolicies } from '../../../axiosAPIs/rolesAPIV1'; +import RichTextEditor from '../../../components/common/rich-text-editor/RichTextEditor'; +import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../../constants/globalSettings.constants'; +import { Policy } from '../../../generated/entity/policies/policy'; +import { getRoleWithFqnPath, getSettingPath } from '../../../utils/RouterUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +const { Option } = Select; +const breadcrumb = [ + { + name: 'Roles', + url: getSettingPath( + GlobalSettingsMenuCategory.ACCESS, + GlobalSettingOptions.ROLES + ), + }, + { + name: 'Add New Role', + url: '', + }, +]; + +const AddRolePage = () => { + const history = useHistory(); + const [policies, setPolicies] = useState([]); + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [selectedPolicies, setSelectedPolicies] = useState([]); + + const fetchPolicies = async () => { + try { + const data = await getPolicies('owner,location,roles,teams'); + + setPolicies(data.data || []); + } catch (error) { + showErrorToast(error as AxiosError); + } + }; + + const handleCancel = () => { + history.push( + getSettingPath( + GlobalSettingsMenuCategory.ACCESS, + GlobalSettingOptions.ROLES + ) + ); + }; + + const handleSumbit = async () => { + const data = { + name, + description, + policies: selectedPolicies.map((policy) => ({ + id: policy, + type: 'policy', + })), + }; + + try { + const dataResponse = await addRole(data); + if (dataResponse) { + history.push(getRoleWithFqnPath(dataResponse.fullyQualifiedName || '')); + } + } catch (error) { + showErrorToast(error as AxiosError); + } + }; + + useEffect(() => { + fetchPolicies(); + }, []); + + return ( + + + + + + +
+ + setName(e.target.value)} + /> + + + setDescription(value)} + /> + + + + + + + + + + +
+ + + ); +}; + +export default AddRolePage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetail.less b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetail.less new file mode 100644 index 00000000000..227a3a7e04a --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetail.less @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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. + */ + +.roles-detail { + .policies-list-table { + .ant-table-thead > tr > th { + font-weight: bold; + background: #fff; + } + + .ant-table-row .ant-table-cell:first-child, + .ant-table-thead .ant-table-cell:first-child { + padding-left: 16px; + } + + table { + border: 1px solid #dde3ea; + box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.12); + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx new file mode 100644 index 00000000000..68a6bd88443 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx @@ -0,0 +1,177 @@ +/* + * 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 { Card, Col, Empty, Row, Table, Tabs } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { AxiosError } from 'axios'; +import { isEmpty, uniqueId } from 'lodash'; +import { EntityReference } from 'Models'; +import React, { useEffect, useMemo, useState } from 'react'; +import { Link, useParams } from 'react-router-dom'; +import { getRoleByName } from '../../../axiosAPIs/rolesAPIV1'; +import Description from '../../../components/common/description/Description'; +import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component'; +import Loader from '../../../components/Loader/Loader'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../../constants/globalSettings.constants'; +import { Role } from '../../../generated/entity/teams/role'; +import { getEntityName } from '../../../utils/CommonUtils'; +import { + getPolicyWithFqnPath, + getSettingPath, +} from '../../../utils/RouterUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import './RolesDetail.less'; + +const { TabPane } = Tabs; + +const PoliciesList = ({ policies }: { policies: EntityReference[] }) => { + const columns: ColumnsType = useMemo(() => { + return [ + { + title: 'Name', + dataIndex: 'name', + width: '200px', + key: 'name', + render: (_, record) => ( + + {getEntityName(record)} + + ), + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + render: (_, record) => ( + + ), + }, + ]; + }, []); + + return ( +
+ ); +}; + +const RolesDetailPage = () => { + const { fqn } = useParams<{ fqn: string }>(); + + const [role, setRole] = useState({} as Role); + const [isLoading, setLoading] = useState(false); + + const breadcrumb = useMemo( + () => [ + { + name: 'Roles', + url: getSettingPath( + GlobalSettingsMenuCategory.ACCESS, + GlobalSettingOptions.ROLES + ), + }, + { + name: fqn, + url: '', + }, + ], + [fqn] + ); + + const fetchRole = async () => { + setLoading(true); + try { + const data = await getRoleByName(fqn, 'policies,teams,users'); + setRole(data ?? ({} as Role)); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchRole(); + }, [fqn]); + + if (isLoading) { + return ; + } + + return ( +
+ + {isEmpty(role) ? ( + + ) : ( +
+
+ +
+ + + + + + {isEmpty(role.teams) ? ( + + ) : ( + + {role.teams?.map((team) => ( +
+ + + + + ))} + + )} + + + {isEmpty(role.users) ? ( + + ) : ( + + {role.users?.map((user) => ( + + + + + + ))} + + )} + + + + )} + + ); +}; + +export default RolesDetailPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.less b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.less new file mode 100644 index 00000000000..291ddd8fe15 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.less @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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. + */ + +.roles-list-container { + .ant-btn { + border-radius: 4px; + } +} + +.roles-list-table { + .ant-table-thead > tr > th { + font-weight: bold; + background: #fff; + } + + .ant-table-row .ant-table-cell:first-child, + .ant-table-thead .ant-table-cell:first-child { + padding-left: 16px; + } + + table { + border: 1px solid #dde3ea; + box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.12); + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx new file mode 100644 index 00000000000..72ed753764b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx @@ -0,0 +1,100 @@ +/* + * 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 { Space, Table } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { uniqueId } from 'lodash'; +import React, { FC, useMemo } from 'react'; +import { Link } from 'react-router-dom'; +import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import { Role } from '../../../generated/entity/teams/role'; +import { getEntityName } from '../../../utils/CommonUtils'; +import { + getPolicyWithFqnPath, + getRoleWithFqnPath, +} from '../../../utils/RouterUtils'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; + +interface RolesListProps { + roles: Role[]; +} + +const RolesList: FC = ({ roles }) => { + const columns: ColumnsType = useMemo(() => { + return [ + { + title: 'Name', + dataIndex: 'name', + width: '200px', + key: 'name', + render: (_, record) => ( + + {getEntityName(record)} + + ), + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + render: (_, record) => ( + + ), + }, + { + title: 'Policies', + dataIndex: 'policies', + width: '200px', + key: 'policies', + render: (_, record) => { + return record.policies?.length ? ( + + {record.policies.map((policy) => ( + + {getEntityName(policy)} + + ))} + + ) : ( + '-- ' + ); + }, + }, + { + title: 'Actions', + dataIndex: 'actions', + width: '80px', + key: 'actions', + render: () => { + return ; + }, + }, + ]; + }, []); + + return ( +
+ ); +}; + +export default RolesList; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx new file mode 100644 index 00000000000..e568a86227f --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx @@ -0,0 +1,76 @@ +/* + * 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 { Button, Col, Row, Space } from 'antd'; +import { AxiosError } from 'axios'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { getRoles } from '../../../axiosAPIs/rolesAPIV1'; +import Loader from '../../../components/Loader/Loader'; +import { ROUTES } from '../../../constants/constants'; +import { Role } from '../../../generated/entity/teams/role'; +import { Paging } from '../../../generated/type/paging'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import RolesList from './RolesList'; +import './RolesList.less'; + +const RolesListPage = () => { + const history = useHistory(); + + const [roles, setRoles] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const fetchRoles = async (paging?: Paging) => { + setIsLoading(true); + try { + const data = await getRoles( + 'policies,teams,users', + paging?.after, + paging?.before + ); + + setRoles(data.data || []); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsLoading(false); + } + }; + + const handleAddRole = () => { + history.push(ROUTES.ADD_ROLE); + }; + + useEffect(() => { + fetchRoles(); + }, []); + + return isLoading ? ( + + ) : ( + + + + + + + + + + + ); +}; + +export default RolesListPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx index c280e5c3ebc..1ea89c344ac 100644 --- a/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx @@ -13,6 +13,7 @@ import React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; +import { ROUTES } from '../constants/constants'; import { GlobalSettingOptions, GlobalSettingsMenuCategory, @@ -35,9 +36,28 @@ const CustomPropertiesPageV1 = withSuspenseFallback( () => import('../pages/CustomPropertiesPage/CustomPropertiesPageV1') ) ); -const RolesPageComponent = withSuspenseFallback( - React.lazy(() => import('../pages/RolesPage/RolesPage.component')) +const RolesListPage = withSuspenseFallback( + React.lazy(() => import('../pages/RolesPage/RolesListPage/RolesListPage')) ); +const RolesDetailPage = withSuspenseFallback( + React.lazy(() => import('../pages/RolesPage/RolesDetailPage/RolesDetailPage')) +); + +const AddRolePage = withSuspenseFallback( + React.lazy(() => import('../pages/RolesPage/AddRolePage/AddRolePage')) +); + +const PoliciesDetailPage = withSuspenseFallback( + React.lazy( + () => import('../pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage') + ) +); +const PoliciesListPage = withSuspenseFallback( + React.lazy( + () => import('../pages/PoliciesPage/PoliciesListPage/PoliciesListPage') + ) +); + const UserListPageV1 = withSuspenseFallback( React.lazy(() => import('../pages/UserListPage/UserListPageV1')) ); @@ -70,14 +90,48 @@ const GlobalSettingRouter = () => { true )} /> + {/* Roles route start + * Do not change the order of these route + */} + + + {/* Roles route end + * Do not change the order of these route + */} + + + { return entity?.displayName || entity?.name || ''; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts index 20a0296b908..d3ec99a254d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts @@ -239,3 +239,25 @@ export const getTeamsWithFqnPath = (fqn: string) => { return path; }; + +export const getRoleWithFqnPath = (fqn: string) => { + let path = ROUTES.SETTINGS_WITH_TAB_FQN; + + path = path + .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingsMenuCategory.ACCESS) + .replace(PLACEHOLDER_ROUTE_TAB, GlobalSettingOptions.ROLES) + .replace(PLACEHOLDER_ROUTE_FQN, fqn); + + return path; +}; + +export const getPolicyWithFqnPath = (fqn: string) => { + let path = ROUTES.SETTINGS_WITH_TAB_FQN; + + path = path + .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingsMenuCategory.ACCESS) + .replace(PLACEHOLDER_ROUTE_TAB, GlobalSettingOptions.POLICIES) + .replace(PLACEHOLDER_ROUTE_FQN, fqn); + + return path; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx index cf1e3c0bb86..ab946c48c85 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx @@ -131,6 +131,7 @@ import IconPipelineGrey from '../assets/svg/pipeline-grey.svg'; import IconPipeline from '../assets/svg/pipeline.svg'; import IconPlusPrimery from '../assets/svg/plus-primery.svg'; import IconPlus from '../assets/svg/plus.svg'; +import IconPolicies from '../assets/svg/policies.svg'; import IconProfilerColor from '../assets/svg/profiler-color.svg'; import IconProfiler from '../assets/svg/profiler.svg'; import IconHelpCircle from '../assets/svg/question-circle.svg'; @@ -327,6 +328,7 @@ export const Icons = { TASK_OPEN: 'task-open', FOREGIN_KEY: 'foreign-key', ROLE_GREY: 'role-grey', + POLICIES: 'policies', }; const SVGIcons: FunctionComponent = ({ @@ -957,6 +959,10 @@ const SVGIcons: FunctionComponent = ({ case Icons.FOREGIN_KEY: IconComponent = IconForeignKey; + break; + case Icons.POLICIES: + IconComponent = IconPolicies; + break; default: