diff --git a/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamEntitiesList.tsx b/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamEntitiesList.tsx
index 84b41f8abe..ceb94d255e 100644
--- a/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamEntitiesList.tsx
+++ b/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamEntitiesList.tsx
@@ -1,17 +1,19 @@
import React from 'react';
import styled from 'styled-components';
-import { Dataset } from '@src/types.generated';
import { Divider } from 'antd';
import { EntityLinkList } from '@src/app/homeV2/reference/sections/EntityLinkList';
+import { GenericEntityProperties } from '@src/app/entity/shared/types';
import { ANTD_GRAY } from '../../constants';
type Props = {
- directEntities: Dataset[];
- indirectEntities: Dataset[];
+ directEntities: GenericEntityProperties[];
+ indirectEntities: GenericEntityProperties[];
loadMoreDirectEntities: () => void;
loadMoreIndirectEntities: () => void;
remainingDirectEntities: number;
remainingIndirectEntities: number;
+ showHealthIcon?: boolean;
+ showDeprecatedIcon?: boolean;
};
const Container = styled.div`
@@ -47,6 +49,8 @@ const UpstreamEntitiesList = ({
loadMoreIndirectEntities,
remainingDirectEntities,
remainingIndirectEntities,
+ showHealthIcon,
+ showDeprecatedIcon,
}: Props) => {
return (
@@ -63,7 +67,8 @@ const UpstreamEntitiesList = ({
showMoreComponent={
{`Show ${remainingDirectEntities} more`}
}
- showHealthIcon
+ showHealthIcon={showHealthIcon}
+ showDeprecatedIcon={showDeprecatedIcon}
/>
>
)}
diff --git a/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamHealth.tsx b/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamHealth.tsx
index 928f7c83e8..fae4f42e02 100644
--- a/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamHealth.tsx
+++ b/datahub-web-react/src/app/entityV2/shared/embed/UpstreamHealth/UpstreamHealth.tsx
@@ -3,12 +3,15 @@ import { Divider } from 'antd';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { ErrorRounded } from '@mui/icons-material';
-import { isUnhealthy } from '@src/app/shared/health/healthUtils';
+import { isDeprecated, isUnhealthy } from '@src/app/shared/health/healthUtils';
+import { useEntityRegistry } from '@src/app/useEntityRegistry';
+import { GenericEntityProperties } from '@src/app/entity/shared/types';
import { useSearchAcrossLineageQuery } from '../../../../../graphql/search.generated';
-import { Dataset, EntityType, FilterOperator, LineageDirection } from '../../../../../types.generated';
+import { FilterOperator, LineageDirection } from '../../../../../types.generated';
import {
HAS_ACTIVE_INCIDENTS_FILTER_NAME,
HAS_FAILING_ASSERTIONS_FILTER_NAME,
+ IS_DEPRECATED_FILTER_NAME,
} from '../../../../search/utils/constants';
import { useAppConfig } from '../../../../useAppConfig';
import { useEntityData } from '../../../../entity/shared/EntityContext';
@@ -49,10 +52,11 @@ const Container = styled.div`
export default function UpstreamHealth() {
const { entityData } = useEntityData();
+ const entityRegistry = useEntityRegistry();
const [isOpen, setIsOpen] = useState(false);
- const [directUpstreamEntities, setDirectUpstreamEntities] = useState([]);
- const [indirectUpstreamEntities, setIndirectUpstreamEntities] = useState([]);
+ const [directUpstreamEntities, setDirectUpstreamEntities] = useState([]);
+ const [indirectUpstreamEntities, setIndirectUpstreamEntities] = useState([]);
const [directUpstreamsDataStart, setDirectUpstreamsDataStart] = useState(0);
const [indirectUpstreamsDataStart, setIndirectUpstreamsDataStart] = useState(0);
@@ -71,10 +75,13 @@ export default function UpstreamHealth() {
skip: !lineageEnabled,
variables: {
input: {
+ searchFlags: {
+ skipCache: true,
+ },
urn,
query: '*',
startTimeMillis,
- types: [EntityType.Dataset],
+ types: [],
count: DATASET_COUNT,
start,
direction: LineageDirection.Upstream,
@@ -107,6 +114,20 @@ export default function UpstreamHealth() {
},
],
},
+ {
+ and: [
+ {
+ field: 'degree',
+ condition: FilterOperator.Equal,
+ values: degree,
+ },
+ {
+ field: IS_DEPRECATED_FILTER_NAME,
+ condition: FilterOperator.Equal,
+ values: ['true'],
+ },
+ ],
+ },
],
},
includeAssertions: false,
@@ -126,7 +147,9 @@ export default function UpstreamHealth() {
useEffect(() => {
if (directUpstreamData?.searchAcrossLineage?.searchResults?.length && !directUpstreamEntities.length) {
setDirectUpstreamEntities(
- directUpstreamData.searchAcrossLineage.searchResults.map((result) => result.entity as Dataset),
+ directUpstreamData.searchAcrossLineage.searchResults
+ .map((result) => entityRegistry.getGenericEntityProperties(result.entity.type, result.entity))
+ .filter((e) => e !== null) as GenericEntityProperties[],
);
setDirectUpstreamsDataTotal(directUpstreamData.searchAcrossLineage.total);
}
@@ -134,12 +157,15 @@ export default function UpstreamHealth() {
directUpstreamData?.searchAcrossLineage?.searchResults,
directUpstreamEntities.length,
directUpstreamData?.searchAcrossLineage?.total,
+ entityRegistry,
]);
useEffect(() => {
if (indirectUpstreamData?.searchAcrossLineage?.searchResults?.length && !indirectUpstreamEntities.length) {
setIndirectUpstreamEntities(
- indirectUpstreamData.searchAcrossLineage.searchResults.map((result) => result.entity as Dataset),
+ indirectUpstreamData.searchAcrossLineage.searchResults
+ .map((result) => entityRegistry.getGenericEntityProperties(result.entity.type, result.entity))
+ .filter((e) => e !== null) as GenericEntityProperties[],
);
setIndirectUpstreamsDataTotal(indirectUpstreamData.searchAcrossLineage.total);
}
@@ -147,6 +173,7 @@ export default function UpstreamHealth() {
indirectUpstreamData?.searchAcrossLineage?.searchResults,
indirectUpstreamEntities.length,
indirectUpstreamData?.searchAcrossLineage?.total,
+ entityRegistry,
]);
function loadMoreDirectUpstreamData() {
@@ -160,7 +187,9 @@ export default function UpstreamHealth() {
if (result.data.searchAcrossLineage?.searchResults) {
setDirectUpstreamEntities([
...directUpstreamEntities,
- ...result.data.searchAcrossLineage.searchResults.map((r) => r.entity as Dataset),
+ ...(result.data.searchAcrossLineage.searchResults
+ .map((r) => entityRegistry.getGenericEntityProperties(r.entity.type, r.entity))
+ .filter((e) => e !== null) as GenericEntityProperties[]),
]);
}
});
@@ -178,15 +207,21 @@ export default function UpstreamHealth() {
if (result.data.searchAcrossLineage?.searchResults) {
setIndirectUpstreamEntities([
...indirectUpstreamEntities,
- ...result.data.searchAcrossLineage.searchResults.map((r) => r.entity as Dataset),
+ ...(result.data.searchAcrossLineage.searchResults
+ .map((r) => entityRegistry.getGenericEntityProperties(r.entity.type, r.entity))
+ .filter((e) => e !== null) as GenericEntityProperties[]),
]);
}
});
setIndirectUpstreamsDataStart(newStart);
}
- const unhealthyDirectUpstreams = directUpstreamEntities.filter((e) => e.health && isUnhealthy(e.health));
- const unhealthyIndirectUpstreams = indirectUpstreamEntities.filter((e) => e.health && isUnhealthy(e.health));
+ const unhealthyDirectUpstreams = directUpstreamEntities.filter(
+ (e) => (e.health && isUnhealthy(e.health)) || isDeprecated(e),
+ );
+ const unhealthyIndirectUpstreams = indirectUpstreamEntities.filter(
+ (e) => (e.health && isUnhealthy(e.health)) || isDeprecated(e),
+ );
const hasUnhealthyUpstreams = unhealthyDirectUpstreams.length || unhealthyIndirectUpstreams.length;
@@ -216,6 +251,8 @@ export default function UpstreamHealth() {
indirectUpstreamsDataTotal - (indirectUpstreamsDataStart + DATASET_COUNT),
0,
)}
+ showDeprecatedIcon
+ showHealthIcon
/>
)}
diff --git a/datahub-web-react/src/app/homeV2/reference/sections/EntityLink.tsx b/datahub-web-react/src/app/homeV2/reference/sections/EntityLink.tsx
index 715fdb4a43..aeef2c318a 100644
--- a/datahub-web-react/src/app/homeV2/reference/sections/EntityLink.tsx
+++ b/datahub-web-react/src/app/homeV2/reference/sections/EntityLink.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import styled, { CSSObject } from 'styled-components';
import HealthIcon from '@src/app/previewV2/HealthIcon';
+import { DeprecationIcon } from '@src/app/entityV2/shared/components/styled/DeprecationIcon';
import { useEmbeddedProfileLinkProps } from '@src/app/shared/useEmbeddedProfileLinkProps';
import PlatformHeaderIcons from '@src/app/entityV2/shared/containers/profile/header/PlatformContent/PlatformHeaderIcons';
import { getEntityPlatforms } from '@src/app/entityV2/shared/containers/profile/header/utils';
@@ -32,7 +33,7 @@ const Container = styled.div<{ showHover: boolean; entity: GenericEntityProperti
`;
const IconWrapper = styled.div`
- padding-right: 4px;
+ padding-right: 8px;
`;
const LinkButton = styled(Link)<{ includePadding: boolean }>`
@@ -82,9 +83,18 @@ type Props = {
render?: (entity: GenericEntityProperties) => React.ReactNode;
onClick?: (e) => void;
showHealthIcon?: boolean;
+ showDeprecatedIcon?: boolean;
};
-export const EntityLink = ({ entity, styles, render, displayTextStyle, onClick, showHealthIcon = false }: Props) => {
+export const EntityLink = ({
+ entity,
+ styles,
+ render,
+ displayTextStyle,
+ onClick,
+ showHealthIcon = false,
+ showDeprecatedIcon = true,
+}: Props) => {
const entityRegistry = useEntityRegistry();
const linkProps = useEmbeddedProfileLinkProps();
@@ -134,7 +144,17 @@ export const EntityLink = ({ entity, styles, render, displayTextStyle, onClick,
- {entity?.health && showHealthIcon && (
+ {entity?.deprecation?.deprecated && showDeprecatedIcon ? (
+
+
+
+ ) : null}
+ {entity?.health && showHealthIcon ? (
- )}
+ ) : null}
>
)}
diff --git a/datahub-web-react/src/app/homeV2/reference/sections/EntityLinkList.tsx b/datahub-web-react/src/app/homeV2/reference/sections/EntityLinkList.tsx
index d244d23beb..2ed483fac0 100644
--- a/datahub-web-react/src/app/homeV2/reference/sections/EntityLinkList.tsx
+++ b/datahub-web-react/src/app/homeV2/reference/sections/EntityLinkList.tsx
@@ -49,6 +49,7 @@ type Props = {
showMoreComponent?: React.ReactNode;
showMoreCount?: number;
showHealthIcon?: boolean;
+ showDeprecatedIcon?: boolean;
empty?: React.ReactNode;
onClickMore?: () => void;
onClickTitle?: () => void;
@@ -64,6 +65,7 @@ export const EntityLinkList = ({
showMore = false,
showMoreCount,
showHealthIcon = false,
+ showDeprecatedIcon = false,
empty,
onClickMore,
onClickTitle,
@@ -99,6 +101,7 @@ export const EntityLinkList = ({
}
render={render}
showHealthIcon={showHealthIcon}
+ showDeprecatedIcon={showDeprecatedIcon}
/>
);
})) || <>{empty || }>}
diff --git a/datahub-web-react/src/app/search/utils/constants.ts b/datahub-web-react/src/app/search/utils/constants.ts
index bb74c72316..c724c58d83 100644
--- a/datahub-web-react/src/app/search/utils/constants.ts
+++ b/datahub-web-react/src/app/search/utils/constants.ts
@@ -41,6 +41,7 @@ export const VERIFIED_FORMS_FILTER_NAME = 'verifiedForms';
export const COMPLETED_FORMS_COMPLETED_PROMPT_IDS_FILTER_NAME = 'completedFormsCompletedPromptIds';
export const INCOMPLETE_FORMS_COMPLETED_PROMPT_IDS_FILTER_NAME = 'incompleteFormsCompletedPromptIds';
export const SCHEMA_FIELD_ALIASES_FILTER_NAME = 'schemaFieldAliases';
+export const IS_DEPRECATED_FILTER_NAME = 'deprecated';
export const LEGACY_ENTITY_FILTER_FIELDS = [ENTITY_FILTER_NAME, LEGACY_ENTITY_FILTER_NAME];
diff --git a/datahub-web-react/src/app/searchV2/filters/render/acrylRenderers.ts b/datahub-web-react/src/app/searchV2/filters/render/acrylRenderers.ts
index f2d366ddb4..baf14999e7 100644
--- a/datahub-web-react/src/app/searchV2/filters/render/acrylRenderers.ts
+++ b/datahub-web-react/src/app/searchV2/filters/render/acrylRenderers.ts
@@ -1,4 +1,5 @@
import { HasFailingAssertionsRenderer } from './assertion/HasFailingAssertionsRenderer';
+import { DeprecationRenderer } from './deprecation/DeprecationRenderer';
import { FilterRenderer } from './FilterRenderer';
import { HasActiveIncidentsRenderer } from './incident/HasActiveIncidentsRenderer';
import { HasSiblingsRenderer } from './siblings/HasSiblingsRenderer';
@@ -7,4 +8,5 @@ export const renderers: Array = [
new HasFailingAssertionsRenderer(),
new HasActiveIncidentsRenderer(),
new HasSiblingsRenderer(),
+ new DeprecationRenderer(),
];
diff --git a/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationFilter.tsx b/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationFilter.tsx
new file mode 100644
index 0000000000..ead759268e
--- /dev/null
+++ b/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationFilter.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { FilterScenarioType } from '../types';
+import { BooleanSimpleSearchFilter } from '../shared/BooleanSimpleSearchFilter';
+import BooleanMoreFilter from '../shared/BooleanMoreFilter';
+import { FacetFilterInput, FacetMetadata, FacetFilter } from '../../../../../types.generated';
+import BooleanSearchFilter from '../shared/BooleanSearchFilter';
+
+export interface Props {
+ scenario: FilterScenarioType;
+ filter: FacetMetadata;
+ activeFilters: FacetFilterInput[];
+ onChangeFilters: (newFilters: FacetFilter[]) => void;
+ icon?: React.ReactNode;
+}
+
+export function DeprecationFilter({ icon, scenario, filter, activeFilters, onChangeFilters }: Props) {
+ const isSelected = activeFilters?.find((f) => f.field === 'deprecated')?.values?.includes('true');
+
+ const toggleFilter = () => {
+ let newFilters;
+ if (isSelected) {
+ newFilters = activeFilters.filter((f) => f.field !== 'deprecated');
+ } else {
+ newFilters = [...activeFilters, { field: 'deprecated', values: ['true'] }];
+ }
+ onChangeFilters(newFilters);
+ };
+
+ const aggregateCount = filter.aggregations.find((agg) => agg.value === 'true')?.count;
+
+ if (!aggregateCount) {
+ return null;
+ }
+
+ return (
+ <>
+ {scenario === FilterScenarioType.SEARCH_V1 && (
+
+ )}
+ {scenario === FilterScenarioType.SEARCH_V2_PRIMARY && (
+
+ )}
+ {scenario === FilterScenarioType.SEARCH_V2_SECONDARY && (
+
+ )}
+ >
+ );
+}
diff --git a/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationRenderer.tsx b/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationRenderer.tsx
new file mode 100644
index 0000000000..0c6017dd80
--- /dev/null
+++ b/datahub-web-react/src/app/searchV2/filters/render/deprecation/DeprecationRenderer.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import styled from 'styled-components';
+import DeprecatedIcon from '../../../../../images/deprecated-status.svg?react';
+import { FilterRenderer } from '../FilterRenderer';
+import { FilterRenderProps } from '../types';
+import { DeprecationFilter } from './DeprecationFilter';
+
+const StyledDeprecatedIcon = styled(DeprecatedIcon)`
+ color: inherit;
+ path {
+ fill: currentColor;
+ }
+ && {
+ fill: currentColor;
+ }
+ align-items: center;
+`;
+
+export class DeprecationRenderer implements FilterRenderer {
+ field = 'deprecated';
+
+ render = (props: FilterRenderProps) => ;
+
+ icon = () => ;
+
+ valueLabel = (value: string) => {
+ if (value === 'true') {
+ return <>Is Deprecated>;
+ }
+ return <>Is Not Deprecated>;
+ };
+}
diff --git a/datahub-web-react/src/app/searchV2/filters/render/shared/BooleanSearchFilter.tsx b/datahub-web-react/src/app/searchV2/filters/render/shared/BooleanSearchFilter.tsx
index f4522bc29a..888770f26b 100644
--- a/datahub-web-react/src/app/searchV2/filters/render/shared/BooleanSearchFilter.tsx
+++ b/datahub-web-react/src/app/searchV2/filters/render/shared/BooleanSearchFilter.tsx
@@ -6,13 +6,15 @@ import FilterOption from '../../FilterOption';
import { SearchFilterLabel } from '../../styledComponents';
import BooleanSearchFilterMenu from './BooleanMoreFilterMenu';
-const IconNameWrapper = styled.span`
+const IconNameWrapper = styled.div`
display: flex;
align-items: center;
`;
const IconWrapper = styled.span`
margin-right: 8px;
+ display: flex;
+ flex-direction: column;
`;
interface Props {
diff --git a/datahub-web-react/src/app/shared/health/healthUtils.tsx b/datahub-web-react/src/app/shared/health/healthUtils.tsx
index 426e5d986c..c1408c06a2 100644
--- a/datahub-web-react/src/app/shared/health/healthUtils.tsx
+++ b/datahub-web-react/src/app/shared/health/healthUtils.tsx
@@ -9,6 +9,7 @@ import {
} from '@ant-design/icons';
import React from 'react';
import styled from 'styled-components';
+import { GenericEntityProperties } from '@src/app/entity/shared/types';
import { HealthStatus, HealthStatusType, Health } from '../../../types.generated';
import { FAILURE_COLOR_HEX, SUCCESS_COLOR_HEX } from '../../entity/shared/tabs/Incident/incidentUtils';
@@ -40,6 +41,10 @@ export const isUnhealthy = (healths: Health[]) => {
return isFailingAssertions || hasActiveIncidents;
};
+export const isDeprecated = (entity: GenericEntityProperties) => {
+ return entity.deprecation?.deprecated;
+};
+
export const isHealthy = (healths: Health[]) => {
const assertionHealth = healths.filter((health) => health.type === HealthStatusType.Assertions);
if (assertionHealth?.length > 0) {
diff --git a/metadata-io/src/main/java/com/linkedin/metadata/search/elasticsearch/query/request/SearchFieldConfig.java b/metadata-io/src/main/java/com/linkedin/metadata/search/elasticsearch/query/request/SearchFieldConfig.java
index 7415c6e5ce..88fe421d2e 100644
--- a/metadata-io/src/main/java/com/linkedin/metadata/search/elasticsearch/query/request/SearchFieldConfig.java
+++ b/metadata-io/src/main/java/com/linkedin/metadata/search/elasticsearch/query/request/SearchFieldConfig.java
@@ -26,7 +26,10 @@ import lombok.experimental.Accessors;
public class SearchFieldConfig {
public static final float DEFAULT_BOOST = 1.0f;
- public static final Set KEYWORD_FIELDS = Set.of("urn", "runId", "_index");
+ // Fields that can be filtered on directly, without appending the ".keyword" suffix.
+ // TODO: This exclusion should be dynamic, based on @Searchable annotation field type. Not
+ // hardcoded.
+ public static final Set KEYWORD_FIELDS = Set.of("urn", "runId", "_index", "deprecated");
public static final Set PATH_HIERARCHY_FIELDS = Set.of("browsePathV2");
public static final float URN_BOOST_SCORE = 10.0f;
diff --git a/metadata-models/src/main/pegasus/com/linkedin/common/Deprecation.pdl b/metadata-models/src/main/pegasus/com/linkedin/common/Deprecation.pdl
index 7c6f7d155a..c3aab7fdbf 100644
--- a/metadata-models/src/main/pegasus/com/linkedin/common/Deprecation.pdl
+++ b/metadata-models/src/main/pegasus/com/linkedin/common/Deprecation.pdl
@@ -13,7 +13,9 @@ record Deprecation {
*/
@Searchable = {
"fieldType": "BOOLEAN",
- "weightsPerFieldValue": { "true": 0.5 }
+ "weightsPerFieldValue": { "true": 0.5 },
+ "addToFilters": true,
+ "filterNameOverride": "Deprecated"
}
deprecated: boolean