mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-29 02:48:24 +00:00
feat(siblings) Add flag to prevent combining siblings in the UI (#10952)
This commit is contained in:
parent
30e9cfdce0
commit
eace710aee
@ -21,4 +21,5 @@ public class FeatureFlags {
|
||||
private boolean schemaFieldEntityFetchEnabled = false;
|
||||
private boolean businessAttributeEntityEnabled = false;
|
||||
private boolean dataContractsEnabled = false;
|
||||
private boolean showSeparateSiblings = false;
|
||||
}
|
||||
|
||||
@ -186,6 +186,7 @@ public class AppConfigResolver implements DataFetcher<CompletableFuture<AppConfi
|
||||
.setNestedDomainsEnabled(_featureFlags.isNestedDomainsEnabled())
|
||||
.setPlatformBrowseV2(_featureFlags.isPlatformBrowseV2())
|
||||
.setDataContractsEnabled(_featureFlags.isDataContractsEnabled())
|
||||
.setShowSeparateSiblings(_featureFlags.isShowSeparateSiblings())
|
||||
.build();
|
||||
|
||||
appConfig.setFeatureFlags(featureFlagsConfig);
|
||||
|
||||
@ -497,6 +497,11 @@ type FeatureFlagsConfig {
|
||||
Whether data contracts should be enabled
|
||||
"""
|
||||
dataContractsEnabled: Boolean!
|
||||
|
||||
"""
|
||||
If turned on, all siblings will be separated with no way to get to a "combined" sibling view
|
||||
"""
|
||||
showSeparateSiblings: Boolean!
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
StyledTooltip,
|
||||
} from './ParentNodesView';
|
||||
import ParentEntities from '../../../../../../search/filters/ParentEntities';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../../../../../../useAppConfig';
|
||||
|
||||
export const LogoIcon = styled.span`
|
||||
display: flex;
|
||||
@ -100,6 +101,10 @@ function PlatformContentView(props: Props) {
|
||||
const directParentContainer = parentContainers && parentContainers[0];
|
||||
const remainingParentContainers = parentContainers && parentContainers.slice(1, parentContainers.length);
|
||||
|
||||
const shouldShowSeparateSiblings = useIsShowSeparateSiblingsEnabled();
|
||||
const showSiblingPlatformLogos = !shouldShowSeparateSiblings && !!platformLogoUrls;
|
||||
const showSiblingPlatformNames = !shouldShowSeparateSiblings && !!platformNames;
|
||||
|
||||
return (
|
||||
<PlatformContentWrapper>
|
||||
{typeIcon && <LogoIcon>{typeIcon}</LogoIcon>}
|
||||
@ -110,10 +115,10 @@ function PlatformContentView(props: Props) {
|
||||
{platformName && (
|
||||
<LogoIcon>
|
||||
{!platformLogoUrl && !platformLogoUrls && entityLogoComponent}
|
||||
{!!platformLogoUrl && !platformLogoUrls && (
|
||||
{!!platformLogoUrl && !showSiblingPlatformLogos && (
|
||||
<PreviewImage preview={false} src={platformLogoUrl} alt={platformName} />
|
||||
)}
|
||||
{!!platformLogoUrls &&
|
||||
{showSiblingPlatformLogos &&
|
||||
platformLogoUrls.slice(0, 2).map((platformLogoUrlsEntry) => (
|
||||
<>
|
||||
<PreviewImage preview={false} src={platformLogoUrlsEntry || ''} alt={platformName} />
|
||||
@ -122,7 +127,7 @@ function PlatformContentView(props: Props) {
|
||||
</LogoIcon>
|
||||
)}
|
||||
<PlatformText>
|
||||
{platformNames ? platformNames.join(' & ') : platformName}
|
||||
{showSiblingPlatformNames ? platformNames.join(' & ') : platformName}
|
||||
{(directParentContainer || instanceId) && <StyledRightOutlined data-testid="right-arrow" />}
|
||||
</PlatformText>
|
||||
{instanceId && (
|
||||
|
||||
@ -7,6 +7,7 @@ import { CompactEntityNameList } from '../../../../../recommendations/renderer/c
|
||||
import { Dataset, Entity } from '../../../../../../types.generated';
|
||||
import { SEPARATE_SIBLINGS_URL_PARAM, stripSiblingsFromEntity, useIsSeparateSiblingsMode } from '../../../siblingUtils';
|
||||
import { GetDatasetQuery } from '../../../../../../graphql/dataset.generated';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../../../../../useAppConfig';
|
||||
|
||||
const EntityListContainer = styled.div`
|
||||
margin-left: -8px;
|
||||
@ -16,13 +17,15 @@ export const SidebarSiblingsSection = () => {
|
||||
const { entityData } = useEntityData();
|
||||
const dataNotCombinedWithSiblings = useDataNotCombinedWithSiblings<GetDatasetQuery>();
|
||||
|
||||
const showSeparateSiblings = useIsShowSeparateSiblingsEnabled();
|
||||
const isHideSiblingMode = useIsSeparateSiblingsMode();
|
||||
|
||||
if (!entityData) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (isHideSiblingMode) {
|
||||
// showSeparateSiblings disables the combined view, but with this flag on we show siblings in the sidebar to navigate to them
|
||||
if (!showSeparateSiblings && isHideSiblingMode) {
|
||||
return (
|
||||
<div>
|
||||
<SidebarHeader title="Part Of" />
|
||||
@ -36,13 +39,19 @@ export const SidebarSiblingsSection = () => {
|
||||
const siblingEntities = entityData?.siblings?.siblings || [];
|
||||
const entityDataWithoutSiblings = stripSiblingsFromEntity(dataNotCombinedWithSiblings.dataset);
|
||||
|
||||
const allSiblingsInGroup = [...siblingEntities, entityDataWithoutSiblings] as Dataset[];
|
||||
const allSiblingsInGroup = showSeparateSiblings
|
||||
? (siblingEntities as Dataset[])
|
||||
: ([...siblingEntities, entityDataWithoutSiblings] as Dataset[]);
|
||||
|
||||
const allSiblingsInGroupThatExist = allSiblingsInGroup.filter((sibling) => sibling.exists);
|
||||
|
||||
if (!allSiblingsInGroupThatExist.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
// you are always going to be in the sibling group, so if the sibling group is just you do not render.
|
||||
// The less than case is likely not neccessary but just there as a safety case for unexpected scenarios
|
||||
if (allSiblingsInGroupThatExist.length <= 1) {
|
||||
if (!showSeparateSiblings && allSiblingsInGroupThatExist.length <= 1) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { useLocation } from 'react-router-dom';
|
||||
import * as QueryString from 'query-string';
|
||||
import { Dataset, Entity, Maybe, SiblingProperties } from '../../../types.generated';
|
||||
import { GenericEntityProperties } from './types';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../../useAppConfig';
|
||||
|
||||
export function stripSiblingsFromEntity(entity: any) {
|
||||
return {
|
||||
@ -267,8 +268,9 @@ export const SEPARATE_SIBLINGS_URL_PARAM = 'separate_siblings';
|
||||
|
||||
// used to determine whether sibling entities should be shown merged or not
|
||||
export function useIsSeparateSiblingsMode() {
|
||||
const showSeparateSiblings = useIsShowSeparateSiblingsEnabled();
|
||||
const location = useLocation();
|
||||
const params = QueryString.parse(location.search, { arrayFormat: 'comma' });
|
||||
|
||||
return params[SEPARATE_SIBLINGS_URL_PARAM] === 'true';
|
||||
return showSeparateSiblings || params[SEPARATE_SIBLINGS_URL_PARAM] === 'true';
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import { ANTD_GRAY } from '../../entity/shared/constants';
|
||||
import { navigateToLineageUrl } from '../utils/navigateToLineageUrl';
|
||||
import { useIsSeparateSiblingsMode } from '../../entity/shared/siblingUtils';
|
||||
import { useIsShowColumnsMode } from '../utils/useIsShowColumnsMode';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../../useAppConfig';
|
||||
|
||||
const ControlDiv = styled.div`
|
||||
display: flex;
|
||||
@ -38,6 +39,7 @@ export function LineageVizToggles({ showExpandedTitles, setShowExpandedTitles }:
|
||||
const location = useLocation();
|
||||
const isHideSiblingMode = useIsSeparateSiblingsMode();
|
||||
const showColumns = useIsShowColumnsMode();
|
||||
const showSeparateSiblings = useIsShowSeparateSiblingsEnabled();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -51,26 +53,28 @@ export function LineageVizToggles({ showExpandedTitles, setShowExpandedTitles }:
|
||||
<b>Show Full Titles</b>
|
||||
</ControlLabel>
|
||||
</ControlDiv>
|
||||
<ControlDiv>
|
||||
<ControlsSwitch
|
||||
data-testid="compress-lineage-toggle"
|
||||
checked={!isHideSiblingMode}
|
||||
onChange={(checked) => {
|
||||
navigateToLineageUrl({
|
||||
location,
|
||||
history,
|
||||
isLineageMode: true,
|
||||
isHideSiblingMode: !checked,
|
||||
});
|
||||
}}
|
||||
/>{' '}
|
||||
<ControlLabel>
|
||||
<b>Compress Lineage</b>
|
||||
<Tooltip title="Collapses related entities into a single lineage node" placement="topRight">
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
</ControlLabel>
|
||||
</ControlDiv>
|
||||
{!showSeparateSiblings && (
|
||||
<ControlDiv>
|
||||
<ControlsSwitch
|
||||
data-testid="compress-lineage-toggle"
|
||||
checked={!isHideSiblingMode}
|
||||
onChange={(checked) => {
|
||||
navigateToLineageUrl({
|
||||
location,
|
||||
history,
|
||||
isLineageMode: true,
|
||||
isHideSiblingMode: !checked,
|
||||
});
|
||||
}}
|
||||
/>{' '}
|
||||
<ControlLabel>
|
||||
<b>Compress Lineage</b>
|
||||
<Tooltip title="Collapses related entities into a single lineage node" placement="topRight">
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
</ControlLabel>
|
||||
</ControlDiv>
|
||||
)}
|
||||
<ControlDiv>
|
||||
<ControlsSwitch
|
||||
data-testid="column-toggle"
|
||||
|
||||
@ -24,6 +24,7 @@ import ViewAllSearchItem from './ViewAllSearchItem';
|
||||
import { ViewSelect } from '../entity/view/select/ViewSelect';
|
||||
import { combineSiblingsInAutoComplete } from './utils/combineSiblingsInAutoComplete';
|
||||
import { CommandK } from './CommandK';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../useAppConfig';
|
||||
|
||||
const StyledAutoComplete = styled(AutoComplete)`
|
||||
width: 100%;
|
||||
@ -157,6 +158,8 @@ export const SearchBar = ({
|
||||
const [selected, setSelected] = useState<string>();
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const isShowSeparateSiblingsEnabled = useIsShowSeparateSiblingsEnabled();
|
||||
const finalCombineSiblings = isShowSeparateSiblingsEnabled ? false : combineSiblings;
|
||||
|
||||
useEffect(() => setSelected(initialQuery), [initialQuery]);
|
||||
|
||||
@ -223,7 +226,9 @@ export const SearchBar = ({
|
||||
|
||||
const autoCompleteEntityOptions = useMemo(() => {
|
||||
return suggestions.map((suggestion: AutoCompleteResultForEntity) => {
|
||||
const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combineSiblings });
|
||||
const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, {
|
||||
combineSiblings: finalCombineSiblings,
|
||||
});
|
||||
return {
|
||||
label: <SectionHeader entityType={combinedSuggestion.type} />,
|
||||
options: combinedSuggestion.combinedEntities.map((combinedEntity) => ({
|
||||
@ -232,7 +237,7 @@ export const SearchBar = ({
|
||||
<AutoCompleteItem
|
||||
query={effectiveQuery}
|
||||
entity={combinedEntity.entity}
|
||||
siblings={combineSiblings ? combinedEntity.matchedEntities : undefined}
|
||||
siblings={finalCombineSiblings ? combinedEntity.matchedEntities : undefined}
|
||||
/>
|
||||
),
|
||||
type: combinedEntity.entity.type,
|
||||
@ -240,7 +245,7 @@ export const SearchBar = ({
|
||||
})),
|
||||
};
|
||||
});
|
||||
}, [combineSiblings, effectiveQuery, suggestions]);
|
||||
}, [finalCombineSiblings, effectiveQuery, suggestions]);
|
||||
|
||||
const previousSelectedQuickFilterValue = usePrevious(selectedQuickFilter?.value);
|
||||
useEffect(() => {
|
||||
|
||||
@ -29,6 +29,7 @@ import SearchQuerySuggester from './suggestions/SearchQuerySugggester';
|
||||
import { ANTD_GRAY_V2 } from '../entity/shared/constants';
|
||||
import { formatNumberWithoutAbbreviation } from '../shared/formatNumber';
|
||||
import SearchResultsLoadingSection from './SearchResultsLoadingSection';
|
||||
import { useIsShowSeparateSiblingsEnabled } from '../useAppConfig';
|
||||
|
||||
const SearchResultsWrapper = styled.div<{ v2Styles: boolean }>`
|
||||
display: flex;
|
||||
@ -173,7 +174,11 @@ export const SearchResults = ({
|
||||
const totalResults = searchResponse?.total || 0;
|
||||
const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize;
|
||||
const authenticatedUserUrn = useUserContext().user?.urn;
|
||||
const combinedSiblingSearchResults = combineSiblingsInSearchResults(searchResponse?.searchResults);
|
||||
const showSeparateSiblings = useIsShowSeparateSiblingsEnabled();
|
||||
const combinedSiblingSearchResults = combineSiblingsInSearchResults(
|
||||
showSeparateSiblings,
|
||||
searchResponse?.searchResults,
|
||||
);
|
||||
|
||||
const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || [];
|
||||
const selectedEntityUrns = selectedEntities.map((entity) => entity.urn);
|
||||
|
||||
@ -66,8 +66,13 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT
|
||||
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||
const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query);
|
||||
const entities = siblings?.length ? siblings : [entity];
|
||||
const platformsToShow =
|
||||
/* Only show sibling platforms if there are > 0 explicitly included siblings */
|
||||
siblings?.length
|
||||
? genericEntityProps?.siblingPlatforms
|
||||
: (genericEntityProps?.platform && [genericEntityProps?.platform]) || undefined;
|
||||
const platforms =
|
||||
genericEntityProps?.siblingPlatforms
|
||||
platformsToShow
|
||||
?.map(
|
||||
(platform) =>
|
||||
getPlatformName(entityRegistry.getGenericEntityProperties(EntityType.DataPlatform, platform)) || '',
|
||||
|
||||
@ -491,7 +491,7 @@ const searchResultWithGhostSiblings = [
|
||||
describe('siblingUtils', () => {
|
||||
describe('combineSiblingsInSearchResults', () => {
|
||||
it('combines search results to deduplicate siblings', () => {
|
||||
const result = combineSiblingsInSearchResults(searchResultWithSiblings as any);
|
||||
const result = combineSiblingsInSearchResults(false, searchResultWithSiblings as any);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual(
|
||||
@ -507,7 +507,7 @@ describe('siblingUtils', () => {
|
||||
});
|
||||
|
||||
it('will not combine an entity with a ghost node', () => {
|
||||
const result = combineSiblingsInSearchResults(searchResultWithGhostSiblings as any);
|
||||
const result = combineSiblingsInSearchResults(false, searchResultWithGhostSiblings as any);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual(
|
||||
|
||||
@ -9,13 +9,16 @@ type UncombinedSeaerchResults = {
|
||||
export type CombinedSearchResult = CombinedEntity & Pick<UncombinedSeaerchResults, 'matchedFields'>;
|
||||
|
||||
export function combineSiblingsInSearchResults(
|
||||
showSeparateSiblings: boolean,
|
||||
searchResults: Array<UncombinedSeaerchResults> | undefined = [],
|
||||
): Array<CombinedSearchResult> {
|
||||
const combine = createSiblingEntityCombiner();
|
||||
const combinedSearchResults: Array<CombinedSearchResult> = [];
|
||||
|
||||
searchResults.forEach((searchResult) => {
|
||||
const combinedResult = combine(searchResult.entity);
|
||||
const combinedResult = showSeparateSiblings
|
||||
? { combinedEntity: searchResult.entity, skipped: false }
|
||||
: combine(searchResult.entity);
|
||||
if (!combinedResult.skipped) {
|
||||
combinedSearchResults.push({
|
||||
...searchResult,
|
||||
|
||||
@ -27,3 +27,8 @@ export function useIsAppConfigContextLoaded() {
|
||||
const appConfig = useAppConfig();
|
||||
return appConfig.loaded;
|
||||
}
|
||||
|
||||
export function useIsShowSeparateSiblingsEnabled() {
|
||||
const appConfig = useAppConfig();
|
||||
return appConfig.config.featureFlags.showSeparateSiblings;
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ export const DEFAULT_APP_CONFIG = {
|
||||
platformBrowseV2: false,
|
||||
businessAttributeEntityEnabled: false,
|
||||
dataContractsEnabled: false,
|
||||
showSeparateSiblings: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -69,6 +69,7 @@ query appConfig {
|
||||
platformBrowseV2
|
||||
businessAttributeEntityEnabled
|
||||
dataContractsEnabled
|
||||
showSeparateSiblings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,6 +371,7 @@ featureFlags:
|
||||
schemaFieldEntityFetchEnabled: ${SCHEMA_FIELD_ENTITY_FETCH_ENABLED:true} # Enables fetching for schema field entities from the database when we hydrate them on schema fields
|
||||
businessAttributeEntityEnabled: ${BUSINESS_ATTRIBUTE_ENTITY_ENABLED:false} # Enables business attribute entity which can be associated with field of dataset
|
||||
dataContractsEnabled: ${DATA_CONTRACTS_ENABLED:true} # Enables the Data Contracts feature (Tab) in the UI
|
||||
showSeparateSiblings: ${SHOW_SEPARATE_SIBLINGS:false} # If turned on, all siblings will be separated with no way to get to a "combined" sibling view
|
||||
|
||||
entityChangeEvents:
|
||||
enabled: ${ENABLE_ENTITY_CHANGE_EVENTS_HOOK:true}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user