mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-07 16:11:30 +00:00
* Change task title in activity feeds * added link to entity and make label same as feed in user profile page for task * fix entity link redirection, code smell * added unit test for tasks * fix unit test and styling issue
This commit is contained in:
parent
357888af87
commit
5fbce50c05
@ -68,6 +68,7 @@ export const ActivityFeedTab = ({
|
||||
owner,
|
||||
columns,
|
||||
entityType,
|
||||
isForFeedTab = true,
|
||||
onUpdateEntityDetails,
|
||||
}: ActivityFeedTabProps) => {
|
||||
const history = useHistory();
|
||||
@ -379,10 +380,10 @@ export const ActivityFeedTab = ({
|
||||
)}
|
||||
<ActivityFeedListV1
|
||||
hidePopover
|
||||
isForFeedTab
|
||||
activeFeedId={selectedThread?.id}
|
||||
emptyPlaceholderText={placeholderText}
|
||||
feedList={threads}
|
||||
isForFeedTab={isForFeedTab}
|
||||
isLoading={false}
|
||||
showThread={false}
|
||||
tab={activeTab}
|
||||
@ -413,11 +414,11 @@ export const ActivityFeedTab = ({
|
||||
/>
|
||||
</div>
|
||||
<FeedPanelBodyV1
|
||||
isForFeedTab
|
||||
isOpenInDrawer
|
||||
showThread
|
||||
feed={selectedThread}
|
||||
hidePopover={false}
|
||||
isForFeedTab={isForFeedTab}
|
||||
/>
|
||||
<ActivityFeedEditor className="m-md" onSave={onSave} />
|
||||
</div>
|
||||
@ -427,6 +428,7 @@ export const ActivityFeedTab = ({
|
||||
<TaskTab
|
||||
columns={columns}
|
||||
entityType={EntityType.TABLE}
|
||||
isForFeedTab={isForFeedTab}
|
||||
owner={owner}
|
||||
taskThread={selectedThread}
|
||||
onAfterClose={handleAfterTaskClose}
|
||||
@ -435,6 +437,7 @@ export const ActivityFeedTab = ({
|
||||
) : (
|
||||
<TaskTab
|
||||
entityType={isUserEntity ? entityTypeTask : entityType}
|
||||
isForFeedTab={isForFeedTab}
|
||||
owner={owner}
|
||||
taskThread={selectedThread}
|
||||
onAfterClose={handleAfterTaskClose}
|
||||
|
@ -25,6 +25,7 @@ export enum ActivityFeedTabs {
|
||||
|
||||
export interface ActivityFeedTabBasicProps {
|
||||
fqn: string;
|
||||
isForFeedTab?: boolean;
|
||||
onFeedUpdate: () => void;
|
||||
onUpdateEntityDetails?: () => void;
|
||||
owner?: EntityReference;
|
||||
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2023 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 { act, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { TASK_FEED, TASK_POST } from '../../../mocks/Task.mock';
|
||||
import TaskFeedCard from './TaskFeedCard.component';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
Link: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }: { children: React.ReactNode }) => (
|
||||
<p data-testid="link">{children}</p>
|
||||
)),
|
||||
useHistory: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../ActivityFeedProvider/ActivityFeedProvider', () => ({
|
||||
useActivityFeedProvider: jest.fn().mockImplementation(() => ({
|
||||
showDrawer: jest.fn(),
|
||||
setActiveThread: jest.fn(),
|
||||
})),
|
||||
__esModule: true,
|
||||
default: 'ActivityFeedProvider',
|
||||
}));
|
||||
|
||||
jest.mock('../../../components/common/AssigneeList/AssigneeList', () => {
|
||||
return jest.fn().mockImplementation(() => <p>AssigneeList</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../components/common/PopOverCard/EntityPopOverCard', () => {
|
||||
return jest.fn().mockImplementation(() => <p>EntityPopOverCard</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../components/common/PopOverCard/UserPopOverCard', () => {
|
||||
return jest.fn().mockImplementation(() => <p>UserPopOverCard</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../components/common/ProfilePicture/ProfilePicture', () => {
|
||||
return jest.fn().mockImplementation(() => <p>ProfilePicture</p>);
|
||||
});
|
||||
|
||||
jest.mock('../Shared/ActivityFeedActions', () => {
|
||||
return jest.fn().mockImplementation(() => <p>ActivityFeedActions</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../utils/TasksUtils', () => ({
|
||||
getTaskDetailPath: jest.fn().mockReturnValue('/'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/TableUtils', () => ({
|
||||
getEntityLink: jest.fn().mockReturnValue('/'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/FeedUtils', () => ({
|
||||
getEntityFQN: jest.fn().mockReturnValue('entityFQN'),
|
||||
getEntityType: jest.fn().mockReturnValue('entityType'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/date-time/DateTimeUtils', () => ({
|
||||
formatDateTime: jest.fn().mockReturnValue('formatDateTime'),
|
||||
getRelativeTime: jest.fn().mockReturnValue('getRelativeTime'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/CommonUtils', () => ({
|
||||
getNameFromFQN: jest.fn().mockReturnValue('formatDateTime'),
|
||||
}));
|
||||
|
||||
const mockProps = {
|
||||
post: TASK_POST,
|
||||
feed: TASK_FEED,
|
||||
showThread: false,
|
||||
isActive: true,
|
||||
hidePopover: true,
|
||||
isForFeedTab: true,
|
||||
};
|
||||
|
||||
describe('Test TaskFeedCard Component', () => {
|
||||
it('Should render TaskFeedCard component', async () => {
|
||||
await act(async () => {
|
||||
render(<TaskFeedCard {...mockProps} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('task-feed-card')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('task-status-icon-open')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('redirect-task-button-link')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -13,7 +13,7 @@
|
||||
import Icon from '@ant-design/icons';
|
||||
import { Button, Col, Row, Tooltip, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty, isUndefined, noop } from 'lodash';
|
||||
import { isEmpty, isUndefined, lowerCase, noop } from 'lodash';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
@ -35,11 +35,8 @@ import {
|
||||
getRelativeTime,
|
||||
} from '../../../utils/date-time/DateTimeUtils';
|
||||
import EntityLink from '../../../utils/EntityLink';
|
||||
import {
|
||||
getEntityFQN,
|
||||
getEntityType,
|
||||
prepareFeedLink,
|
||||
} from '../../../utils/FeedUtils';
|
||||
import { getEntityFQN, getEntityType } from '../../../utils/FeedUtils';
|
||||
import { getEntityLink } from '../../../utils/TableUtils';
|
||||
import { getTaskDetailPath } from '../../../utils/TasksUtils';
|
||||
import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider';
|
||||
import ActivityFeedActions from '../Shared/ActivityFeedActions';
|
||||
@ -110,8 +107,11 @@ const TaskFeedCard = ({
|
||||
<Typography.Text>
|
||||
<Button
|
||||
className="p-0"
|
||||
data-testid="redirect-task-button-link"
|
||||
type="link"
|
||||
onClick={handleTaskLinkClick}>{`#${taskDetails?.id} `}</Button>
|
||||
onClick={handleTaskLinkClick}>
|
||||
{`#${taskDetails?.id} `}
|
||||
</Button>
|
||||
|
||||
<Typography.Text className="p-l-xss">{taskDetails?.type}</Typography.Text>
|
||||
<span className="m-x-xss">{t('label.for-lowercase')}</span>
|
||||
@ -122,8 +122,8 @@ const TaskFeedCard = ({
|
||||
<EntityPopOverCard entityFQN={entityFQN} entityType={entityType}>
|
||||
<Link
|
||||
className="break-all"
|
||||
data-testid="entitylink"
|
||||
to={prepareFeedLink(entityType, entityFQN)}
|
||||
data-testid="entity-link"
|
||||
to={getEntityLink(entityType, entityFQN)}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
{getNameFromFQN(entityFQN)}
|
||||
</Link>
|
||||
@ -140,104 +140,106 @@ const TaskFeedCard = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'task-feed-card-v1 activity-feed-card activity-feed-card-v1',
|
||||
{ active: isActive }
|
||||
)}>
|
||||
<Row gutter={[0, 8]}>
|
||||
<Col className="d-flex items-center" span={24}>
|
||||
<Icon
|
||||
className="m-r-xs"
|
||||
component={
|
||||
taskDetails?.status === ThreadTaskStatus.Open
|
||||
? TaskOpenIcon
|
||||
: TaskCloseIcon
|
||||
}
|
||||
style={{ fontSize: '18px' }}
|
||||
/>
|
||||
|
||||
{getTaskLinkElement}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="task-feed-body text-xs text-grey-muted">
|
||||
<UserPopOverCard
|
||||
key={feed.createdBy}
|
||||
userName={feed.createdBy ?? ''}>
|
||||
<span className="p-r-xss">{feed.createdBy}</span>
|
||||
</UserPopOverCard>
|
||||
{t('message.created-this-task-lowercase')}
|
||||
{timeStamp && (
|
||||
<Tooltip title={formatDateTime(timeStamp)}>
|
||||
<span className="p-l-xss" data-testid="timestamp">
|
||||
{getRelativeTime(timeStamp)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
{!showThread ? (
|
||||
<Col span={24}>
|
||||
<div className="d-flex items-center p-l-lg gap-2">
|
||||
{postLength > 0 && (
|
||||
<>
|
||||
<div className="thread-users-profile-pic">
|
||||
{repliedUniqueUsersList.map((user) => (
|
||||
<UserPopOverCard key={user} userName={user}>
|
||||
<span
|
||||
className="profile-image-span cursor-pointer"
|
||||
data-testid="authorAvatar">
|
||||
<ProfilePicture
|
||||
id=""
|
||||
name={user}
|
||||
type="circle"
|
||||
width="24"
|
||||
/>
|
||||
</span>
|
||||
</UserPopOverCard>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="d-flex items-center thread-count cursor-pointer m-l-xs"
|
||||
onClick={!hidePopover ? showReplies : noop}>
|
||||
<ThreadIcon width={20} />{' '}
|
||||
<span className="text-xs p-l-xss">{postLength}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Typography.Text
|
||||
className={
|
||||
postLength > 0
|
||||
? 'm-l-sm text-sm text-grey-muted'
|
||||
: 'text-sm text-grey-muted'
|
||||
}>
|
||||
{`${t('label.assignee-plural')}: `}
|
||||
</Typography.Text>
|
||||
<AssigneeList
|
||||
assignees={feed?.task?.assignees || []}
|
||||
className="d-flex gap-1"
|
||||
profilePicType="circle"
|
||||
profileWidth="24"
|
||||
showUserName={false}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
|
||||
{!hidePopover && (
|
||||
<ActivityFeedActions
|
||||
feed={feed}
|
||||
isPost={false}
|
||||
post={post}
|
||||
onEditPost={onEditPost}
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'task-feed-card-v1 activity-feed-card activity-feed-card-v1',
|
||||
{ active: isActive }
|
||||
)}
|
||||
data-testid="task-feed-card">
|
||||
<Row gutter={[0, 8]}>
|
||||
<Col className="d-flex items-center" span={24}>
|
||||
<Icon
|
||||
className="m-r-xs"
|
||||
component={
|
||||
taskDetails?.status === ThreadTaskStatus.Open
|
||||
? TaskOpenIcon
|
||||
: TaskCloseIcon
|
||||
}
|
||||
data-testid={`task-status-icon-${lowerCase(taskDetails?.status)}`}
|
||||
style={{ fontSize: '18px' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
{getTaskLinkElement}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="task-feed-body text-xs text-grey-muted">
|
||||
<UserPopOverCard
|
||||
key={feed.createdBy}
|
||||
userName={feed.createdBy ?? ''}>
|
||||
<span className="p-r-xss" data-testid="task-created-by">
|
||||
{feed.createdBy}
|
||||
</span>
|
||||
</UserPopOverCard>
|
||||
{t('message.created-this-task-lowercase')}
|
||||
{timeStamp && (
|
||||
<Tooltip title={formatDateTime(timeStamp)}>
|
||||
<span className="p-l-xss" data-testid="timestamp">
|
||||
{getRelativeTime(timeStamp)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
{!showThread ? (
|
||||
<Col span={24}>
|
||||
<div className="d-flex items-center p-l-lg gap-2">
|
||||
{postLength > 0 && (
|
||||
<>
|
||||
<div className="thread-users-profile-pic">
|
||||
{repliedUniqueUsersList.map((user) => (
|
||||
<UserPopOverCard key={user} userName={user}>
|
||||
<span
|
||||
className="profile-image-span cursor-pointer"
|
||||
data-testid="authorAvatar">
|
||||
<ProfilePicture
|
||||
id=""
|
||||
name={user}
|
||||
type="circle"
|
||||
width="24"
|
||||
/>
|
||||
</span>
|
||||
</UserPopOverCard>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="d-flex items-center thread-count cursor-pointer m-l-xs"
|
||||
onClick={!hidePopover ? showReplies : noop}>
|
||||
<ThreadIcon width={20} />{' '}
|
||||
<span className="text-xs p-l-xss">{postLength}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Typography.Text
|
||||
className={
|
||||
postLength > 0
|
||||
? 'm-l-sm text-sm text-grey-muted'
|
||||
: 'text-sm text-grey-muted'
|
||||
}>
|
||||
{`${t('label.assignee-plural')}: `}
|
||||
</Typography.Text>
|
||||
<AssigneeList
|
||||
assignees={feed?.task?.assignees || []}
|
||||
className="d-flex gap-1"
|
||||
profilePicType="circle"
|
||||
profileWidth="24"
|
||||
showUserName={false}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
|
||||
{!hidePopover && (
|
||||
<ActivityFeedActions
|
||||
feed={feed}
|
||||
isPost={false}
|
||||
post={post}
|
||||
onEditPost={onEditPost}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2023 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 { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { TASK_COLUMNS, TASK_FEED } from '../../../mocks/Task.mock';
|
||||
import { mockUserData } from '../../Users/mocks/User.mocks';
|
||||
import { TaskTab } from './TaskTab.component';
|
||||
import { TaskTabProps } from './TaskTab.interface';
|
||||
|
||||
jest.mock('../../../rest/feedsAPI', () => ({
|
||||
updateTask: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
updateThread: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
Link: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }: { children: React.ReactNode }) => (
|
||||
<p data-testid="link">{children}</p>
|
||||
)),
|
||||
useHistory: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../../components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <p>ActivityFeedCardV1</p>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../../components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <p>ActivityFeedEditor</p>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('../../../components/common/AssigneeList/AssigneeList', () => {
|
||||
return jest.fn().mockImplementation(() => <p>AssigneeList</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../components/common/OwnerLabel/OwnerLabel.component', () => ({
|
||||
OwnerLabel: jest.fn().mockImplementation(() => <p>OwnerLabel</p>),
|
||||
}));
|
||||
|
||||
jest.mock('../../../components/InlineEdit/InlineEdit.component', () => {
|
||||
return jest.fn().mockImplementation(() => <p>InlineEdit</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../pages/TasksPage/shared/Assignees', () => {
|
||||
return jest.fn().mockImplementation(() => <p>Assignees</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../pages/TasksPage/shared/DescriptionTask', () => {
|
||||
return jest.fn().mockImplementation(() => <p>DescriptionTask</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../pages/TasksPage/shared/TagsTask', () => {
|
||||
return jest.fn().mockImplementation(() => <p>TagsTask</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../common/PopOverCard/EntityPopOverCard', () => {
|
||||
return jest.fn().mockImplementation(() => <p>EntityPopOverCard</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../utils/CommonUtils', () => ({
|
||||
getNameFromFQN: jest.fn().mockReturnValue('getNameFromFQN'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/EntityUtils', () => ({
|
||||
getEntityName: jest.fn().mockReturnValue('getEntityName'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/FeedUtils', () => ({
|
||||
getEntityFQN: jest.fn().mockReturnValue('getEntityFQN'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/TableUtils', () => ({
|
||||
getEntityLink: jest.fn().mockReturnValue('getEntityLink'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/TasksUtils', () => ({
|
||||
fetchOptions: jest.fn().mockReturnValue('getEntityLink'),
|
||||
getTaskDetailPath: jest.fn().mockReturnValue('/'),
|
||||
isDescriptionTask: jest.fn().mockReturnValue(false),
|
||||
isTagsTask: jest.fn().mockReturnValue(true),
|
||||
TASK_ACTION_LIST: jest.fn().mockReturnValue([]),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/ToastUtils', () => ({
|
||||
showErrorToast: jest.fn(),
|
||||
showSuccessToast: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../Auth/AuthProviders/AuthProvider', () => ({
|
||||
useAuthContext: jest.fn(() => ({
|
||||
currentUser: mockUserData,
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('../../../rest/feedsAPI', () => ({
|
||||
updateTask: jest.fn(),
|
||||
updateThread: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../../components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider',
|
||||
() => ({
|
||||
useActivityFeedProvider: jest.fn().mockImplementation(() => ({
|
||||
postFeed: jest.fn(),
|
||||
setActiveThread: jest.fn(),
|
||||
})),
|
||||
__esModule: true,
|
||||
default: 'ActivityFeedProvider',
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock('../../../hooks/authHooks', () => ({
|
||||
useAuth: () => {
|
||||
return {
|
||||
isAdminUser: false,
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
const mockOnAfterClose = jest.fn();
|
||||
const mockOnUpdateEntityDetails = jest.fn();
|
||||
|
||||
const mockProps: TaskTabProps = {
|
||||
taskThread: TASK_FEED,
|
||||
entityType: EntityType.TABLE,
|
||||
isForFeedTab: true,
|
||||
columns: TASK_COLUMNS,
|
||||
onAfterClose: mockOnAfterClose,
|
||||
onUpdateEntityDetails: mockOnUpdateEntityDetails,
|
||||
};
|
||||
|
||||
describe('Test TaskFeedCard component', () => {
|
||||
it('Should render the component', async () => {
|
||||
render(<TaskTab {...mockProps} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
const activityFeedCard = screen.getByTestId('task-tab');
|
||||
|
||||
expect(activityFeedCard).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -31,6 +31,7 @@ import { isEmpty, isEqual, isUndefined, noop } from 'lodash';
|
||||
import { MenuInfo } from 'rc-menu/lib/interface';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||
import { ReactComponent as TaskCloseIcon } from '../../../assets/svg/ic-close-task.svg';
|
||||
import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg';
|
||||
@ -58,17 +59,21 @@ import {
|
||||
TaskActionMode,
|
||||
} from '../../../pages/TasksPage/TasksPage.interface';
|
||||
import { updateTask, updateThread } from '../../../rest/feedsAPI';
|
||||
import { getNameFromFQN } from '../../../utils/CommonUtils';
|
||||
import EntityLink from '../../../utils/EntityLink';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getEntityFQN } from '../../../utils/FeedUtils';
|
||||
import { getEntityLink } from '../../../utils/TableUtils';
|
||||
import {
|
||||
fetchOptions,
|
||||
getTaskDetailPath,
|
||||
isDescriptionTask,
|
||||
isTagsTask,
|
||||
TASK_ACTION_LIST,
|
||||
} from '../../../utils/TasksUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import EntityPopOverCard from '../../common/PopOverCard/EntityPopOverCard';
|
||||
import './task-tab.less';
|
||||
import { TaskTabProps } from './TaskTab.interface';
|
||||
|
||||
@ -76,8 +81,10 @@ export const TaskTab = ({
|
||||
taskThread,
|
||||
owner,
|
||||
entityType,
|
||||
isForFeedTab,
|
||||
...rest
|
||||
}: TaskTabProps) => {
|
||||
const history = useHistory();
|
||||
const [assigneesForm] = useForm();
|
||||
const { currentUser } = useAuthContext();
|
||||
const updatedAssignees = Form.useWatch('assignees', assigneesForm);
|
||||
@ -142,14 +149,44 @@ export const TaskTab = ({
|
||||
|
||||
const isTaskGlossaryApproval = taskDetails?.type === TaskType.RequestApproval;
|
||||
|
||||
const handleTaskLinkClick = () => {
|
||||
history.push({
|
||||
pathname: getTaskDetailPath(taskThread),
|
||||
});
|
||||
};
|
||||
|
||||
const getTaskLinkElement = entityCheck && (
|
||||
<Typography.Text className="font-medium text-md" data-testid="task-title">
|
||||
<span>{`#${taskDetails?.id} `}</span>
|
||||
<Button
|
||||
className="p-r-xss text-md font-medium"
|
||||
type="link"
|
||||
onClick={handleTaskLinkClick}>
|
||||
{`#${taskDetails?.id} `}
|
||||
</Button>
|
||||
|
||||
<Typography.Text>{taskDetails?.type}</Typography.Text>
|
||||
<span className="m-x-xss">{t('label.for-lowercase')}</span>
|
||||
|
||||
{!isEmpty(taskField) ? <span>{taskField}</span> : null}
|
||||
{!isForFeedTab && (
|
||||
<>
|
||||
<span className="p-r-xss">{entityType}</span>
|
||||
<EntityPopOverCard entityFQN={entityFQN} entityType={entityType}>
|
||||
<Link
|
||||
className="break-all p-r-xss"
|
||||
data-testid="entitylink"
|
||||
to={getEntityLink(entityType, entityFQN)}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
<Typography.Text className="text-md font-medium text-color-inherit">
|
||||
{' '}
|
||||
{getNameFromFQN(entityFQN)}
|
||||
</Typography.Text>
|
||||
</Link>
|
||||
</EntityPopOverCard>
|
||||
</>
|
||||
)}
|
||||
{!isEmpty(taskField) ? (
|
||||
<span className="break-all">{taskField}</span>
|
||||
) : null}
|
||||
</Typography.Text>
|
||||
);
|
||||
|
||||
@ -418,7 +455,7 @@ export const TaskTab = ({
|
||||
}, [initialAssignees]);
|
||||
|
||||
return (
|
||||
<Row className="p-y-sm p-x-md" gutter={[0, 24]}>
|
||||
<Row className="p-y-sm p-x-md" data-testid="task-tab" gutter={[0, 24]}>
|
||||
<Col className="d-flex items-center" span={24}>
|
||||
<Icon
|
||||
className="m-r-xs"
|
||||
|
@ -18,6 +18,7 @@ import { EntityReference } from '../../../generated/entity/type';
|
||||
export type TaskTabProps = {
|
||||
taskThread: Thread;
|
||||
owner?: EntityReference;
|
||||
isForFeedTab?: boolean;
|
||||
onUpdateEntityDetails?: () => void;
|
||||
onAfterClose?: () => void;
|
||||
} & (
|
||||
|
@ -159,6 +159,7 @@ const Users = ({
|
||||
<ActivityFeedTab
|
||||
entityType={EntityType.USER}
|
||||
fqn={username}
|
||||
isForFeedTab={false}
|
||||
onFeedUpdate={noop}
|
||||
/>
|
||||
</ActivityFeedProvider>
|
||||
|
@ -46,3 +46,5 @@ export const ENDS_WITH_NUMBER_REGEX = /\d+$/;
|
||||
|
||||
export const VALID_OBJECT_KEY_REGEX = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
|
||||
export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
||||
|
||||
export const TASK_SANITIZE_VALUE_REGEX = /^"|"$/g;
|
||||
|
@ -12,7 +12,13 @@
|
||||
*/
|
||||
|
||||
import { TableVersionProp } from '../components/TableVersion/TableVersion.interface';
|
||||
import { DatabaseServiceType, TableType } from '../generated/entity/data/table';
|
||||
import {
|
||||
Constraint,
|
||||
DatabaseServiceType,
|
||||
DataType,
|
||||
Table,
|
||||
TableType,
|
||||
} from '../generated/entity/data/table';
|
||||
import { ENTITY_PERMISSIONS } from '../mocks/Permissions.mock';
|
||||
import {
|
||||
mockBackHandler,
|
||||
@ -23,7 +29,7 @@ import {
|
||||
mockVersionList,
|
||||
} from '../mocks/VersionCommon.mock';
|
||||
|
||||
export const mockTableData = {
|
||||
export const mockTableData: Table = {
|
||||
id: 'ab4f893b-c303-43d9-9375-3e620a670b02',
|
||||
name: 'raw_product_catalog',
|
||||
fullyQualifiedName: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
|
||||
@ -33,7 +39,20 @@ export const mockTableData = {
|
||||
updatedAt: 1688442727895,
|
||||
updatedBy: 'admin',
|
||||
tableType: TableType.Regular,
|
||||
columns: [],
|
||||
columns: [
|
||||
{
|
||||
name: 'shop_id',
|
||||
displayName: 'Shop Id Customer',
|
||||
dataType: DataType.Number,
|
||||
dataTypeDisplay: 'numeric',
|
||||
description:
|
||||
'Unique identifier for the store. This column is the primary key for this table.',
|
||||
fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".shop_id',
|
||||
tags: [],
|
||||
constraint: Constraint.PrimaryKey,
|
||||
ordinalPosition: 1,
|
||||
},
|
||||
],
|
||||
owner: {
|
||||
id: '38be030f-f817-4712-bc3b-ff7b9b9b805e',
|
||||
type: 'user',
|
||||
|
103
openmetadata-ui/src/main/resources/ui/src/mocks/Task.mock.ts
Normal file
103
openmetadata-ui/src/main/resources/ui/src/mocks/Task.mock.ts
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
Column,
|
||||
Constraint,
|
||||
DataType,
|
||||
} from '../generated/entity/data/container';
|
||||
import {
|
||||
Post,
|
||||
TaskType,
|
||||
Thread,
|
||||
ThreadTaskStatus,
|
||||
ThreadType,
|
||||
} from '../generated/entity/feed/thread';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
export const TASK_FEED: Thread = {
|
||||
id: '8b5076bb-8284-46b0-b00d-5e43a184ba9b',
|
||||
type: ThreadType.Task,
|
||||
href: 'http://localhost:8585/api/v1/feed/8b5076bb-8284-46b0-b00d-5e43a184ba9b',
|
||||
threadTs: 1701686127533,
|
||||
about:
|
||||
'<#E::table::sample_data.ecommerce_db.shopify."dim.shop"::columns::shop_id::tags>',
|
||||
entityId: 'defcff8c-0823-40e6-9c1e-9b0458ba0fa5',
|
||||
createdBy: 'admin',
|
||||
updatedAt: 1701686127534,
|
||||
updatedBy: 'admin',
|
||||
resolved: false,
|
||||
message: 'Request tags for table dim.shop columns/shop_id',
|
||||
postsCount: 0,
|
||||
posts: [],
|
||||
reactions: [],
|
||||
task: {
|
||||
id: 2,
|
||||
type: TaskType.RequestTag,
|
||||
assignees: [
|
||||
{
|
||||
id: '31d072f8-7873-4976-88ea-ac0d2f51f632',
|
||||
type: 'team',
|
||||
name: 'Sales',
|
||||
fullyQualifiedName: 'Sales',
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
status: ThreadTaskStatus.Open,
|
||||
oldValue: '[]',
|
||||
suggestion:
|
||||
'[{"tagFQN":"PersonalData.SpecialCategory","source":"Classification","name":"SpecialCategory","description":"GDPR special category data is personal information of data subjects that is especially sensitive, the exposure of which could significantly impact the rights and freedoms of data subjects and potentially be used against them for unlawful discrimination."}]',
|
||||
},
|
||||
};
|
||||
|
||||
export const TASK_POST: Post = {
|
||||
message: 'Request tags for table dim.shop columns/shop_id',
|
||||
postTs: 1701686127533,
|
||||
from: 'admin',
|
||||
id: '8b5076bb-8284-46b0-b00d-5e43a184ba9b',
|
||||
reactions: [],
|
||||
};
|
||||
|
||||
export const TASK_COLUMNS: Column[] = [
|
||||
{
|
||||
name: 'shop_id',
|
||||
dataType: DataType.Number,
|
||||
dataTypeDisplay: 'numeric',
|
||||
description:
|
||||
'Unique identifier for the store. This column is the primary key for this table.',
|
||||
fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".shop_id',
|
||||
tags: [],
|
||||
constraint: Constraint.PrimaryKey,
|
||||
ordinalPosition: 1,
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
dataType: DataType.Varchar,
|
||||
dataLength: 100,
|
||||
dataTypeDisplay: 'varchar',
|
||||
description: 'Name of your store.',
|
||||
fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".name',
|
||||
tags: [],
|
||||
ordinalPosition: 2,
|
||||
},
|
||||
{
|
||||
name: 'domain',
|
||||
dataType: DataType.Varchar,
|
||||
dataLength: 1000,
|
||||
dataTypeDisplay: 'varchar',
|
||||
description:
|
||||
'Primary domain specified for your online store. Your primary domain is the one that your customers and search engines see. For example, www.mycompany.com.',
|
||||
fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".domain',
|
||||
tags: [],
|
||||
ordinalPosition: 3,
|
||||
},
|
||||
];
|
@ -46,6 +46,7 @@ import {
|
||||
fetchEntityDetail,
|
||||
fetchOptions,
|
||||
getBreadCrumbList,
|
||||
getTaskMessage,
|
||||
} from '../../../utils/TasksUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import Assignees from '../shared/Assignees';
|
||||
@ -72,11 +73,17 @@ const RequestDescription = () => {
|
||||
const [assignees, setAssignees] = useState<Array<Option>>([]);
|
||||
const [suggestion, setSuggestion] = useState<string>('');
|
||||
|
||||
const getSanitizeValue = value?.replaceAll(/^"|"$/g, '') || '';
|
||||
|
||||
const message = `Request description for ${getSanitizeValue || entityType} ${
|
||||
field !== EntityField.COLUMNS ? getEntityName(entityData) : ''
|
||||
}`;
|
||||
const taskMessage = useMemo(
|
||||
() =>
|
||||
getTaskMessage({
|
||||
value,
|
||||
entityType,
|
||||
entityData,
|
||||
field,
|
||||
startMessage: 'Request description',
|
||||
}),
|
||||
[value, entityType, field, entityData]
|
||||
);
|
||||
|
||||
const decodedEntityFQN = useMemo(
|
||||
() => getDecodedFqn(entityFQN),
|
||||
@ -105,7 +112,7 @@ const RequestDescription = () => {
|
||||
if (assignees.length) {
|
||||
const data: CreateThread = {
|
||||
from: currentUser?.name as string,
|
||||
message: value.title || message,
|
||||
message: value.title || taskMessage,
|
||||
about: getEntityFeedLink(entityType, decodedEntityFQN, getTaskAbout()),
|
||||
taskDetails: {
|
||||
assignees: assignees.map((assignee) => ({
|
||||
@ -159,7 +166,7 @@ const RequestDescription = () => {
|
||||
setOptions(defaultAssignee);
|
||||
}
|
||||
form.setFieldsValue({
|
||||
title: message.trimEnd(),
|
||||
title: taskMessage.trimEnd(),
|
||||
assignees: defaultAssignee,
|
||||
});
|
||||
}, [entityData]);
|
||||
|
@ -45,6 +45,7 @@ import {
|
||||
fetchEntityDetail,
|
||||
fetchOptions,
|
||||
getBreadCrumbList,
|
||||
getTaskMessage,
|
||||
} from '../../../utils/TasksUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import Assignees from '../shared/Assignees';
|
||||
@ -70,11 +71,17 @@ const RequestTag = () => {
|
||||
const [assignees, setAssignees] = useState<Option[]>([]);
|
||||
const [suggestion] = useState<TagLabel[]>([]);
|
||||
|
||||
const getSanitizeValue = value?.replaceAll(/^"|"$/g, '') || '';
|
||||
|
||||
const message = `Request tags for ${getSanitizeValue || entityType} ${
|
||||
field !== EntityField.COLUMNS ? getEntityName(entityData) : ''
|
||||
}`;
|
||||
const taskMessage = useMemo(
|
||||
() =>
|
||||
getTaskMessage({
|
||||
value,
|
||||
entityType,
|
||||
entityData,
|
||||
field,
|
||||
startMessage: 'Request tags',
|
||||
}),
|
||||
[value, entityType, field, entityData]
|
||||
);
|
||||
|
||||
const decodedEntityFQN = useMemo(
|
||||
() => getDecodedFqn(entityFQN),
|
||||
@ -98,7 +105,7 @@ const RequestTag = () => {
|
||||
const onCreateTask: FormProps['onFinish'] = (value) => {
|
||||
const data: CreateThread = {
|
||||
from: currentUser?.name as string,
|
||||
message: value.title || message,
|
||||
message: value.title || taskMessage,
|
||||
about: getEntityFeedLink(entityType, decodedEntityFQN, getTaskAbout()),
|
||||
taskDetails: {
|
||||
assignees: assignees.map((assignee) => ({
|
||||
@ -149,7 +156,7 @@ const RequestTag = () => {
|
||||
setOptions((prev) => [...defaultAssignee, ...prev]);
|
||||
}
|
||||
form.setFieldsValue({
|
||||
title: message.trimEnd(),
|
||||
title: taskMessage.trimEnd(),
|
||||
assignees: defaultAssignee,
|
||||
});
|
||||
}, [entityData]);
|
||||
|
@ -27,6 +27,7 @@ import Loader from '../../../components/Loader/Loader';
|
||||
import { SearchedDataProps } from '../../../components/SearchedData/SearchedData.interface';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||
import { EntityField } from '../../../constants/Feeds.constants';
|
||||
import { TASK_SANITIZE_VALUE_REGEX } from '../../../constants/regex.constants';
|
||||
import { EntityTabs, EntityType } from '../../../enums/entity.enum';
|
||||
import {
|
||||
CreateThread,
|
||||
@ -47,6 +48,7 @@ import {
|
||||
getBreadCrumbList,
|
||||
getColumnObject,
|
||||
getEntityColumnsDetails,
|
||||
getTaskMessage,
|
||||
} from '../../../utils/TasksUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import Assignees from '../shared/Assignees';
|
||||
@ -73,18 +75,29 @@ const UpdateDescription = () => {
|
||||
const [assignees, setAssignees] = useState<Array<Option>>([]);
|
||||
const [currentDescription, setCurrentDescription] = useState<string>('');
|
||||
|
||||
const getSanitizeValue = value?.replaceAll(/^"|"$/g, '') || '';
|
||||
const sanitizeValue = useMemo(
|
||||
() => value?.replaceAll(TASK_SANITIZE_VALUE_REGEX, '') ?? '',
|
||||
[value]
|
||||
);
|
||||
|
||||
const decodedEntityFQN = useMemo(() => getDecodedFqn(entityFQN), [entityFQN]);
|
||||
|
||||
const message = `Update description for ${getSanitizeValue || entityType} ${
|
||||
field !== EntityField.COLUMNS ? getEntityName(entityData) : ''
|
||||
}`;
|
||||
const taskMessage = useMemo(
|
||||
() =>
|
||||
getTaskMessage({
|
||||
value,
|
||||
entityType,
|
||||
entityData,
|
||||
field,
|
||||
startMessage: 'Update description',
|
||||
}),
|
||||
[value, entityType, field, entityData]
|
||||
);
|
||||
|
||||
const back = () => history.goBack();
|
||||
|
||||
const columnObject = useMemo(() => {
|
||||
const column = getSanitizeValue.split(FQN_SEPARATOR_CHAR).slice(-1);
|
||||
const column = sanitizeValue.split(FQN_SEPARATOR_CHAR).slice(-1);
|
||||
|
||||
return getColumnObject(
|
||||
column[0],
|
||||
@ -116,7 +129,7 @@ const UpdateDescription = () => {
|
||||
const onCreateTask: FormProps['onFinish'] = (value) => {
|
||||
const data: CreateThread = {
|
||||
from: currentUser?.name as string,
|
||||
message: value.title || message,
|
||||
message: value.title || taskMessage,
|
||||
about: getEntityFeedLink(entityType, decodedEntityFQN, getTaskAbout()),
|
||||
taskDetails: {
|
||||
assignees: assignees.map((assignee) => ({
|
||||
@ -167,7 +180,7 @@ const UpdateDescription = () => {
|
||||
setOptions(defaultAssignee);
|
||||
}
|
||||
form.setFieldsValue({
|
||||
title: message.trimEnd(),
|
||||
title: taskMessage.trimEnd(),
|
||||
assignees: defaultAssignee,
|
||||
description: getDescription(),
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ import Loader from '../../../components/Loader/Loader';
|
||||
import { SearchedDataProps } from '../../../components/SearchedData/SearchedData.interface';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||
import { EntityField } from '../../../constants/Feeds.constants';
|
||||
import { TASK_SANITIZE_VALUE_REGEX } from '../../../constants/regex.constants';
|
||||
import { EntityTabs, EntityType } from '../../../enums/entity.enum';
|
||||
import {
|
||||
CreateThread,
|
||||
@ -49,6 +50,7 @@ import {
|
||||
getBreadCrumbList,
|
||||
getColumnObject,
|
||||
getEntityColumnsDetails,
|
||||
getTaskMessage,
|
||||
} from '../../../utils/TasksUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import Assignees from '../shared/Assignees';
|
||||
@ -78,17 +80,29 @@ const UpdateTag = () => {
|
||||
const [currentTags, setCurrentTags] = useState<TagLabel[]>([]);
|
||||
const [suggestion, setSuggestion] = useState<TagLabel[]>([]);
|
||||
|
||||
const getSanitizeValue = value?.replaceAll(/^"|"$/g, '') || '';
|
||||
const sanitizeValue = useMemo(
|
||||
() => value?.replaceAll(TASK_SANITIZE_VALUE_REGEX, '') ?? '',
|
||||
[value]
|
||||
);
|
||||
|
||||
const taskMessage = useMemo(
|
||||
() =>
|
||||
getTaskMessage({
|
||||
value,
|
||||
entityType,
|
||||
entityData,
|
||||
field,
|
||||
startMessage: 'Update tags',
|
||||
}),
|
||||
[value, entityType, field, entityData]
|
||||
);
|
||||
|
||||
const message = `Update tags for ${getSanitizeValue || entityType} ${
|
||||
field !== EntityField.COLUMNS ? getEntityName(entityData) : ''
|
||||
}`;
|
||||
const decodedEntityFQN = useMemo(() => getDecodedFqn(entityFQN), [entityFQN]);
|
||||
|
||||
const back = () => history.goBack();
|
||||
|
||||
const columnObject = useMemo(() => {
|
||||
const column = getSanitizeValue.split(FQN_SEPARATOR_CHAR).slice(-1);
|
||||
const column = sanitizeValue.split(FQN_SEPARATOR_CHAR).slice(-1);
|
||||
|
||||
return getColumnObject(
|
||||
column[0],
|
||||
@ -121,7 +135,7 @@ const UpdateTag = () => {
|
||||
const onCreateTask: FormProps['onFinish'] = (value) => {
|
||||
const data: CreateThread = {
|
||||
from: currentUser?.name as string,
|
||||
message: value.title || message,
|
||||
message: value.title || taskMessage,
|
||||
about: getEntityFeedLink(entityType, decodedEntityFQN, getTaskAbout()),
|
||||
taskDetails: {
|
||||
assignees: assignees.map((assignee) => ({
|
||||
@ -177,7 +191,7 @@ const UpdateTag = () => {
|
||||
setOptions(defaultAssignee);
|
||||
}
|
||||
form.setFieldsValue({
|
||||
title: message.trimEnd(),
|
||||
title: taskMessage.trimEnd(),
|
||||
updatedTags: getTags(),
|
||||
assignees: defaultAssignee,
|
||||
});
|
||||
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2023 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 { EntityType } from '../enums/entity.enum';
|
||||
import { mockTableData } from '../mocks/TableVersion.mock';
|
||||
import { getEntityTableName, getTaskMessage } from './TasksUtils';
|
||||
|
||||
describe('Tests for DataAssetsHeaderUtils', () => {
|
||||
it('function getEntityTableName should return name if no data found', () => {
|
||||
const entityName = getEntityTableName(
|
||||
EntityType.TABLE,
|
||||
'data_test_id',
|
||||
mockTableData
|
||||
);
|
||||
|
||||
expect(entityName).toEqual('data_test_id');
|
||||
});
|
||||
|
||||
it('function getEntityTableName should return name if it contains dot in it name', () => {
|
||||
const entityName = getEntityTableName(
|
||||
EntityType.TABLE,
|
||||
'data.test_id',
|
||||
mockTableData
|
||||
);
|
||||
|
||||
expect(entityName).toEqual('data.test_id');
|
||||
});
|
||||
|
||||
it('function getEntityTableName should return name if entity type not found', () => {
|
||||
const entityName = getEntityTableName(
|
||||
EntityType.DATABASE_SERVICE,
|
||||
'cyber_test',
|
||||
mockTableData
|
||||
);
|
||||
|
||||
expect(entityName).toEqual('cyber_test');
|
||||
});
|
||||
|
||||
it('function getEntityTableName should return entity display name for all entities', () => {
|
||||
const entityTableName = getEntityTableName(
|
||||
EntityType.TABLE,
|
||||
'shop_id',
|
||||
mockTableData
|
||||
);
|
||||
|
||||
expect(entityTableName).toEqual('Shop Id Customer');
|
||||
});
|
||||
});
|
||||
|
||||
const taskTagMessage = {
|
||||
value: null,
|
||||
entityType: EntityType.TABLE,
|
||||
entityData: mockTableData,
|
||||
field: null,
|
||||
startMessage: 'Request Tag',
|
||||
};
|
||||
|
||||
const taskDescriptionMessage = {
|
||||
...taskTagMessage,
|
||||
startMessage: 'Request Description',
|
||||
};
|
||||
|
||||
describe('Tests for getTaskMessage', () => {
|
||||
it('function getTaskMessage should return task message for tags', () => {
|
||||
// entity request task message
|
||||
const requestTagsEntityMessage = getTaskMessage(taskTagMessage);
|
||||
|
||||
expect(requestTagsEntityMessage).toEqual(
|
||||
'Request Tag for table raw_product_catalog '
|
||||
);
|
||||
|
||||
// entity request column message
|
||||
const requestTagsEntityColumnMessage = getTaskMessage({
|
||||
...taskTagMessage,
|
||||
value: 'order_id',
|
||||
field: 'columns',
|
||||
});
|
||||
|
||||
expect(requestTagsEntityColumnMessage).toEqual(
|
||||
'Request Tag for table raw_product_catalog columns/order_id'
|
||||
);
|
||||
|
||||
// entity update task message
|
||||
const updateTagsEntityMessage = getTaskMessage({
|
||||
...taskTagMessage,
|
||||
startMessage: 'Update Tag',
|
||||
});
|
||||
|
||||
expect(updateTagsEntityMessage).toEqual(
|
||||
'Update Tag for table raw_product_catalog '
|
||||
);
|
||||
|
||||
// entity update column message
|
||||
const updateTagsEntityColumnMessage = getTaskMessage({
|
||||
...taskTagMessage,
|
||||
value: 'order_id',
|
||||
field: 'columns',
|
||||
startMessage: 'Update Tag',
|
||||
});
|
||||
|
||||
expect(updateTagsEntityColumnMessage).toEqual(
|
||||
'Update Tag for table raw_product_catalog columns/order_id'
|
||||
);
|
||||
});
|
||||
|
||||
it('function getTaskMessage should return task message for description', () => {
|
||||
// entity request task message
|
||||
const requestDescriptionEntityMessage = getTaskMessage(
|
||||
taskDescriptionMessage
|
||||
);
|
||||
|
||||
expect(requestDescriptionEntityMessage).toEqual(
|
||||
'Request Description for table raw_product_catalog '
|
||||
);
|
||||
|
||||
// entity request column message
|
||||
const requestDescriptionEntityColumnMessage = getTaskMessage({
|
||||
...taskDescriptionMessage,
|
||||
value: 'order_id',
|
||||
field: 'columns',
|
||||
});
|
||||
|
||||
expect(requestDescriptionEntityColumnMessage).toEqual(
|
||||
'Request Description for table raw_product_catalog columns/order_id'
|
||||
);
|
||||
|
||||
// entity update task message
|
||||
const updateDescriptionEntityMessage = getTaskMessage({
|
||||
...taskDescriptionMessage,
|
||||
startMessage: 'Update Description',
|
||||
});
|
||||
|
||||
expect(updateDescriptionEntityMessage).toEqual(
|
||||
'Update Description for table raw_product_catalog '
|
||||
);
|
||||
|
||||
// entity update column message
|
||||
const updateDescriptionEntityColumnMessage = getTaskMessage({
|
||||
...taskDescriptionMessage,
|
||||
value: 'order_id',
|
||||
field: 'columns',
|
||||
startMessage: 'Update Description',
|
||||
});
|
||||
|
||||
expect(updateDescriptionEntityColumnMessage).toEqual(
|
||||
'Update Description for table raw_product_catalog columns/order_id'
|
||||
);
|
||||
});
|
||||
});
|
@ -25,6 +25,7 @@ import {
|
||||
ROUTES,
|
||||
} from '../constants/constants';
|
||||
import { EntityField } from '../constants/Feeds.constants';
|
||||
import { TASK_SANITIZE_VALUE_REGEX } from '../constants/regex.constants';
|
||||
import {
|
||||
EntityTabs,
|
||||
EntityType,
|
||||
@ -35,8 +36,10 @@ import { ServiceCategory } from '../enums/service.enum';
|
||||
import { Chart } from '../generated/entity/data/chart';
|
||||
import { Container } from '../generated/entity/data/container';
|
||||
import { Dashboard } from '../generated/entity/data/dashboard';
|
||||
import { DashboardDataModel } from '../generated/entity/data/dashboardDataModel';
|
||||
import { MlFeature, Mlmodel } from '../generated/entity/data/mlmodel';
|
||||
import { Pipeline, Task } from '../generated/entity/data/pipeline';
|
||||
import { SearchIndex } from '../generated/entity/data/searchIndex';
|
||||
import { Column, Table } from '../generated/entity/data/table';
|
||||
import { Field, Topic } from '../generated/entity/data/topic';
|
||||
import { TaskType, Thread } from '../generated/entity/feed/thread';
|
||||
@ -590,3 +593,105 @@ export const getEntityTaskDetails = (
|
||||
|
||||
return { fqnPart: [fqnPartTypes], entityField };
|
||||
};
|
||||
|
||||
export const getEntityTableName = (
|
||||
entityType: EntityType,
|
||||
name: string,
|
||||
entityData: EntityData
|
||||
): string => {
|
||||
if (name.includes('.')) {
|
||||
return name;
|
||||
}
|
||||
let entityReference;
|
||||
|
||||
switch (entityType) {
|
||||
case EntityType.TABLE:
|
||||
entityReference = (entityData as Table).columns?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.TOPIC:
|
||||
entityReference = (entityData as Topic).messageSchema?.schemaFields?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.DASHBOARD:
|
||||
entityReference = (entityData as Dashboard).charts?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.PIPELINE:
|
||||
entityReference = (entityData as Pipeline).tasks?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.MLMODEL:
|
||||
entityReference = (entityData as Mlmodel).mlFeatures?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.CONTAINER:
|
||||
entityReference = (entityData as Container).dataModel?.columns?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.SEARCH_INDEX:
|
||||
entityReference = (entityData as SearchIndex).fields?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case EntityType.DASHBOARD_DATA_MODEL:
|
||||
entityReference = (entityData as DashboardDataModel).columns?.find(
|
||||
(item) => item.name === name
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return name;
|
||||
}
|
||||
|
||||
if (isUndefined(entityReference)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return getEntityName(entityReference);
|
||||
};
|
||||
|
||||
export const getTaskMessage = ({
|
||||
value,
|
||||
entityType,
|
||||
entityData,
|
||||
field,
|
||||
startMessage,
|
||||
}: {
|
||||
value: string | null;
|
||||
entityType: EntityType;
|
||||
entityData: EntityData;
|
||||
field: string | null;
|
||||
startMessage: string;
|
||||
}) => {
|
||||
const sanitizeValue = value?.replaceAll(TASK_SANITIZE_VALUE_REGEX, '') ?? '';
|
||||
|
||||
const entityColumnsName = field
|
||||
? `${field}/${getEntityTableName(entityType, sanitizeValue, entityData)}`
|
||||
: '';
|
||||
|
||||
return `${startMessage} for ${entityType} ${getEntityName(
|
||||
entityData
|
||||
)} ${entityColumnsName}`;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user