From a66ef4800ce9daf2dd4d509b2cecdf23377157ba Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Tue, 22 Mar 2022 02:25:32 +0530 Subject: [PATCH] Fix issue-600: UI: doesn't allow table level tags to be added (#3547) --- .../DatasetDetails.component.tsx | 23 ++++ .../DatasetDetails.interface.ts | 1 + .../DatasetDetails/DatasetDetails.test.tsx | 1 + .../DatasetVersion.component.tsx | 44 +++++- .../common/entityPageInfo/EntityPageInfo.tsx | 13 +- .../src/main/resources/ui/src/jsons/en.ts | 8 ++ .../DatasetDetailsPage.component.tsx | 125 +++++++++++------- .../ui/src/pages/database-details/index.tsx | 11 -- .../pages/tour-page/TourPage.component.tsx | 1 + 9 files changed, 157 insertions(+), 70 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx index b8b93161ed0..f529bbccd39 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx @@ -94,6 +94,7 @@ const DatasetDetails: React.FC = ({ isNodeLoading, dataModel, deleted, + tagUpdateHandler, addLineageHandler, removeLineageHandler, entityLineageHandler, @@ -423,6 +424,18 @@ const DatasetDetails: React.FC = ({ } }; + /** + * Formulates updated tags and updates table entity data for API call + * @param selectedTags + */ + const onTagUpdate = (selectedTags?: Array) => { + if (selectedTags) { + const updatedTags = [...(tier ? [tier] : []), ...selectedTags]; + const updatedTable = { ...tableDetails, tags: updatedTags }; + tagUpdateHandler(updatedTable); + } + }; + const followTable = () => { if (isFollowing) { setFollowersCount((preValu) => preValu - 1); @@ -487,18 +500,28 @@ const DatasetDetails: React.FC = ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.interface.ts index 6bab123d7d1..75df4c8f154 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.interface.ts @@ -96,6 +96,7 @@ export interface DatasetDetailsProps { settingsUpdateHandler: (updatedTable: Table) => Promise; columnsUpdateHandler: (updatedTable: Table) => void; descriptionUpdateHandler: (updatedTable: Table) => void; + tagUpdateHandler: (updatedTable: Table) => void; versionHandler: () => void; handleSelectedColumn: (value: string | undefined) => void; loadNodeHandler: (node: EntityReference, pos: LineagePos) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx index cc2d37aa386..699f8f89be9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx @@ -112,6 +112,7 @@ const DatasetDetailsProps = { handleTestModeChange: jest.fn(), qualityTestFormHandler: jest.fn(), deletePostHandler: jest.fn(), + tagUpdateHandler: jest.fn(), }; jest.mock('../ManageTab/ManageTab.component', () => { return jest.fn().mockReturnValue(

