mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-29 17:49:14 +00:00
App settings - lineage config (#18522)
* add lineage settings page * update locales * minor ui changes * add playwright for lineage config (cherry picked from commit 3dcbeb469f655ee3372bedf0c94fab7f247e4882)
This commit is contained in:
parent
ea9b4fe484
commit
3d62581b29
@ -72,6 +72,9 @@ export enum GlobalSettingOptions {
|
||||
API_ENDPOINTS = 'apiEndpoints',
|
||||
DATA_PRODUCTS = 'dataProducts',
|
||||
DASHBOARD_DATA_MODEL = 'dashboardDataModels',
|
||||
METRICS = 'metrics',
|
||||
SEARCH_RBAC = 'search-rbac',
|
||||
LINEAGE_CONFIG = 'lineageConfig',
|
||||
}
|
||||
|
||||
export const SETTINGS_OPTIONS_PATH = {
|
||||
@ -188,6 +191,15 @@ export const SETTINGS_OPTIONS_PATH = {
|
||||
GlobalSettingsMenuCategory.CUSTOM_PROPERTIES,
|
||||
`${GlobalSettingsMenuCategory.CUSTOM_PROPERTIES}.${GlobalSettingOptions.GLOSSARY_TERM}`,
|
||||
],
|
||||
|
||||
[GlobalSettingOptions.SEARCH_RBAC]: [
|
||||
GlobalSettingsMenuCategory.PREFERENCES,
|
||||
`${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.SEARCH_RBAC}`,
|
||||
],
|
||||
[GlobalSettingOptions.LINEAGE_CONFIG]: [
|
||||
GlobalSettingsMenuCategory.PREFERENCES,
|
||||
`${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.LINEAGE_CONFIG}`,
|
||||
],
|
||||
};
|
||||
|
||||
export const SETTING_CUSTOM_PROPERTIES_PATH = {
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import test from '@playwright/test';
|
||||
import { get } from 'lodash';
|
||||
import { GlobalSettingOptions } from '../../constant/settings';
|
||||
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
||||
import { ContainerClass } from '../../support/entity/ContainerClass';
|
||||
import { DashboardClass } from '../../support/entity/DashboardClass';
|
||||
@ -33,13 +34,16 @@ import {
|
||||
connectEdgeBetweenNodes,
|
||||
deleteEdge,
|
||||
deleteNode,
|
||||
fillLineageConfigForm,
|
||||
performZoomOut,
|
||||
removeColumnLineage,
|
||||
setupEntitiesForLineage,
|
||||
verifyColumnLayerActive,
|
||||
verifyColumnLayerInactive,
|
||||
verifyNodePresent,
|
||||
visitLineageTab,
|
||||
} from '../../utils/lineage';
|
||||
import { settingClick } from '../../utils/sidebar';
|
||||
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
@ -276,3 +280,148 @@ test('Verify column lineage between table and api endpoint', async ({
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test('Verify function data in edge drawer', async ({ browser }) => {
|
||||
const { page } = await createNewPage(browser);
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
const table1 = new TableClass();
|
||||
const table2 = new TableClass();
|
||||
|
||||
try {
|
||||
await table1.create(apiContext);
|
||||
await table2.create(apiContext);
|
||||
const sourceTableFqn = get(table1, 'entityResponseData.fullyQualifiedName');
|
||||
const sourceColName = `${sourceTableFqn}.${get(
|
||||
table1,
|
||||
'entityResponseData.columns[0].name'
|
||||
)}`;
|
||||
|
||||
const targetTableFqn = get(table2, 'entityResponseData.fullyQualifiedName');
|
||||
const targetColName = `${targetTableFqn}.${get(
|
||||
table2,
|
||||
'entityResponseData.columns[0].name'
|
||||
)}`;
|
||||
|
||||
await addPipelineBetweenNodes(page, table1, table2);
|
||||
await activateColumnLayer(page);
|
||||
await addColumnLineage(page, sourceColName, targetColName);
|
||||
|
||||
const lineageReq = page.waitForResponse('/api/v1/lineage/getLineage?*');
|
||||
await page.reload();
|
||||
const lineageRes = await lineageReq;
|
||||
const jsonRes = await lineageRes.json();
|
||||
const edge = jsonRes.edges[0];
|
||||
const columnData = edge.columns[0];
|
||||
|
||||
const newEdge = {
|
||||
edge: {
|
||||
fromEntity: {
|
||||
id: edge.fromEntity.id,
|
||||
type: edge.fromEntity.type,
|
||||
},
|
||||
toEntity: {
|
||||
id: edge.toEntity.id,
|
||||
type: edge.toEntity.type,
|
||||
},
|
||||
lineageDetails: {
|
||||
columnsLineage: [
|
||||
{
|
||||
fromColumns: [columnData.fromColumns[0]],
|
||||
function: 'count',
|
||||
toColumn: columnData.toColumn,
|
||||
},
|
||||
],
|
||||
description: 'test',
|
||||
},
|
||||
},
|
||||
};
|
||||
await apiContext.put(`/api/v1/lineage`, {
|
||||
data: newEdge,
|
||||
});
|
||||
const lineageReq1 = page.waitForResponse('/api/v1/lineage/getLineage?*');
|
||||
await page.reload();
|
||||
await lineageReq1;
|
||||
|
||||
await activateColumnLayer(page);
|
||||
await page
|
||||
.locator(
|
||||
`[data-testid="column-edge-${btoa(sourceColName)}-${btoa(
|
||||
targetColName
|
||||
)}"]`
|
||||
)
|
||||
.dispatchEvent('click');
|
||||
|
||||
await page.locator('.edge-info-drawer').isVisible();
|
||||
|
||||
await expect(await page.locator('[data-testid="Function"]')).toContainText(
|
||||
'count'
|
||||
);
|
||||
} finally {
|
||||
await table1.delete(apiContext);
|
||||
await table2.delete(apiContext);
|
||||
await afterAction();
|
||||
}
|
||||
});
|
||||
|
||||
test('Verify global lineage config', async ({ browser }) => {
|
||||
const { page } = await createNewPage(browser);
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
const table = new TableClass();
|
||||
const topic = new TopicClass();
|
||||
const dashboard = new DashboardClass();
|
||||
const mlModel = new MlModelClass();
|
||||
|
||||
try {
|
||||
await table.create(apiContext);
|
||||
await topic.create(apiContext);
|
||||
await dashboard.create(apiContext);
|
||||
await mlModel.create(apiContext);
|
||||
|
||||
await addPipelineBetweenNodes(page, table, topic);
|
||||
await addPipelineBetweenNodes(page, topic, dashboard);
|
||||
await addPipelineBetweenNodes(page, dashboard, mlModel);
|
||||
|
||||
await topic.visitEntityPage(page);
|
||||
await visitLineageTab(page);
|
||||
|
||||
await verifyNodePresent(page, table);
|
||||
await verifyNodePresent(page, dashboard);
|
||||
await verifyNodePresent(page, mlModel);
|
||||
|
||||
await settingClick(page, GlobalSettingOptions.LINEAGE_CONFIG);
|
||||
await fillLineageConfigForm(page, {
|
||||
upstreamDepth: 1,
|
||||
downstreamDepth: 1,
|
||||
layer: 'Column Level Lineage',
|
||||
});
|
||||
|
||||
await topic.visitEntityPage(page);
|
||||
await visitLineageTab(page);
|
||||
|
||||
await verifyNodePresent(page, table);
|
||||
await verifyNodePresent(page, dashboard);
|
||||
|
||||
const mlModelFqn = get(mlModel, 'entityResponseData.fullyQualifiedName');
|
||||
const mlModelNode = page.locator(
|
||||
`[data-testid="lineage-node-${mlModelFqn}"]`
|
||||
);
|
||||
|
||||
await expect(mlModelNode).not.toBeVisible();
|
||||
|
||||
await verifyColumnLayerActive(page);
|
||||
|
||||
await settingClick(page, GlobalSettingOptions.LINEAGE_CONFIG);
|
||||
await fillLineageConfigForm(page, {
|
||||
upstreamDepth: 2,
|
||||
downstreamDepth: 2,
|
||||
layer: 'Entity Lineage',
|
||||
});
|
||||
} finally {
|
||||
await table.delete(apiContext);
|
||||
await topic.delete(apiContext);
|
||||
await dashboard.delete(apiContext);
|
||||
await mlModel.delete(apiContext);
|
||||
|
||||
await afterAction();
|
||||
}
|
||||
});
|
||||
|
||||
@ -21,7 +21,11 @@ import { PipelineClass } from '../support/entity/PipelineClass';
|
||||
import { SearchIndexClass } from '../support/entity/SearchIndexClass';
|
||||
import { TableClass } from '../support/entity/TableClass';
|
||||
import { TopicClass } from '../support/entity/TopicClass';
|
||||
import { getApiContext, getEntityTypeSearchIndexMapping } from './common';
|
||||
import {
|
||||
getApiContext,
|
||||
getEntityTypeSearchIndexMapping,
|
||||
toastNotification,
|
||||
} from './common';
|
||||
|
||||
export const verifyColumnLayerInactive = async (page: Page) => {
|
||||
await page.click('[data-testid="lineage-layer-btn"]'); // Open Layer popover
|
||||
@ -395,3 +399,29 @@ export const visitLineageTab = async (page: Page) => {
|
||||
await page.click('[data-testid="lineage"]');
|
||||
await lineageRes;
|
||||
};
|
||||
|
||||
export const fillLineageConfigForm = async (
|
||||
page: Page,
|
||||
config: { upstreamDepth: number; downstreamDepth: number; layer: string }
|
||||
) => {
|
||||
await page
|
||||
.getByTestId('field-upstream')
|
||||
.fill(config.upstreamDepth.toString());
|
||||
await page
|
||||
.getByTestId('field-downstream')
|
||||
.fill(config.downstreamDepth.toString());
|
||||
await page.getByTestId('field-lineage-layer').click();
|
||||
await page.locator(`.ant-select-item[title="${config.layer}"]`).click();
|
||||
|
||||
const saveRes = page.waitForResponse('/api/v1/system/settings');
|
||||
await page.getByTestId('save-button').click();
|
||||
await saveRes;
|
||||
|
||||
await toastNotification(page, /Lineage Config updated successfully/);
|
||||
};
|
||||
|
||||
export const verifyColumnLayerActive = async (page: Page) => {
|
||||
await page.click('[data-testid="lineage-layer-btn"]'); // Open Layer popover
|
||||
await page.waitForSelector('[data-testid="lineage-layer-column-btn"].active');
|
||||
await page.click('[data-testid="lineage-layer-btn"]'); // Close Layer popover
|
||||
};
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
# Lineage Configuration
|
||||
|
||||
Configure lineage view settings.
|
||||
|
||||
$$section
|
||||
|
||||
### Upstream Depth $(id="upstreamDepth")
|
||||
|
||||
Display up to 5 nodes of upstream lineage to identify the source (parent levels).
|
||||
$$
|
||||
|
||||
$$section
|
||||
|
||||
### Downstream Depth $(id="downstreamDepth")
|
||||
|
||||
Display up to 5 nodes of downstream lineage to identify the target (child levels).
|
||||
$$
|
||||
|
||||
$$section
|
||||
|
||||
### Lineage Layer $(id="lineageLayer")
|
||||
|
||||
Select a lineage layer to view specific data relationships:
|
||||
- **Entity Lineage**: Displays relationships at the entity level, focusing on higher-level data connections.
|
||||
- **Column Level Lineage**: Shows lineage at the column level, detailing relationships between individual columns.
|
||||
- **Data Observability**: Highlights data quality and monitoring insights within the lineage.
|
||||
$$
|
||||
@ -0,0 +1 @@
|
||||
<svg id="Layer_1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><g><g fill="#006db7"><path d="m50 54.608c-1.15 0-2.083-.933-2.083-2.084v-18.862c0-1.151.933-2.084 2.083-2.084s2.083.933 2.083 2.084v18.862c0 1.152-.933 2.084-2.083 2.084z"/><path d="m50 54.608h-33.229c-1.15 0-2.083-.933-2.083-2.084 0-1.15.933-2.083 2.083-2.083h33.229c1.15 0 2.083.933 2.083 2.083 0 1.152-.933 2.084-2.083 2.084z"/><path d="m16.771 68.419c-1.15 0-2.083-.933-2.083-2.083v-13.812c0-1.15.933-2.083 2.083-2.083s2.083.933 2.083 2.083v13.812c.001 1.15-.932 2.083-2.083 2.083z"/><path d="m83.228 54.608h-33.228c-1.15 0-2.083-.933-2.083-2.084 0-1.15.933-2.083 2.083-2.083h33.228c1.15 0 2.083.933 2.083 2.083 0 1.152-.933 2.084-2.083 2.084z"/><path d="m83.228 68.419c-1.15 0-2.083-.933-2.083-2.083v-13.812c0-1.15.933-2.083 2.083-2.083s2.083.933 2.083 2.083v13.812c0 1.15-.933 2.083-2.083 2.083z"/></g><path d="m33.229 4.865h33.544v9.493h-33.544z" fill="#09afaa"/><path d="m33.229 14.358h33.544v19.304h-33.544z" fill="#0fd8ff"/><path d="m57.279 26.095h-14.557c-1.15 0-2.083-.934-2.083-2.084s.933-2.083 2.083-2.083h14.558c1.15 0 2.084.933 2.084 2.083s-.934 2.084-2.085 2.084z" fill="#61a8aa"/><path d="m0 66.336h33.544v9.496h-33.544z" fill="#09afaa"/><path d="m0 75.832h33.544v19.303h-33.544z" fill="#0fd8ff"/><path d="m24.051 87.565h-14.558c-1.151 0-2.083-.934-2.083-2.084s.933-2.083 2.083-2.083h14.558c1.15 0 2.083.933 2.083 2.083s-.933 2.084-2.083 2.084z" fill="#61a8aa"/><path d="m66.457 66.336h33.543v9.496h-33.543z" fill="#09afaa"/><path d="m66.457 75.832h33.543v19.303h-33.543z" fill="#0fd8ff"/><path d="m90.507 87.565h-14.557c-1.15 0-2.083-.934-2.083-2.084s.933-2.083 2.083-2.083h14.557c1.15 0 2.083.933 2.083 2.083s-.933 2.084-2.083 2.084z" fill="#61a8aa"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@ -16,8 +16,11 @@ import classNames from 'classnames';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLimitStore } from '../../context/LimitsProvider/useLimitsStore';
|
||||
import { LineageSettings } from '../../generated/configuration/lineageSettings';
|
||||
import { SettingType } from '../../generated/settings/settings';
|
||||
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||
import { getLimitConfig } from '../../rest/limitsAPI';
|
||||
import { getSettingsByType } from '../../rest/settingConfigAPI';
|
||||
import applicationRoutesClass from '../../utils/ApplicationRoutesClassBase';
|
||||
import Appbar from '../AppBar/Appbar';
|
||||
import { LimitBanner } from '../common/LimitBanner/LimitBanner';
|
||||
@ -29,18 +32,22 @@ import './app-container.less';
|
||||
const AppContainer = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const { Header, Sider, Content } = Layout;
|
||||
const { currentUser } = useApplicationStore();
|
||||
const { currentUser, setAppPreferences } = useApplicationStore();
|
||||
const { applications } = useApplicationsProvider();
|
||||
const AuthenticatedRouter = applicationRoutesClass.getRouteElements();
|
||||
const ApplicationExtras = applicationsClassBase.getApplicationExtension();
|
||||
const isDirectionRTL = useMemo(() => i18n.dir() === 'rtl', [i18n]);
|
||||
const { setConfig, bannerDetails } = useLimitStore();
|
||||
|
||||
const fetchLimitConfig = useCallback(async () => {
|
||||
const fetchAppConfigurations = useCallback(async () => {
|
||||
try {
|
||||
const response = await getLimitConfig();
|
||||
const [response, lineageConfig] = await Promise.all([
|
||||
getLimitConfig(),
|
||||
getSettingsByType(SettingType.LineageSettings),
|
||||
]);
|
||||
|
||||
setConfig(response);
|
||||
setAppPreferences({ lineageConfig: lineageConfig as LineageSettings });
|
||||
} catch (error) {
|
||||
// silent fail
|
||||
}
|
||||
@ -53,7 +60,7 @@ const AppContainer = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser?.id) {
|
||||
fetchLimitConfig();
|
||||
fetchAppConfigurations();
|
||||
}
|
||||
}, [currentUser?.id]);
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import EditEmailConfigPage from '../../pages/EditEmailConfigPage/EditEmailConfig
|
||||
import EmailConfigSettingsPage from '../../pages/EmailConfigSettingsPage/EmailConfigSettingsPage.component';
|
||||
import GlobalSettingCategoryPage from '../../pages/GlobalSettingPage/GlobalSettingCategory/GlobalSettingCategoryPage';
|
||||
import GlobalSettingPage from '../../pages/GlobalSettingPage/GlobalSettingPage';
|
||||
import LineageConfigPage from '../../pages/LineageConfigPage/LineageConfigPage';
|
||||
import NotificationListPage from '../../pages/NotificationListPage/NotificationListPage';
|
||||
import OmHealthPage from '../../pages/OmHealth/OmHealthPage';
|
||||
import { PersonaDetailsPage } from '../../pages/Persona/PersonaDetailsPage/PersonaDetailsPage';
|
||||
@ -245,6 +246,15 @@ const SettingsRouter = () => {
|
||||
* Do not change the order of these route
|
||||
*/}
|
||||
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={LineageConfigPage}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.PREFERENCES,
|
||||
GlobalSettingOptions.LINEAGE_CONFIG
|
||||
)}
|
||||
/>
|
||||
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={PoliciesListPage}
|
||||
|
||||
@ -32,8 +32,8 @@ import { ReactComponent as ExportIcon } from '../../../assets/svg/ic-export.svg'
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../../constants/HelperTextUtil';
|
||||
import { LINEAGE_DEFAULT_QUICK_FILTERS } from '../../../constants/Lineage.constants';
|
||||
import { useLineageProvider } from '../../../context/LineageProvider/LineageProvider';
|
||||
import { LineageLayerView } from '../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { SearchIndex } from '../../../enums/search.enum';
|
||||
import { LineageLayer } from '../../../generated/settings/settings';
|
||||
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
||||
import { getAssetsPageQuickFilters } from '../../../utils/AdvancedSearchUtils';
|
||||
import { getLoadingStatusValue } from '../../../utils/EntityLineageUtils';
|
||||
@ -75,7 +75,7 @@ const CustomControls: FC<ControlProps> = ({
|
||||
>([]);
|
||||
const [filters, setFilters] = useState<ExploreQuickFilterField[]>([]);
|
||||
const isColumnLayerActive = useMemo(() => {
|
||||
return activeLayer.includes(LineageLayerView.COLUMN);
|
||||
return activeLayer.includes(LineageLayer.ColumnLevelLineage);
|
||||
}, [activeLayer]);
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
*/
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { LineageLayerView } from '../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { LOADING_STATE } from '../../../enums/common.enum';
|
||||
import { LineageLayer } from '../../../generated/settings/settings';
|
||||
import { MOCK_LINEAGE_DATA } from '../../../mocks/Lineage.mock';
|
||||
import CustomControlsComponent from './CustomControls.component';
|
||||
|
||||
@ -51,7 +51,7 @@ jest.mock('../../../context/LineageProvider/LineageProvider', () => ({
|
||||
useLineageProvider: jest.fn().mockImplementation(() => ({
|
||||
onLineageEditClick: mockOnEditLineageClick,
|
||||
onExportClick: mockOnExportClick,
|
||||
activeLayer: [LineageLayerView.COLUMN],
|
||||
activeLayer: [LineageLayer.ColumnLevelLineage],
|
||||
})),
|
||||
}));
|
||||
|
||||
|
||||
@ -15,8 +15,8 @@ import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { EdgeProps, Position } from 'reactflow';
|
||||
import { LineageLayerView } from '../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { LineageLayer } from '../../../generated/settings/settings';
|
||||
import { CustomEdge } from './CustomEdge.component';
|
||||
|
||||
jest.mock('../../../constants/Lineage.constants', () => ({
|
||||
@ -71,7 +71,7 @@ jest.mock('../../../context/LineageProvider/LineageProvider', () => ({
|
||||
tracedNodes: [],
|
||||
tracedColumns: [],
|
||||
pipelineStatus: {},
|
||||
activeLayer: [LineageLayerView.COLUMN],
|
||||
activeLayer: [LineageLayer.ColumnLevelLineage],
|
||||
fetchPipelineStatus: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
@ -21,9 +21,9 @@ import { ReactComponent as IconTimesCircle } from '../../../assets/svg/ic-times-
|
||||
import { ReactComponent as PipelineIcon } from '../../../assets/svg/pipeline-grey.svg';
|
||||
import { FOREIGN_OBJECT_SIZE } from '../../../constants/Lineage.constants';
|
||||
import { useLineageProvider } from '../../../context/LineageProvider/LineageProvider';
|
||||
import { LineageLayerView } from '../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { StatusType } from '../../../generated/entity/data/pipeline';
|
||||
import { LineageLayer } from '../../../generated/settings/settings';
|
||||
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
||||
import { getColumnSourceTargetHandles } from '../../../utils/EntityLineageUtils';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
@ -207,7 +207,7 @@ export const CustomEdge = ({
|
||||
|
||||
const currentPipelineStatus = useMemo(() => {
|
||||
const isPipelineActiveNow = activeLayer.includes(
|
||||
LineageLayerView.DATA_OBSERVARABILITY
|
||||
LineageLayer.DataObservability
|
||||
);
|
||||
const pipelineData = pipeline?.pipelineStatus;
|
||||
if (pipelineData && isPipelineActiveNow) {
|
||||
|
||||
@ -13,8 +13,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
import { LineageLayerView } from '../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { ModelType } from '../../../generated/entity/data/table';
|
||||
import { LineageLayer } from '../../../generated/settings/settings';
|
||||
import CustomNodeV1Component from './CustomNodeV1.component';
|
||||
|
||||
const mockNodeDataProps = {
|
||||
@ -87,7 +87,7 @@ jest.mock('../../../context/LineageProvider/LineageProvider', () => ({
|
||||
downstreamEdges: [],
|
||||
},
|
||||
columnsHavingLineage: [],
|
||||
activeLayer: [LineageLayerView.COLUMN],
|
||||
activeLayer: [LineageLayer.ColumnLevelLineage],
|
||||
fetchPipelineStatus: jest.fn(),
|
||||
onColumnClick: onMockColumnClick,
|
||||
})),
|
||||
|
||||
@ -11,13 +11,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
LineageConfig,
|
||||
LineageConfigModalProps,
|
||||
} from './EntityLineage.interface';
|
||||
|
||||
type LineageConfigFormFields = {
|
||||
upstreamDepth: number;
|
||||
downstreamDepth: number;
|
||||
nodesPerLayer: number;
|
||||
};
|
||||
|
||||
const LineageConfigModal: React.FC<LineageConfigModalProps> = ({
|
||||
visible,
|
||||
config,
|
||||
@ -26,21 +32,12 @@ const LineageConfigModal: React.FC<LineageConfigModalProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [upstreamDepth, setUpstreamDepth] = useState<number>(
|
||||
config?.upstreamDepth || 1
|
||||
);
|
||||
const [downstreamDepth, setDownstreamDepth] = useState<number>(
|
||||
config?.downstreamDepth || 1
|
||||
);
|
||||
const [nodesPerLayer, setNodesPerLayer] = useState<number>(
|
||||
config?.nodesPerLayer || 1
|
||||
);
|
||||
|
||||
const handleSave = () => {
|
||||
const handleSave = (values: LineageConfigFormFields) => {
|
||||
const updatedConfig: LineageConfig = {
|
||||
upstreamDepth,
|
||||
downstreamDepth,
|
||||
nodesPerLayer,
|
||||
upstreamDepth: Number(values.upstreamDepth),
|
||||
downstreamDepth: Number(values.downstreamDepth),
|
||||
nodesPerLayer: Number(values.nodesPerLayer),
|
||||
};
|
||||
onSave(updatedConfig);
|
||||
};
|
||||
@ -67,12 +64,7 @@ const LineageConfigModal: React.FC<LineageConfigModalProps> = ({
|
||||
},
|
||||
]}
|
||||
tooltip={t('message.upstream-depth-tooltip')}>
|
||||
<Input
|
||||
data-testid="field-upstream"
|
||||
type="number"
|
||||
value={upstreamDepth}
|
||||
onChange={(e) => setUpstreamDepth(Number(e.target.value))}
|
||||
/>
|
||||
<Input data-testid="field-upstream" type="number" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -85,12 +77,7 @@ const LineageConfigModal: React.FC<LineageConfigModalProps> = ({
|
||||
},
|
||||
]}
|
||||
tooltip={t('message.downstream-depth-tooltip')}>
|
||||
<Input
|
||||
data-testid="field-downstream"
|
||||
type="number"
|
||||
value={downstreamDepth}
|
||||
onChange={(e) => setDownstreamDepth(Number(e.target.value))}
|
||||
/>
|
||||
<Input data-testid="field-downstream" type="number" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -108,7 +95,6 @@ const LineageConfigModal: React.FC<LineageConfigModalProps> = ({
|
||||
data-testid="field-nodes-per-layer"
|
||||
min={5}
|
||||
type="number"
|
||||
onChange={(e) => setNodesPerLayer(Number(e.target.value))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@ -14,7 +14,7 @@ import { act, queryByText, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
import { LineageLayerView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { LineageLayer } from '../../../../generated/settings/settings';
|
||||
import LineageLayers from './LineageLayers';
|
||||
|
||||
const onMockColumnClick = jest.fn();
|
||||
@ -113,13 +113,13 @@ describe('LineageLayers component', () => {
|
||||
userEvent.click(columnButton as HTMLElement);
|
||||
|
||||
expect(onMockUpdateLayerView).toHaveBeenCalledWith([
|
||||
LineageLayerView.COLUMN,
|
||||
LineageLayer.ColumnLevelLineage,
|
||||
]);
|
||||
|
||||
userEvent.click(dataObservabilityBtn as HTMLElement);
|
||||
|
||||
expect(onMockUpdateLayerView).toHaveBeenCalledWith([
|
||||
LineageLayerView.DATA_OBSERVARABILITY,
|
||||
LineageLayer.DataObservability,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -18,15 +18,15 @@ import React from 'react';
|
||||
import { ReactComponent as DataQualityIcon } from '../../../../assets/svg/ic-data-contract.svg';
|
||||
import { ReactComponent as Layers } from '../../../../assets/svg/ic-layers.svg';
|
||||
import { useLineageProvider } from '../../../../context/LineageProvider/LineageProvider';
|
||||
import { LineageLayerView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { EntityType } from '../../../../enums/entity.enum';
|
||||
import { LineageLayer } from '../../../../generated/settings/settings';
|
||||
import searchClassBase from '../../../../utils/SearchClassBase';
|
||||
import './lineage-layers.less';
|
||||
|
||||
const LineageLayers = () => {
|
||||
const { activeLayer, onUpdateLayerView, isEditMode } = useLineageProvider();
|
||||
|
||||
const onButtonClick = (value: LineageLayerView) => {
|
||||
const onButtonClick = (value: LineageLayer) => {
|
||||
const index = activeLayer.indexOf(value);
|
||||
if (index === -1) {
|
||||
onUpdateLayerView([...activeLayer, value]);
|
||||
@ -41,10 +41,10 @@ const LineageLayers = () => {
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
className={classNames('lineage-layer-button h-15', {
|
||||
active: activeLayer.includes(LineageLayerView.COLUMN),
|
||||
active: activeLayer.includes(LineageLayer.ColumnLevelLineage),
|
||||
})}
|
||||
data-testid="lineage-layer-column-btn"
|
||||
onClick={() => onButtonClick(LineageLayerView.COLUMN)}>
|
||||
onClick={() => onButtonClick(LineageLayer.ColumnLevelLineage)}>
|
||||
<div className="lineage-layer-btn">
|
||||
<div className="layer-icon">
|
||||
{searchClassBase.getEntityIcon(EntityType.TABLE)}
|
||||
@ -56,14 +56,10 @@ const LineageLayers = () => {
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames('lineage-layer-button h-15', {
|
||||
active: activeLayer.includes(
|
||||
LineageLayerView.DATA_OBSERVARABILITY
|
||||
),
|
||||
active: activeLayer.includes(LineageLayer.DataObservability),
|
||||
})}
|
||||
data-testid="lineage-layer-observability-btn"
|
||||
onClick={() =>
|
||||
onButtonClick(LineageLayerView.DATA_OBSERVARABILITY)
|
||||
}>
|
||||
onClick={() => onButtonClick(LineageLayer.DataObservability)}>
|
||||
<div className="lineage-layer-btn">
|
||||
<div className="layer-icon">
|
||||
<DataQualityIcon />
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
*/
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { LineageLayerView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { EntityType } from '../../../../enums/entity.enum';
|
||||
import { LineageLayer } from '../../../../generated/settings/settings';
|
||||
import LineageSearchSelect from './LineageSearchSelect';
|
||||
|
||||
const mockedNodes = [
|
||||
@ -38,7 +38,7 @@ const mockColumnClick = jest.fn();
|
||||
|
||||
jest.mock('../../../../context/LineageProvider/LineageProvider', () => ({
|
||||
useLineageProvider: jest.fn().mockImplementation(() => ({
|
||||
activeLayer: [LineageLayerView.COLUMN],
|
||||
activeLayer: [LineageLayer.ColumnLevelLineage],
|
||||
nodes: mockedNodes,
|
||||
onNodeClick: mockNodeClick,
|
||||
onColumnClick: mockColumnClick,
|
||||
|
||||
@ -21,9 +21,9 @@ import {
|
||||
LINEAGE_COLUMN_NODE_SUPPORTED,
|
||||
} from '../../../../constants/Lineage.constants';
|
||||
import { useLineageProvider } from '../../../../context/LineageProvider/LineageProvider';
|
||||
import { LineageLayerView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
||||
import { EntityType } from '../../../../enums/entity.enum';
|
||||
import { Column, Table } from '../../../../generated/entity/data/table';
|
||||
import { LineageLayer } from '../../../../generated/settings/settings';
|
||||
import { getEntityChildrenAndLabel } from '../../../../utils/EntityLineageUtils';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import searchClassBase from '../../../../utils/SearchClassBase';
|
||||
@ -50,9 +50,9 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
|
||||
|
||||
const { showColumns, showDataObservability } = useMemo(() => {
|
||||
return {
|
||||
showColumns: activeLayer.includes(LineageLayerView.COLUMN),
|
||||
showColumns: activeLayer.includes(LineageLayer.ColumnLevelLineage),
|
||||
showDataObservability: activeLayer.includes(
|
||||
LineageLayerView.DATA_OBSERVARABILITY
|
||||
LineageLayer.DataObservability
|
||||
),
|
||||
};
|
||||
}, [activeLayer]);
|
||||
|
||||
@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, { FC } from 'react';
|
||||
import './banner.less';
|
||||
|
||||
|
||||
@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.message-banner-wrapper {
|
||||
|
||||
@ -75,6 +75,8 @@ export enum GlobalSettingOptions {
|
||||
API_COLLECTIONS = 'apiCollections',
|
||||
API_ENDPOINTS = 'apiEndpoints',
|
||||
DATA_PRODUCT = 'dataProducts',
|
||||
METRICS = 'metrics',
|
||||
LINEAGE_CONFIG = 'lineageConfig',
|
||||
}
|
||||
|
||||
export const GLOBAL_SETTING_PERMISSION_RESOURCES = [
|
||||
|
||||
@ -198,6 +198,10 @@ export const PAGE_HEADERS = {
|
||||
header: i18n.t('label.login'),
|
||||
subHeader: i18n.t('message.page-sub-header-for-login-configuration'),
|
||||
},
|
||||
LINEAGE_CONFIG: {
|
||||
header: i18n.t('label.lineage-config'),
|
||||
subHeader: i18n.t('message.page-sub-header-for-lineage-config-setting'),
|
||||
},
|
||||
OM_HEALTH: {
|
||||
header: i18n.t('label.health-check'),
|
||||
subHeader: i18n.t('message.page-sub-header-for-om-health-configuration'),
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
import { SourceType } from '../../components/SearchedData/SearchedData.interface';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { EntityReference } from '../../generated/entity/type';
|
||||
import { LineageLayer } from '../../generated/settings/settings';
|
||||
|
||||
export interface LineageProviderProps {
|
||||
children: ReactNode;
|
||||
@ -44,11 +45,6 @@ export type UpstreamDownstreamData = {
|
||||
upstreamNodes: EntityReference[];
|
||||
};
|
||||
|
||||
export enum LineageLayerView {
|
||||
COLUMN = 'COLUMN',
|
||||
DATA_OBSERVARABILITY = 'DATA_OBSERVARABILITY',
|
||||
}
|
||||
|
||||
export interface LineageContextType {
|
||||
reactFlowInstance?: ReactFlowInstance;
|
||||
nodes: Node[];
|
||||
@ -67,7 +63,7 @@ export interface LineageContextType {
|
||||
selectedNode: SourceType;
|
||||
upstreamDownstreamData: UpstreamDownstreamData;
|
||||
selectedColumn: string;
|
||||
activeLayer: LineageLayerView[];
|
||||
activeLayer: LineageLayer[];
|
||||
expandAllColumns: boolean;
|
||||
toggleColumnView: () => void;
|
||||
onInitReactFlow: (reactFlowInstance: ReactFlowInstance) => void;
|
||||
@ -99,5 +95,5 @@ export interface LineageContextType {
|
||||
onAddPipelineClick: () => void;
|
||||
onConnect: (connection: Edge | Connection) => void;
|
||||
updateEntityType: (entityType: EntityType) => void;
|
||||
onUpdateLayerView: (layers: LineageLayerView[]) => void;
|
||||
onUpdateLayerView: (layers: LineageLayer[]) => void;
|
||||
}
|
||||
|
||||
@ -23,6 +23,20 @@ const mockLocation = {
|
||||
pathname: '/lineage',
|
||||
};
|
||||
|
||||
const mockData = {
|
||||
lineageConfig: {
|
||||
upstreamDepth: 1,
|
||||
downstreamDepth: 1,
|
||||
lineageLayer: 'EntityLineage',
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../hooks/useApplicationStore', () => ({
|
||||
useApplicationStore: jest.fn().mockImplementation(() => ({
|
||||
appPreferences: mockData,
|
||||
})),
|
||||
}));
|
||||
|
||||
const DummyChildrenComponent = () => {
|
||||
const {
|
||||
loadChildNodesHandler,
|
||||
@ -56,6 +70,7 @@ const DummyChildrenComponent = () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const handleButtonClick = () => {
|
||||
// Trigger the loadChildNodesHandler method when the button is clicked
|
||||
loadChildNodesHandler(nodeData, EdgeTypeEnum.DOWN_STREAM);
|
||||
@ -85,6 +100,10 @@ const DummyChildrenComponent = () => {
|
||||
);
|
||||
};
|
||||
|
||||
jest.mock('../../hooks/useCustomLocation/useCustomLocation', () => {
|
||||
return jest.fn().mockImplementation(() => ({ ...mockLocation }));
|
||||
});
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn().mockReturnValue({ push: jest.fn(), listen: jest.fn() }),
|
||||
useLocation: jest.fn().mockImplementation(() => mockLocation),
|
||||
@ -141,7 +160,7 @@ describe('LineageProvider', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const loadButton = await screen.getByTestId('load-nodes');
|
||||
const loadButton = screen.getByTestId('load-nodes');
|
||||
fireEvent.click(loadButton);
|
||||
|
||||
expect(getLineageDataByFQN).toHaveBeenCalled();
|
||||
@ -156,7 +175,7 @@ describe('LineageProvider', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const loadButton = await screen.getByTestId('editLineage');
|
||||
const loadButton = screen.getByTestId('editLineage');
|
||||
fireEvent.click(loadButton);
|
||||
|
||||
const edgeDrawer = screen.getByText('Entity Lineage Sidebar');
|
||||
@ -173,7 +192,7 @@ describe('LineageProvider', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const edgeClick = await screen.getByTestId('edge-click');
|
||||
const edgeClick = screen.getByTestId('edge-click');
|
||||
fireEvent.click(edgeClick);
|
||||
|
||||
const edgeDrawer = screen.getByText('Edge Info Drawer');
|
||||
|
||||
@ -68,11 +68,14 @@ import {
|
||||
EntityType,
|
||||
} from '../../enums/entity.enum';
|
||||
import { AddLineage } from '../../generated/api/lineage/addLineage';
|
||||
import { LineageSettings } from '../../generated/configuration/lineageSettings';
|
||||
import { LineageLayer } from '../../generated/settings/settings';
|
||||
import {
|
||||
ColumnLineage,
|
||||
EntityReference,
|
||||
LineageDetails,
|
||||
} from '../../generated/type/entityLineage';
|
||||
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||
import { useFqn } from '../../hooks/useFqn';
|
||||
import { getLineageDataByFQN, updateLineageEdge } from '../../rest/lineageAPI';
|
||||
import {
|
||||
@ -105,7 +108,6 @@ import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { useTourProvider } from '../TourProvider/TourProvider';
|
||||
import {
|
||||
LineageContextType,
|
||||
LineageLayerView,
|
||||
LineageProviderProps,
|
||||
UpstreamDownstreamData,
|
||||
} from './LineageProvider.interface';
|
||||
@ -114,9 +116,11 @@ export const LineageContext = createContext({} as LineageContextType);
|
||||
|
||||
const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { fqn: decodedFqn } = useFqn();
|
||||
const { isTourOpen, isTourPage } = useTourProvider();
|
||||
const { appPreferences } = useApplicationStore();
|
||||
const defaultLineageConfig = appPreferences?.lineageConfig as LineageSettings;
|
||||
const isLineageSettingsLoaded = !isUndefined(defaultLineageConfig);
|
||||
const [reactFlowInstance, setReactFlowInstance] =
|
||||
useState<ReactFlowInstance>();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
@ -124,7 +128,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
const [selectedNode, setSelectedNode] = useState<SourceType>(
|
||||
{} as SourceType
|
||||
);
|
||||
const [activeLayer, setActiveLayer] = useState<LineageLayerView[]>([]);
|
||||
const [activeLayer, setActiveLayer] = useState<LineageLayer[]>([]);
|
||||
const [activeNode, setActiveNode] = useState<Node>();
|
||||
const [expandAllColumns, setExpandAllColumns] = useState(false);
|
||||
const [selectedColumn, setSelectedColumn] = useState<string>('');
|
||||
@ -375,7 +379,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
[nodes, edges]
|
||||
);
|
||||
|
||||
const onUpdateLayerView = useCallback((layers: LineageLayerView[]) => {
|
||||
const onUpdateLayerView = useCallback((layers: LineageLayer[]) => {
|
||||
setActiveLayer(layers);
|
||||
}, []);
|
||||
|
||||
@ -1036,7 +1040,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
|
||||
const repositionLayout = useCallback(
|
||||
(activateNode = false) => {
|
||||
const isColView = activeLayer.includes(LineageLayerView.COLUMN);
|
||||
const isColView = activeLayer.includes(LineageLayer.ColumnLevelLineage);
|
||||
const { node, edge } = getLayoutedElements(
|
||||
{
|
||||
node: nodes,
|
||||
@ -1092,7 +1096,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
allNodes,
|
||||
lineageData.edges ?? [],
|
||||
decodedFqn,
|
||||
activeLayer.includes(LineageLayerView.COLUMN)
|
||||
activeLayer.includes(LineageLayer.ColumnLevelLineage)
|
||||
);
|
||||
const { edges: updatedEdges, columnsHavingLineage } = createEdges(
|
||||
allNodes,
|
||||
@ -1119,10 +1123,32 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (decodedFqn && entityType) {
|
||||
if (defaultLineageConfig) {
|
||||
setLineageConfig({
|
||||
upstreamDepth: defaultLineageConfig.upstreamDepth,
|
||||
downstreamDepth: defaultLineageConfig.downstreamDepth,
|
||||
nodesPerLayer: 50,
|
||||
});
|
||||
|
||||
setActiveLayer(
|
||||
defaultLineageConfig.lineageLayer === LineageLayer.EntityLineage
|
||||
? []
|
||||
: [defaultLineageConfig.lineageLayer]
|
||||
);
|
||||
}
|
||||
}, [defaultLineageConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (decodedFqn && entityType && isLineageSettingsLoaded) {
|
||||
fetchLineageData(decodedFqn, entityType, lineageConfig);
|
||||
}
|
||||
}, [lineageConfig, decodedFqn, queryFilter, entityType]);
|
||||
}, [
|
||||
lineageConfig,
|
||||
decodedFqn,
|
||||
queryFilter,
|
||||
entityType,
|
||||
isLineageSettingsLoaded,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
|
||||
@ -52,6 +52,7 @@ export const useApplicationStore = create<ApplicationStore>()(
|
||||
searchCriteria: '',
|
||||
inlineAlertDetails: undefined,
|
||||
applications: [],
|
||||
appPreferences: {},
|
||||
|
||||
setInlineAlertDetails: (inlineAlertDetails) => {
|
||||
set({ inlineAlertDetails });
|
||||
@ -150,6 +151,16 @@ export const useApplicationStore = create<ApplicationStore>()(
|
||||
setRefreshToken: (refreshToken) => {
|
||||
set({ refreshTokenKey: refreshToken });
|
||||
},
|
||||
setAppPreferences: (
|
||||
preferences: Partial<ApplicationStore['appPreferences']>
|
||||
) => {
|
||||
set((state) => ({
|
||||
appPreferences: {
|
||||
...state.appPreferences,
|
||||
...preferences,
|
||||
},
|
||||
}));
|
||||
},
|
||||
getOidcToken: () => {
|
||||
return get()?.oidcIdToken;
|
||||
},
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
} from '../components/Explore/ExplorePage.interface';
|
||||
import { AuthenticationConfiguration } from '../generated/configuration/authenticationConfiguration';
|
||||
import { AuthorizerConfiguration } from '../generated/configuration/authorizerConfiguration';
|
||||
import { LineageSettings } from '../generated/configuration/lineageSettings';
|
||||
import { LoginConfiguration } from '../generated/configuration/loginConfiguration';
|
||||
import { LogoConfiguration } from '../generated/configuration/logoConfiguration';
|
||||
import { UIThemePreference } from '../generated/configuration/uiThemePreference';
|
||||
@ -39,6 +40,10 @@ export interface HelperFunctions {
|
||||
trySilentSignIn: (forceLogout?: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface AppPreferences {
|
||||
lineageConfig?: LineageSettings;
|
||||
}
|
||||
|
||||
export interface ApplicationStore
|
||||
extends IAuthContext,
|
||||
LogoConfiguration,
|
||||
@ -56,9 +61,11 @@ export interface ApplicationStore
|
||||
theme: UIThemePreference['customTheme'];
|
||||
inlineAlertDetails?: InlineAlertProps;
|
||||
applications: string[];
|
||||
appPreferences: AppPreferences;
|
||||
setInlineAlertDetails: (alertDetails?: InlineAlertProps) => void;
|
||||
setSelectedPersona: (persona: EntityReference) => void;
|
||||
setApplicationConfig: (config: UIThemePreference) => void;
|
||||
setAppPreferences: (preferences: AppPreferences) => void;
|
||||
setCurrentUser: (user: User) => void;
|
||||
setAuthConfig: (authConfig: AuthenticationConfigurationWithScope) => void;
|
||||
setAuthorizerConfig: (authorizerConfig: AuthorizerConfiguration) => void;
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "Spalte",
|
||||
"column-entity": "{{entity}} Spalten",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "spalte",
|
||||
"column-lowercase-plural": "spalten",
|
||||
"column-plural": "Spalten",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Entitäts-ID-Übereinstimmung",
|
||||
"entity-index": "{{entity}}-Index",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "{{entity}}-Name",
|
||||
"entity-plural": "Entitäten",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Ergebnis der letzten Ausführung",
|
||||
"last-updated": "Zuletzt aktualisiert",
|
||||
"latest": "Neueste",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Abstammungskonfiguration",
|
||||
"lineage-data-lowercase": "Abstammungsdaten",
|
||||
"lineage-ingestion": "Abstammungsinjektion",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "Abstammung",
|
||||
"lineage-node-lowercase": "Abstammungsknoten",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Vertrauen in deine Daten aufbauen mit Qualitätsprüfungen und zuverlässige Datenerzeugnisse erstellen.",
|
||||
"page-sub-header-for-databases": "Ingestion von Metadaten aus den beliebtesten Datenbankdiensten.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "Ingestion von Metadaten aus den am häufigsten verwendeten Messaging-Diensten.",
|
||||
"page-sub-header-for-metadata": "Ingestion von Metadaten aus Metadatendiensten direkt über die Benutzeroberfläche.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "Column",
|
||||
"column-entity": "Column {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "column",
|
||||
"column-lowercase-plural": "columns",
|
||||
"column-plural": "Columns",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Match By Id",
|
||||
"entity-index": "{{entity}} index",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "{{entity}} Name",
|
||||
"entity-plural": "Entities",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Last Run Result",
|
||||
"last-updated": "Last Updated",
|
||||
"latest": "Latest",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Lineage Config",
|
||||
"lineage-data-lowercase": "lineage data",
|
||||
"lineage-ingestion": "Lineage Ingestion",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "lineage",
|
||||
"lineage-node-lowercase": "lineage node",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and create reliable data products.",
|
||||
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Define the handling of failed login attempts and JWT token expiry.",
|
||||
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
|
||||
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "Columna",
|
||||
"column-entity": "Columna {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "columna",
|
||||
"column-lowercase-plural": "columnas",
|
||||
"column-plural": "Columnas",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Coincidir por Id",
|
||||
"entity-index": "Índice de {{entity}}",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "Lista de {{entity}}",
|
||||
"entity-name": "Nombre de {{entity}}",
|
||||
"entity-plural": "Entidades",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Último resultado de la ejecución",
|
||||
"last-updated": "Última actualización",
|
||||
"latest": "Último",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Más información",
|
||||
"learn-more-and-support": "Más información y soporte",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Configuración del linaje",
|
||||
"lineage-data-lowercase": "lineage data",
|
||||
"lineage-ingestion": "Ingesta de lineaje",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "lineaje",
|
||||
"lineage-node-lowercase": "lineage node",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Construye confianza en tus datos con pruebas de calidad y crea productos de datos fiables.",
|
||||
"page-sub-header-for-databases": "Ingresa metadatos desde los servicios de base de datos más populares.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Configuración de inicio de sesión como intentos fallidos o temporizador de vencimiento.",
|
||||
"page-sub-header-for-messagings": "Ingresa metadatos desde los servicios de mensajería más utilizados.",
|
||||
"page-sub-header-for-metadata": "Ingresa metadatos desde servicios de metadatos directamente desde la interfaz de usuario.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "Colonne",
|
||||
"column-entity": "{{entity}} Colonnes",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "colonne",
|
||||
"column-lowercase-plural": "colonnes",
|
||||
"column-plural": "Colonnes",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Correspondance par ID de l'Entité",
|
||||
"entity-index": "Index de {{entity}}",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "Liste de {{entity}}",
|
||||
"entity-name": "Nom de {{entity}}",
|
||||
"entity-plural": "Entités",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Résultat de la Dernière Exécution",
|
||||
"last-updated": "Dernière Mise à Jour",
|
||||
"latest": "Dernier·ère",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Couches",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Config de Lignage",
|
||||
"lineage-data-lowercase": "lignage des données",
|
||||
"lineage-ingestion": "Ingestion de lignage",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "lignage",
|
||||
"lineage-node-lowercase": "Nœud de Lignage",
|
||||
"lineage-source": "Source du Lignage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingestion de metadonnées à partir des services de tests directement depuis l'interface utilisateur.",
|
||||
"page-sub-header-for-data-quality": "Gagnez en confiance dans vos données grâce à des tests de qualité et créez des produits de données fiables.",
|
||||
"page-sub-header-for-databases": "Ingestion de métadonnées à partir des services de base de données les plus populaires.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Configuration du login comme les tentatives infructueuses ou l'expiration du délai.",
|
||||
"page-sub-header-for-messagings": "Ingestion de métadonnées à partir des services de messagerie les plus utilisés.",
|
||||
"page-sub-header-for-metadata": "Ingestion de métadonnées à partir des services de métadonnées directement depuis l'interface utilisateur.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "צבע",
|
||||
"column": "עמודה",
|
||||
"column-entity": "עמודה {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "עמודה",
|
||||
"column-lowercase-plural": "עמודות",
|
||||
"column-plural": "עמודות",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "התאם לפי זיהוי",
|
||||
"entity-index": "אינדקס {{entity}}",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "שם {{entity}}",
|
||||
"entity-plural": "ישויות",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "תוצאת הרצה אחרונה",
|
||||
"last-updated": "עודכן לאחרונה",
|
||||
"latest": "אחרון",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "למידע נוסף ותמיכה",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "תצורת שורשים",
|
||||
"lineage-data-lowercase": "נתוני שורשים",
|
||||
"lineage-ingestion": "כניסת שורשים",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "שורשים",
|
||||
"lineage-node-lowercase": "צומת שורש",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "בנה אמון בנתונים שלך עם בדיקות איכות וצור מוצרי נתונים אמינים.",
|
||||
"page-sub-header-for-databases": "שלב מטה-דאטה מבסיס הנתונים הארגוניים. רוב בסיסי הנתונים הפופולריים נתמכים.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "שלב מטה-דאטה משירותי הזרמת המידע המובילים כגון קפקא ואחרים",
|
||||
"page-sub-header-for-metadata": "שלב מטה-דאטה משירותי מטה-דאטה אחרים הקיימים בארגון כגון Atlas ואחרים.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "カラム",
|
||||
"column-entity": "カラム {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "column",
|
||||
"column-lowercase-plural": "columns",
|
||||
"column-plural": "カラム",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "IDによるマッチング",
|
||||
"entity-index": "{{entity}} インデックス",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "{{entity}} 名",
|
||||
"entity-plural": "エンティティ",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Last Run Result",
|
||||
"last-updated": "最終更新日",
|
||||
"latest": "最新",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Lineage Config",
|
||||
"lineage-data-lowercase": "lineage data",
|
||||
"lineage-ingestion": "リネージのインジェスチョン",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "リネージ",
|
||||
"lineage-node-lowercase": "lineage node",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and create reliable data products.",
|
||||
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
|
||||
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Kleur",
|
||||
"column": "Kolom",
|
||||
"column-entity": "Kolom {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "kolom",
|
||||
"column-lowercase-plural": "kolommen",
|
||||
"column-plural": "Kolommen",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Overeenkomen op ID",
|
||||
"entity-index": "{{entity}}-index",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "{{entity}}-naam",
|
||||
"entity-plural": "Entiteiten",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Laatste uitvoerresultaat",
|
||||
"last-updated": "Laatst bijgewerkt",
|
||||
"latest": "Laatste",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Meer leren & Ondersteuning",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Herkomstconfiguratie",
|
||||
"lineage-data-lowercase": "Herkomstdata",
|
||||
"lineage-ingestion": "Herkomstingestie",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "herkomst",
|
||||
"lineage-node-lowercase": "herkomstknooppunt",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Bouw vertrouwen op in je data met kwaliteitstests en maak betrouwbare dataproducten.",
|
||||
"page-sub-header-for-databases": "Ingest metadata van de meestgebruikte databaseservices.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Loginconfiguratie zoals mislukte pogingen of verlooptimer.",
|
||||
"page-sub-header-for-messagings": "Ingest metadata van de meestgebruikte berichtenservices.",
|
||||
"page-sub-header-for-metadata": "Ingest metadata van metadataservices direct vanuit de UI.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "رنگ",
|
||||
"column": "ستون",
|
||||
"column-entity": "ستون {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "ستون",
|
||||
"column-lowercase-plural": "ستونها",
|
||||
"column-plural": "ستونها",
|
||||
@ -433,6 +434,7 @@
|
||||
"entity-id": "شناسه {{entity}}",
|
||||
"entity-id-match": "مطابقت با شناسه",
|
||||
"entity-index": "ایندکس {{entity}}",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "لیست {{entity}}",
|
||||
"entity-name": "نام {{entity}}",
|
||||
"entity-plural": "نهادها",
|
||||
@ -647,6 +649,7 @@
|
||||
"last-run-result": "نتیجه آخرین اجرا",
|
||||
"last-updated": "آخرین بهروزرسانی",
|
||||
"latest": "آخرین",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "لایهها",
|
||||
"learn-more": "بیشتر بدانید",
|
||||
"learn-more-and-support": "بیشتر بدانید و پشتیبانی کنید",
|
||||
@ -659,6 +662,7 @@
|
||||
"lineage-config": "تنظیمات شجره داده",
|
||||
"lineage-data-lowercase": "دادههای شجره داده",
|
||||
"lineage-ingestion": "دریافت شجره داده",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "شجره داده",
|
||||
"lineage-node-lowercase": "گره شجره داده",
|
||||
"lineage-source": "منبع شجره داده",
|
||||
@ -1709,6 +1713,7 @@
|
||||
"page-sub-header-for-data-observability": "متادیتا را از سرویسهای مجموعه آزمایشی مستقیماً از رابط کاربری ingest کنید.",
|
||||
"page-sub-header-for-data-quality": "با تستهای کیفیت اعتماد به دادههای خود را بسازید و محصولات داده قابل اعتماد ایجاد کنید.",
|
||||
"page-sub-header-for-databases": "متادیتا را از محبوبترین سرویسهای پایگاه داده ingest کنید.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "تعریف نحوه برخورد با تلاشهای ناموفق ورود و انقضای توکن JWT.",
|
||||
"page-sub-header-for-messagings": "ورود متادیتا از پرکاربردترین سرویسهای پیامرسانی.",
|
||||
"page-sub-header-for-metadata": "ورود متادیتا از سرویسهای متادیتا مستقیماً از رابط کاربری.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Cor",
|
||||
"column": "Coluna",
|
||||
"column-entity": "Coluna {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "coluna",
|
||||
"column-lowercase-plural": "colunas",
|
||||
"column-plural": "Colunas",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Correspondência por ID de Entidade",
|
||||
"entity-index": "índice de {{entity}}",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "Nome de {{entity}}",
|
||||
"entity-plural": "Entidades",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Resultado da Última Execução",
|
||||
"last-updated": "Última Atualização",
|
||||
"latest": "Mais Recente",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Saiba mais e Suporte",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Configuração de Linhagem",
|
||||
"lineage-data-lowercase": "dados de linhagem",
|
||||
"lineage-ingestion": "Ingestão de Linhagem",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "linhagem",
|
||||
"lineage-node-lowercase": "nó de linhagem",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Construa confiança em seus dados com testes de qualidade e crie produtos de dados confiáveis.",
|
||||
"page-sub-header-for-databases": "Ingestão de metadados dos serviços de banco de dados mais populares.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "Ingestão de metadados dos serviços de mensagens mais utilizados.",
|
||||
"page-sub-header-for-metadata": "Ingestão de metadados de serviços de metadados diretamente da interface do usuário.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "Color",
|
||||
"column": "Столбец",
|
||||
"column-entity": "Столбец {{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "столбец",
|
||||
"column-lowercase-plural": "столбцы",
|
||||
"column-plural": "Столбцы",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} Id",
|
||||
"entity-id-match": "Совпадение по Id",
|
||||
"entity-index": "{{entity}} индекс",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}} List",
|
||||
"entity-name": "{{entity}} Имя",
|
||||
"entity-plural": "Сущности",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "Результат последнего запуска",
|
||||
"last-updated": "Последнее обновление",
|
||||
"latest": "Последний",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "Layers",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "Конфигурация происхождения",
|
||||
"lineage-data-lowercase": "данные о происхождении",
|
||||
"lineage-ingestion": "Получение проихождения",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "происходение",
|
||||
"lineage-node-lowercase": "узел происхождения",
|
||||
"lineage-source": "Source of Lineage",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "Ingest metadata from test suite services right from the UI.",
|
||||
"page-sub-header-for-data-quality": "Укрепляйте доверие к своим данным с помощью тестов качества и создавайте надежные информационные продукты.",
|
||||
"page-sub-header-for-databases": "Получение метаданных из самых популярных сервисов баз данных.",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "Получение метаданных из наиболее часто используемых служб обмена сообщениями.",
|
||||
"page-sub-header-for-metadata": "Получайте метаданные из служб метаданных прямо из пользовательского интерфейса.",
|
||||
|
||||
@ -181,6 +181,7 @@
|
||||
"color": "颜色",
|
||||
"column": "列",
|
||||
"column-entity": "列{{entity}}",
|
||||
"column-level-lineage": "Column Level Lineage",
|
||||
"column-lowercase": "列",
|
||||
"column-lowercase-plural": "列",
|
||||
"column-plural": "列",
|
||||
@ -432,6 +433,7 @@
|
||||
"entity-id": "{{entity}} ID",
|
||||
"entity-id-match": "根据ID匹配",
|
||||
"entity-index": "{{entity}}索引",
|
||||
"entity-lineage": "Entity Lineage",
|
||||
"entity-list": "{{entity}}列表",
|
||||
"entity-name": "{{entity}}名称",
|
||||
"entity-plural": "实体",
|
||||
@ -643,6 +645,7 @@
|
||||
"last-run-result": "最近运行结果",
|
||||
"last-updated": "最近更新",
|
||||
"latest": "最新",
|
||||
"layer": "Layer",
|
||||
"layer-plural": "面板",
|
||||
"learn-more": "Learn More",
|
||||
"learn-more-and-support": "Learn more & Support",
|
||||
@ -655,6 +658,7 @@
|
||||
"lineage-config": "血缘关系配置",
|
||||
"lineage-data-lowercase": "血缘关系数据",
|
||||
"lineage-ingestion": "血缘关系提取",
|
||||
"lineage-layer": "Lineage Layer",
|
||||
"lineage-lowercase": "血缘",
|
||||
"lineage-node-lowercase": "血缘关系节点",
|
||||
"lineage-source": "血缘关系来源",
|
||||
@ -1701,6 +1705,7 @@
|
||||
"page-sub-header-for-data-observability": "通过 UI 界面, 从质控测试集服务中提取元数据",
|
||||
"page-sub-header-for-data-quality": "通过引入数据质控测试提升数据的可信任度, 构建稳健的衍生数据产品",
|
||||
"page-sub-header-for-databases": "从最流行的数据库类型服务中提取元数据",
|
||||
"page-sub-header-for-lineage-config-setting": "Configure the lineage view settings.",
|
||||
"page-sub-header-for-login-configuration": "Login configuration such as failed attempts or expiry timer.",
|
||||
"page-sub-header-for-messagings": "从最常用的消息队列类型服务中提取元数据",
|
||||
"page-sub-header-for-metadata": "通过 UI 界面, 从元数据类型服务中提取元数据",
|
||||
|
||||
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Button, Col, Form, Input, Row, Select, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import React, {
|
||||
FocusEvent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import Loader from '../../components/common/Loader/Loader';
|
||||
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
|
||||
import ServiceDocPanel from '../../components/common/ServiceDocPanel/ServiceDocPanel';
|
||||
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
|
||||
import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
|
||||
import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants';
|
||||
import { OPEN_METADATA } from '../../constants/service-guide.constant';
|
||||
import {
|
||||
LineageLayer,
|
||||
LineageSettings,
|
||||
} from '../../generated/configuration/lineageSettings';
|
||||
import { Settings, SettingType } from '../../generated/settings/settings';
|
||||
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||
import {
|
||||
getSettingsByType,
|
||||
updateSettingsConfig,
|
||||
} from '../../rest/settingConfigAPI';
|
||||
import { getSettingPageEntityBreadCrumb } from '../../utils/GlobalSettingsUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||
|
||||
const LineageConfigPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [activeField, setActiveField] = useState<string>('');
|
||||
const [lineageConfig, setLineageConfig] = useState<LineageSettings>();
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const { setAppPreferences } = useApplicationStore();
|
||||
const breadcrumbs: TitleBreadcrumbProps['titleLinks'] = useMemo(
|
||||
() =>
|
||||
getSettingPageEntityBreadCrumb(
|
||||
GlobalSettingsMenuCategory.PREFERENCES,
|
||||
t('label.lineage')
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const fetchSearchConfig = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const config = await getSettingsByType(SettingType.LineageSettings);
|
||||
setLineageConfig(config as LineageSettings);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFieldFocus = useCallback((event: FocusEvent<HTMLFormElement>) => {
|
||||
setActiveField(event.target.id);
|
||||
}, []);
|
||||
|
||||
const handleSave = async (values: LineageSettings) => {
|
||||
try {
|
||||
setIsUpdating(true);
|
||||
|
||||
const configData = {
|
||||
config_type: SettingType.LineageSettings,
|
||||
config_value: {
|
||||
upstreamDepth: Number(values.upstreamDepth),
|
||||
downstreamDepth: Number(values.downstreamDepth),
|
||||
lineageLayer: values.lineageLayer,
|
||||
},
|
||||
};
|
||||
const { data } = await updateSettingsConfig(configData as Settings);
|
||||
showSuccessToast(
|
||||
t('server.update-entity-success', {
|
||||
entity: t('label.lineage-config'),
|
||||
})
|
||||
);
|
||||
|
||||
const lineageConfig = data.config_value as LineageSettings;
|
||||
setLineageConfig(lineageConfig);
|
||||
|
||||
// Update lineage config in store
|
||||
setAppPreferences({ lineageConfig });
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSearchConfig();
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ResizablePanels
|
||||
className="content-height-with-resizable-panel"
|
||||
firstPanel={{
|
||||
className: 'content-resizable-panel-container',
|
||||
children: (
|
||||
<div
|
||||
className="max-width-md w-9/10 service-form-container"
|
||||
data-testid="add-metric-container">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<TitleBreadcrumb titleLinks={breadcrumbs} />
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Typography.Title
|
||||
className="m-b-0"
|
||||
data-testid="heading"
|
||||
level={5}>
|
||||
{t('label.lineage')}
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form
|
||||
form={form}
|
||||
id="lineage-config"
|
||||
initialValues={lineageConfig}
|
||||
layout="vertical"
|
||||
onFinish={handleSave}
|
||||
onFocus={handleFieldFocus}>
|
||||
<Form.Item
|
||||
id="root/upstreamDepth"
|
||||
label={t('label.upstream-depth')}
|
||||
name="upstreamDepth"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('message.upstream-depth-message'),
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
data-testid="field-upstream"
|
||||
max={5}
|
||||
min={1}
|
||||
type="number"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
className="m-t-sm"
|
||||
id="root/downstreamDepth"
|
||||
label={t('label.downstream-depth')}
|
||||
name="downstreamDepth"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('message.downstream-depth-message'),
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
data-testid="field-downstream"
|
||||
max={5}
|
||||
min={1}
|
||||
type="number"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
className="m-t-sm"
|
||||
id="root/lineageLayer"
|
||||
label={t('label.lineage-layer')}
|
||||
name="lineageLayer">
|
||||
<Select data-testid="field-lineage-layer">
|
||||
<Select.Option value={LineageLayer.EntityLineage}>
|
||||
{t('label.entity-lineage')}
|
||||
</Select.Option>
|
||||
<Select.Option value={LineageLayer.ColumnLevelLineage}>
|
||||
{t('label.column-level-lineage')}
|
||||
</Select.Option>
|
||||
<Select.Option value={LineageLayer.DataObservability}>
|
||||
{t('label.data-observability')}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Row className="m-b-xl" justify="end">
|
||||
<Col className="d-flex justify-end gap-2" span={24}>
|
||||
<Button
|
||||
data-testid="cancel-button"
|
||||
onClick={() => history.goBack()}>
|
||||
{t('label.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="save-button"
|
||||
form="lineage-config"
|
||||
htmlType="submit"
|
||||
loading={isUpdating}
|
||||
type="primary">
|
||||
{t('label.save')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
),
|
||||
minWidth: 700,
|
||||
flex: 0.7,
|
||||
}}
|
||||
pageTitle={t('label.lineage-config')}
|
||||
secondPanel={{
|
||||
className: 'service-doc-panel content-resizable-panel-container',
|
||||
minWidth: 400,
|
||||
flex: 0.3,
|
||||
children: (
|
||||
<ServiceDocPanel
|
||||
activeField={activeField}
|
||||
serviceName="LineageConfiguration"
|
||||
serviceType={OPEN_METADATA}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LineageConfigPage;
|
||||
@ -14,7 +14,9 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import axiosClient from '.';
|
||||
import { APPLICATION_JSON_CONTENT_TYPE_HEADER } from '../constants/constants';
|
||||
import { LineageSettings } from '../generated/configuration/lineageSettings';
|
||||
import { LoginConfiguration } from '../generated/configuration/loginConfiguration';
|
||||
import { SearchSettings } from '../generated/configuration/searchSettings';
|
||||
import { UIThemePreference } from '../generated/configuration/uiThemePreference';
|
||||
import { Settings, SettingType } from '../generated/settings/settings';
|
||||
|
||||
@ -59,3 +61,13 @@ export const testEmailConnection = async (data: { email: string }) => {
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const getSettingsByType = async (
|
||||
settingType: SettingType
|
||||
): Promise<SearchSettings | LineageSettings> => {
|
||||
const response = await axiosClient.get<Settings>(
|
||||
`/system/settings/${settingType}`
|
||||
);
|
||||
|
||||
return response.data.config_value as SearchSettings | LineageSettings;
|
||||
};
|
||||
|
||||
@ -28,6 +28,7 @@ import { ReactComponent as IconAPI } from '../assets/svg/ic-api-service.svg';
|
||||
import { ReactComponent as DashboardDataModelIcon } from '../assets/svg/ic-dashboard-data-model-colored.svg';
|
||||
import { ReactComponent as DataProductIcon } from '../assets/svg/ic-data-product-colored.svg';
|
||||
import { ReactComponent as SchemaIcon } from '../assets/svg/ic-database-schema-colored.svg';
|
||||
import { ReactComponent as LineageIcon } from '../assets/svg/ic-lineage-config.svg';
|
||||
import { ReactComponent as LoginIcon } from '../assets/svg/login-colored.svg';
|
||||
import { ReactComponent as OpenMetadataIcon } from '../assets/svg/logo-monogram.svg';
|
||||
import { ReactComponent as MessagingIcon } from '../assets/svg/messaging-colored.svg';
|
||||
@ -354,6 +355,15 @@ class GlobalSettingsClassBase {
|
||||
key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.PROFILER_CONFIGURATION}`,
|
||||
icon: ProfilerConfigIcon,
|
||||
},
|
||||
{
|
||||
label: t('label.lineage'),
|
||||
description: t(
|
||||
'message.page-sub-header-for-lineage-config-setting'
|
||||
),
|
||||
isProtected: Boolean(isAdminUser),
|
||||
key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.LINEAGE_CONFIG}`,
|
||||
icon: LineageIcon,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user