= ({
mlModelDetail as CustomPropertyProps['entityDetails']
}
entityType={EntityType.MLMODEL}
- handleExtentionUpdate={onExtensionUpdate}
+ handleExtensionUpdate={onExtensionUpdate}
/>
)}
{
const history = useHistory();
+ const { tab } = useParams<{ tab: PIPELINE_DETAILS_TABS }>();
+ const { t } = useTranslation();
+ const {
+ tier,
+ deleted,
+ owner,
+ serviceType,
+ description,
+ version,
+ pipelineStatus,
+ tags,
+ } = useMemo(() => {
+ return {
+ deleted: pipelineDetails.deleted,
+ owner: pipelineDetails.owner,
+ serviceType: pipelineDetails.serviceType,
+ description: pipelineDetails.description,
+ version: pipelineDetails.version,
+ pipelineStatus: pipelineDetails.pipelineStatus,
+ tier: getTierTags(pipelineDetails.tags ?? []),
+ tags: getTagsWithoutTier(pipelineDetails.tags ?? []),
+ };
+ }, [pipelineDetails]);
+
+ // local state variables
+ const [editTaskTags, setEditTaskTags] = useState<{
+ task: Task;
+ index: number;
+ }>();
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
const [isFollowing, setIsFollowing] = useState(false);
@@ -117,6 +163,25 @@ const PipelineDetails = ({
task: Task;
index: number;
}>();
+ const [lineageLoading, setLineageLoading] = useState(false);
+ const [entityLineage, setEntityLineage] = useState
(
+ {} as EntityLineage
+ );
+ const [entityThreadLoading, setEntityThreadLoading] = useState(false);
+ const [entityThreads, setEntityThreads] = useState([]);
+ const [entityThreadPaging, setEntityThreadPaging] = useState({
+ total: 0,
+ } as Paging);
+
+ const [feedCount, setFeedCount] = useState(0);
+ const [entityFieldThreadCount, setEntityFieldThreadCount] = useState<
+ EntityFieldThreadCount[]
+ >([]);
+ const [entityFieldTaskCount, setEntityFieldTaskCount] = useState<
+ EntityFieldThreadCount[]
+ >([]);
+
+ const [tagList, setTagList] = useState();
const [threadLink, setThreadLink] = useState('');
@@ -134,8 +199,20 @@ const PipelineDetails = ({
DEFAULT_ENTITY_PERMISSION
);
+ // local state ends
+
+ const USERId = getCurrentUserId();
const { getEntityPermission } = usePermissionProvider();
+ const tasksInternal = useMemo(
+ () => tasks.map((t) => ({ ...t, tags: t.tags ?? [] })),
+ [tasks]
+ );
+
+ const onEntityFieldSelect = (value: string) => {
+ setSelectedField(value);
+ };
+
const fetchResourcePermission = useCallback(async () => {
try {
const entityPermission = await getEntityPermission(
@@ -156,9 +233,6 @@ const PipelineDetails = ({
}
}, [pipelineDetails.id]);
- const onEntityFieldSelect = (value: string) => {
- setSelectedField(value);
- };
const closeRequestModal = () => {
setSelectedField('');
};
@@ -169,63 +243,11 @@ const PipelineDetails = ({
);
setFollowersCount(followers?.length);
};
- const tabs = [
- {
- name: 'Details',
- icon: {
- alt: 'schema',
- name: 'icon-schema',
- title: 'Details',
- selectedName: 'icon-schemacolor',
- },
- isProtected: false,
- position: 1,
- },
- {
- name: 'Activity Feeds & Tasks',
- icon: {
- alt: 'activity_feed',
- name: 'activity_feed',
- title: 'Activity Feed',
- selectedName: 'activity-feed-color',
- },
- isProtected: false,
- position: 2,
- count: feedCount,
- },
- {
- name: 'Executions',
- icon: {
- alt: 'executions',
- name: 'executions',
- title: 'Executions',
- selectedName: 'activity-feed-color',
- },
- isProtected: false,
- position: 3,
- },
- {
- name: 'Lineage',
- icon: {
- alt: 'lineage',
- name: 'icon-lineage',
- title: 'Lineage',
- selectedName: 'icon-lineagecolor',
- },
- isProtected: false,
- position: 4,
- },
- {
- name: 'Custom Properties',
- isProtected: false,
- position: 5,
- },
- ];
const extraInfo: Array = [
{
key: 'Owner',
- value: getOwnerValue(owner),
+ value: owner && getOwnerValue(owner),
placeholderText: getEntityPlaceHolder(
getEntityName(owner),
owner?.deleted
@@ -378,7 +400,41 @@ const PipelineDetails = ({
};
const getLoader = () => {
- return isentityThreadLoading ? : null;
+ return entityThreadLoading ? : null;
+ };
+
+ const getFeedData = (
+ after?: string,
+ feedFilter?: FeedFilter,
+ threadType?: ThreadType
+ ) => {
+ setEntityThreadLoading(true);
+ getAllFeeds(
+ getEntityFeedLink(EntityType.PIPELINE, pipelineFQN),
+ after,
+ threadType,
+ feedFilter,
+ undefined,
+ USERId
+ )
+ .then((res) => {
+ const { data, paging: pagingObj } = res;
+ if (data) {
+ setEntityThreadPaging(pagingObj);
+ setEntityThreads((prevData) => [...prevData, ...data]);
+ } else {
+ showErrorToast(
+ jsonData['api-error-messages']['fetch-entity-feed-error']
+ );
+ }
+ })
+ .catch((err: AxiosError) => {
+ showErrorToast(
+ err,
+ jsonData['api-error-messages']['fetch-entity-feed-error']
+ );
+ })
+ .finally(() => setEntityThreadLoading(false));
};
const fetchMoreThread = (
@@ -387,7 +443,7 @@ const PipelineDetails = ({
isLoading: boolean
) => {
if (isElementInView && pagingObj?.after && !isLoading) {
- fetchFeedHandler(pagingObj.after);
+ getFeedData(pagingObj.after);
}
};
@@ -396,16 +452,313 @@ const PipelineDetails = ({
}, [followers]);
useEffect(() => {
- fetchMoreThread(isInView as boolean, paging, isentityThreadLoading);
- }, [paging, isentityThreadLoading, isInView]);
+ fetchMoreThread(
+ isInView as boolean,
+ entityThreadPaging,
+ entityThreadLoading
+ );
+ }, [entityThreadPaging, entityThreadLoading, isInView]);
const handleFeedFilterChange = useCallback(
(feedFilter, threadType) => {
- fetchFeedHandler(paging.after, feedFilter, threadType);
+ getFeedData(entityThreadPaging.after, feedFilter, threadType);
},
- [paging]
+ [entityThreadPaging]
);
+ const handleEditTaskTag = (task: Task, index: number): void => {
+ setEditTaskTags({ task: { ...task, tags: [] }, index });
+ };
+
+ const handleTableTagSelection = (selectedTags?: Array) => {
+ if (selectedTags && editTask) {
+ const prevTags = editTask.task.tags?.filter((tag) =>
+ selectedTags.some((selectedTag) => selectedTag.tagFQN === tag.tagFQN)
+ );
+
+ const newTags = selectedTags
+ .filter(
+ (selectedTag) =>
+ !editTask.task.tags?.some(
+ (tag) => tag.tagFQN === selectedTag.tagFQN
+ )
+ )
+ .map((tag) => ({
+ labelType: 'Manual',
+ state: 'Confirmed',
+ source: tag.source,
+ tagFQN: tag.tagFQN,
+ }));
+
+ const updatedTasks: Task[] = [...(pipelineDetails.tasks || [])];
+
+ const updatedTask = {
+ ...editTask.task,
+ tags: [...(prevTags as TagLabel[]), ...newTags],
+ } as Task;
+
+ updatedTasks[editTask.index] = updatedTask;
+
+ const updatedPipeline = { ...pipelineDetails, tasks: updatedTasks };
+ const jsonPatch = compare(pipelineDetails, updatedPipeline);
+
+ taskUpdateHandler(jsonPatch);
+ }
+ setEditTaskTags(undefined);
+ };
+
+ useMemo(() => {
+ fetchTagsAndGlossaryTerms().then((response) => {
+ setTagList(response);
+ });
+ }, [setTagList]);
+
+ const renderTags = useCallback(
+ (text, record, index) => (
+ handleEditTaskTag(record, index)}>
+ {deleted ? (
+
+
+
+ ) : (
+
{
+ handleTableTagSelection();
+ }}
+ onSelectionChange={(tags) => {
+ handleTableTagSelection(tags);
+ }}
+ />
+ )}
+
+ ),
+ [
+ tagList,
+ editTaskTags,
+ pipelinePermissions.EditAll,
+ pipelinePermissions.EditTags,
+ deleted,
+ ]
+ );
+
+ const taskColumns: ColumnsType = useMemo(
+ () => [
+ {
+ key: 'name',
+ dataIndex: 'name',
+ title: t('label.name'),
+ render: (name, record) => (
+
+ {name}
+
+
+ ),
+ },
+ {
+ key: 'type',
+ dataIndex: 'taskType',
+ width: 180,
+ title: t('label.type'),
+ },
+ {
+ key: 'startDate',
+ dataIndex: 'startDate',
+ width: 180,
+ title: t('label.start-date'),
+ render: (startDate: string) =>
+ getDateTimeByTimeStamp(new Date(startDate).valueOf()),
+ },
+ {
+ key: 'description',
+ dataIndex: 'description',
+ width: 350,
+ title: t('label.description'),
+ render: (text, record, index) => (
+
+
+ {text ? (
+
+ ) : (
+ No description
+ )}
+
+ {!deleted && (
+
+
+
+ )}
+
+ ),
+ },
+ {
+ key: 'tags',
+ dataIndex: 'tags',
+ title: t('label.tags'),
+ width: 350,
+ render: renderTags,
+ },
+ ],
+ [pipelinePermissions, editTask, editTaskTags, tagList, deleted]
+ );
+
+ const getLineageData = () => {
+ setLineageLoading(true);
+ getLineageByFQN(pipelineFQN, EntityType.PIPELINE)
+ .then((res) => {
+ if (res) {
+ setEntityLineage(res);
+ } else {
+ throw jsonData['api-error-messages']['unexpected-server-response'];
+ }
+ })
+ .catch((err: AxiosError) => {
+ showErrorToast(
+ err,
+ jsonData['api-error-messages']['fetch-lineage-error']
+ );
+ })
+ .finally(() => {
+ setLineageLoading(false);
+ });
+ };
+
+ useEffect(() => {
+ switch (tab) {
+ case PIPELINE_DETAILS_TABS.EntityLineage:
+ !deleted && isEmpty(entityLineage) && getLineageData();
+
+ break;
+ case PIPELINE_DETAILS_TABS.ActivityFeedsAndTasks:
+ getFeedData();
+
+ break;
+ default:
+ break;
+ }
+ }, [tab]);
+
+ const handleTabChange = (tabValue: string) => {
+ if (tabValue !== tab) {
+ history.push({
+ pathname: getPipelineDetailsPath(pipelineFQN, tabValue),
+ });
+ }
+ };
+
+ const getEntityFeedCount = () => {
+ getFeedCounts(
+ EntityType.PIPELINE,
+ pipelineFQN,
+ setEntityFieldThreadCount,
+ setEntityFieldTaskCount,
+ setFeedCount
+ );
+ };
+
+ const postFeedHandler = (value: string, id: string) => {
+ const currentUser = AppState.userDetails?.name ?? AppState.users[0]?.name;
+
+ const data = {
+ message: value,
+ from: currentUser,
+ } as Post;
+ postFeedById(id, data)
+ .then((res) => {
+ if (res) {
+ const { id, posts } = res;
+ setEntityThreads((pre) => {
+ return pre.map((thread) => {
+ if (thread.id === id) {
+ return { ...res, posts: posts?.slice(-3) };
+ } else {
+ return thread;
+ }
+ });
+ });
+ getEntityFeedCount();
+ } else {
+ throw jsonData['api-error-messages']['unexpected-server-response'];
+ }
+ })
+ .catch((err: AxiosError) => {
+ showErrorToast(err, jsonData['api-error-messages']['add-feed-error']);
+ });
+ };
+
+ const createThread = (data: CreateThread) => {
+ postThread(data)
+ .then((res) => {
+ if (res) {
+ setEntityThreads((pre) => [...pre, res]);
+ getEntityFeedCount();
+ } else {
+ showErrorToast(
+ jsonData['api-error-messages']['unexpected-server-response']
+ );
+ }
+ })
+ .catch((err: AxiosError) => {
+ showErrorToast(
+ err,
+ jsonData['api-error-messages']['create-conversation-error']
+ );
+ });
+ };
+
+ const deletePostHandler = (
+ threadId: string,
+ postId: string,
+ isThread: boolean
+ ) => {
+ deletePost(threadId, postId, isThread, setEntityThreads);
+ };
+
+ const updateThreadHandler = (
+ threadId: string,
+ postId: string,
+ isThread: boolean,
+ data: Operation[]
+ ) => {
+ updateThreadData(threadId, postId, isThread, data, setEntityThreads);
+ };
+
+ useEffect(() => {
+ getEntityFeedCount();
+ }, [pipelineFQN, description, pipelineDetails, tasks]);
+
return (
@@ -443,7 +796,7 @@ const PipelineDetails = ({
? onTierRemove
: undefined
}
- tags={pipelineTags}
+ tags={tags}
tagsHandler={onTagUpdate}
tier={tier}
titleLinks={slashedPipelineName}
@@ -457,139 +810,187 @@ const PipelineDetails = ({
? onTierUpdate
: undefined
}
- version={version}
+ version={version + ''}
versionHandler={versionHandler}
onThreadLinkSelect={onThreadLinkSelect}
/>
-
-
-
-
- {activeTab === 1 && (
- <>
-
-
-
- {!isEmpty(tasks) ? (
+
+
+ {t('label.tasks')}
+
+ }>
+
+
+
+
+
+
+
+ {!isEmpty(tasks) ? (
+
+
+
- ) : (
-
- No task data is available
-
- )}
-
- >
- )}
- {activeTab === 2 && (
+
+
+
+ ) : (
-
-
-
+ className="tw-mt-4 tw-ml-4 tw-flex tw-justify-center tw-font-medium tw-items-center tw-border tw-border-main tw-rounded-md tw-p-8"
+ data-testid="no-tasks-data">
+
{t('label.no-task-available')}
)}
- {activeTab === 3 &&
}
- {activeTab === 4 && (
-
-
-
- )}
- {activeTab === 5 && (
-
- )}
-
}>
- {getLoader()}
-
+
+
+
+ {t('label.activity-feed-and-task-plural')}{' '}
+ {getCountBadge(
+ feedCount,
+ '',
+ PIPELINE_DETAILS_TABS.ActivityFeedsAndTasks === tab
+ )}
+
+ }>
+
+
+
+
+
+
}>
+ {getLoader()}
+
+
+
+
+
+
+
+
+ {t('label.executions')}
+
+ }>
+
+
+
+
{t('label.entity-lineage')}
+ }>
+
+
-
-
+
+
+
+ {t('label.custom-properties')}
+
+ }>
+
+
+
+
+
+
+
{editTask && (
)}
+
{threadLink ? (
;
- pipelineTags: Array;
slashedPipelineName: TitleBreadcrumbProps['titleLinks'];
entityLineage: EntityLineage;
tasks: Task[];
- deleted?: boolean;
- isLineageLoading?: boolean;
- entityThread: Thread[];
- isentityThreadLoading: boolean;
- feedCount: number;
- entityFieldThreadCount: EntityFieldThreadCount[];
- entityFieldTaskCount: EntityFieldThreadCount[];
paging: Paging;
- pipelineStatus: Pipeline['pipelineStatus'];
- fetchFeedHandler: (
- after?: string,
- feedFilter?: FeedFilter,
- threadType?: ThreadType
- ) => void;
- createThread: (data: CreateThread) => void;
- setActiveTabHandler: (value: number) => void;
followPipelineHandler: () => void;
unfollowPipelineHandler: () => void;
settingsUpdateHandler: (updatedPipeline: Pipeline) => Promise;
@@ -76,12 +43,5 @@ export interface PipeLineDetailsProp {
addLineageHandler: (edge: Edge) => Promise;
removeLineageHandler: (data: EdgeData) => void;
entityLineageHandler: (lineage: EntityLineage) => void;
- postFeedHandler: (value: string, id: string) => void;
- deletePostHandler: (
- threadId: string,
- postId: string,
- isThread: boolean
- ) => void;
- updateThreadHandler: ThreadUpdatedFunc;
onExtensionUpdate: (updatedPipeline: Pipeline) => Promise;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.test.tsx
index 75b0e4bc04a..97031a0807a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.test.tsx
@@ -11,10 +11,16 @@
* limitations under the License.
*/
-import { findByTestId, findByText, render } from '@testing-library/react';
+import {
+ findByTestId,
+ findByText,
+ fireEvent,
+ render,
+} from '@testing-library/react';
import { LeafNodes, LoadingNodeState } from 'Models';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
+import { act } from 'react-test-renderer';
import { Pipeline } from '../../generated/entity/data/pipeline';
import { EntityLineage } from '../../generated/type/entityLineage';
import { EntityReference } from '../../generated/type/entityReference';
@@ -189,7 +195,6 @@ jest.mock('../common/CustomPropertyTable/CustomPropertyTable', () => ({
jest.mock('../../utils/CommonUtils', () => ({
addToRecentViewed: jest.fn(),
- getCountBadge: jest.fn(),
getCurrentUserId: jest.fn().mockReturnValue('CurrentUserId'),
getPartialNameFromFQN: jest.fn().mockReturnValue('PartialNameFromFQN'),
getUserTeams: () => mockUserTeam,
@@ -197,11 +202,13 @@ jest.mock('../../utils/CommonUtils', () => ({
getEntityPlaceHolder: jest.fn().mockReturnValue('value'),
getEntityName: jest.fn().mockReturnValue('entityName'),
getOwnerValue: jest.fn().mockReturnValue('Owner'),
+ getFeedCounts: jest.fn(),
+ getCountBadge: jest.fn().mockImplementation((count) => {count}
),
}));
-jest.mock('', () => ({
- ExecutionsTab: jest.fn().mockImplementation(() => Executions
),
-}));
+jest.mock('../Execution/Execution.component', () => {
+ return jest.fn().mockImplementation(() => Executions
);
+});
describe('Test PipelineDetails component', () => {
it('Checks if the PipelineDetails component has all the proper components rendered', async () => {
@@ -213,20 +220,28 @@ describe('Test PipelineDetails component', () => {
);
const EntityPageInfo = await findByText(container, /EntityPageInfo/i);
const description = await findByText(container, /Description Component/i);
- const tabs = await findByTestId(container, 'tabs');
- const detailsTab = await findByTestId(tabs, 'Details');
- const activityFeedTab = await findByTestId(tabs, 'Activity Feeds & Tasks');
- const lineageTab = await findByTestId(tabs, 'Lineage');
+ const tasksTab = await findByText(container, 'label.tasks');
+ const activityFeedTab = await findByText(
+ container,
+ 'label.activity-feed-and-task-plural'
+ );
+ const lineageTab = await findByText(container, 'label.entity-lineage');
+ const executionsTab = await findByText(container, 'label.executions');
+ const customPropertiesTab = await findByText(
+ container,
+ 'label.custom-properties'
+ );
expect(EntityPageInfo).toBeInTheDocument();
expect(description).toBeInTheDocument();
- expect(tabs).toBeInTheDocument();
- expect(detailsTab).toBeInTheDocument();
+ expect(tasksTab).toBeInTheDocument();
expect(activityFeedTab).toBeInTheDocument();
expect(lineageTab).toBeInTheDocument();
+ expect(executionsTab).toBeInTheDocument();
+ expect(customPropertiesTab).toBeInTheDocument();
});
- it('Check if active tab is details', async () => {
+ it('Check if active tab is tasks', async () => {
const { container } = render(
,
{
@@ -251,23 +266,39 @@ describe('Test PipelineDetails component', () => {
it('Check if active tab is activity feed', async () => {
const { container } = render(
- ,
+ ,
{
wrapper: MemoryRouter,
}
);
+
+ const activityFeedTab = await findByText(
+ container,
+ 'label.activity-feed-and-task-plural'
+ );
+
+ await act(async () => {
+ fireEvent.click(activityFeedTab);
+ });
+
const activityFeedList = await findByText(container, /ActivityFeedList/i);
expect(activityFeedList).toBeInTheDocument();
});
- it('should render execution tab if active tab is 3', async () => {
+ it('should render execution tab', async () => {
const { container } = render(
- ,
+ ,
{
wrapper: MemoryRouter,
}
);
+
+ const activityFeedTab = await findByText(container, 'label.executions');
+
+ await act(async () => {
+ fireEvent.click(activityFeedTab);
+ });
const executions = await findByText(container, 'Executions');
expect(executions).toBeInTheDocument();
@@ -275,11 +306,16 @@ describe('Test PipelineDetails component', () => {
it('Check if active tab is lineage', async () => {
const { container } = render(
- ,
+ ,
{
wrapper: MemoryRouter,
}
);
+ const activityFeedTab = await findByText(container, 'label.entity-lineage');
+
+ await act(async () => {
+ fireEvent.click(activityFeedTab);
+ });
const lineage = await findByTestId(container, 'lineage');
expect(lineage).toBeInTheDocument();
@@ -287,11 +323,20 @@ describe('Test PipelineDetails component', () => {
it('Check if active tab is custom properties', async () => {
const { container } = render(
- ,
+ ,
{
wrapper: MemoryRouter,
}
);
+
+ const activityFeedTab = await findByText(
+ container,
+ 'label.custom-properties'
+ );
+
+ await act(async () => {
+ fireEvent.click(activityFeedTab);
+ });
const customProperties = await findByText(
container,
'CustomPropertyTable.component'
@@ -302,16 +347,23 @@ describe('Test PipelineDetails component', () => {
it('Should create an observer if IntersectionObserver is available', async () => {
const { container } = render(
- ,
+ ,
{
wrapper: MemoryRouter,
}
);
+ const activityFeedTab = await findByText(
+ container,
+ 'label.activity-feed-and-task-plural'
+ );
+
+ await act(async () => {
+ fireEvent.click(activityFeedTab);
+ });
+
const obServerElement = await findByTestId(container, 'observer-element');
expect(obServerElement).toBeInTheDocument();
-
- expect(mockObserve).toHaveBeenCalled();
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx
index 7e241bd45cf..91c7d6d7e00 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx
@@ -609,7 +609,7 @@ const TopicDetails: React.FC = ({
topicDetails as CustomPropertyProps['entityDetails']
}
entityType={EntityType.TOPIC}
- handleExtentionUpdate={onExtensionUpdate}
+ handleExtensionUpdate={onExtensionUpdate}
/>
)}
Promise
;
+ handleExtensionUpdate: (updatedTable: EntityDetails) => Promise;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
index b8bf85b58aa..3e953a249b3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.test.tsx
@@ -63,11 +63,11 @@ jest.mock('../../../axiosAPIs/metadataTypeAPI', () => ({
}));
const mockTableDetails = {} as Table & Topic & Dashboard & Pipeline & Mlmodel;
-const handleExtentionUpdate = jest.fn();
+const handleExtensionUpdate = jest.fn();
const mockProp = {
entityDetails: mockTableDetails,
- handleExtentionUpdate,
+ handleExtensionUpdate,
entityType: EntityType.TABLE,
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
index d7a33369d3d..d368f10f8a3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.tsx
@@ -25,7 +25,7 @@ import { PropertyValue } from './PropertyValue';
export const CustomPropertyTable: FC = ({
entityDetails,
- handleExtentionUpdate,
+ handleExtensionUpdate,
entityType,
}) => {
const [entityTypeDetail, setEntityTypeDetail] = useState({} as Type);
@@ -41,7 +41,7 @@ export const CustomPropertyTable: FC = ({
const onExtensionUpdate = async (
updatedExtension: CustomPropertyProps['entityDetails']['extension']
) => {
- await handleExtentionUpdate({
+ await handleExtensionUpdate({
...entityDetails,
extension: updatedExtension,
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx b/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx
index e6269f9cc35..7085f117fa8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx
@@ -15,7 +15,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import { EntityTags, TagOption } from 'Models';
-import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
+import React, {
+ Fragment,
+ FunctionComponent,
+ useCallback,
+ useEffect,
+ useState,
+} from 'react';
import AsyncSelect from 'react-select/async';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { TagSource } from '../../generated/type/tagLabel';
@@ -87,11 +93,14 @@ const TagsContainer: FunctionComponent = ({
setTags(updatedTags);
};
- const handleSave = (event: React.MouseEvent) => {
- event.preventDefault();
- event.stopPropagation();
- onSelectionChange(tags);
- };
+ const handleSave = useCallback(
+ (event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ onSelectionChange(tags);
+ },
+ [tags]
+ );
const handleCancel = (event: React.MouseEvent) => {
event.preventDefault();
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/pipeline.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/pipeline.constants.ts
new file mode 100644
index 00000000000..ebdd6df8f3d
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/pipeline.constants.ts
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+export enum PIPELINE_DETAILS_TABS {
+ Tasks = 'tasks',
+ ActivityFeedsAndTasks = 'activity-feeds-tasks',
+ Executions = 'executions',
+ EntityLineage = 'entity-lineage',
+ CustomProperties = 'custom-properties',
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
index 06739fb64f6..b21f8e442fd 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
@@ -212,6 +212,8 @@
"select-rule-effect": "Select Rule Effect",
"field-required": "{{field}} is required",
"field-required-plural": "{{field}} are required",
+ "edit-task": "Edit Task",
+ "type-filed-name": "Type {{fieldName}}",
"no-execution-runs-found": "No execution runs found for the pipeline.",
"last-no-of-days": "Last {{day}} Days",
"tier-number": "Tier{{tier}}",
@@ -224,6 +226,12 @@
"pipeline": "Pipeline",
"function": "Function",
"edge-information": "Edge Information",
+ "tasks": "Tasks",
+ "activity-feed-and-task-plural": "Activity Feeds & tasks",
+ "executions": "Executions",
+ "entity-lineage": "Entity Lineage",
+ "custom-properties": "Custom Properties",
+ "dag-view": "DAG view",
"read-more": "read more",
"read-less": "read less",
"no-owner": "No Owner",
@@ -269,6 +277,7 @@
"server": {
"no-followed-entities": "You have not followed anything yet.",
"no-owned-entities": "You have not owned anything yet.",
+ "no-task-available": "No task data is available",
"entity-fetch-error": "Error while fetching {{entity}}",
"feed-post-error": "Error while posting the message!",
"unexpected-error": "An unexpected error occurred.",
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx
index 447e67db4ff..d233818f68c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx
@@ -852,7 +852,7 @@ const DatasetDetailsPage: FunctionComponent = () => {
fetchFeedHandler={handleFeedFetchFromFeedList}
followTableHandler={followTable}
followers={followers}
- handleExtentionUpdate={handleExtentionUpdate}
+ handleExtensionUpdate={handleExtentionUpdate}
isLineageLoading={isLineageLoading}
isNodeLoading={isNodeLoading}
isQueriesLoading={isTableQueriesLoading}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/LineagePage/LineagePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/LineagePage/LineagePage.tsx
index f1f219bb2e0..ab1bffed1e2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/LineagePage/LineagePage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/LineagePage/LineagePage.tsx
@@ -42,6 +42,7 @@ import {
getTableTabPath,
getTopicDetailsPath,
} from '../../constants/constants';
+import { PIPELINE_DETAILS_TABS } from '../../constants/pipeline.constants';
import { EntityType, FqnPart } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
import { Dashboard } from '../../generated/entity/data/dashboard';
@@ -202,7 +203,10 @@ const LineagePage = () => {
const pipelineRes = await getPipelineByFqn(entityFQN, '');
updateBreadcrumb(
pipelineRes,
- getPipelineDetailsPath(entityFQN, 'lineage')
+ getPipelineDetailsPath(
+ entityFQN,
+ PIPELINE_DETAILS_TABS.EntityLineage
+ )
);
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
index 527f31a6986..deebc1136be 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
@@ -13,23 +13,11 @@
import { AxiosError } from 'axios';
import { compare, Operation } from 'fast-json-patch';
-import { isEmpty, isUndefined, omitBy } from 'lodash';
+import { isUndefined, omitBy } from 'lodash';
import { observer } from 'mobx-react';
-import {
- EntityFieldThreadCount,
- EntityTags,
- LeafNodes,
- LineagePos,
- LoadingNodeState,
-} from 'Models';
-import React, { useEffect, useState } from 'react';
+import { LeafNodes, LineagePos, LoadingNodeState } from 'Models';
+import React, { useEffect, useMemo, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
-import AppState from '../../AppState';
-import {
- getAllFeeds,
- postFeedById,
- postThread,
-} from '../../axiosAPIs/feedsAPI';
import { getLineageByFQN } from '../../axiosAPIs/lineageAPI';
import { addLineage, deleteLineageEdge } from '../../axiosAPIs/miscAPI';
import {
@@ -50,65 +38,45 @@ import { usePermissionProvider } from '../../components/PermissionProvider/Permi
import { ResourceEntity } from '../../components/PermissionProvider/PermissionProvider.interface';
import PipelineDetails from '../../components/PipelineDetails/PipelineDetails.component';
import {
- getPipelineDetailsPath,
getServiceDetailsPath,
getVersionPath,
} from '../../constants/constants';
import { NO_PERMISSION_TO_VIEW } from '../../constants/HelperTextUtil';
-import { EntityType, TabSpecificField } from '../../enums/entity.enum';
-import { FeedFilter } from '../../enums/mydata.enum';
+import { EntityType } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
-import { CreateThread } from '../../generated/api/feed/createThread';
import { Pipeline, Task } from '../../generated/entity/data/pipeline';
-import { Post, Thread, ThreadType } from '../../generated/entity/feed/thread';
import { Connection } from '../../generated/entity/services/dashboardService';
import { EntityLineage } from '../../generated/type/entityLineage';
import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging';
-import { TagLabel } from '../../generated/type/tagLabel';
import jsonData from '../../jsons/en';
import {
addToRecentViewed,
getCurrentUserId,
getEntityMissingError,
getEntityName,
- getFeedCounts,
} from '../../utils/CommonUtils';
-import { getEntityFeedLink, getEntityLineage } from '../../utils/EntityUtils';
-import { deletePost, updateThreadData } from '../../utils/FeedUtils';
+import { getEntityLineage } from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
-import {
- defaultFields,
- getCurrentPipelineTab,
- pipelineDetailsTabs,
-} from '../../utils/PipelineDetailsUtils';
+import { defaultFields } from '../../utils/PipelineDetailsUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils';
-import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { showErrorToast } from '../../utils/ToastUtils';
const PipelineDetailsPage = () => {
const USERId = getCurrentUserId();
const history = useHistory();
- const { pipelineFQN, tab } = useParams() as Record;
+ const { pipelineFQN } = useParams<{ pipelineFQN: string }>();
const [pipelineDetails, setPipelineDetails] = useState(
{} as Pipeline
);
- const [pipelineId, setPipelineId] = useState('');
+
const [isLoading, setLoading] = useState(true);
- const [isLineageLoading, setIsLineageLoading] = useState(false);
- const [description, setDescription] = useState('');
const [followers, setFollowers] = useState>([]);
- const [owner, setOwner] = useState();
- const [tier, setTier] = useState();
- const [tags, setTags] = useState>([]);
- const [activeTab, setActiveTab] = useState(
- getCurrentPipelineTab(tab)
- );
+
const [tasks, setTasks] = useState([]);
const [pipelineUrl, setPipelineUrl] = useState('');
const [displayName, setDisplayName] = useState('');
- const [serviceType, setServiceType] = useState('');
const [slashedPipelineName, setSlashedPipelineName] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
@@ -122,24 +90,9 @@ const PipelineDetailsPage = () => {
);
const [leafNodes, setLeafNodes] = useState({} as LeafNodes);
- const [currentVersion, setCurrentVersion] = useState();
- const [deleted, setDeleted] = useState(false);
const [isError, setIsError] = useState(false);
- const [entityThread, setEntityThread] = useState([]);
- const [isentityThreadLoading, setIsentityThreadLoading] =
- useState(false);
- const [feedCount, setFeedCount] = useState(0);
- const [entityFieldThreadCount, setEntityFieldThreadCount] = useState<
- EntityFieldThreadCount[]
- >([]);
- const [paging, setPaging] = useState({} as Paging);
-
- const [pipeLineStatus, setPipelineStatus] =
- useState();
- const [entityFieldTaskCount, setEntityFieldTaskCount] = useState<
- EntityFieldThreadCount[]
- >([]);
+ const [paging] = useState({} as Paging);
const [pipelinePermissions, setPipelinePermissions] = useState(
DEFAULT_ENTITY_PERMISSION
@@ -164,30 +117,12 @@ const PipelineDetailsPage = () => {
}
};
- const activeTabHandler = (tabValue: number) => {
- const currentTabIndex = tabValue - 1;
- if (pipelineDetailsTabs[currentTabIndex].path !== tab) {
- setActiveTab(
- getCurrentPipelineTab(pipelineDetailsTabs[currentTabIndex].path)
- );
- history.push({
- pathname: getPipelineDetailsPath(
- pipelineFQN,
- pipelineDetailsTabs[currentTabIndex].path
- ),
- });
- }
- };
-
- const getEntityFeedCount = () => {
- getFeedCounts(
- EntityType.PIPELINE,
- pipelineFQN,
- setEntityFieldThreadCount,
- setEntityFieldTaskCount,
- setFeedCount
- );
- };
+ const { pipelineId, currentVersion } = useMemo(() => {
+ return {
+ pipelineId: pipelineDetails.id,
+ currentVersion: pipelineDetails.version + '',
+ };
+ }, [pipelineDetails]);
const saveUpdatedPipelineData = (updatedData: Pipeline) => {
const jsonPatch = compare(
@@ -198,70 +133,6 @@ const PipelineDetailsPage = () => {
return patchPipelineDetails(pipelineId, jsonPatch);
};
- const getLineageData = () => {
- setIsLineageLoading(true);
- getLineageByFQN(pipelineFQN, EntityType.PIPELINE)
- .then((res) => {
- if (res) {
- setEntityLineage(res);
- } else {
- throw jsonData['api-error-messages']['unexpected-server-response'];
- }
- })
- .catch((err: AxiosError) => {
- showErrorToast(
- err,
- jsonData['api-error-messages']['fetch-lineage-error']
- );
- })
- .finally(() => {
- setIsLineageLoading(false);
- });
- };
-
- const getFeedData = (
- after?: string,
- feedFilter?: FeedFilter,
- threadType?: ThreadType
- ) => {
- setIsentityThreadLoading(true);
- getAllFeeds(
- getEntityFeedLink(EntityType.PIPELINE, pipelineFQN),
- after,
- threadType,
- feedFilter,
- undefined,
- USERId
- )
- .then((res) => {
- const { data, paging: pagingObj } = res;
- if (data) {
- setPaging(pagingObj);
- setEntityThread((prevData) => [...prevData, ...data]);
- } else {
- showErrorToast(
- jsonData['api-error-messages']['fetch-entity-feed-error']
- );
- }
- })
- .catch((err: AxiosError) => {
- showErrorToast(
- err,
- jsonData['api-error-messages']['fetch-entity-feed-error']
- );
- })
- .finally(() => setIsentityThreadLoading(false));
- };
-
- const handleFeedFetchFromFeedList = (
- after?: string,
- filterType?: FeedFilter,
- type?: ThreadType
- ) => {
- !after && setEntityThread([]);
- getFeedData(after, filterType, type);
- };
-
const fetchServiceDetails = (type: string, fqn: string) => {
return new Promise((resolve, reject) => {
getServiceByFQN(type + 's', fqn, ['owner'])
@@ -291,32 +162,16 @@ const PipelineDetailsPage = () => {
if (res) {
const {
id,
- deleted,
- description,
- followers = [],
fullyQualifiedName,
service,
serviceType,
- tags = [],
- owner,
displayName,
name,
tasks,
pipelineUrl = '',
- pipelineStatus,
- version,
} = res;
setDisplayName(displayName || name);
setPipelineDetails(res);
- setCurrentVersion(version + '');
- setPipelineId(id);
- setDescription(description ?? '');
- setFollowers(followers);
- setOwner(owner);
- setTier(getTierTags(tags));
- setTags(getTagsWithoutTier(tags));
- setServiceType(serviceType ?? '');
- setDeleted(Boolean(deleted));
const serviceName = service.name ?? '';
setSlashedPipelineName([
{
@@ -345,11 +200,6 @@ const PipelineDetailsPage = () => {
id: id,
});
- setPipelineUrl(pipelineUrl);
- setTasks(tasks || []);
-
- setPipelineStatus(pipelineStatus as Pipeline['pipelineStatus']);
-
fetchServiceDetails(service.type, service.name ?? '')
.then((hostPort: string) => {
setPipelineUrl(hostPort + pipelineUrl);
@@ -384,30 +234,6 @@ const PipelineDetailsPage = () => {
});
};
- const fetchTabSpecificData = (tabField = '') => {
- switch (tabField) {
- case TabSpecificField.LINEAGE: {
- if (!deleted) {
- if (isEmpty(entityLineage)) {
- getLineageData();
- }
-
- break;
- }
-
- break;
- }
- case TabSpecificField.ACTIVITY_FEED: {
- getFeedData();
-
- break;
- }
-
- default:
- break;
- }
- };
-
const followPipeline = () => {
addFollower(pipelineId, USERId)
.then((res) => {
@@ -452,11 +278,7 @@ const PipelineDetailsPage = () => {
try {
const response = await saveUpdatedPipelineData(updatedPipeline);
if (response) {
- const { description = '', version } = response;
- setCurrentVersion(version + '');
setPipelineDetails(response);
- setDescription(description);
- getEntityFeedCount();
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
}
@@ -471,10 +293,7 @@ const PipelineDetailsPage = () => {
.then((res) => {
if (res) {
setPipelineDetails({ ...res, tags: res.tags ?? [] });
- setCurrentVersion(res.version + '');
- setOwner(res.owner);
- setTier(getTierTags(res.tags ?? []));
- getEntityFeedCount();
+
resolve();
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
@@ -495,10 +314,6 @@ const PipelineDetailsPage = () => {
.then((res) => {
if (res) {
setPipelineDetails(res);
- setTier(getTierTags(res.tags ?? []));
- setCurrentVersion(res.version + '');
- setTags(getTagsWithoutTier(res.tags ?? []));
- getEntityFeedCount();
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
}
@@ -517,7 +332,6 @@ const PipelineDetailsPage = () => {
if (response) {
setTasks(response.tasks || []);
- getEntityFeedCount();
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
}
@@ -605,83 +419,12 @@ const PipelineDetailsPage = () => {
});
};
- const postFeedHandler = (value: string, id: string) => {
- const currentUser = AppState.userDetails?.name ?? AppState.users[0]?.name;
-
- const data = {
- message: value,
- from: currentUser,
- } as Post;
- postFeedById(id, data)
- .then((res) => {
- if (res) {
- const { id, posts } = res;
- setEntityThread((pre) => {
- return pre.map((thread) => {
- if (thread.id === id) {
- return { ...res, posts: posts?.slice(-3) };
- } else {
- return thread;
- }
- });
- });
- getEntityFeedCount();
- } else {
- throw jsonData['api-error-messages']['unexpected-server-response'];
- }
- })
- .catch((err: AxiosError) => {
- showErrorToast(err, jsonData['api-error-messages']['add-feed-error']);
- });
- };
-
- const createThread = (data: CreateThread) => {
- postThread(data)
- .then((res) => {
- if (res) {
- setEntityThread((pre) => [...pre, res]);
- getEntityFeedCount();
- } else {
- showErrorToast(
- jsonData['api-error-messages']['unexpected-server-response']
- );
- }
- })
- .catch((err: AxiosError) => {
- showErrorToast(
- err,
- jsonData['api-error-messages']['create-conversation-error']
- );
- });
- };
-
- const deletePostHandler = (
- threadId: string,
- postId: string,
- isThread: boolean
- ) => {
- deletePost(threadId, postId, isThread, setEntityThread);
- };
-
- const updateThreadHandler = (
- threadId: string,
- postId: string,
- isThread: boolean,
- data: Operation[]
- ) => {
- updateThreadData(threadId, postId, isThread, data, setEntityThread);
- };
-
- const handleExtentionUpdate = async (updatedPipeline: Pipeline) => {
+ const handleExtensionUpdate = async (updatedPipeline: Pipeline) => {
try {
const data = await saveUpdatedPipelineData(updatedPipeline);
if (data) {
- const { version, owner: ownerValue, tags = [] } = data;
- setCurrentVersion(version + '');
setPipelineDetails(data);
- setOwner(ownerValue);
- setTier(getTierTags(tags));
} else {
throw jsonData['api-error-messages']['update-entity-error'];
}
@@ -693,15 +436,10 @@ const PipelineDetailsPage = () => {
}
};
- useEffect(() => {
- fetchTabSpecificData(pipelineDetailsTabs[activeTab - 1].field);
- }, [activeTab]);
-
useEffect(() => {
if (pipelinePermissions.ViewAll || pipelinePermissions.ViewBasic) {
fetchPipelineDetail(pipelineFQN);
setEntityLineage({} as EntityLineage);
- getEntityFeedCount();
}
}, [pipelinePermissions, pipelineFQN]);
@@ -709,13 +447,6 @@ const PipelineDetailsPage = () => {
fetchResourcePermission(pipelineFQN);
}, [pipelineFQN]);
- useEffect(() => {
- if (pipelineDetailsTabs[activeTab - 1].path !== tab) {
- setActiveTab(getCurrentPipelineTab(tab));
- }
- setEntityThread([]);
- }, [tab]);
-
return (
<>
{isLoading ? (
@@ -728,50 +459,29 @@ const PipelineDetailsPage = () => {
<>
{pipelinePermissions.ViewAll || pipelinePermissions.ViewBasic ? (
) : (
{NO_PERMISSION_TO_VIEW}
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 be2d24a5132..eecadc38a10 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
@@ -190,7 +190,7 @@ const TourPage = () => {
fetchFeedHandler={handleCountChange}
followTableHandler={handleCountChange}
followers={mockDatasetData.followers}
- handleExtentionUpdate={handleCountChange}
+ handleExtensionUpdate={handleCountChange}
isNodeLoading={{
id: undefined,
state: false,
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/app.less b/openmetadata-ui/src/main/resources/ui/src/styles/app.less
index 248cf375e62..b0b9f731bb2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/app.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/app.less
@@ -142,6 +142,9 @@
.h-9 {
height: 36px;
}
+.h-100 {
+ height: 400px;
+}
.h-min-100 {
min-height: 100vh;
}
@@ -156,6 +159,10 @@
height: 100%;
}
+.h-min-full {
+ min-height: 100%;
+}
+
// Text alignment
.text-left {
text-align: left;
@@ -280,6 +287,9 @@
text-decoration: none;
}
+.bg-white {
+ background: @white;
+}
.font-semibold {
font-weight: 600;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less
index 9dc55902384..0c7a93fb292 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less
@@ -14,3 +14,9 @@
.ant-tabs-tab.ant-tabs-tab-active {
font-weight: 500;
}
+
+.ant-tabs.ant-tabs-top.h-full {
+ .ant-tabs-content.ant-tabs-content-top {
+ height: 100%;
+ }
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less
index 062e0304954..049b990b66a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less
@@ -194,6 +194,9 @@
.mr-8 {
margin-right: 2rem;
}
+.mb-2 {
+ margin-bottom: 0.5rem;
+}
.mb-4 {
margin-bottom: 1rem;
}
@@ -231,9 +234,6 @@
.m-52 {
margin: 13rem;
}
-.mr-2 {
- margin-right: 8px;
-}
.my-4 {
margin-top: 1rem;
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
index 4868fc528a6..655d1ab5cce 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less
@@ -1,3 +1,16 @@
+/*
+ * 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.
+ */
+
.ant-tree-switcher {
display: flex;
justify-content: center;
@@ -42,3 +55,13 @@
.ant-tree-switcher-icon {
color: black;
}
+
+.execution-node-container {
+ overflow-x: auto;
+ white-space: nowrap;
+ overflow-y: hidden;
+}
+
+.execution-node-container::-webkit-scrollbar {
+ display: none;
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.ts
index 533ca241658..fada284b9b9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TagsUtils.ts
@@ -16,8 +16,10 @@ import { flatten, isEmpty } from 'lodash';
import { Bucket, EntityTags, TableColumn, TagOption } from 'Models';
import { getCategory, getTags } from '../axiosAPIs/tagAPI';
import { TAG_VIEW_CAP } from '../constants/constants';
+import { SettledStatus } from '../enums/axios.enum';
import { TagCategory, TagClass } from '../generated/entity/tags/tagCategory';
import { LabelType, State, TagSource } from '../generated/type/tagLabel';
+import { fetchGlossaryTerms, getGlossaryTermlist } from './GlossaryUtils';
export const getTagCategories = async (fields?: Array | string) => {
try {
@@ -118,3 +120,34 @@ export const getTagsWithLabel = (tags: Array) => {
export const getTagDisplay = (tag: string) => {
return tag.length > TAG_VIEW_CAP ? `${tag.slice(0, TAG_VIEW_CAP)}...` : tag;
};
+
+export const fetchTagsAndGlossaryTerms = async () => {
+ const responses = await Promise.allSettled([
+ getTagCategories(),
+ fetchGlossaryTerms(),
+ ]);
+
+ let tagsAndTerms: TagOption[] = [];
+ if (
+ responses[0].status === SettledStatus.FULFILLED &&
+ responses[0].value.data
+ ) {
+ tagsAndTerms = getTaglist(responses[0].value.data).map((tag) => {
+ return { fqn: tag, source: 'Tag' };
+ });
+ }
+ if (
+ responses[1].status === SettledStatus.FULFILLED &&
+ responses[1].value &&
+ responses[1].value.length > 0
+ ) {
+ const glossaryTerms: TagOption[] = getGlossaryTermlist(
+ responses[1].value
+ ).map((tag) => {
+ return { fqn: tag, source: 'Glossary' };
+ });
+ tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
+ }
+
+ return tagsAndTerms;
+};