ManageTab

); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetVersion/DatasetVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetVersion/DatasetVersion.component.tsx index 58a0bb5cc3b..93524770f9a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetVersion/DatasetVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetVersion/DatasetVersion.component.tsx @@ -30,7 +30,6 @@ import { getTagsDiff, } from '../../utils/EntityVersionUtils'; import { getOwnerFromId } from '../../utils/TableUtils'; -import { getTableTags } from '../../utils/TagsUtils'; import Description from '../common/description/Description'; import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo'; import TabsPane from '../common/TabsPane/TabsPane'; @@ -310,6 +309,47 @@ const DatasetVersion: React.FC = ({ } }; + /** + * Calculates tags for selected version. + * @returns current version's tag. + */ + const getTags = () => { + const tagsDiff = getDiffByFieldName('tags', changeDescription, true); + const oldTags: Array = JSON.parse( + tagsDiff?.added?.oldValue ?? + tagsDiff?.deleted?.oldValue ?? + tagsDiff?.updated?.oldValue ?? + '[]' + ); + const newTags: Array = JSON.parse( + tagsDiff?.added?.newValue ?? + tagsDiff?.deleted?.newValue ?? + tagsDiff?.updated?.newValue ?? + '[]' + ); + const flag: { [x: string]: boolean } = {}; + const uniqueTags: Array = + []; + + [ + ...(getTagsDiff(oldTags, newTags) ?? []), + ...(currentVersionData.tags ?? []), + ].forEach((elem: TagLabel & { added: boolean; removed: boolean }) => { + if (!flag[elem.tagFQN as string]) { + flag[elem.tagFQN as string] = true; + uniqueTags.push(elem); + } + }); + + return [ + ...uniqueTags.map((t) => + t.tagFQN.startsWith('Tier') + ? { ...t, tagFQN: t.tagFQN.split('.')[1] } + : t + ), + ]; + }; + const tabs = [ { name: 'Schema', @@ -346,7 +386,7 @@ const DatasetVersion: React.FC = ({ entityName={currentVersionData.name ?? ''} extraInfo={getExtraInfo()} followersList={[]} - tags={getTableTags(currentVersionData.columns || [])} + tags={getTags()} tier={tier} titleLinks={slashedTableName} version={version} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx index bde90e2c46c..05b942bc649 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx @@ -11,6 +11,8 @@ * limitations under the License. */ +import { faExclamationCircle, faStar } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; import { isEmpty, isUndefined } from 'lodash'; import { @@ -27,6 +29,10 @@ import { User } from '../../../generated/entity/teams/user'; import { LabelType, State, TagLabel } from '../../../generated/type/tagLabel'; import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils'; import { getEntityFeedLink, getInfoElements } from '../../../utils/EntityUtils'; +import { + fetchGlossaryTerms, + getGlossaryTermlist, +} from '../../../utils/GlossaryUtils'; import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import { getFollowerDetail } from '../../../utils/TableUtils'; import { getTagCategories, getTaglist } from '../../../utils/TagsUtils'; @@ -38,12 +44,6 @@ import PopOver from '../popover/PopOver'; import TitleBreadcrumb from '../title-breadcrumb/title-breadcrumb.component'; import { TitleBreadcrumbProps } from '../title-breadcrumb/title-breadcrumb.interface'; import FollowersModal from './FollowersModal'; -import { - fetchGlossaryTerms, - getGlossaryTermlist, -} from '../../../utils/GlossaryUtils'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faExclamationCircle, faStar } from '@fortawesome/free-solid-svg-icons'; type Props = { titleLinks: TitleBreadcrumbProps['titleLinks']; @@ -435,6 +435,7 @@ const EntityPageInfo = ({ setIsEditable(true); }}> { const history = useHistory(); @@ -178,6 +178,13 @@ const DatasetDetailsPage: FunctionComponent = () => { const [tableTestCase, setTableTestCase] = useState([]); const [selectedColumn, setSelectedColumn] = useState(); + const handleShowErrorToast = (errMessage: string) => { + showToast({ + variant: 'error', + body: errMessage, + }); + }; + const handleTestModeChange = (mode: DatasetTestModeType) => { setTestMode(mode); }; @@ -226,10 +233,10 @@ const DatasetDetailsPage: FunctionComponent = () => { setEntityLineage(res.data); }) .catch((err: AxiosError) => { - showToast({ - variant: 'error', - body: err.message ?? 'Error while fetching lineage data.', - }); + const msg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-lineage-error']; + handleShowErrorToast(msg); }) .finally(() => { setIsLineageLoading(false); @@ -243,11 +250,11 @@ const DatasetDetailsPage: FunctionComponent = () => { const { data } = res.data; setEntityThread(data); }) - .catch(() => { - showToast({ - variant: 'error', - body: 'Error while fetching entity feeds', - }); + .catch((err: AxiosError) => { + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-entity-feed-error']; + handleShowErrorToast(errMsg); }) .finally(() => setIsentityThreadLoading(false)); }; @@ -325,7 +332,7 @@ const DatasetDetailsPage: FunctionComponent = () => { setColumns(columns || []); setSampleData(sampleData); setTableProfile(tableProfile || []); - setTableTags(getTableTags(columns || [])); + setTableTags(getTagsWithoutTier(tags)); setUsageSummary(usageSummary); setJoins(joins); }) @@ -333,11 +340,10 @@ const DatasetDetailsPage: FunctionComponent = () => { if (err.response?.status === 404) { setIsError(true); } else { - const errMsg = err.message || 'Error while fetching table details.'; - showToast({ - variant: 'error', - body: errMsg, - }); + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-table-details-error']; + handleShowErrorToast(errMsg); } }) .finally(() => { @@ -357,12 +363,12 @@ const DatasetDetailsPage: FunctionComponent = () => { const { sampleData } = res.data; setSampleData(sampleData); }) - .catch(() => - showToast({ - variant: 'error', - body: 'Error while getting sample data.', - }) - ) + .catch((err: AxiosError) => { + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-sample-data-error']; + handleShowErrorToast(errMsg); + }) .finally(() => setIsSampleDataLoading(false)); break; @@ -391,12 +397,12 @@ const DatasetDetailsPage: FunctionComponent = () => { const { tableQueries } = res.data; setTableQueries(tableQueries); }) - .catch(() => - showToast({ - variant: 'error', - body: 'Error while getting table queries', - }) - ) + .catch((err: AxiosError) => { + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-table-queries-error']; + handleShowErrorToast(errMsg); + }) .finally(() => setIsTableQueriesLoading(false)); break; @@ -429,11 +435,11 @@ const DatasetDetailsPage: FunctionComponent = () => { setFeedCount(res.data.totalCount); setEntityFieldThreadCount(res.data.counts); }) - .catch(() => { - showToast({ - variant: 'error', - body: 'Error while fetching entity feed count', - }); + .catch((err: AxiosError) => { + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['fetch-entity-feed-count-error']; + handleShowErrorToast(errMsg); }); }; @@ -457,12 +463,9 @@ const DatasetDetailsPage: FunctionComponent = () => { }) .catch((err: AxiosError) => { const msg = - err.response?.data.message || - `Error while updating entity description.`; - showToast({ - variant: 'error', - body: msg, - }); + err.response?.data?.message || + jsonData['api-error-messages']['update-description-error']; + handleShowErrorToast(msg); }); }; @@ -473,16 +476,35 @@ const DatasetDetailsPage: FunctionComponent = () => { setCurrentVersion(version); setTableDetails(res.data); setColumns(columns); - setTableTags(getTableTags(columns || [])); getEntityFeedCount(); }) .catch((err: AxiosError) => { const msg = - err.response?.data.message || `Error while updating entity.`; - showToast({ - variant: 'error', - body: msg, - }); + err.response?.data?.message || + jsonData['api-error-messages']['update-entity-error']; + handleShowErrorToast(msg); + }); + }; + + const onTagUpdate = (updatedTable: Table) => { + saveUpdatedTableData(updatedTable) + .then((res: AxiosResponse) => { + if (res.data) { + setTier(getTierTags(res.data.tags)); + setCurrentVersion(res.data.version); + setTableTags(getTagsWithoutTier(res.data.tags)); + getEntityFeedCount(); + } else { + handleShowErrorToast( + jsonData['api-error-messages']['update-tags-error'] + ); + } + }) + .catch((err: AxiosError) => { + const errMsg = + err.response?.data?.message || + jsonData['api-error-messages']['update-tags-error']; + handleShowErrorToast(errMsg); }); }; @@ -500,16 +522,16 @@ const DatasetDetailsPage: FunctionComponent = () => { }) .catch((err: AxiosError) => { const msg = - err.response?.data.message || `Error while updating entity.`; + err.response?.data?.message || + jsonData['api-error-messages']['update-entity-error']; + handleShowErrorToast(msg); reject(); - showToast({ - variant: 'error', - body: msg, - }); }); }); }; + // TODO: move all the error from below code to en.ts and use handleShowErrorToast to show error. + const followTable = () => { addFollower(tableId, USERId).then((res: AxiosResponse) => { const { newValue } = res.data.changeDescription.fieldsAdded[0]; @@ -895,6 +917,7 @@ const DatasetDetailsPage: FunctionComponent = () => { tableTags={tableTags} tableTestCase={tableTestCase} tableType={tableType} + tagUpdateHandler={onTagUpdate} testMode={testMode} tier={tier as TagLabel} unfollowTableHandler={unfollowTable} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx index 8e9d258e65b..adf5eab9468 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx @@ -88,7 +88,6 @@ import { } from '../../utils/FeedUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { getOwnerFromId, getUsagePercentile } from '../../utils/TableUtils'; -import { getTableTags } from '../../utils/TagsUtils'; const DatabaseDetails: FunctionComponent = () => { // User Id for getting followers @@ -673,16 +672,6 @@ const DatabaseDetails: FunctionComponent = () => { type="label" /> ))} - {getTableTags(table.columns).map( - (tag, tagIdx) => ( - - ) - )} )) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx index a224431e7f9..86ad35bc1e9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx @@ -224,6 +224,7 @@ const TourPage = () => { tableTags={mockDatasetData.tableTags} tableTestCase={[]} tableType={mockDatasetData.tableType as TableType} + tagUpdateHandler={handleCountChange} testMode="table" tier={'' as unknown as TagLabel} unfollowTableHandler={handleCountChange}