mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-18 14:06:59 +00:00
Appconfig fixes (#20960)
* fix initial config for json logic query builder * show alert for application * hide hidden fields from doc
This commit is contained in:
parent
b3d7f97590
commit
dbebaff32b
@ -17,6 +17,12 @@ const path = require('path');
|
|||||||
const SCHEMA_DIR = path.join(__dirname, './src/utils/ApplicationSchemas');
|
const SCHEMA_DIR = path.join(__dirname, './src/utils/ApplicationSchemas');
|
||||||
const DOCS_DIR = path.join(__dirname, './public/locales/en-US/Applications');
|
const DOCS_DIR = path.join(__dirname, './public/locales/en-US/Applications');
|
||||||
|
|
||||||
|
const IGNORE_FIELDS = [
|
||||||
|
'moduleConfiguration.dataAssets.serviceFilter',
|
||||||
|
'entityLink',
|
||||||
|
'type',
|
||||||
|
];
|
||||||
|
|
||||||
const resolveRef = (schema, ref) => {
|
const resolveRef = (schema, ref) => {
|
||||||
const path = ref.split('/').slice(1);
|
const path = ref.split('/').slice(1);
|
||||||
let current = schema;
|
let current = schema;
|
||||||
@ -27,6 +33,12 @@ const resolveRef = (schema, ref) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const processProperty = (key, prop, schema) => {
|
const processProperty = (key, prop, schema) => {
|
||||||
|
// Skip if the key is in IGNORE_FIELDS
|
||||||
|
const currentKey = key.split('.').pop();
|
||||||
|
if (IGNORE_FIELDS.includes(currentKey)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
let markdown = `$$section\n`;
|
let markdown = `$$section\n`;
|
||||||
markdown += `### ${prop.title || key} $(id="${key}")\n\n`;
|
markdown += `### ${prop.title || key} $(id="${key}")\n\n`;
|
||||||
|
|
||||||
|
@ -2,23 +2,9 @@
|
|||||||
|
|
||||||
Configuration for the AutoPilot Application.
|
Configuration for the AutoPilot Application.
|
||||||
|
|
||||||
$$section
|
|
||||||
### Application Type $(id="type")
|
|
||||||
|
|
||||||
Application Type
|
|
||||||
|
|
||||||
$$
|
|
||||||
|
|
||||||
$$section
|
$$section
|
||||||
### Active $(id="active")
|
### Active $(id="active")
|
||||||
|
|
||||||
Whether the AutoPilot Workflow should be active or not.
|
Whether the AutoPilot Workflow should be active or not.
|
||||||
|
|
||||||
$$
|
$$
|
||||||
|
|
||||||
$$section
|
|
||||||
### Service Entity Link $(id="entityLink")
|
|
||||||
|
|
||||||
Service Entity Link for which to trigger the application.
|
|
||||||
|
|
||||||
$$
|
|
@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
This schema defines configuration for the Data Insights Application.
|
This schema defines configuration for the Data Insights Application.
|
||||||
|
|
||||||
$$section
|
|
||||||
### Application Type $(id="type")
|
|
||||||
|
|
||||||
Application Type
|
|
||||||
|
|
||||||
$$
|
|
||||||
|
|
||||||
$$section
|
$$section
|
||||||
### batchSize $(id="batchSize")
|
### batchSize $(id="batchSize")
|
||||||
|
|
||||||
|
@ -30,13 +30,13 @@ import {
|
|||||||
ValueSource,
|
ValueSource,
|
||||||
} from 'react-awesome-query-builder';
|
} from 'react-awesome-query-builder';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { emptyJsonTree } from '../../../constants/AdvancedSearch.constants';
|
|
||||||
import { SearchIndex } from '../../../enums/search.enum';
|
import { SearchIndex } from '../../../enums/search.enum';
|
||||||
import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
|
import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
|
||||||
import { TabsInfoData } from '../../../pages/ExplorePage/ExplorePage.interface';
|
import { TabsInfoData } from '../../../pages/ExplorePage/ExplorePage.interface';
|
||||||
import { getAllCustomProperties } from '../../../rest/metadataTypeAPI';
|
import { getAllCustomProperties } from '../../../rest/metadataTypeAPI';
|
||||||
import advancedSearchClassBase from '../../../utils/AdvancedSearchClassBase';
|
import advancedSearchClassBase from '../../../utils/AdvancedSearchClassBase';
|
||||||
import {
|
import {
|
||||||
|
getEmptyJsonTree,
|
||||||
getTierOptions,
|
getTierOptions,
|
||||||
getTreeConfig,
|
getTreeConfig,
|
||||||
} from '../../../utils/AdvancedSearchUtils';
|
} from '../../../utils/AdvancedSearchUtils';
|
||||||
@ -114,7 +114,7 @@ export const AdvanceSearchProvider = ({
|
|||||||
const [initialised, setInitialised] = useState(false);
|
const [initialised, setInitialised] = useState(false);
|
||||||
|
|
||||||
const defaultTree = useMemo(
|
const defaultTree = useMemo(
|
||||||
() => QbUtils.checkTree(QbUtils.loadTree(emptyJsonTree), config),
|
() => QbUtils.checkTree(QbUtils.loadTree(getEmptyJsonTree()), config),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -197,7 +197,9 @@ export const AdvanceSearchProvider = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = useCallback(() => {
|
const handleReset = useCallback(() => {
|
||||||
setTreeInternal(QbUtils.checkTree(QbUtils.loadTree(emptyJsonTree), config));
|
setTreeInternal(
|
||||||
|
QbUtils.checkTree(QbUtils.loadTree(getEmptyJsonTree()), config)
|
||||||
|
);
|
||||||
setQueryFilter(undefined);
|
setQueryFilter(undefined);
|
||||||
setSQLQuery('');
|
setSQLQuery('');
|
||||||
}, [config]);
|
}, [config]);
|
||||||
|
@ -35,19 +35,23 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
Utils as QbUtils,
|
Utils as QbUtils,
|
||||||
} from 'react-awesome-query-builder';
|
} from 'react-awesome-query-builder';
|
||||||
|
import {
|
||||||
|
EntityFields,
|
||||||
|
EntityReferenceFields,
|
||||||
|
} from '../../../../../../enums/AdvancedSearch.enum';
|
||||||
import { EntityType } from '../../../../../../enums/entity.enum';
|
import { EntityType } from '../../../../../../enums/entity.enum';
|
||||||
import { SearchIndex } from '../../../../../../enums/search.enum';
|
import { SearchIndex } from '../../../../../../enums/search.enum';
|
||||||
import {
|
import { QueryFilterInterface } from '../../../../../../pages/ExplorePage/ExplorePage.interface';
|
||||||
EsBoolQuery,
|
|
||||||
QueryFieldInterface,
|
|
||||||
} from '../../../../../../pages/ExplorePage/ExplorePage.interface';
|
|
||||||
import { searchQuery } from '../../../../../../rest/searchAPI';
|
import { searchQuery } from '../../../../../../rest/searchAPI';
|
||||||
|
import { getEmptyJsonTree } from '../../../../../../utils/AdvancedSearchUtils';
|
||||||
import {
|
import {
|
||||||
elasticSearchFormat,
|
elasticSearchFormat,
|
||||||
elasticSearchFormatForJSONLogic,
|
elasticSearchFormatForJSONLogic,
|
||||||
} from '../../../../../../utils/QueryBuilderElasticsearchFormatUtils';
|
} from '../../../../../../utils/QueryBuilderElasticsearchFormatUtils';
|
||||||
import {
|
import {
|
||||||
|
addEntityTypeFilter,
|
||||||
elasticsearchToJsonLogic,
|
elasticsearchToJsonLogic,
|
||||||
|
getEntityTypeAggregationFilter,
|
||||||
getJsonTreeFromQueryFilter,
|
getJsonTreeFromQueryFilter,
|
||||||
jsonLogicToElasticsearch,
|
jsonLogicToElasticsearch,
|
||||||
READONLY_SETTINGS,
|
READONLY_SETTINGS,
|
||||||
@ -82,29 +86,46 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
const outputType = schema?.outputType ?? SearchOutputType.ElasticSearch;
|
const outputType = schema?.outputType ?? SearchOutputType.ElasticSearch;
|
||||||
const isSearchIndexUpdatedInContext = searchIndexFromContext === searchIndex;
|
const isSearchIndexUpdatedInContext = searchIndexFromContext === searchIndex;
|
||||||
const [initDone, setInitDone] = useState<boolean>(false);
|
const [initDone, setInitDone] = useState<boolean>(false);
|
||||||
|
const [queryURL, setQueryURL] = useState<string>('');
|
||||||
|
|
||||||
const fetchEntityCount = useCallback(
|
const fetchEntityCount = useCallback(
|
||||||
async (queryFilter: Record<string, unknown>) => {
|
async (queryFilter: Record<string, unknown>) => {
|
||||||
|
const qFilter = getEntityTypeAggregationFilter(
|
||||||
|
queryFilter as unknown as QueryFilterInterface,
|
||||||
|
entityType
|
||||||
|
);
|
||||||
|
|
||||||
|
const tree = QbUtils.checkTree(
|
||||||
|
QbUtils.loadTree(getJsonTreeFromQueryFilter(qFilter) as JsonTree),
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryFilterString = !isEmpty(tree)
|
||||||
|
? Qs.stringify({ queryFilter: JSON.stringify(tree) })
|
||||||
|
: '';
|
||||||
|
|
||||||
|
setQueryURL(`${getExplorePath({})}${queryFilterString}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsCountLoading(true);
|
setIsCountLoading(true);
|
||||||
const res = await searchQuery({
|
const res = await searchQuery({
|
||||||
query: '',
|
query: '',
|
||||||
pageNumber: 0,
|
pageNumber: 0,
|
||||||
pageSize: 0,
|
pageSize: 0,
|
||||||
queryFilter,
|
queryFilter: qFilter as unknown as Record<string, unknown>,
|
||||||
searchIndex: SearchIndex.ALL,
|
searchIndex: SearchIndex.ALL,
|
||||||
includeDeleted: false,
|
includeDeleted: false,
|
||||||
trackTotalHits: true,
|
trackTotalHits: true,
|
||||||
fetchSource: false,
|
fetchSource: false,
|
||||||
});
|
});
|
||||||
setSearchResults(res.hits.total.value ?? 0);
|
setSearchResults(res.hits.total.value ?? 0);
|
||||||
} catch (_) {
|
} catch {
|
||||||
// silent fail
|
// silent fail
|
||||||
} finally {
|
} finally {
|
||||||
setIsCountLoading(false);
|
setIsCountLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[]
|
[entityType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const debouncedFetchEntityCount = useMemo(
|
const debouncedFetchEntityCount = useMemo(
|
||||||
@ -112,14 +133,6 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
[fetchEntityCount]
|
[fetchEntityCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
const queryURL = useMemo(() => {
|
|
||||||
const queryFilterString = !isEmpty(treeInternal)
|
|
||||||
? Qs.stringify({ queryFilter: JSON.stringify(treeInternal) })
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return `${getExplorePath({})}${queryFilterString}`;
|
|
||||||
}, [treeInternal]);
|
|
||||||
|
|
||||||
const showFilteredResourceCount = useMemo(
|
const showFilteredResourceCount = useMemo(
|
||||||
() =>
|
() =>
|
||||||
outputType === SearchOutputType.ElasticSearch &&
|
outputType === SearchOutputType.ElasticSearch &&
|
||||||
@ -138,31 +151,14 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
query: data,
|
query: data,
|
||||||
};
|
};
|
||||||
if (data) {
|
if (data) {
|
||||||
if (entityType !== EntityType.ALL) {
|
const qFilterWithEntityType = addEntityTypeFilter(
|
||||||
// Scope the search to the passed entity type
|
qFilter as unknown as QueryFilterInterface,
|
||||||
if (
|
entityType
|
||||||
Array.isArray(
|
);
|
||||||
((qFilter.query as QueryFieldInterface)?.bool as EsBoolQuery)
|
|
||||||
?.must
|
debouncedFetchEntityCount(
|
||||||
)
|
qFilterWithEntityType as unknown as Record<string, unknown>
|
||||||
) {
|
);
|
||||||
(
|
|
||||||
(qFilter.query as QueryFieldInterface)?.bool
|
|
||||||
?.must as QueryFieldInterface[]
|
|
||||||
)?.push({
|
|
||||||
bool: {
|
|
||||||
must: [
|
|
||||||
{
|
|
||||||
term: {
|
|
||||||
entityType: entityType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debouncedFetchEntityCount(qFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(!isEmpty(data) ? JSON.stringify(qFilter) : '');
|
onChange(!isEmpty(data) ? JSON.stringify(qFilter) : '');
|
||||||
@ -187,22 +183,22 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
|
|
||||||
const loadDefaultValueInTree = useCallback(() => {
|
const loadDefaultValueInTree = useCallback(() => {
|
||||||
if (!isEmpty(value)) {
|
if (!isEmpty(value)) {
|
||||||
|
const parsedValue = JSON.parse(value ?? '{}');
|
||||||
if (outputType === SearchOutputType.ElasticSearch) {
|
if (outputType === SearchOutputType.ElasticSearch) {
|
||||||
const parsedTree = getJsonTreeFromQueryFilter(
|
const parsedTree = getJsonTreeFromQueryFilter(
|
||||||
JSON.parse(value || ''),
|
parsedValue,
|
||||||
config.fields
|
config.fields
|
||||||
) as JsonTree;
|
) as JsonTree;
|
||||||
|
|
||||||
if (Object.keys(parsedTree).length > 0) {
|
if (Object.keys(parsedTree).length > 0) {
|
||||||
const tree = QbUtils.checkTree(QbUtils.loadTree(parsedTree), config);
|
const tree = QbUtils.checkTree(QbUtils.loadTree(parsedTree), config);
|
||||||
onTreeUpdate(tree, config);
|
onTreeUpdate(tree, config);
|
||||||
|
// Fetch count for default value
|
||||||
|
debouncedFetchEntityCount(parsedValue);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const query = jsonLogicToElasticsearch(
|
const query = jsonLogicToElasticsearch(parsedValue, config.fields);
|
||||||
JSON.parse(value || ''),
|
|
||||||
config.fields
|
|
||||||
);
|
|
||||||
const updatedQ = {
|
const updatedQ = {
|
||||||
query: query,
|
query: query,
|
||||||
};
|
};
|
||||||
@ -225,6 +221,16 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const emptyJsonTree = getEmptyJsonTree(
|
||||||
|
outputType === SearchOutputType.JSONLogic
|
||||||
|
? EntityReferenceFields.OWNERS
|
||||||
|
: EntityFields.OWNERS
|
||||||
|
);
|
||||||
|
onTreeUpdate(
|
||||||
|
QbUtils.checkTree(QbUtils.loadTree(emptyJsonTree), config),
|
||||||
|
config
|
||||||
|
);
|
||||||
}
|
}
|
||||||
setInitDone(true);
|
setInitDone(true);
|
||||||
}, [config, value, outputType]);
|
}, [config, value, outputType]);
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { JsonTree, Utils as QbUtils } from 'react-awesome-query-builder';
|
|
||||||
import { EntityFields } from '../enums/AdvancedSearch.enum';
|
import { EntityFields } from '../enums/AdvancedSearch.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
|
|
||||||
@ -304,39 +303,6 @@ export const RANGE_FIELD_OPERATORS = ['between', 'not_between'];
|
|||||||
|
|
||||||
export const LIST_VALUE_OPERATORS = ['select_equals', 'select_not_equals'];
|
export const LIST_VALUE_OPERATORS = ['select_equals', 'select_not_equals'];
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a query builder tree with a group containing an empty rule
|
|
||||||
*/
|
|
||||||
export const emptyJsonTree: JsonTree = {
|
|
||||||
id: QbUtils.uuid(),
|
|
||||||
type: 'group',
|
|
||||||
properties: {
|
|
||||||
conjunction: 'AND',
|
|
||||||
not: false,
|
|
||||||
},
|
|
||||||
children1: {
|
|
||||||
[QbUtils.uuid()]: {
|
|
||||||
type: 'group',
|
|
||||||
properties: {
|
|
||||||
conjunction: 'AND',
|
|
||||||
not: false,
|
|
||||||
},
|
|
||||||
children1: {
|
|
||||||
[QbUtils.uuid()]: {
|
|
||||||
type: 'rule',
|
|
||||||
properties: {
|
|
||||||
// owner is common field , so setting owner as default field here
|
|
||||||
field: EntityFields.OWNERS,
|
|
||||||
operator: null,
|
|
||||||
value: [],
|
|
||||||
valueSrc: ['value'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MISC_FIELDS = ['owner.displayName', 'tags.tagFQN'];
|
export const MISC_FIELDS = ['owner.displayName', 'tags.tagFQN'];
|
||||||
|
|
||||||
export const OWNER_QUICK_FILTER_DEFAULT_OPTIONS_KEY = 'displayName.keyword';
|
export const OWNER_QUICK_FILTER_DEFAULT_OPTIONS_KEY = 'displayName.keyword';
|
||||||
|
@ -12,10 +12,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { SearchDropdownOption } from '../components/SearchDropdown/SearchDropdown.interface';
|
import { SearchDropdownOption } from '../components/SearchDropdown/SearchDropdown.interface';
|
||||||
|
import { EntityFields } from '../enums/AdvancedSearch.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
import {
|
import {
|
||||||
getChartsOptions,
|
getChartsOptions,
|
||||||
getColumnsOptions,
|
getColumnsOptions,
|
||||||
|
getEmptyJsonTree,
|
||||||
getOptionsFromAggregationBucket,
|
getOptionsFromAggregationBucket,
|
||||||
getSchemaFieldOptions,
|
getSchemaFieldOptions,
|
||||||
getSearchDropdownLabels,
|
getSearchDropdownLabels,
|
||||||
@ -45,6 +47,13 @@ import {
|
|||||||
mockShortOptionsArray,
|
mockShortOptionsArray,
|
||||||
} from './mocks/AdvancedSearchUtils.mock';
|
} from './mocks/AdvancedSearchUtils.mock';
|
||||||
|
|
||||||
|
// Mock QbUtils
|
||||||
|
jest.mock('react-awesome-query-builder', () => ({
|
||||||
|
Utils: {
|
||||||
|
uuid: jest.fn().mockReturnValue('test-uuid'),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
describe('AdvancedSearchUtils tests', () => {
|
describe('AdvancedSearchUtils tests', () => {
|
||||||
it('Function getSearchDropdownLabels should return menuItems for passed options', () => {
|
it('Function getSearchDropdownLabels should return menuItems for passed options', () => {
|
||||||
const resultMenuItems = getSearchDropdownLabels(mockOptionsArray, true);
|
const resultMenuItems = getSearchDropdownLabels(mockOptionsArray, true);
|
||||||
@ -215,4 +224,55 @@ describe('AdvancedSearchUtils tests', () => {
|
|||||||
{ count: 3, key: 'chart', label: 'chart' },
|
{ count: 3, key: 'chart', label: 'chart' },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getEmptyJsonTree', () => {
|
||||||
|
it('should return a default JsonTree structure with OWNERS as the default field', () => {
|
||||||
|
const result = getEmptyJsonTree();
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
id: 'test-uuid',
|
||||||
|
type: 'group',
|
||||||
|
properties: {
|
||||||
|
conjunction: 'AND',
|
||||||
|
not: false,
|
||||||
|
},
|
||||||
|
children1: {
|
||||||
|
'test-uuid': {
|
||||||
|
type: 'group',
|
||||||
|
properties: {
|
||||||
|
conjunction: 'AND',
|
||||||
|
not: false,
|
||||||
|
},
|
||||||
|
children1: {
|
||||||
|
'test-uuid': {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: EntityFields.OWNERS,
|
||||||
|
operator: null,
|
||||||
|
value: [],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the provided field when passed as parameter', () => {
|
||||||
|
const customField = EntityFields.TAG;
|
||||||
|
const result = getEmptyJsonTree(customField);
|
||||||
|
|
||||||
|
const children1 = result.children1 as Record<
|
||||||
|
string,
|
||||||
|
{ children1: Record<string, { properties: { field: string } }> }
|
||||||
|
>;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
children1['test-uuid'].children1['test-uuid'].properties.field
|
||||||
|
).toEqual(customField);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,8 +18,10 @@ import { isArray, isEmpty, toLower } from 'lodash';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
AsyncFetchListValues,
|
AsyncFetchListValues,
|
||||||
|
JsonTree,
|
||||||
ListValues,
|
ListValues,
|
||||||
RenderSettings,
|
RenderSettings,
|
||||||
|
Utils as QbUtils,
|
||||||
} from 'react-awesome-query-builder';
|
} from 'react-awesome-query-builder';
|
||||||
import { ReactComponent as IconDeleteColored } from '../assets/svg/ic-delete-colored.svg';
|
import { ReactComponent as IconDeleteColored } from '../assets/svg/ic-delete-colored.svg';
|
||||||
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
|
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
|
||||||
@ -33,6 +35,7 @@ import {
|
|||||||
LINEAGE_DROPDOWN_ITEMS,
|
LINEAGE_DROPDOWN_ITEMS,
|
||||||
} from '../constants/AdvancedSearch.constants';
|
} from '../constants/AdvancedSearch.constants';
|
||||||
import { NOT_INCLUDE_AGGREGATION_QUICK_FILTER } from '../constants/explore.constants';
|
import { NOT_INCLUDE_AGGREGATION_QUICK_FILTER } from '../constants/explore.constants';
|
||||||
|
import { EntityFields } from '../enums/AdvancedSearch.enum';
|
||||||
import { EntityType } from '../enums/entity.enum';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
import {
|
import {
|
||||||
@ -410,3 +413,36 @@ export const getCustomPropertyAdvanceSearchEnumOptions = (
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getEmptyJsonTree = (
|
||||||
|
defaultField: string = EntityFields.OWNERS
|
||||||
|
): JsonTree => {
|
||||||
|
return {
|
||||||
|
id: QbUtils.uuid(),
|
||||||
|
type: 'group',
|
||||||
|
properties: {
|
||||||
|
conjunction: 'AND',
|
||||||
|
not: false,
|
||||||
|
},
|
||||||
|
children1: {
|
||||||
|
[QbUtils.uuid()]: {
|
||||||
|
type: 'group',
|
||||||
|
properties: {
|
||||||
|
conjunction: 'AND',
|
||||||
|
not: false,
|
||||||
|
},
|
||||||
|
children1: {
|
||||||
|
[QbUtils.uuid()]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: defaultField,
|
||||||
|
operator: null,
|
||||||
|
value: [],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -11,8 +11,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Fields } from 'react-awesome-query-builder';
|
import { Fields } from 'react-awesome-query-builder';
|
||||||
import { QueryFilterInterface } from '../pages/ExplorePage/ExplorePage.interface';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import {
|
import {
|
||||||
|
QueryFieldInterface,
|
||||||
|
QueryFilterInterface,
|
||||||
|
} from '../pages/ExplorePage/ExplorePage.interface';
|
||||||
|
import {
|
||||||
|
addEntityTypeFilter,
|
||||||
|
getEntityTypeAggregationFilter,
|
||||||
getJsonTreeFromQueryFilter,
|
getJsonTreeFromQueryFilter,
|
||||||
resolveFieldType,
|
resolveFieldType,
|
||||||
} from './QueryBuilderUtils';
|
} from './QueryBuilderUtils';
|
||||||
@ -152,3 +158,175 @@ describe('resolveFieldType', () => {
|
|||||||
expect(resolveFieldType(undefined, 'name')).toBe('');
|
expect(resolveFieldType(undefined, 'name')).toBe('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('addEntityTypeFilter', () => {
|
||||||
|
const baseQueryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
field1: 'value1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return the original filter when entityType is ALL', () => {
|
||||||
|
const result = addEntityTypeFilter({ ...baseQueryFilter }, EntityType.ALL);
|
||||||
|
|
||||||
|
expect(result).toEqual(baseQueryFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add entity type filter for non-ALL entity types', () => {
|
||||||
|
const result = addEntityTypeFilter(
|
||||||
|
{ ...baseQueryFilter },
|
||||||
|
EntityType.TABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert the must array exists and has correct length
|
||||||
|
expect(result.query?.bool?.must).toBeDefined();
|
||||||
|
|
||||||
|
const mustArray = result.query?.bool?.must as QueryFieldInterface[];
|
||||||
|
|
||||||
|
expect(Array.isArray(mustArray)).toBe(true);
|
||||||
|
expect(mustArray).toHaveLength(2);
|
||||||
|
|
||||||
|
// Assert the entity type filter is added correctly
|
||||||
|
expect(mustArray[1]).toEqual({
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
entityType: EntityType.TABLE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undefined must array gracefully', () => {
|
||||||
|
const queryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = addEntityTypeFilter(queryFilter, EntityType.TABLE);
|
||||||
|
|
||||||
|
expect(result).toEqual(queryFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty query gracefully', () => {
|
||||||
|
const queryFilter = {} as QueryFilterInterface;
|
||||||
|
const result = addEntityTypeFilter(queryFilter, EntityType.TABLE);
|
||||||
|
|
||||||
|
expect(result).toEqual(queryFilter);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEntityTypeAggregationFilter', () => {
|
||||||
|
const baseQueryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
field1: 'value1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should add entity type to the first must block', () => {
|
||||||
|
const result = getEntityTypeAggregationFilter(
|
||||||
|
{ ...baseQueryFilter },
|
||||||
|
EntityType.TABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert the must array exists
|
||||||
|
expect(result.query?.bool?.must).toBeDefined();
|
||||||
|
|
||||||
|
const mustArray = result.query?.bool?.must as QueryFieldInterface[];
|
||||||
|
|
||||||
|
expect(Array.isArray(mustArray)).toBe(true);
|
||||||
|
expect(mustArray.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Get the first must block and assert its structure
|
||||||
|
const firstMustBlock = mustArray[0];
|
||||||
|
const mustBlockArray = firstMustBlock.bool?.must as QueryFieldInterface[];
|
||||||
|
|
||||||
|
expect(mustBlockArray).toBeDefined();
|
||||||
|
expect(Array.isArray(mustBlockArray)).toBe(true);
|
||||||
|
expect(mustBlockArray).toHaveLength(2);
|
||||||
|
|
||||||
|
// Assert the entity type filter is added correctly
|
||||||
|
expect(mustBlockArray[1]).toEqual({
|
||||||
|
term: {
|
||||||
|
entityType: EntityType.TABLE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undefined must array in first block gracefully', () => {
|
||||||
|
const queryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
bool: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = getEntityTypeAggregationFilter(
|
||||||
|
queryFilter,
|
||||||
|
EntityType.TABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(queryFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty must array gracefully', () => {
|
||||||
|
const queryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = getEntityTypeAggregationFilter(
|
||||||
|
queryFilter,
|
||||||
|
EntityType.TABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(queryFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty query gracefully', () => {
|
||||||
|
const queryFilter = {} as QueryFilterInterface;
|
||||||
|
const result = getEntityTypeAggregationFilter(
|
||||||
|
queryFilter,
|
||||||
|
EntityType.TABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(queryFilter);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
RenderSettings,
|
RenderSettings,
|
||||||
} from 'react-awesome-query-builder';
|
} from 'react-awesome-query-builder';
|
||||||
import { EntityReferenceFields } from '../enums/AdvancedSearch.enum';
|
import { EntityReferenceFields } from '../enums/AdvancedSearch.enum';
|
||||||
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import {
|
import {
|
||||||
EsBoolQuery,
|
EsBoolQuery,
|
||||||
EsExistsQuery,
|
EsExistsQuery,
|
||||||
@ -801,3 +802,54 @@ export const jsonLogicToElasticsearch = (
|
|||||||
|
|
||||||
throw new Error('Unsupported JSON Logic format');
|
throw new Error('Unsupported JSON Logic format');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds entity type filter to the query filter if entity type is specified
|
||||||
|
* @param qFilter Query filter to add entity type to
|
||||||
|
* @param entityType Entity type to filter by
|
||||||
|
* @returns Updated query filter with entity type
|
||||||
|
*/
|
||||||
|
export const addEntityTypeFilter = (
|
||||||
|
qFilter: QueryFilterInterface,
|
||||||
|
entityType: string
|
||||||
|
): QueryFilterInterface => {
|
||||||
|
if (entityType === EntityType.ALL) {
|
||||||
|
return qFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray((qFilter.query?.bool as EsBoolQuery)?.must)) {
|
||||||
|
(qFilter.query?.bool?.must as QueryFieldInterface[])?.push({
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
entityType: entityType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return qFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEntityTypeAggregationFilter = (
|
||||||
|
qFilter: QueryFilterInterface,
|
||||||
|
entityType: string
|
||||||
|
): QueryFilterInterface => {
|
||||||
|
if (Array.isArray((qFilter.query?.bool as EsBoolQuery)?.must)) {
|
||||||
|
const firstMustBlock = (
|
||||||
|
qFilter.query?.bool?.must as QueryFieldInterface[]
|
||||||
|
)[0];
|
||||||
|
if (firstMustBlock?.bool?.must) {
|
||||||
|
(firstMustBlock.bool.must as QueryFieldInterface[]).push({
|
||||||
|
term: {
|
||||||
|
entityType: entityType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qFilter;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user