feat(siblings) Add flag to prevent combining siblings in the UI (#10952)

This commit is contained in:
Chris Collins 2024-07-19 13:41:18 -04:00 committed by GitHub
parent 30e9cfdce0
commit eace710aee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 88 additions and 35 deletions

View File

@ -21,4 +21,5 @@ public class FeatureFlags {
private boolean schemaFieldEntityFetchEnabled = false;
private boolean businessAttributeEntityEnabled = false;
private boolean dataContractsEnabled = false;
private boolean showSeparateSiblings = false;
}

View File

@ -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);

View File

@ -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!
}
"""

View File

@ -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 && (

View File

@ -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 <></>;
}

View File

@ -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';
}

View File

@ -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"

View File

@ -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(() => {

View File

@ -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);

View File

@ -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)) || '',

View File

@ -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(

View File

@ -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,

View File

@ -27,3 +27,8 @@ export function useIsAppConfigContextLoaded() {
const appConfig = useAppConfig();
return appConfig.loaded;
}
export function useIsShowSeparateSiblingsEnabled() {
const appConfig = useAppConfig();
return appConfig.config.featureFlags.showSeparateSiblings;
}

View File

@ -54,6 +54,7 @@ export const DEFAULT_APP_CONFIG = {
platformBrowseV2: false,
businessAttributeEntityEnabled: false,
dataContractsEnabled: false,
showSeparateSiblings: false,
},
};

View File

@ -69,6 +69,7 @@ query appConfig {
platformBrowseV2
businessAttributeEntityEnabled
dataContractsEnabled
showSeparateSiblings
}
}
}

View File

@ -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}