mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-22 14:24:35 +00:00
Minor: add base class for tags (#14218)
* Minor: exclude knowledge center classification tags from tag suggestion * add base class for tags * add unit test * clean up constants
This commit is contained in:
parent
5f88c8ce9d
commit
7ec6d77a80
@ -30,7 +30,6 @@ export type TagsContainerV2Props = {
|
|||||||
children?: ReactElement;
|
children?: ReactElement;
|
||||||
displayType?: DisplayType;
|
displayType?: DisplayType;
|
||||||
layoutType?: LayoutType;
|
layoutType?: LayoutType;
|
||||||
filterClassifications?: string[];
|
|
||||||
onSelectionChange?: (selectedTags: EntityTags[]) => Promise<void>;
|
onSelectionChange?: (selectedTags: EntityTags[]) => Promise<void>;
|
||||||
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
||||||
};
|
};
|
||||||
|
@ -22,19 +22,13 @@ import { ReactComponent as IconComments } from '../../../assets/svg/comment.svg'
|
|||||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||||
import { ReactComponent as IconRequest } from '../../../assets/svg/request-icon.svg';
|
import { ReactComponent as IconRequest } from '../../../assets/svg/request-icon.svg';
|
||||||
import { TableTagsProps } from '../../../components/TableTags/TableTags.interface';
|
import { TableTagsProps } from '../../../components/TableTags/TableTags.interface';
|
||||||
import {
|
import { DE_ACTIVE_COLOR } from '../../../constants/constants';
|
||||||
DE_ACTIVE_COLOR,
|
|
||||||
KNOWLEDGE_CENTER_CLASSIFICATION,
|
|
||||||
} from '../../../constants/constants';
|
|
||||||
import { TAG_CONSTANT, TAG_START_WITH } from '../../../constants/Tag.constants';
|
import { TAG_CONSTANT, TAG_START_WITH } from '../../../constants/Tag.constants';
|
||||||
import { TagSource } from '../../../generated/type/tagLabel';
|
import { TagSource } from '../../../generated/type/tagLabel';
|
||||||
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
||||||
import { getFilterTags } from '../../../utils/TableTags/TableTags.utils';
|
import { getFilterTags } from '../../../utils/TableTags/TableTags.utils';
|
||||||
import {
|
import tagClassBase from '../../../utils/TagClassBase';
|
||||||
fetchGlossaryList,
|
import { fetchGlossaryList, getTagPlaceholder } from '../../../utils/TagsUtils';
|
||||||
fetchTagsElasticSearch,
|
|
||||||
getTagPlaceholder,
|
|
||||||
} from '../../../utils/TagsUtils';
|
|
||||||
import {
|
import {
|
||||||
getRequestTagsPath,
|
getRequestTagsPath,
|
||||||
getUpdateTagsPath,
|
getUpdateTagsPath,
|
||||||
@ -62,7 +56,6 @@ const TagsContainerV2 = ({
|
|||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
onThreadLinkSelect,
|
onThreadLinkSelect,
|
||||||
children,
|
children,
|
||||||
filterClassifications = [KNOWLEDGE_CENTER_CLASSIFICATION],
|
|
||||||
}: TagsContainerV2Props) => {
|
}: TagsContainerV2Props) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@ -95,12 +88,12 @@ const TagsContainerV2 = ({
|
|||||||
const fetchAPI = useCallback(
|
const fetchAPI = useCallback(
|
||||||
(searchValue: string, page: number) => {
|
(searchValue: string, page: number) => {
|
||||||
if (tagType === TagSource.Classification) {
|
if (tagType === TagSource.Classification) {
|
||||||
return fetchTagsElasticSearch(searchValue, page, filterClassifications);
|
return tagClassBase.getTags(searchValue, page);
|
||||||
} else {
|
} else {
|
||||||
return fetchGlossaryList(searchValue, page);
|
return fetchGlossaryList(searchValue, page);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[tagType, filterClassifications]
|
[tagType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showNoDataPlaceholder = useMemo(
|
const showNoDataPlaceholder = useMemo(
|
||||||
|
@ -818,5 +818,3 @@ export const ICON_DIMENSION = {
|
|||||||
export const COMMON_ICON_STYLES: CSSProperties = {
|
export const COMMON_ICON_STYLES: CSSProperties = {
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KNOWLEDGE_CENTER_CLASSIFICATION = 'KnowledgeCenter';
|
|
||||||
|
@ -20,10 +20,8 @@ import AsyncSelectList from '../../../components/AsyncSelectList/AsyncSelectList
|
|||||||
import { SelectOption } from '../../../components/AsyncSelectList/AsyncSelectList.interface';
|
import { SelectOption } from '../../../components/AsyncSelectList/AsyncSelectList.interface';
|
||||||
import { TagSource } from '../../../generated/entity/data/container';
|
import { TagSource } from '../../../generated/entity/data/container';
|
||||||
import { TagLabel } from '../../../generated/type/tagLabel';
|
import { TagLabel } from '../../../generated/type/tagLabel';
|
||||||
import {
|
import tagClassBase from '../../../utils/TagClassBase';
|
||||||
fetchGlossaryList,
|
import { fetchGlossaryList } from '../../../utils/TagsUtils';
|
||||||
fetchTagsElasticSearch,
|
|
||||||
} from '../../../utils/TagsUtils';
|
|
||||||
|
|
||||||
export interface TagSuggestionProps {
|
export interface TagSuggestionProps {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@ -83,7 +81,7 @@ const TagSuggestion: React.FC<TagSuggestionProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncSelectList
|
<AsyncSelectList
|
||||||
fetchOptions={isGlossaryType ? fetchGlossaryList : fetchTagsElasticSearch}
|
fetchOptions={isGlossaryType ? fetchGlossaryList : tagClassBase.getTags}
|
||||||
initialOptions={initialOptions}
|
initialOptions={initialOptions}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder={
|
placeholder={
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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 { SearchIndex } from '../enums/search.enum';
|
||||||
|
import { searchQuery } from '../rest/searchAPI';
|
||||||
|
import tagClassBase, { TagClassBase } from './TagClassBase';
|
||||||
|
|
||||||
|
jest.mock('../rest/searchAPI');
|
||||||
|
|
||||||
|
describe('TagClassBase', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(searchQuery as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance of TagClassBase', () => {
|
||||||
|
expect(tagClassBase).toBeInstanceOf(TagClassBase);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call searchQuery with correct parameters', async () => {
|
||||||
|
const searchText = 'test';
|
||||||
|
const page = 1;
|
||||||
|
|
||||||
|
const mockResponse = {
|
||||||
|
hits: {
|
||||||
|
hits: [
|
||||||
|
{
|
||||||
|
_source: {
|
||||||
|
fullyQualifiedName: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: {
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(searchQuery as jest.Mock).mockResolvedValue(mockResponse);
|
||||||
|
await tagClassBase.getTags(searchText, page);
|
||||||
|
|
||||||
|
expect(searchQuery).toHaveBeenCalledWith({
|
||||||
|
query: `*${searchText}*`,
|
||||||
|
filters: 'disabled:false',
|
||||||
|
pageNumber: page,
|
||||||
|
pageSize: 10, // Assuming PAGE_SIZE is 10
|
||||||
|
queryFilter: {},
|
||||||
|
searchIndex: SearchIndex.TAG,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct data structure', async () => {
|
||||||
|
const searchText = 'test';
|
||||||
|
const page = 1;
|
||||||
|
const mockResponse = {
|
||||||
|
hits: {
|
||||||
|
hits: [
|
||||||
|
{
|
||||||
|
_source: {
|
||||||
|
fullyQualifiedName: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: {
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(searchQuery as jest.Mock).mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
const result = await tagClassBase.getTags(searchText, page);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
label: 'test',
|
||||||
|
value: 'test',
|
||||||
|
data: {
|
||||||
|
fullyQualifiedName: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
paging: {
|
||||||
|
total: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 { PAGE_SIZE } from '../constants/constants';
|
||||||
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
|
import { searchQuery } from '../rest/searchAPI';
|
||||||
|
|
||||||
|
class TagClassBase {
|
||||||
|
public async getTags(searchText: string, page: number) {
|
||||||
|
const res = await searchQuery({
|
||||||
|
query: `*${searchText}*`,
|
||||||
|
filters: 'disabled:false',
|
||||||
|
pageNumber: page,
|
||||||
|
pageSize: PAGE_SIZE,
|
||||||
|
queryFilter: {},
|
||||||
|
searchIndex: SearchIndex.TAG,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: res.hits.hits.map(({ _source }) => ({
|
||||||
|
label: _source.fullyQualifiedName ?? '',
|
||||||
|
value: _source.fullyQualifiedName ?? '',
|
||||||
|
data: _source,
|
||||||
|
})),
|
||||||
|
paging: {
|
||||||
|
total: res.hits.total.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagClassBase = new TagClassBase();
|
||||||
|
|
||||||
|
export default tagClassBase;
|
||||||
|
export { TagClassBase };
|
@ -23,7 +23,7 @@ import { ReactComponent as DeleteIcon } from '../assets/svg/ic-delete.svg';
|
|||||||
import RichTextEditorPreviewer from '../components/common/RichTextEditor/RichTextEditorPreviewer';
|
import RichTextEditorPreviewer from '../components/common/RichTextEditor/RichTextEditorPreviewer';
|
||||||
import Loader from '../components/Loader/Loader';
|
import Loader from '../components/Loader/Loader';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
||||||
import { getExplorePath, PAGE_SIZE } from '../constants/constants';
|
import { getExplorePath } from '../constants/constants';
|
||||||
import { SettledStatus } from '../enums/axios.enum';
|
import { SettledStatus } from '../enums/axios.enum';
|
||||||
import { ExplorePageTabs } from '../enums/Explore.enum';
|
import { ExplorePageTabs } from '../enums/Explore.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
@ -296,50 +296,12 @@ export const tagRender = (customTagProps: CustomTagProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type ResultType = {
|
export type ResultType = {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
data: Tag;
|
data: Tag;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchTagsElasticSearch = async (
|
|
||||||
searchText: string,
|
|
||||||
page: number,
|
|
||||||
filterClassifications?: string[]
|
|
||||||
): Promise<{
|
|
||||||
data: ResultType[];
|
|
||||||
paging: Paging;
|
|
||||||
}> => {
|
|
||||||
const res = await searchQuery({
|
|
||||||
query: `*${searchText}*`,
|
|
||||||
filters: 'disabled:false',
|
|
||||||
pageNumber: page,
|
|
||||||
pageSize: PAGE_SIZE,
|
|
||||||
queryFilter: {},
|
|
||||||
searchIndex: SearchIndex.TAG,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: res.hits.hits.reduce((result: ResultType[], { _source }) => {
|
|
||||||
const classificationName =
|
|
||||||
_source.classification?.fullyQualifiedName ?? '';
|
|
||||||
|
|
||||||
if (!filterClassifications?.includes(classificationName)) {
|
|
||||||
result.push({
|
|
||||||
label: _source.fullyQualifiedName ?? '',
|
|
||||||
value: _source.fullyQualifiedName ?? '',
|
|
||||||
data: _source,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, []),
|
|
||||||
paging: {
|
|
||||||
total: res.hits.total.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchGlossaryList = async (
|
export const fetchGlossaryList = async (
|
||||||
searchQueryParam: string,
|
searchQueryParam: string,
|
||||||
page: number
|
page: number
|
||||||
|
Loading…
x
Reference in New Issue
Block a user