mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-15 12:37:18 +00:00
Merge branch '1.7.1' of https://github.com/open-metadata/OpenMetadata into 1.7.1
This commit is contained in:
commit
7cd3e40e9d
@ -71,6 +71,7 @@ from metadata.ingestion.source.database.postgres.utils import (
|
|||||||
get_columns,
|
get_columns,
|
||||||
get_etable_owner,
|
get_etable_owner,
|
||||||
get_foreign_keys,
|
get_foreign_keys,
|
||||||
|
get_schema_names,
|
||||||
get_table_comment,
|
get_table_comment,
|
||||||
get_table_owner,
|
get_table_owner,
|
||||||
get_view_definition,
|
get_view_definition,
|
||||||
@ -115,6 +116,7 @@ Inspector.get_table_ddl = get_table_ddl
|
|||||||
Inspector.get_table_owner = get_etable_owner
|
Inspector.get_table_owner = get_etable_owner
|
||||||
|
|
||||||
PGDialect.get_foreign_keys = get_foreign_keys
|
PGDialect.get_foreign_keys = get_foreign_keys
|
||||||
|
PGDialect.get_schema_names = get_schema_names
|
||||||
|
|
||||||
|
|
||||||
class PostgresSource(CommonDbSourceService, MultiDBSource):
|
class PostgresSource(CommonDbSourceService, MultiDBSource):
|
||||||
@ -281,7 +283,7 @@ class PostgresSource(CommonDbSourceService, MultiDBSource):
|
|||||||
for row in results:
|
for row in results:
|
||||||
try:
|
try:
|
||||||
stored_procedure = PostgresStoredProcedure.model_validate(
|
stored_procedure = PostgresStoredProcedure.model_validate(
|
||||||
dict(row._mapping)
|
dict(row._mapping) # pylint: disable=protected-access
|
||||||
)
|
)
|
||||||
yield stored_procedure
|
yield stored_procedure
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
@ -196,6 +196,13 @@ POSTGRES_GET_SERVER_VERSION = """
|
|||||||
show server_version_num
|
show server_version_num
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# pylint: disable=anomalous-backslash-in-string
|
||||||
|
POSTGRES_GET_SCHEMA_NAMES = """
|
||||||
|
SELECT nspname FROM pg_namespace
|
||||||
|
WHERE nspname NOT LIKE 'pg\_%'
|
||||||
|
ORDER BY nspname
|
||||||
|
"""
|
||||||
|
|
||||||
POSTGRES_FETCH_FK = """
|
POSTGRES_FETCH_FK = """
|
||||||
SELECT r.conname,
|
SELECT r.conname,
|
||||||
pg_catalog.pg_get_constraintdef(r.oid, true) as condef,
|
pg_catalog.pg_get_constraintdef(r.oid, true) as condef,
|
||||||
|
@ -26,6 +26,7 @@ from sqlalchemy.sql import sqltypes
|
|||||||
from metadata.ingestion.source.database.postgres.queries import (
|
from metadata.ingestion.source.database.postgres.queries import (
|
||||||
POSTGRES_COL_IDENTITY,
|
POSTGRES_COL_IDENTITY,
|
||||||
POSTGRES_FETCH_FK,
|
POSTGRES_FETCH_FK,
|
||||||
|
POSTGRES_GET_SCHEMA_NAMES,
|
||||||
POSTGRES_GET_SERVER_VERSION,
|
POSTGRES_GET_SERVER_VERSION,
|
||||||
POSTGRES_SQL_COLUMNS,
|
POSTGRES_SQL_COLUMNS,
|
||||||
POSTGRES_TABLE_COMMENTS,
|
POSTGRES_TABLE_COMMENTS,
|
||||||
@ -43,10 +44,8 @@ logger = utils_logger()
|
|||||||
|
|
||||||
OLD_POSTGRES_VERSION = "130000"
|
OLD_POSTGRES_VERSION = "130000"
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument,too-many-arguments,invalid-name,too-many-locals
|
||||||
def get_etable_owner(
|
def get_etable_owner(self, connection, table_name=None, schema=None):
|
||||||
self, connection, table_name=None, schema=None
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
"""Return all owners.
|
"""Return all owners.
|
||||||
|
|
||||||
:param schema: Optional, retrieve names from a non-default schema.
|
:param schema: Optional, retrieve names from a non-default schema.
|
||||||
@ -67,6 +66,16 @@ def get_etable_owner(
|
|||||||
def get_foreign_keys(
|
def get_foreign_keys(
|
||||||
self, connection, table_name, schema=None, postgresql_ignore_search_path=False, **kw
|
self, connection, table_name, schema=None, postgresql_ignore_search_path=False, **kw
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
connection (_type_): _description_
|
||||||
|
table_name (_type_): _description_
|
||||||
|
schema (_type_, optional): _description_. Defaults to None.
|
||||||
|
postgresql_ignore_search_path (bool, optional): _description_. Defaults to False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_type_: _description_
|
||||||
|
"""
|
||||||
preparer = self.identifier_preparer
|
preparer = self.identifier_preparer
|
||||||
table_oid = self.get_table_oid(
|
table_oid = self.get_table_oid(
|
||||||
connection, table_name, schema, info_cache=kw.get("info_cache")
|
connection, table_name, schema, info_cache=kw.get("info_cache")
|
||||||
@ -87,7 +96,7 @@ def get_foreign_keys(
|
|||||||
t = sql.text(POSTGRES_FETCH_FK).columns(
|
t = sql.text(POSTGRES_FETCH_FK).columns(
|
||||||
conname=sqltypes.Unicode, condef=sqltypes.Unicode, con_db_name=sqltypes.Unicode
|
conname=sqltypes.Unicode, condef=sqltypes.Unicode, con_db_name=sqltypes.Unicode
|
||||||
)
|
)
|
||||||
c = connection.execute(t, dict(table=table_oid))
|
c = connection.execute(t, {"table": table_oid})
|
||||||
fkeys = []
|
fkeys = []
|
||||||
for conname, condef, conschema, con_db_name in c.fetchall():
|
for conname, condef, conschema, con_db_name in c.fetchall():
|
||||||
m = re.search(FK_REGEX, condef).groups()
|
m = re.search(FK_REGEX, condef).groups()
|
||||||
@ -109,7 +118,7 @@ def get_foreign_keys(
|
|||||||
) = m
|
) = m
|
||||||
|
|
||||||
if deferrable is not None:
|
if deferrable is not None:
|
||||||
deferrable = True if deferrable == "DEFERRABLE" else False
|
deferrable = deferrable == "DEFERRABLE"
|
||||||
constrained_columns = tuple(re.split(r"\s*,\s*", constrained_columns))
|
constrained_columns = tuple(re.split(r"\s*,\s*", constrained_columns))
|
||||||
constrained_columns = [
|
constrained_columns = [
|
||||||
preparer._unquote_identifier(x) for x in constrained_columns
|
preparer._unquote_identifier(x) for x in constrained_columns
|
||||||
@ -161,9 +170,7 @@ def get_foreign_keys(
|
|||||||
|
|
||||||
|
|
||||||
@reflection.cache
|
@reflection.cache
|
||||||
def get_table_owner(
|
def get_table_owner(self, connection, table_name, schema=None, **kw):
|
||||||
self, connection, table_name, schema=None, **kw
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
return get_table_owner_wrapper(
|
return get_table_owner_wrapper(
|
||||||
self,
|
self,
|
||||||
connection=connection,
|
connection=connection,
|
||||||
@ -174,9 +181,7 @@ def get_table_owner(
|
|||||||
|
|
||||||
|
|
||||||
@reflection.cache
|
@reflection.cache
|
||||||
def get_table_comment(
|
def get_table_comment(self, connection, table_name, schema=None, **kw):
|
||||||
self, connection, table_name, schema=None, **kw
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
return get_table_comment_wrapper(
|
return get_table_comment_wrapper(
|
||||||
self,
|
self,
|
||||||
connection,
|
connection,
|
||||||
@ -187,9 +192,7 @@ def get_table_comment(
|
|||||||
|
|
||||||
|
|
||||||
@reflection.cache
|
@reflection.cache
|
||||||
def get_columns( # pylint: disable=too-many-locals
|
def get_columns(self, connection, table_name, schema=None, **kw):
|
||||||
self, connection, table_name, schema=None, **kw
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Overriding the dialect method to add raw_data_type in response
|
Overriding the dialect method to add raw_data_type in response
|
||||||
"""
|
"""
|
||||||
@ -368,7 +371,7 @@ def _handle_array_type(attype):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-statements,too-many-branches,too-many-locals,too-many-arguments
|
# pylint: disable=too-many-statements,too-many-branches
|
||||||
def get_column_info(
|
def get_column_info(
|
||||||
self,
|
self,
|
||||||
name,
|
name,
|
||||||
@ -477,9 +480,7 @@ def get_column_info(
|
|||||||
|
|
||||||
|
|
||||||
@reflection.cache
|
@reflection.cache
|
||||||
def get_view_definition(
|
def get_view_definition(self, connection, table_name, schema=None, **kw):
|
||||||
self, connection, table_name, schema=None, **kw
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
return get_view_definition_wrapper(
|
return get_view_definition_wrapper(
|
||||||
self,
|
self,
|
||||||
connection,
|
connection,
|
||||||
@ -515,3 +516,11 @@ def get_postgres_time_column_name(engine) -> str:
|
|||||||
):
|
):
|
||||||
time_column_name = "total_time"
|
time_column_name = "total_time"
|
||||||
return time_column_name
|
return time_column_name
|
||||||
|
|
||||||
|
|
||||||
|
@reflection.cache
|
||||||
|
def get_schema_names(self, connection, **kw):
|
||||||
|
result = connection.execute(
|
||||||
|
sql.text(POSTGRES_GET_SCHEMA_NAMES).columns(nspname=sqltypes.Unicode)
|
||||||
|
)
|
||||||
|
return [name for name, in result]
|
||||||
|
@ -95,6 +95,7 @@ export const CustomEdge = ({
|
|||||||
onAddPipelineClick,
|
onAddPipelineClick,
|
||||||
onColumnEdgeRemove,
|
onColumnEdgeRemove,
|
||||||
dataQualityLineage,
|
dataQualityLineage,
|
||||||
|
dqHighlightedEdges,
|
||||||
} = useLineageProvider();
|
} = useLineageProvider();
|
||||||
|
|
||||||
const { theme } = useApplicationStore();
|
const { theme } = useApplicationStore();
|
||||||
@ -124,14 +125,15 @@ export const CustomEdge = ({
|
|||||||
|
|
||||||
// Compute if should show DQ tracing
|
// Compute if should show DQ tracing
|
||||||
const showDqTracing = useMemo(() => {
|
const showDqTracing = useMemo(() => {
|
||||||
return (
|
if (
|
||||||
(activeLayer.includes(LineageLayer.DataObservability) &&
|
!activeLayer.includes(LineageLayer.DataObservability) ||
|
||||||
dataQualityLineage?.edges?.some(
|
!dataQualityLineage?.nodes
|
||||||
(dqEdge) => dqEdge?.docId === edge?.docId
|
) {
|
||||||
)) ??
|
return false;
|
||||||
false
|
}
|
||||||
);
|
|
||||||
}, [activeLayer, dataQualityLineage?.edges, edge?.docId]);
|
return dqHighlightedEdges?.has(id);
|
||||||
|
}, [activeLayer, dataQualityLineage?.nodes, id, dqHighlightedEdges]);
|
||||||
|
|
||||||
// Determine if column is highlighted based on traced columns
|
// Determine if column is highlighted based on traced columns
|
||||||
const isColumnHighlighted = useMemo(() => {
|
const isColumnHighlighted = useMemo(() => {
|
||||||
|
@ -164,7 +164,9 @@ const Lineage = ({
|
|||||||
ref={reactFlowWrapper}>
|
ref={reactFlowWrapper}>
|
||||||
{entityLineage && (
|
{entityLineage && (
|
||||||
<>
|
<>
|
||||||
|
{isPlatformLineage ? null : (
|
||||||
<CustomControlsComponent className="absolute top-1 right-1 p-xs" />
|
<CustomControlsComponent className="absolute top-1 right-1 p-xs" />
|
||||||
|
)}
|
||||||
<LineageControlButtons
|
<LineageControlButtons
|
||||||
deleted={deleted}
|
deleted={deleted}
|
||||||
entityType={entityType}
|
entityType={entityType}
|
||||||
|
@ -98,4 +98,5 @@ export interface LineageContextType {
|
|||||||
) => void;
|
) => void;
|
||||||
onUpdateLayerView: (layers: LineageLayer[]) => void;
|
onUpdateLayerView: (layers: LineageLayer[]) => void;
|
||||||
redraw: () => Promise<void>;
|
redraw: () => Promise<void>;
|
||||||
|
dqHighlightedEdges?: Set<string>;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
@ -96,6 +97,7 @@ import {
|
|||||||
createNewEdge,
|
createNewEdge,
|
||||||
createNodes,
|
createNodes,
|
||||||
decodeLineageHandles,
|
decodeLineageHandles,
|
||||||
|
getAllDownstreamEdges,
|
||||||
getAllTracedColumnEdge,
|
getAllTracedColumnEdge,
|
||||||
getClassifiedEdge,
|
getClassifiedEdge,
|
||||||
getConnectedNodesEdges,
|
getConnectedNodesEdges,
|
||||||
@ -135,6 +137,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { fqn: decodedFqn } = useFqn();
|
const { fqn: decodedFqn } = useFqn();
|
||||||
const location = useCustomLocation();
|
const location = useCustomLocation();
|
||||||
|
const history = useHistory();
|
||||||
const { isTourOpen, isTourPage } = useTourProvider();
|
const { isTourOpen, isTourPage } = useTourProvider();
|
||||||
const { appPreferences } = useApplicationStore();
|
const { appPreferences } = useApplicationStore();
|
||||||
const defaultLineageConfig = appPreferences?.lineageConfig as LineageSettings;
|
const defaultLineageConfig = appPreferences?.lineageConfig as LineageSettings;
|
||||||
@ -197,6 +200,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
const backspacePressed = useKeyPress('Backspace');
|
const backspacePressed = useKeyPress('Backspace');
|
||||||
const { showModal } = useEntityExportModalProvider();
|
const { showModal } = useEntityExportModalProvider();
|
||||||
const [isPlatformLineage, setIsPlatformLineage] = useState(false);
|
const [isPlatformLineage, setIsPlatformLineage] = useState(false);
|
||||||
|
const [dqHighlightedEdges, setDqHighlightedEdges] = useState<Set<string>>();
|
||||||
|
|
||||||
const lineageLayer = useMemo(() => {
|
const lineageLayer = useMemo(() => {
|
||||||
const param = location.search;
|
const param = location.search;
|
||||||
@ -426,12 +430,29 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
[queryFilter, decodedFqn]
|
[queryFilter, decodedFqn]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onPlatformViewChange = useCallback((view: LineagePlatformView) => {
|
const onPlatformViewChange = useCallback(
|
||||||
|
(view: LineagePlatformView) => {
|
||||||
setPlatformView(view);
|
setPlatformView(view);
|
||||||
if (view !== LineagePlatformView.None) {
|
if (view !== LineagePlatformView.None) {
|
||||||
setActiveLayer([]);
|
setActiveLayer([]);
|
||||||
}
|
}
|
||||||
}, []);
|
|
||||||
|
if (isPlatformLineage) {
|
||||||
|
const searchData = QueryString.parse(
|
||||||
|
location.search.startsWith('?')
|
||||||
|
? location.search.substring(1)
|
||||||
|
: location.search
|
||||||
|
);
|
||||||
|
history.push({
|
||||||
|
search: QueryString.stringify({
|
||||||
|
...searchData,
|
||||||
|
platformView: view !== LineagePlatformView.None ? view : undefined,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[isPlatformLineage, location.search]
|
||||||
|
);
|
||||||
|
|
||||||
const exportLineageData = useCallback(
|
const exportLineageData = useCallback(
|
||||||
async (_: string) => {
|
async (_: string) => {
|
||||||
@ -596,7 +617,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
setEntityType(entityType);
|
setEntityType(entityType);
|
||||||
setIsPlatformLineage(isPlatformLineage ?? false);
|
setIsPlatformLineage(isPlatformLineage ?? false);
|
||||||
if (isPlatformLineage && !entity) {
|
if (isPlatformLineage && !entity) {
|
||||||
setPlatformView(LineagePlatformView.Service);
|
onPlatformViewChange(LineagePlatformView.Service);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
@ -1527,6 +1548,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
dataQualityLineage,
|
dataQualityLineage,
|
||||||
redraw,
|
redraw,
|
||||||
onPlatformViewChange,
|
onPlatformViewChange,
|
||||||
|
dqHighlightedEdges,
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
dataQualityLineage,
|
dataQualityLineage,
|
||||||
@ -1575,6 +1597,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
onExportClick,
|
onExportClick,
|
||||||
redraw,
|
redraw,
|
||||||
onPlatformViewChange,
|
onPlatformViewChange,
|
||||||
|
dqHighlightedEdges,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -1599,6 +1622,20 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
}
|
}
|
||||||
}, [activeLayer, decodedFqn, lineageConfig]);
|
}, [activeLayer, decodedFqn, lineageConfig]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
dataQualityLineage?.nodes &&
|
||||||
|
!isUndefined(edges) &&
|
||||||
|
isUndefined(dqHighlightedEdges)
|
||||||
|
) {
|
||||||
|
const edgesToHighlight = dataQualityLineage.nodes
|
||||||
|
.flatMap((dqNode) => getAllDownstreamEdges(dqNode.id, edges ?? []))
|
||||||
|
.map((edge) => edge.id);
|
||||||
|
const edgesToHighlightSet = new Set(edgesToHighlight);
|
||||||
|
setDqHighlightedEdges(edgesToHighlightSet);
|
||||||
|
}
|
||||||
|
}, [dataQualityLineage, edges, dqHighlightedEdges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LineageContext.Provider value={activityFeedContextValues}>
|
<LineageContext.Provider value={activityFeedContextValues}>
|
||||||
<div
|
<div
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Pipeline-Status",
|
"pipeline-state": "Pipeline-Status",
|
||||||
"platform": "Plattform",
|
"platform": "Plattform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Abstammung",
|
||||||
"play": "Abspielen",
|
"play": "Abspielen",
|
||||||
"please-enter-value": "Bitte einen Wert für {{name}} eingeben",
|
"please-enter-value": "Bitte einen Wert für {{name}} eingeben",
|
||||||
"please-password-type-first": "Bitte zuerst das Passwort eingeben",
|
"please-password-type-first": "Bitte zuerst das Passwort eingeben",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Pipeline State",
|
"pipeline-state": "Pipeline State",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Lineage",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Please enter {{name}} value",
|
"please-enter-value": "Please enter {{name}} value",
|
||||||
"please-password-type-first": "Please type password first",
|
"please-password-type-first": "Please type password first",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Estado de la pipeline",
|
"pipeline-state": "Estado de la pipeline",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Linaje",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Ingrese el valor de {{name}}",
|
"please-enter-value": "Ingrese el valor de {{name}}",
|
||||||
"please-password-type-first": "Ingrese primero la contraseña",
|
"please-password-type-first": "Ingrese primero la contraseña",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "État de la Pipeline",
|
"pipeline-state": "État de la Pipeline",
|
||||||
"platform": "Plateforme",
|
"platform": "Plateforme",
|
||||||
|
"platform-type-lineage": "{{platformType}} Lignage",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Merci d'entrer une valeur pour {{name}} ",
|
"please-enter-value": "Merci d'entrer une valeur pour {{name}} ",
|
||||||
"please-password-type-first": "Merci d'entrer le mot de passe d'abord",
|
"please-password-type-first": "Merci d'entrer le mot de passe d'abord",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Estado do pipeline",
|
"pipeline-state": "Estado do pipeline",
|
||||||
"platform": "Plataforma",
|
"platform": "Plataforma",
|
||||||
|
"platform-type-lineage": "{{platformType}} Liñaxe",
|
||||||
"play": "Reproducir",
|
"play": "Reproducir",
|
||||||
"please-enter-value": "Por favor, introduce o valor de {{name}}",
|
"please-enter-value": "Por favor, introduce o valor de {{name}}",
|
||||||
"please-password-type-first": "Por favor, introduce primeiro o contrasinal",
|
"please-password-type-first": "Por favor, introduce primeiro o contrasinal",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "תהליכי טעינה/עיבוד",
|
"pipeline-plural": "תהליכי טעינה/עיבוד",
|
||||||
"pipeline-state": "מצב תהליך הטעינה/עיבוד",
|
"pipeline-state": "מצב תהליך הטעינה/עיבוד",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} שורשים",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "נא להזין את ערך {{name}}",
|
"please-enter-value": "נא להזין את ערך {{name}}",
|
||||||
"please-password-type-first": "נא להקליד סיסמה תחילה",
|
"please-password-type-first": "נא להקליד סיסמה תחילה",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "パイプライン",
|
"pipeline-plural": "パイプライン",
|
||||||
"pipeline-state": "パイプラインの状態",
|
"pipeline-state": "パイプラインの状態",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} リネージ",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "{{name}}の値を入力してください",
|
"please-enter-value": "{{name}}の値を入力してください",
|
||||||
"please-password-type-first": "パスワードを入力してください",
|
"please-password-type-first": "パスワードを入力してください",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "파이프라인들",
|
"pipeline-plural": "파이프라인들",
|
||||||
"pipeline-state": "파이프라인 상태",
|
"pipeline-state": "파이프라인 상태",
|
||||||
"platform": "플랫폼",
|
"platform": "플랫폼",
|
||||||
|
"platform-type-lineage": "{{platformType}} 계보",
|
||||||
"play": "재생",
|
"play": "재생",
|
||||||
"please-enter-value": "{{name}} 값을 입력해주세요",
|
"please-enter-value": "{{name}} 값을 입력해주세요",
|
||||||
"please-password-type-first": "먼저 비밀번호를 입력해주세요",
|
"please-password-type-first": "먼저 비밀번호를 입력해주세요",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "पाइपलाइन",
|
"pipeline-plural": "पाइपलाइन",
|
||||||
"pipeline-state": "पाइपलाइन स्थिती",
|
"pipeline-state": "पाइपलाइन स्थिती",
|
||||||
"platform": "प्लॅटफॉर्म",
|
"platform": "प्लॅटफॉर्म",
|
||||||
|
"platform-type-lineage": "{{platformType}} वंशावळ",
|
||||||
"play": "प्ले",
|
"play": "प्ले",
|
||||||
"please-enter-value": "कृपया {{name}} मूल्य प्रविष्ट करा",
|
"please-enter-value": "कृपया {{name}} मूल्य प्रविष्ट करा",
|
||||||
"please-password-type-first": "कृपया प्रथम पासवर्ड टाइप करा",
|
"please-password-type-first": "कृपया प्रथम पासवर्ड टाइप करा",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Pipelinestatus",
|
"pipeline-state": "Pipelinestatus",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Herkomst",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Voer alstublieft de waarde voor {{name}} in",
|
"please-enter-value": "Voer alstublieft de waarde voor {{name}} in",
|
||||||
"please-password-type-first": "Typ eerst het wachtwoord alstublieft",
|
"please-password-type-first": "Typ eerst het wachtwoord alstublieft",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "خطوط لوله",
|
"pipeline-plural": "خطوط لوله",
|
||||||
"pipeline-state": "وضعیت خط لوله",
|
"pipeline-state": "وضعیت خط لوله",
|
||||||
"platform": "پلتفرم",
|
"platform": "پلتفرم",
|
||||||
|
"platform-type-lineage": "{{platformType}} شجره داده",
|
||||||
"play": "پخش",
|
"play": "پخش",
|
||||||
"please-enter-value": "لطفاً مقدار {{name}} را وارد کنید",
|
"please-enter-value": "لطفاً مقدار {{name}} را وارد کنید",
|
||||||
"please-password-type-first": "لطفاً ابتدا رمز عبور را وارد کنید",
|
"please-password-type-first": "لطفاً ابتدا رمز عبور را وارد کنید",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Estado do Pipeline",
|
"pipeline-state": "Estado do Pipeline",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Linhagem",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Por favor, insira o valor de {{name}}",
|
"please-enter-value": "Por favor, insira o valor de {{name}}",
|
||||||
"please-password-type-first": "Por favor, digite a senha primeiro",
|
"please-password-type-first": "Por favor, digite a senha primeiro",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Pipelines",
|
"pipeline-plural": "Pipelines",
|
||||||
"pipeline-state": "Estado do Pipeline",
|
"pipeline-state": "Estado do Pipeline",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Linhagem",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Por favor, insira o valor de {{name}}",
|
"please-enter-value": "Por favor, insira o valor de {{name}}",
|
||||||
"please-password-type-first": "Por favor, digite a senha primeiro",
|
"please-password-type-first": "Por favor, digite a senha primeiro",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "Пайплайны",
|
"pipeline-plural": "Пайплайны",
|
||||||
"pipeline-state": "Состояние",
|
"pipeline-state": "Состояние",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
|
"platform-type-lineage": "{{platformType}} Происхождение",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "Пожалуйста введите значение {{name}} ",
|
"please-enter-value": "Пожалуйста введите значение {{name}} ",
|
||||||
"please-password-type-first": "Пожалуйста, сначала введите пароль",
|
"please-password-type-first": "Пожалуйста, сначала введите пароль",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "ท่อหลายรายการ",
|
"pipeline-plural": "ท่อหลายรายการ",
|
||||||
"pipeline-state": "สถานะท่อ",
|
"pipeline-state": "สถานะท่อ",
|
||||||
"platform": "แพลตฟอร์ม",
|
"platform": "แพลตฟอร์ม",
|
||||||
|
"platform-type-lineage": "{{platformType}} ลำดับชั้น",
|
||||||
"play": "เล่น",
|
"play": "เล่น",
|
||||||
"please-enter-value": "กรุณากรอกค่าของ {{name}}",
|
"please-enter-value": "กรุณากรอกค่าของ {{name}}",
|
||||||
"please-password-type-first": "กรุณาพิมพ์รหัสผ่านก่อน",
|
"please-password-type-first": "กรุณาพิมพ์รหัสผ่านก่อน",
|
||||||
|
@ -1000,6 +1000,7 @@
|
|||||||
"pipeline-plural": "工作流",
|
"pipeline-plural": "工作流",
|
||||||
"pipeline-state": "工作流状态",
|
"pipeline-state": "工作流状态",
|
||||||
"platform": "平台",
|
"platform": "平台",
|
||||||
|
"platform-type-lineage": "{{platformType}} 血缘关系",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"please-enter-value": "请输入{{name}}值",
|
"please-enter-value": "请输入{{name}}值",
|
||||||
"please-password-type-first": "请先输入密码",
|
"please-password-type-first": "请先输入密码",
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
import { Col, Row, Select } from 'antd';
|
import { Col, Row, Select } from 'antd';
|
||||||
import { DefaultOptionType } from 'antd/lib/select';
|
import { DefaultOptionType } from 'antd/lib/select';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { debounce } from 'lodash';
|
import { debounce, startCase } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
@ -27,6 +27,7 @@ import { SourceType } from '../../components/SearchedData/SearchedData.interface
|
|||||||
import { PAGE_SIZE_BASE } from '../../constants/constants';
|
import { PAGE_SIZE_BASE } from '../../constants/constants';
|
||||||
import { PAGE_HEADERS } from '../../constants/PageHeaders.constant';
|
import { PAGE_HEADERS } from '../../constants/PageHeaders.constant';
|
||||||
import LineageProvider from '../../context/LineageProvider/LineageProvider';
|
import LineageProvider from '../../context/LineageProvider/LineageProvider';
|
||||||
|
import { LineagePlatformView } from '../../context/LineageProvider/LineageProvider.interface';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
ResourceEntity,
|
ResourceEntity,
|
||||||
@ -34,6 +35,7 @@ import {
|
|||||||
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 { EntityReference } from '../../generated/entity/type';
|
import { EntityReference } from '../../generated/entity/type';
|
||||||
|
import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
|
||||||
import { useFqn } from '../../hooks/useFqn';
|
import { useFqn } from '../../hooks/useFqn';
|
||||||
import { getEntityPermissionByFqn } from '../../rest/permissionAPI';
|
import { getEntityPermissionByFqn } from '../../rest/permissionAPI';
|
||||||
import { searchQuery } from '../../rest/searchAPI';
|
import { searchQuery } from '../../rest/searchAPI';
|
||||||
@ -45,7 +47,11 @@ import './platform-lineage.less';
|
|||||||
|
|
||||||
const PlatformLineage = () => {
|
const PlatformLineage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const location = useCustomLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const queryParams = new URLSearchParams(location.search);
|
||||||
|
const platformView =
|
||||||
|
queryParams.get('platformView') ?? LineagePlatformView.Service;
|
||||||
const { entityType } = useParams<{ entityType: EntityType }>();
|
const { entityType } = useParams<{ entityType: EntityType }>();
|
||||||
const { fqn: decodedFqn } = useFqn();
|
const { fqn: decodedFqn } = useFqn();
|
||||||
const [selectedEntity, setSelectedEntity] = useState<SourceType>();
|
const [selectedEntity, setSelectedEntity] = useState<SourceType>();
|
||||||
@ -57,6 +63,16 @@ const PlatformLineage = () => {
|
|||||||
);
|
);
|
||||||
const [permissions, setPermissions] = useState<OperationPermission>();
|
const [permissions, setPermissions] = useState<OperationPermission>();
|
||||||
|
|
||||||
|
const handleEntitySelect = useCallback(
|
||||||
|
(value: EntityReference) => {
|
||||||
|
history.push(
|
||||||
|
`/lineage/${(value as SourceType).entityType}/${
|
||||||
|
value.fullyQualifiedName
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[history]
|
||||||
|
);
|
||||||
const debouncedSearch = useCallback(
|
const debouncedSearch = useCallback(
|
||||||
debounce(async (value: string) => {
|
debounce(async (value: string) => {
|
||||||
try {
|
try {
|
||||||
@ -94,17 +110,6 @@ const PlatformLineage = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEntitySelect = useCallback(
|
|
||||||
(value: EntityReference) => {
|
|
||||||
history.push(
|
|
||||||
`/lineage/${(value as SourceType).entityType}/${
|
|
||||||
value.fullyQualifiedName
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[history]
|
|
||||||
);
|
|
||||||
|
|
||||||
const init = useCallback(async () => {
|
const init = useCallback(async () => {
|
||||||
if (!decodedFqn || !entityType) {
|
if (!decodedFqn || !entityType) {
|
||||||
setDefaultValue(undefined);
|
setDefaultValue(undefined);
|
||||||
@ -169,7 +174,14 @@ const PlatformLineage = () => {
|
|||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Row className="">
|
<Row className="">
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<PageHeader data={PAGE_HEADERS.PLATFORM_LINEAGE} />
|
<PageHeader
|
||||||
|
data={{
|
||||||
|
...PAGE_HEADERS.PLATFORM_LINEAGE,
|
||||||
|
header: t('label.platform-type-lineage', {
|
||||||
|
platformType: startCase(platformView),
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<div className="m-t-md w-full">
|
<div className="m-t-md w-full">
|
||||||
|
@ -1791,3 +1791,40 @@ export const getEntityTypeFromPlatformView = (
|
|||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively finds all downstream edges from a given node in a graph.
|
||||||
|
* This function traverses the graph depth-first, collecting all edges that flow downstream
|
||||||
|
* from the specified node while avoiding cycles by tracking visited nodes.
|
||||||
|
*
|
||||||
|
* @param {string} nodeId - The ID of the starting node
|
||||||
|
* @param {Edge[]} edges - Array of all edges in the graph
|
||||||
|
* @param {Set<string>} [visitedNodes=new Set()] - Set of already visited node IDs to prevent cycles
|
||||||
|
* @returns {Edge[]} Array of all downstream edges from the starting node
|
||||||
|
*/
|
||||||
|
export const getAllDownstreamEdges = (
|
||||||
|
nodeId: string,
|
||||||
|
edges: Edge[],
|
||||||
|
visitedNodes: Set<string> = new Set()
|
||||||
|
): Edge[] => {
|
||||||
|
// If we've already visited this node, return empty array to avoid cycles
|
||||||
|
if (visitedNodes.has(nodeId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
visitedNodes.add(nodeId);
|
||||||
|
|
||||||
|
// Get direct downstream edges
|
||||||
|
const directDownstreamEdges = edges.filter((edge) => edge.source === nodeId);
|
||||||
|
|
||||||
|
// Get target nodes from direct downstream edges
|
||||||
|
const targetNodes = directDownstreamEdges.map((edge) => edge.target);
|
||||||
|
|
||||||
|
// Recursively get downstream edges for each target node
|
||||||
|
const nestedDownstreamEdges = targetNodes.flatMap((targetNodeId) =>
|
||||||
|
getAllDownstreamEdges(targetNodeId, edges, visitedNodes)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combine direct and nested downstream edges
|
||||||
|
return [...directDownstreamEdges, ...nestedDownstreamEdges];
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user