fix: add request tags and desc for glossary and terms (#14071)

* fix: add request tags and desc for glossary and terms

* fix: breadcrumbs

* fix: add request tags cypress
This commit is contained in:
karanh37 2023-11-23 18:46:46 +05:30 committed by GitHub
parent 631145d090
commit 312b038b2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 235 additions and 31 deletions

View File

@ -39,10 +39,12 @@ import {
SEARCH_ENTITY_TABLE, SEARCH_ENTITY_TABLE,
} from '../../constants/constants'; } from '../../constants/constants';
const name = `test_dataconsumer${uuid()}`;
const CREDENTIALS = { const CREDENTIALS = {
firstName: 'Cypress', firstName: 'Cypress',
lastName: 'UserDC', lastName: 'UserDC',
email: `test_dataconsumer${uuid()}@openmetadata.org`, email: `${name}@openmetadata.org`,
password: 'User@OMD123', password: 'User@OMD123',
username: 'CypressUserDC', username: 'CypressUserDC',
}; };
@ -278,9 +280,7 @@ const updateTags = (inTerm) => {
'/api/v1/search/query?q=*&index=tag_search_index&from=0&size=*&query_filter=*', '/api/v1/search/query?q=*&index=tag_search_index&from=0&size=*&query_filter=*',
'tags' 'tags'
); );
cy.get( cy.get('[data-testid="tags-container"] [data-testid="add-tag"]').click();
'[data-testid="tags-input-container"] [data-testid="add-tag"]'
).click();
verifyResponseStatusCode('@tags', 200); verifyResponseStatusCode('@tags', 200);
@ -294,7 +294,7 @@ const updateTags = (inTerm) => {
cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click(); cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click();
const container = inTerm const container = inTerm
? '[data-testid="tags-input-container"]' ? '[data-testid="tags-container"]'
: '[data-testid="glossary-details"]'; : '[data-testid="glossary-details"]';
cy.wait(1000); cy.wait(1000);
cy.get(container).scrollIntoView().contains('Personal').should('be.visible'); cy.get(container).scrollIntoView().contains('Personal').should('be.visible');
@ -636,7 +636,7 @@ describe('Glossary page should work properly', () => {
// Remove Tag // Remove Tag
cy.get( cy.get(
'[data-testid="tags-input-container"] [data-testid="edit-button"]' '[data-testid="tags-container"] [data-testid="edit-button"]'
).click(); ).click();
cy.get('[data-testid="remove-tags"]').should('be.visible').click(); cy.get('[data-testid="remove-tags"]').should('be.visible').click();
@ -768,6 +768,64 @@ describe('Glossary page should work properly', () => {
voteGlossary(); voteGlossary();
}); });
it('Request Tags workflow for Glossary', function () {
cy.get('[data-testid="glossary-left-panel"]')
.contains(NEW_GLOSSARY_1.name)
.click();
interceptURL(
'GET',
`/api/v1/search/query?q=*%20AND%20disabled:false&index=tag_search_index*`,
'suggestTag'
);
interceptURL('POST', '/api/v1/feed', 'taskCreated');
interceptURL('PUT', '/api/v1/feed/tasks/*/resolve', 'taskResolve');
cy.get('[data-testid="request-entity-tags"]').should('exist').click();
// set assignees for task
cy.get(
'[data-testid="select-assignee"] > .ant-select-selector > .ant-select-selection-overflow'
)
.click()
.type(name);
cy.get(`[data-testid="assignee-option-${name}"]`).click();
cy.clickOutside();
cy.get('[data-testid="tag-selector"]')
.click()
.type('{backspace}')
.type('{backspace}')
.type('Personal');
verifyResponseStatusCode('@suggestTag', 200);
cy.get(
'.ant-select-dropdown [data-testid="tag-PersonalData.Personal"]'
).click();
cy.clickOutside();
cy.get('[data-testid="submit-tag-request"]').click();
verifyResponseStatusCode('@taskCreated', 201);
// Accept the tag suggestion which is created
cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
verifyResponseStatusCode('@taskResolve', 200);
cy.reload();
cy.get('[data-testid="glossary-left-panel"]')
.contains(NEW_GLOSSARY_1.name)
.click();
checkDisplayName(NEW_GLOSSARY_1.name);
// Verify Tags which is added at the time of creating glossary
cy.get('[data-testid="tags-container"]')
.contains('Personal')
.should('be.visible');
});
it('Assets Tab should work properly', () => { it('Assets Tab should work properly', () => {
selectActiveGlossary(NEW_GLOSSARY.name); selectActiveGlossary(NEW_GLOSSARY.name);
const glossary = NEW_GLOSSARY.name; const glossary = NEW_GLOSSARY.name;

View File

@ -46,6 +46,7 @@ const GlossaryDetails = ({
onAddGlossaryTerm, onAddGlossaryTerm,
onEditGlossaryTerm, onEditGlossaryTerm,
isVersionView, isVersionView,
onThreadLinkSelect,
}: GlossaryDetailsProps) => { }: GlossaryDetailsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
@ -131,15 +132,19 @@ const GlossaryDetails = ({
<Space className="w-full" direction="vertical" size={24}> <Space className="w-full" direction="vertical" size={24}>
<DescriptionV1 <DescriptionV1
description={description} description={description}
entityFqn={glossary.fullyQualifiedName}
entityName={glossary.displayName ?? glossary.name} entityName={glossary.displayName ?? glossary.name}
entityType={EntityType.GLOSSARY} entityType={EntityType.GLOSSARY}
hasEditAccess={permissions.EditDescription || permissions.EditAll} hasEditAccess={permissions.EditDescription || permissions.EditAll}
isEdit={isDescriptionEditable} isEdit={isDescriptionEditable}
showCommentsIcon={false} owner={glossary?.owner}
showActions={!glossary.deleted}
onCancel={() => setIsDescriptionEditable(false)} onCancel={() => setIsDescriptionEditable(false)}
onDescriptionEdit={() => setIsDescriptionEditable(true)} onDescriptionEdit={() => setIsDescriptionEditable(true)}
onDescriptionUpdate={onDescriptionUpdate} onDescriptionUpdate={onDescriptionUpdate}
onThreadLinkSelect={onThreadLinkSelect}
/> />
<GlossaryTermTab <GlossaryTermTab
isGlossary isGlossary
childGlossaryTerms={glossaryTerms} childGlossaryTerms={glossaryTerms}
@ -158,6 +163,7 @@ const GlossaryDetails = ({
isVersionView={isVersionView} isVersionView={isVersionView}
permissions={permissions} permissions={permissions}
selectedData={glossary} selectedData={glossary}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={updateGlossary} onUpdate={updateGlossary}
/> />
</Col> </Col>

View File

@ -33,4 +33,5 @@ export type GlossaryDetailsProps = {
refreshGlossaryTerms: () => void; refreshGlossaryTerms: () => void;
onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void; onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void;
onEditGlossaryTerm: (glossaryTerm: GlossaryTerm) => void; onEditGlossaryTerm: (glossaryTerm: GlossaryTerm) => void;
onThreadLinkSelect: (value: string) => void;
}; };

View File

@ -65,6 +65,7 @@ const mockProps = {
onAddGlossaryTerm: jest.fn(), onAddGlossaryTerm: jest.fn(),
onEditGlossaryTerm: jest.fn(), onEditGlossaryTerm: jest.fn(),
updateVote: jest.fn(), updateVote: jest.fn(),
onThreadLinkSelect: jest.fn(),
}; };
describe('Test Glossary-details component', () => { describe('Test Glossary-details component', () => {

View File

@ -22,7 +22,6 @@ import { UserSelectableList } from '../../../components/common/UserSelectableLis
import { UserTeamSelectableList } from '../../../components/common/UserTeamSelectableList/UserTeamSelectableList.component'; import { UserTeamSelectableList } from '../../../components/common/UserTeamSelectableList/UserTeamSelectableList.component';
import { OperationPermission } from '../../../components/PermissionProvider/PermissionProvider.interface'; import { OperationPermission } from '../../../components/PermissionProvider/PermissionProvider.interface';
import TagButton from '../../../components/TagButton/TagButton.component'; import TagButton from '../../../components/TagButton/TagButton.component';
import TagsInput from '../../../components/TagsInput/TagsInput.component';
import { import {
DE_ACTIVE_COLOR, DE_ACTIVE_COLOR,
getTeamAndUserDetailsPath, getTeamAndUserDetailsPath,
@ -31,7 +30,7 @@ import {
} from '../../../constants/constants'; } from '../../../constants/constants';
import { EntityField } from '../../../constants/Feeds.constants'; import { EntityField } from '../../../constants/Feeds.constants';
import { EntityType } from '../../../enums/entity.enum'; import { EntityType } from '../../../enums/entity.enum';
import { Glossary } from '../../../generated/entity/data/glossary'; import { Glossary, TagSource } from '../../../generated/entity/data/glossary';
import { import {
GlossaryTerm, GlossaryTerm,
TagLabel, TagLabel,
@ -47,6 +46,8 @@ import {
getEntityVersionTags, getEntityVersionTags,
} from '../../../utils/EntityVersionUtils'; } from '../../../utils/EntityVersionUtils';
import { DomainLabel } from '../../common/DomainLabel/DomainLabel.component'; import { DomainLabel } from '../../common/DomainLabel/DomainLabel.component';
import TagsContainerV2 from '../../Tag/TagsContainerV2/TagsContainerV2';
import { DisplayType } from '../../Tag/TagsViewer/TagsViewer.interface';
import GlossaryReviewers from './GlossaryReviewers'; import GlossaryReviewers from './GlossaryReviewers';
type Props = { type Props = {
@ -55,6 +56,7 @@ type Props = {
selectedData: Glossary | GlossaryTerm; selectedData: Glossary | GlossaryTerm;
isGlossary: boolean; isGlossary: boolean;
onUpdate: (data: GlossaryTerm | Glossary) => void; onUpdate: (data: GlossaryTerm | Glossary) => void;
onThreadLinkSelect: (value: string) => void;
}; };
const GlossaryDetailsRightPanel = ({ const GlossaryDetailsRightPanel = ({
@ -63,6 +65,7 @@ const GlossaryDetailsRightPanel = ({
isGlossary, isGlossary,
onUpdate, onUpdate,
isVersionView, isVersionView,
onThreadLinkSelect,
}: Props) => { }: Props) => {
const hasEditReviewerAccess = useMemo(() => { const hasEditReviewerAccess = useMemo(() => {
return permissions.EditAll || permissions.EditReviewers; return permissions.EditAll || permissions.EditReviewers;
@ -307,11 +310,15 @@ const GlossaryDetailsRightPanel = ({
<Col span="24"> <Col span="24">
<div data-testid="glossary-tags-name"> <div data-testid="glossary-tags-name">
{isGlossary && ( {isGlossary && (
<TagsInput <TagsContainerV2
editable={permissions.EditAll || permissions.EditTags} displayType={DisplayType.READ_MORE}
isVersionView={isVersionView} entityFqn={selectedData.fullyQualifiedName}
tags={tags} entityType={EntityType.GLOSSARY}
onTagsUpdate={handleTagsUpdate} permission={permissions.EditAll || permissions.EditTags}
selectedTags={tags ?? []}
tagType={TagSource.Classification}
onSelectionChange={handleTagsUpdate}
onThreadLinkSelect={onThreadLinkSelect}
/> />
)} )}
</div> </div>

View File

@ -48,6 +48,7 @@ describe('GlossaryDetailsRightPanel', () => {
isGlossary isGlossary
permissions={mockPermissions} permissions={mockPermissions}
selectedData={mockedGlossaries[0]} selectedData={mockedGlossaries[0]}
onThreadLinkSelect={jest.fn()}
onUpdate={jest.fn()} onUpdate={jest.fn()}
/> />
</BrowserRouter> </BrowserRouter>

View File

@ -63,6 +63,7 @@ const GlossaryTermsV1 = ({
updateVote, updateVote,
refreshActiveGlossaryTerm, refreshActiveGlossaryTerm,
isVersionView, isVersionView,
onThreadLinkSelect,
}: GlossaryTermsV1Props) => { }: GlossaryTermsV1Props) => {
const { const {
fqn: glossaryFqn, fqn: glossaryFqn,
@ -130,6 +131,7 @@ const GlossaryTermsV1 = ({
isVersionView={isVersionView} isVersionView={isVersionView}
permissions={permissions} permissions={permissions}
selectedData={glossaryTerm} selectedData={glossaryTerm}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)} onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)}
/> />
), ),

View File

@ -30,4 +30,5 @@ export interface GlossaryTermsV1Props {
onEditGlossaryTerm: (glossaryTerm: GlossaryTerm) => void; onEditGlossaryTerm: (glossaryTerm: GlossaryTerm) => void;
updateVote?: (data: VotingDataProps) => Promise<void>; updateVote?: (data: VotingDataProps) => Promise<void>;
refreshActiveGlossaryTerm?: () => void; refreshActiveGlossaryTerm?: () => void;
onThreadLinkSelect: (value: string) => void;
} }

View File

@ -83,6 +83,7 @@ const mockProps = {
refreshActiveGlossaryTerm: jest.fn(), refreshActiveGlossaryTerm: jest.fn(),
onAddGlossaryTerm: jest.fn(), onAddGlossaryTerm: jest.fn(),
onEditGlossaryTerm: jest.fn(), onEditGlossaryTerm: jest.fn(),
onThreadLinkSelect: jest.fn(),
}; };
describe('Test Glossary-term component', () => { describe('Test Glossary-term component', () => {

View File

@ -17,7 +17,7 @@ import { EntityType } from '../../../../enums/entity.enum';
import { Glossary } from '../../../../generated/entity/data/glossary'; import { Glossary } from '../../../../generated/entity/data/glossary';
import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm';
import { ChangeDescription } from '../../../../generated/entity/type'; import { ChangeDescription } from '../../../../generated/entity/type';
import { TagLabel } from '../../../../generated/type/tagLabel'; import { TagLabel, TagSource } from '../../../../generated/type/tagLabel';
import { getEntityName } from '../../../../utils/EntityUtils'; import { getEntityName } from '../../../../utils/EntityUtils';
import { import {
getEntityVersionByField, getEntityVersionByField,
@ -25,7 +25,8 @@ import {
} from '../../../../utils/EntityVersionUtils'; } from '../../../../utils/EntityVersionUtils';
import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1'; import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1';
import { OperationPermission } from '../../../PermissionProvider/PermissionProvider.interface'; import { OperationPermission } from '../../../PermissionProvider/PermissionProvider.interface';
import TagsInput from '../../../TagsInput/TagsInput.component'; import TagsContainerV2 from '../../../Tag/TagsContainerV2/TagsContainerV2';
import { DisplayType } from '../../../Tag/TagsViewer/TagsViewer.interface';
import GlossaryDetailsRightPanel from '../../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component'; import GlossaryDetailsRightPanel from '../../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component';
import GlossaryTermReferences from './GlossaryTermReferences'; import GlossaryTermReferences from './GlossaryTermReferences';
import GlossaryTermSynonyms from './GlossaryTermSynonyms'; import GlossaryTermSynonyms from './GlossaryTermSynonyms';
@ -37,6 +38,7 @@ type Props = {
onUpdate: (data: GlossaryTerm | Glossary) => Promise<void>; onUpdate: (data: GlossaryTerm | Glossary) => Promise<void>;
isGlossary: boolean; isGlossary: boolean;
isVersionView?: boolean; isVersionView?: boolean;
onThreadLinkSelect: (value: string) => void;
}; };
const GlossaryOverviewTab = ({ const GlossaryOverviewTab = ({
@ -45,6 +47,7 @@ const GlossaryOverviewTab = ({
onUpdate, onUpdate,
isGlossary, isGlossary,
isVersionView, isVersionView,
onThreadLinkSelect,
}: Props) => { }: Props) => {
const [isDescriptionEditable, setIsDescriptionEditable] = const [isDescriptionEditable, setIsDescriptionEditable] =
useState<boolean>(false); useState<boolean>(false);
@ -110,14 +113,17 @@ const GlossaryOverviewTab = ({
<Col span={24}> <Col span={24}>
<DescriptionV1 <DescriptionV1
description={glossaryDescription} description={glossaryDescription}
entityFqn={selectedData.fullyQualifiedName}
entityName={getEntityName(selectedData)} entityName={getEntityName(selectedData)}
entityType={EntityType.GLOSSARY} entityType={EntityType.GLOSSARY_TERM}
hasEditAccess={permissions.EditDescription || permissions.EditAll} hasEditAccess={permissions.EditDescription || permissions.EditAll}
isEdit={isDescriptionEditable} isEdit={isDescriptionEditable}
showCommentsIcon={false} owner={selectedData?.owner}
showActions={!selectedData.deleted}
onCancel={() => setIsDescriptionEditable(false)} onCancel={() => setIsDescriptionEditable(false)}
onDescriptionEdit={() => setIsDescriptionEditable(true)} onDescriptionEdit={() => setIsDescriptionEditable(true)}
onDescriptionUpdate={onDescriptionUpdate} onDescriptionUpdate={onDescriptionUpdate}
onThreadLinkSelect={onThreadLinkSelect}
/> />
</Col> </Col>
<Col span={24}> <Col span={24}>
@ -153,11 +159,15 @@ const GlossaryOverviewTab = ({
<Col span={12}> <Col span={12}>
<Space className="w-full" direction="vertical"> <Space className="w-full" direction="vertical">
<TagsInput <TagsContainerV2
editable={hasEditTagsPermissions} displayType={DisplayType.READ_MORE}
isVersionView={isVersionView} entityFqn={selectedData.fullyQualifiedName}
tags={tags} entityType={EntityType.GLOSSARY_TERM}
onTagsUpdate={handleTagsUpdate} permission={hasEditTagsPermissions}
selectedTags={tags ?? []}
tagType={TagSource.Classification}
onSelectionChange={handleTagsUpdate}
onThreadLinkSelect={onThreadLinkSelect}
/> />
</Space> </Space>
</Col> </Col>
@ -171,6 +181,7 @@ const GlossaryOverviewTab = ({
isVersionView={isVersionView} isVersionView={isVersionView}
permissions={permissions} permissions={permissions}
selectedData={selectedData} selectedData={selectedData}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={onUpdate} onUpdate={onUpdate}
/> />
</Col> </Col>

View File

@ -50,6 +50,7 @@ describe('GlossaryOverviewTab', () => {
isGlossary={isGlossary} isGlossary={isGlossary}
permissions={permissions} permissions={permissions}
selectedData={selectedData} selectedData={selectedData}
onThreadLinkSelect={jest.fn()}
onUpdate={onUpdate} onUpdate={onUpdate}
/> />
</BrowserRouter> </BrowserRouter>

View File

@ -25,9 +25,14 @@ import {
getGlossaryTermDetailsPath, getGlossaryTermDetailsPath,
} from '../../constants/constants'; } from '../../constants/constants';
import { EntityAction } from '../../enums/entity.enum'; import { EntityAction } from '../../enums/entity.enum';
import {
CreateThread,
ThreadType,
} from '../../generated/api/feed/createThread';
import { Glossary } from '../../generated/entity/data/glossary'; import { Glossary } from '../../generated/entity/data/glossary';
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm'; import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock'; import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock';
import { postThread } from '../../rest/feedsAPI';
import { import {
addGlossaryTerm, addGlossaryTerm,
getGlossaryTerms, getGlossaryTerms,
@ -37,6 +42,8 @@ import {
import { getEntityDeleteMessage } from '../../utils/CommonUtils'; import { getEntityDeleteMessage } from '../../utils/CommonUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
import { useActivityFeedProvider } from '../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal'; import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal';
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider'; import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
import { import {
@ -69,6 +76,11 @@ const GlossaryV1 = ({
const { action, tab } = const { action, tab } =
useParams<{ action: EntityAction; glossaryName: string; tab: string }>(); useParams<{ action: EntityAction; glossaryName: string; tab: string }>();
const history = useHistory(); const history = useHistory();
const [threadLink, setThreadLink] = useState<string>('');
const [threadType, setThreadType] = useState<ThreadType>(
ThreadType.Conversation
);
const { postFeed, deleteFeed, updateFeed } = useActivityFeedProvider();
const { getEntityPermission } = usePermissionProvider(); const { getEntityPermission } = usePermissionProvider();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@ -96,6 +108,17 @@ const GlossaryV1 = ({
[action] [action]
); );
const onThreadPanelClose = () => {
setThreadLink('');
};
const onThreadLinkSelect = (link: string, threadType?: ThreadType) => {
setThreadLink(link);
if (threadType) {
setThreadType(threadType);
}
};
const fetchGlossaryTerm = async ( const fetchGlossaryTerm = async (
params?: ListGlossaryTermsParams, params?: ListGlossaryTermsParams,
refresh?: boolean refresh?: boolean
@ -139,6 +162,19 @@ const GlossaryV1 = ({
} }
}; };
const createThread = async (data: CreateThread) => {
try {
await postThread(data);
} catch (error) {
showErrorToast(
error as AxiosError,
t('server.create-entity-error', {
entity: t('label.conversation'),
})
);
}
};
const handleDelete = () => { const handleDelete = () => {
const { id } = selectedData; const { id } = selectedData;
if (isGlossaryActive) { if (isGlossaryActive) {
@ -329,6 +365,7 @@ const GlossaryV1 = ({
onEditGlossaryTerm={(term) => onEditGlossaryTerm={(term) =>
handleGlossaryTermModalAction(true, term) handleGlossaryTermModalAction(true, term)
} }
onThreadLinkSelect={onThreadLinkSelect}
/> />
) : ( ) : (
<GlossaryTermsV1 <GlossaryTermsV1
@ -350,6 +387,7 @@ const GlossaryV1 = ({
onEditGlossaryTerm={(term) => onEditGlossaryTerm={(term) =>
handleGlossaryTermModalAction(true, term) handleGlossaryTermModalAction(true, term)
} }
onThreadLinkSelect={onThreadLinkSelect}
/> />
))} ))}
@ -376,6 +414,19 @@ const GlossaryV1 = ({
onSave={handleGlossaryTermSave} onSave={handleGlossaryTermSave}
/> />
)} )}
{threadLink ? (
<ActivityThreadPanel
createThread={createThread}
deletePostHandler={deleteFeed}
open={Boolean(threadLink)}
postFeedHandler={postFeed}
threadLink={threadLink}
threadType={threadType}
updateThreadHandler={updateFeed}
onCancel={onThreadPanelClose}
/>
) : null}
</> </>
); );
}; };

View File

@ -342,6 +342,7 @@ export const TaskTab = ({
</Button> </Button>
) : ( ) : (
<Dropdown.Button <Dropdown.Button
data-testid="edit-accept-task-dropdown"
icon={<DownOutlined />} icon={<DownOutlined />}
menu={{ menu={{
items: TASK_ACTION_LIST, items: TASK_ACTION_LIST,

View File

@ -16,6 +16,8 @@ import { Dashboard } from '../../generated/entity/data/dashboard';
import { DashboardDataModel } from '../../generated/entity/data/dashboardDataModel'; import { DashboardDataModel } from '../../generated/entity/data/dashboardDataModel';
import { Database } from '../../generated/entity/data/database'; import { Database } from '../../generated/entity/data/database';
import { DatabaseSchema } from '../../generated/entity/data/databaseSchema'; import { DatabaseSchema } from '../../generated/entity/data/databaseSchema';
import { Glossary } from '../../generated/entity/data/glossary';
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
import { Mlmodel } from '../../generated/entity/data/mlmodel'; import { Mlmodel } from '../../generated/entity/data/mlmodel';
import { Pipeline } from '../../generated/entity/data/pipeline'; import { Pipeline } from '../../generated/entity/data/pipeline';
import { SearchIndex } from '../../generated/entity/data/searchIndex'; import { SearchIndex } from '../../generated/entity/data/searchIndex';
@ -34,7 +36,9 @@ export type EntityData =
| Database | Database
| DatabaseSchema | DatabaseSchema
| DashboardDataModel | DashboardDataModel
| SearchIndex; | SearchIndex
| Glossary
| GlossaryTerm;
export interface Option { export interface Option {
label: string; label: string;

View File

@ -15,7 +15,10 @@ import { AxiosError } from 'axios';
import { isUndefined, omit } from 'lodash'; import { isUndefined, omit } from 'lodash';
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface'; import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface'; import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
import { WILD_CARD_CHAR } from '../constants/char.constants'; import {
FQN_SEPARATOR_CHAR,
WILD_CARD_CHAR,
} from '../constants/char.constants';
import { SearchIndex } from '../enums/search.enum'; import { SearchIndex } from '../enums/search.enum';
import { Glossary } from '../generated/entity/data/glossary'; import { Glossary } from '../generated/entity/data/glossary';
import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm'; import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm';
@ -24,6 +27,8 @@ import { SearchResponse } from '../interface/search.interface';
import { ListGlossaryTermsParams } from '../rest/glossaryAPI'; import { ListGlossaryTermsParams } from '../rest/glossaryAPI';
import { searchData } from '../rest/miscAPI'; import { searchData } from '../rest/miscAPI';
import { formatSearchGlossaryTermResponse } from './APIUtils'; import { formatSearchGlossaryTermResponse } from './APIUtils';
import Fqn from './Fqn';
import { getGlossaryPath } from './RouterUtils';
export interface GlossaryTermTreeNode { export interface GlossaryTermTreeNode {
children?: GlossaryTermTreeNode[]; children?: GlossaryTermTreeNode[];
@ -234,3 +239,26 @@ export const StatusFilters = Object.values(Status)
text: status, text: status,
value: status, value: status,
})); }));
export const getGlossaryBreadcrumbs = (fqn: string) => {
const arr = Fqn.split(fqn);
const dataFQN: Array<string> = [];
const breadcrumbList = [
{
name: 'Glossaries',
url: getGlossaryPath(''),
activeTitle: false,
},
...arr.map((d) => {
dataFQN.push(d);
return {
name: d,
url: getGlossaryPath(dataFQN.join(FQN_SEPARATOR_CHAR)),
activeTitle: false,
};
}),
];
return breadcrumbList;
};

View File

@ -53,6 +53,7 @@ import {
getDatabaseSchemaDetailsByFQN, getDatabaseSchemaDetailsByFQN,
} from '../rest/databaseAPI'; } from '../rest/databaseAPI';
import { getDataModelDetailsByFQN } from '../rest/dataModelsAPI'; import { getDataModelDetailsByFQN } from '../rest/dataModelsAPI';
import { getGlossariesByName, getGlossaryTermByFQN } from '../rest/glossaryAPI';
import { getUserSuggestions } from '../rest/miscAPI'; import { getUserSuggestions } from '../rest/miscAPI';
import { getMlModelByFQN } from '../rest/mlModelAPI'; import { getMlModelByFQN } from '../rest/mlModelAPI';
import { getPipelineByFqn } from '../rest/pipelineAPI'; import { getPipelineByFqn } from '../rest/pipelineAPI';
@ -73,11 +74,12 @@ import { defaultFields as DataModelFields } from './DataModelsUtils';
import { defaultFields as TableFields } from './DatasetDetailsUtils'; import { defaultFields as TableFields } from './DatasetDetailsUtils';
import { getEntityName } from './EntityUtils'; import { getEntityName } from './EntityUtils';
import { getEntityFQN, getEntityType } from './FeedUtils'; import { getEntityFQN, getEntityType } from './FeedUtils';
import { getGlossaryBreadcrumbs } from './GlossaryUtils';
import { defaultFields as MlModelFields } from './MlModelDetailsUtils'; import { defaultFields as MlModelFields } from './MlModelDetailsUtils';
import { defaultFields as PipelineFields } from './PipelineDetailsUtils'; import { defaultFields as PipelineFields } from './PipelineDetailsUtils';
import serviceUtilClassBase from './ServiceUtilClassBase'; import serviceUtilClassBase from './ServiceUtilClassBase';
import { STORED_PROCEDURE_DEFAULT_FIELDS } from './StoredProceduresUtils'; import { STORED_PROCEDURE_DEFAULT_FIELDS } from './StoredProceduresUtils';
import { getEncodedFqn } from './StringsUtils'; import { getDecodedFqn, getEncodedFqn } from './StringsUtils';
import { getEntityLink } from './TableUtils'; import { getEntityLink } from './TableUtils';
import { showErrorToast } from './ToastUtils'; import { showErrorToast } from './ToastUtils';
@ -275,6 +277,8 @@ export const TASK_ENTITIES = [
EntityType.DASHBOARD_DATA_MODEL, EntityType.DASHBOARD_DATA_MODEL,
EntityType.STORED_PROCEDURE, EntityType.STORED_PROCEDURE,
EntityType.SEARCH_INDEX, EntityType.SEARCH_INDEX,
EntityType.GLOSSARY,
EntityType.GLOSSARY_TERM,
]; ];
export const getBreadCrumbList = ( export const getBreadCrumbList = (
@ -308,12 +312,17 @@ export const getBreadCrumbList = (
const service = (serviceCategory: ServiceCategory) => { const service = (serviceCategory: ServiceCategory) => {
return { return {
name: getEntityName(entityData.service), name: getEntityName((entityData as Table).service),
url: getEntityName(entityData.service) url: getEntityName((entityData as Table).service)
? getServiceDetailsPath(entityData.service?.name || '', serviceCategory) ? getServiceDetailsPath(
(entityData as Table).service?.name ?? '',
serviceCategory
)
: '', : '',
imgSrc: entityData.serviceType imgSrc: (entityData as Table).serviceType
? serviceUtilClassBase.getServiceTypeLogo(entityData.serviceType) ? serviceUtilClassBase.getServiceTypeLogo(
(entityData as Table).serviceType as string
)
: undefined, : undefined,
}; };
}; };
@ -372,6 +381,11 @@ export const getBreadCrumbList = (
]; ];
} }
case EntityType.GLOSSARY:
case EntityType.GLOSSARY_TERM: {
return getGlossaryBreadcrumbs(entityData.fullyQualifiedName ?? '');
}
default: default:
return []; return [];
} }
@ -485,6 +499,22 @@ export const fetchEntityDetail = (
.catch((err: AxiosError) => showErrorToast(err)); .catch((err: AxiosError) => showErrorToast(err));
break; break;
case EntityType.GLOSSARY:
getGlossariesByName(entityFQN, TabSpecificField.TAGS)
.then((res) => {
setEntityData(res);
})
.catch((err: AxiosError) => showErrorToast(err));
break;
case EntityType.GLOSSARY_TERM:
getGlossaryTermByFQN(getDecodedFqn(entityFQN), TabSpecificField.TAGS)
.then((res) => {
setEntityData(res);
})
.catch((err: AxiosError) => showErrorToast(err));
break;
default: default:
break; break;