= ({
+ id,
+ name,
+ displayName,
+ onEditDisplayName,
+ link,
+ allowRename,
+}) => {
+ const { t } = useTranslation();
+
+ const [isDisplayNameEditing, setIsDisplayNameEditing] = useState(false);
+
+ const handleDisplayNameUpdate = async (data: EntityName) => {
+ setIsDisplayNameEditing(true);
+ try {
+ await onEditDisplayName?.(data, id);
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ } finally {
+ setIsDisplayNameEditing(false);
+ }
+ };
+
+ return (
+
+
+ {isEmpty(displayName) ? (
+
+ {name}
+
+ ) : (
+ <>
+ {name}
+
+
+ {displayName}
+
+
+ >
+ )}
+
+
+ {allowRename ? (
+
+ }
+ type="text"
+ onClick={() => setIsDisplayNameEditing(true)}
+ />
+
+ ) : null}
+ {isDisplayNameEditing && (
+ setIsDisplayNameEditing(false)}
+ onSave={handleDisplayNameUpdate}
+ />
+ )}
+
+ );
+};
+
+export default DisplayName;
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
index 7b415b948dcd..c3ca33b88743 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/Services.constant.ts
@@ -52,11 +52,11 @@ import lightDash from '../assets/img/service-icon-lightdash.png';
import looker from '../assets/img/service-icon-looker.png';
import mariadb from '../assets/img/service-icon-mariadb.png';
import metabase from '../assets/img/service-icon-metabase.png';
+import microstrategy from '../assets/img/service-icon-microstrategy.svg';
import mode from '../assets/img/service-icon-mode.png';
import mongodb from '../assets/img/service-icon-mongodb.png';
import msAzure from '../assets/img/service-icon-ms-azure.png';
import mssql from '../assets/img/service-icon-mssql.png';
-import mstr from '../assets/img/service-icon-mstr.png';
import nifi from '../assets/img/service-icon-nifi.png';
import openlineage from '../assets/img/service-icon-openlineage.svg';
import oracle from '../assets/img/service-icon-oracle.png';
@@ -153,7 +153,7 @@ export const REDPANDA = redpanda;
export const SUPERSET = superset;
export const SYNAPSE = synapse;
export const LOOKER = looker;
-export const MSTR = mstr;
+export const MICROSTRATEGY = microstrategy;
export const TABLEAU = tableau;
export const REDASH = redash;
export const METABASE = metabase;
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json
index a35924c5c410..5748e124c160 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json
@@ -56,7 +56,6 @@
"aggregate": "एकूण",
"airflow-config-plural": "एअरफ्लो संरचना",
"alert": "सूचना",
- "alert-details": "Alert Details",
"alert-lowercase": "सूचना",
"alert-lowercase-plural": "सूचना",
"alert-plural": "सूचना",
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/SchemaTablesTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/SchemaTablesTab.tsx
index d9ef74254263..cc397a63a3d9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/SchemaTablesTab.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/SchemaTablesTab.tsx
@@ -13,23 +13,29 @@
import { Col, Row, Switch, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
+import { AxiosError } from 'axios';
+import { compare } from 'fast-json-patch';
import { isEmpty, isUndefined } from 'lodash';
-import React, { useMemo } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Link } from 'react-router-dom';
+import DisplayName from '../../components/common/DisplayName/DisplayName';
import DescriptionV1 from '../../components/common/EntityDescription/DescriptionV1';
import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder';
import NextPrevious from '../../components/common/NextPrevious/NextPrevious';
import { NextPreviousProps } from '../../components/common/NextPrevious/NextPrevious.interface';
import RichTextEditorPreviewer from '../../components/common/RichTextEditor/RichTextEditorPreviewer';
import TableAntd from '../../components/common/Table/Table';
+import { EntityName } from '../../components/Modals/EntityNameModal/EntityNameModal.interface';
+import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
import { ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum';
import { EntityType } from '../../enums/entity.enum';
import { DatabaseSchema } from '../../generated/entity/data/databaseSchema';
import { Table } from '../../generated/entity/data/table';
import { UsePagingInterface } from '../../hooks/paging/usePaging';
+import { patchTableDetails } from '../../rest/tableAPI';
import entityUtilClassBase from '../../utils/EntityUtilClassBase';
import { getEntityName } from '../../utils/EntityUtils';
+import { showErrorToast } from '../../utils/ToastUtils';
interface SchemaTablesTabProps {
databaseSchemaDetails: DatabaseSchema;
@@ -69,6 +75,48 @@ function SchemaTablesTab({
pagingInfo,
}: Readonly) {
const { t } = useTranslation();
+ const [localTableData, setLocalTableData] = useState([]);
+
+ const { permissions } = usePermissionProvider();
+
+ const allowEditDisplayNamePermission = useMemo(() => {
+ return (
+ !isVersionView &&
+ (permissions.table.EditAll || permissions.table.EditDisplayName)
+ );
+ }, [permissions, isVersionView]);
+
+ const handleDisplayNameUpdate = useCallback(
+ async (data: EntityName, id?: string) => {
+ try {
+ const tableDetails = localTableData.find((table) => table.id === id);
+ if (!tableDetails) {
+ return;
+ }
+ const updatedData = {
+ ...tableDetails,
+ displayName: data.displayName || undefined,
+ };
+ const jsonPatch = compare(tableDetails, updatedData);
+ await patchTableDetails(tableDetails.id, jsonPatch);
+
+ setLocalTableData((prevData) =>
+ prevData.map((table) =>
+ table.id === id
+ ? { ...table, displayName: data.displayName }
+ : table
+ )
+ );
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ }
+ },
+ [localTableData]
+ );
+
+ useEffect(() => {
+ setLocalTableData(tableData);
+ }, [tableData]);
const tableColumn: ColumnsType = useMemo(
() => [
@@ -79,17 +127,18 @@ function SchemaTablesTab({
width: 500,
render: (_, record: Table) => {
return (
-
-
- {getEntityName(record)}
-
-
+
);
},
},
@@ -105,7 +154,7 @@ function SchemaTablesTab({
),
},
],
- []
+ [handleDisplayNameUpdate, allowEditDisplayNamePermission]
);
return (
@@ -158,7 +207,7 @@ function SchemaTablesTab({
bordered
columns={tableColumn}
data-testid="databaseSchema-tables"
- dataSource={tableData}
+ dataSource={localTableData}
loading={tableDataLoading}
locale={{
emptyText: (
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseVersionPage/DatabaseVersionPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseVersionPage/DatabaseVersionPage.tsx
index dc7eef110c1f..84eeee904ffe 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseVersionPage/DatabaseVersionPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseVersionPage/DatabaseVersionPage.tsx
@@ -209,7 +209,7 @@ function DatabaseVersionPage() {
/>
-
+
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceMainTabContent.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceMainTabContent.tsx
index b24b1f2af7dd..bf9643b01fe1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceMainTabContent.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceMainTabContent.tsx
@@ -13,9 +13,11 @@
import { Col, Row, Space, Switch, Table, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
+import { AxiosError } from 'axios';
+import { compare } from 'fast-json-patch';
import { isUndefined } from 'lodash';
import { EntityTags, ServiceTypes } from 'Models';
-import React, { useCallback, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import DescriptionV1 from '../../components/common/EntityDescription/DescriptionV1';
@@ -25,7 +27,9 @@ import NextPrevious from '../../components/common/NextPrevious/NextPrevious';
import { NextPreviousProps } from '../../components/common/NextPrevious/NextPrevious.interface';
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
import EntityRightPanel from '../../components/Entity/EntityRightPanel/EntityRightPanel';
+import { EntityName } from '../../components/Modals/EntityNameModal/EntityNameModal.interface';
import { COMMON_RESIZABLE_PANEL_CONFIG } from '../../constants/ResizablePanel.constants';
+import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
import { OperationPermission } from '../../context/PermissionProvider/PermissionProvider.interface';
import { EntityType } from '../../enums/entity.enum';
import { DatabaseService } from '../../generated/entity/services/databaseService';
@@ -33,10 +37,14 @@ import { Paging } from '../../generated/type/paging';
import { UsePagingInterface } from '../../hooks/paging/usePaging';
import { useFqn } from '../../hooks/useFqn';
import { ServicesType } from '../../interface/service.interface';
-import { getServiceMainTabColumns } from '../../utils/ServiceMainTabContentUtils';
+import {
+ callServicePatchAPI,
+ getServiceMainTabColumns,
+} from '../../utils/ServiceMainTabContentUtils';
import { getEntityTypeFromServiceCategory } from '../../utils/ServiceUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { createTagObject } from '../../utils/TagsUtils';
+import { showErrorToast } from '../../utils/ToastUtils';
import { ServicePageData } from './ServiceDetailsPage';
interface ServiceMainTabContentProps {
@@ -53,6 +61,7 @@ interface ServiceMainTabContentProps {
pagingHandler: NextPreviousProps['pagingHandler'];
saveUpdatedServiceData: (updatedData: ServicesType) => Promise;
pagingInfo: UsePagingInterface;
+ isVersionPage?: boolean;
}
function ServiceMainTabContent({
@@ -69,6 +78,7 @@ function ServiceMainTabContent({
serviceDetails,
saveUpdatedServiceData,
pagingInfo,
+ isVersionPage = false,
}: Readonly) {
const { t } = useTranslation();
const { serviceCategory } = useParams<{
@@ -76,7 +86,10 @@ function ServiceMainTabContent({
}>();
const { fqn: serviceFQN } = useFqn();
+ const { permissions } = usePermissionProvider();
+
const [isEdit, setIsEdit] = useState(false);
+ const [pageData, setPageData] = useState([]);
const tier = getTierTags(serviceDetails?.tags ?? []);
const tags = getTagsWithoutTier(serviceDetails?.tags ?? []);
@@ -131,9 +144,69 @@ function ServiceMainTabContent({
setIsEdit(false);
};
+ const handleDisplayNameUpdate = useCallback(
+ async (entityData: EntityName, id?: string) => {
+ try {
+ const pageDataDetails = pageData.find((data) => data.id === id);
+ if (!pageDataDetails) {
+ return;
+ }
+ const updatedData = {
+ ...pageDataDetails,
+ displayName: entityData.displayName || undefined,
+ };
+ const jsonPatch = compare(pageDataDetails, updatedData);
+ await callServicePatchAPI(
+ serviceCategory,
+ pageDataDetails.id,
+ jsonPatch
+ );
+ setPageData((prevData) =>
+ prevData.map((data) =>
+ data.id === id
+ ? { ...data, displayName: entityData.displayName }
+ : data
+ )
+ );
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ }
+ },
+ [pageData, serviceCategory]
+ );
+
+ const editDisplayNamePermission = useMemo(() => {
+ if (isVersionPage) {
+ return false;
+ }
+
+ const servicePermissions = {
+ databaseServices: permissions.databaseService,
+ messagingServices: permissions.messagingService,
+ dashboardServices: permissions.dashboardService,
+ pipelineServices: permissions.pipelineService,
+ mlmodelServices: permissions.mlmodelService,
+ storageServices: permissions.storageService,
+ searchServices: permissions.searchService,
+ apiServices: permissions.apiService,
+ };
+
+ const currentPermission =
+ servicePermissions[serviceCategory as keyof typeof servicePermissions];
+
+ return (
+ currentPermission?.EditAll || currentPermission?.EditDisplayName || false
+ );
+ }, [permissions, serviceCategory, isVersionPage]);
+
const tableColumn: ColumnsType = useMemo(
- () => getServiceMainTabColumns(serviceCategory),
- [serviceCategory]
+ () =>
+ getServiceMainTabColumns(
+ serviceCategory,
+ editDisplayNamePermission,
+ handleDisplayNameUpdate
+ ),
+ [serviceCategory, handleDisplayNameUpdate, editDisplayNamePermission]
);
const entityType = useMemo(
@@ -160,6 +233,10 @@ function ServiceMainTabContent({
[servicePermission, serviceDetails]
);
+ useEffect(() => {
+ setPageData(data);
+ }, [data]);
+
return (
@@ -210,7 +287,7 @@ function ServiceMainTabContent({
bordered
columns={tableColumn}
data-testid="service-children-table"
- dataSource={data}
+ dataSource={pageData}
locale={{
emptyText: ,
}}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
index ab71882577b4..d0ffe2488928 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardServiceUtils.ts
@@ -22,8 +22,8 @@ import domoDashboardConnection from '../jsons/connectionSchemas/connections/dash
import lightdashConnection from '../jsons/connectionSchemas/connections/dashboard/lightdashConnection.json';
import lookerConnection from '../jsons/connectionSchemas/connections/dashboard/lookerConnection.json';
import metabaseConnection from '../jsons/connectionSchemas/connections/dashboard/metabaseConnection.json';
+import microStrategyConnection from '../jsons/connectionSchemas/connections/dashboard/microStrategyConnection.json';
import modeConnection from '../jsons/connectionSchemas/connections/dashboard/modeConnection.json';
-import mstrConnection from '../jsons/connectionSchemas/connections/dashboard/mstrConnection.json';
import powerBIConnection from '../jsons/connectionSchemas/connections/dashboard/powerBIConnection.json';
import qlikcloudConnection from '../jsons/connectionSchemas/connections/dashboard/qlikCloudConnection.json';
import qliksenseConnection from '../jsons/connectionSchemas/connections/dashboard/qlikSenseConnection.json';
@@ -118,8 +118,8 @@ export const getDashboardConfig = (type: DashboardServiceType) => {
break;
}
- case DashboardServiceType.Mstr: {
- schema = mstrConnection;
+ case DashboardServiceType.MicroStrategy: {
+ schema = microStrategyConnection;
break;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceMainTabContentUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceMainTabContentUtils.tsx
index c629f20711fd..2c58500e1216 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceMainTabContentUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceMainTabContentUtils.tsx
@@ -17,44 +17,51 @@ import { t } from 'i18next';
import { isUndefined } from 'lodash';
import { ServiceTypes } from 'Models';
import React from 'react';
-import { Link } from 'react-router-dom';
+import DisplayName from '../components/common/DisplayName/DisplayName';
import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
import RichTextEditorPreviewer from '../components/common/RichTextEditor/RichTextEditorPreviewer';
+import { EntityName } from '../components/Modals/EntityNameModal/EntityNameModal.interface';
import TagsViewer from '../components/Tag/TagsViewer/TagsViewer';
import { NO_DATA_PLACEHOLDER } from '../constants/constants';
import { ServiceCategory } from '../enums/service.enum';
import { Database } from '../generated/entity/data/database';
import { Pipeline } from '../generated/entity/data/pipeline';
import { ServicePageData } from '../pages/ServiceDetailsPage/ServiceDetailsPage';
-import { getEntityName } from './EntityUtils';
+import { patchApiCollection } from '../rest/apiCollectionsAPI';
+import { patchDashboardDetails } from '../rest/dashboardAPI';
+import { patchDatabaseDetails } from '../rest/databaseAPI';
+import { patchMlModelDetails } from '../rest/mlModelAPI';
+import { patchPipelineDetails } from '../rest/pipelineAPI';
+import { patchSearchIndexDetails } from '../rest/SearchIndexAPI';
+import { patchContainerDetails } from '../rest/storageAPI';
+import { patchTopicDetails } from '../rest/topicsAPI';
import { getLinkForFqn } from './ServiceUtils';
import { getUsagePercentile } from './TableUtils';
export const getServiceMainTabColumns = (
- serviceCategory: ServiceTypes
+ serviceCategory: ServiceTypes,
+ editDisplayNamePermission?: boolean,
+ handleDisplayNameUpdate?: (
+ entityData: EntityName,
+ id?: string
+ ) => Promise
): ColumnsType => [
{
title: t('label.name'),
dataIndex: 'name',
key: 'name',
width: 280,
- render: (_, record: ServicePageData) => {
- return (
-
-
- {getEntityName(record)}
-
-
- );
- },
+ render: (_, record: ServicePageData) => (
+
+ ),
},
{
title: t('label.description'),
@@ -123,3 +130,30 @@ export const getServiceMainTabColumns = (
]
: []),
];
+
+export const callServicePatchAPI = async (
+ serviceCategory: ServiceTypes,
+ id: string,
+ jsonPatch: any
+) => {
+ switch (serviceCategory) {
+ case ServiceCategory.DATABASE_SERVICES:
+ return await patchDatabaseDetails(id, jsonPatch);
+ case ServiceCategory.MESSAGING_SERVICES:
+ return await patchTopicDetails(id, jsonPatch);
+ case ServiceCategory.DASHBOARD_SERVICES:
+ return await patchDashboardDetails(id, jsonPatch);
+ case ServiceCategory.PIPELINE_SERVICES:
+ return await patchPipelineDetails(id, jsonPatch);
+ case ServiceCategory.ML_MODEL_SERVICES:
+ return await patchMlModelDetails(id, jsonPatch);
+ case ServiceCategory.STORAGE_SERVICES:
+ return await patchContainerDetails(id, jsonPatch);
+ case ServiceCategory.SEARCH_SERVICES:
+ return await patchSearchIndexDetails(id, jsonPatch);
+ case ServiceCategory.API_SERVICES:
+ return await patchApiCollection(id, jsonPatch);
+ default:
+ return;
+ }
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
index 6bc96a9852a8..e8e8317af669 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtilClassBase.ts
@@ -58,6 +58,7 @@ import {
LOOKER,
MARIADB,
METABASE,
+ MICROSTRATEGY,
MLFLOW,
ML_MODEL_DEFAULT,
MODE,
@@ -376,6 +377,7 @@ class ServiceUtilClassBase {
case this.DashboardServiceTypeSmallCase.CustomDashboard:
return DASHBOARD_DEFAULT;
+
case this.DashboardServiceTypeSmallCase.Superset:
return SUPERSET;
@@ -468,6 +470,7 @@ class ServiceUtilClassBase {
case this.MlModelServiceTypeSmallCase.Sklearn:
return SCIKIT;
+
case this.MlModelServiceTypeSmallCase.SageMaker:
return SAGEMAKER;
@@ -504,6 +507,9 @@ class ServiceUtilClassBase {
case this.ApiServiceTypeSmallCase.REST:
return REST_SERVICE;
+ case this.DashboardServiceTypeSmallCase.MicroStrategy:
+ return MICROSTRATEGY;
+
default: {
let logo;
if (serviceTypes.messagingServices.includes(type)) {