diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-stored-procedure.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-stored-procedure.svg
new file mode 100644
index 000000000000..d78213314b89
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-stored-procedure.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface.ts
index 27ede19f766b..7b90285c7482 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface.ts
@@ -21,6 +21,7 @@ import { Database } from 'generated/entity/data/database';
import { DatabaseSchema } from 'generated/entity/data/databaseSchema';
import { Mlmodel } from 'generated/entity/data/mlmodel';
import { Pipeline } from 'generated/entity/data/pipeline';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { Table } from 'generated/entity/data/table';
import { Topic } from 'generated/entity/data/topic';
import { DashboardService } from 'generated/entity/services/dashboardService';
@@ -43,6 +44,7 @@ export type DataAssetsType =
| Container
| Database
| DashboardDataModel
+ | StoredProcedure
| DatabaseSchema
| DatabaseService
| MessagingService
@@ -90,6 +92,7 @@ export type DataAssetsHeaderProps = {
| DataAssetMlmodel
| DataAssetContainer
| DataAssetDashboardDataModel
+ | DataAssetStoredProcedure
| DataAssetDatabase
| DataAssetDatabaseSchema
| DataAssetDatabaseService
@@ -135,6 +138,11 @@ export interface DataAssetDashboardDataModel {
entityType: EntityType.DASHBOARD_DATA_MODEL;
}
+export interface DataAssetStoredProcedure {
+ dataAsset: StoredProcedure;
+ entityType: EntityType.STORED_PROCEDURE;
+}
+
export interface DataAssetDatabase {
dataAsset: Database;
entityType: EntityType.DATABASE;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx
index 8eea039a20bf..f92240358c1a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx
@@ -19,12 +19,14 @@ import DashboardSummary from 'components/Explore/EntitySummaryPanel/DashboardSum
import DataModelSummary from 'components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component';
import MlModelSummary from 'components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component';
import PipelineSummary from 'components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component';
+import StoredProcedureSummary from 'components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component';
import TableSummary from 'components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component';
import TopicSummary from 'components/Explore/EntitySummaryPanel/TopicSummary/TopicSummary.component';
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
import { Container } from 'generated/entity/data/container';
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
import { Mlmodel } from 'generated/entity/data/mlmodel';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { EntityDetailUnion } from 'Models';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -33,6 +35,7 @@ import { getDataModelsByName } from 'rest/dataModelsAPI';
import { getMlModelByFQN } from 'rest/mlModelAPI';
import { getPipelineByFqn } from 'rest/pipelineAPI';
import { getContainerByName } from 'rest/storageAPI';
+import { getStoredProceduresByName } from 'rest/storedProceduresAPI';
import { getTableDetailsByFQN } from 'rest/tableAPI';
import { getTopicByFqn } from 'rest/topicsAPI';
import { EntityType } from '../../../enums/entity.enum';
@@ -130,6 +133,12 @@ const EntityInfoDrawer = ({
break;
}
+
+ case EntityType.STORED_PROCEDURE: {
+ response = await getStoredProceduresByName(encodedFqn, 'owner,tags');
+
+ break;
+ }
default:
break;
}
@@ -221,6 +230,16 @@ const EntityInfoDrawer = ({
/>
);
+ case EntityType.STORED_PROCEDURE:
+ return (
+
+ );
+
default:
return null;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx
new file mode 100644
index 000000000000..c24fe1456422
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2023 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 { Col, Divider, Row, Typography } from 'antd';
+import classNames from 'classnames';
+import SummaryTagsDescription from 'components/common/SummaryTagsDescription/SummaryTagsDescription.component';
+import SchemaEditor from 'components/schema-editor/SchemaEditor';
+import SummaryPanelSkeleton from 'components/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component';
+import { CSMode } from 'enums/codemirror.enum';
+import { ExplorePageTabs } from 'enums/Explore.enum';
+import { StoredProcedureCodeObject } from 'generated/entity/data/storedProcedure';
+import { isObject } from 'lodash';
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
+import {
+ DRAWER_NAVIGATION_OPTIONS,
+ getEntityOverview,
+} from 'utils/EntityUtils';
+import { StoredProcedureSummaryProps } from './StoredProcedureSummary.interface';
+
+const StoredProcedureSummary = ({
+ entityDetails,
+ componentType = DRAWER_NAVIGATION_OPTIONS.explore,
+ tags,
+ isLoading,
+}: StoredProcedureSummaryProps) => {
+ const { t } = useTranslation();
+
+ const entityInfo = useMemo(
+ () => getEntityOverview(ExplorePageTabs.STORED_PROCEDURE, entityDetails),
+ [entityDetails]
+ );
+
+ return (
+
+ <>
+
+
+
+ {entityInfo.map((info) => {
+ const isOwner = info.name === t('label.owner');
+
+ return info.visible?.includes(componentType) ? (
+
+
+ {!isOwner ? (
+
+
+ {info.name}
+
+
+ ) : null}
+
+ {info.isLink ? (
+
+ {info.value}
+
+ ) : (
+
+ {info.value}
+
+ )}
+
+
+
+ ) : null;
+ })}
+
+
+
+
+
+
+
+
+
+ {isObject(entityDetails.storedProcedureCode) && (
+
+
+
+ {t('label.code')}
+
+
+
+
+
+
+ )}
+ >
+
+ );
+};
+
+export default StoredProcedureSummary;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts
new file mode 100644
index 000000000000..f5ae6e8905a2
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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 { StoredProcedure } from 'generated/entity/data/storedProcedure';
+import { TagLabel } from 'generated/type/tagLabel';
+
+export interface StoredProcedureSummaryProps {
+ entityDetails: StoredProcedure;
+ componentType?: string;
+ tags?: TagLabel[];
+ isLoading: boolean;
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts
index b20b317ff008..82d3b9a8d6ef 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts
@@ -20,6 +20,7 @@ import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
import { Database } from 'generated/entity/data/database';
import { DatabaseSchema } from 'generated/entity/data/databaseSchema';
import { Glossary } from 'generated/entity/data/glossary';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { QueryFilterInterface } from 'pages/explore/ExplorePage.interface';
import { SearchIndex } from '../../enums/search.enum';
import { Dashboard } from '../../generated/entity/data/dashboard';
@@ -120,7 +121,8 @@ export type EntityUnion =
| Database
| Glossary
| Tag
- | DashboardDataModel;
+ | DashboardDataModel
+ | StoredProcedure;
export type EntityWithServices =
| Topic
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.interface.ts
index 618140e8cdb2..fcca04e47411 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.interface.ts
@@ -72,6 +72,7 @@ export enum ResourceEntity {
QUERY = 'query',
DASHBOARD_DATA_MODEL = 'dashboardDataModel',
EVENT_SUBSCRIPTION = 'eventsubscription',
+ STORED_PROCEDURE = 'storedProcedure',
}
export interface PermissionContextType {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.interface.ts
index f4ce98ecc94a..70e357806921 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/CustomPropertyTable.interface.ts
@@ -12,6 +12,7 @@
*/
import { Container } from 'generated/entity/data/container';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { EntityType } from '../../../enums/entity.enum';
import { Dashboard } from '../../../generated/entity/data/dashboard';
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
@@ -24,7 +25,8 @@ export type EntityDetails = Table &
Dashboard &
Pipeline &
Mlmodel &
- Container;
+ Container &
+ StoredProcedure;
export interface CustomPropertyProps {
isVersionView?: boolean;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/router/AuthenticatedAppRouter.tsx
index 35ba8cb33e81..8dc4b4bb3b9b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/router/AuthenticatedAppRouter.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/router/AuthenticatedAppRouter.tsx
@@ -132,6 +132,10 @@ const DataModelDetailsPage = withSuspenseFallback(
React.lazy(() => import('pages/DataModelPage/DataModelPage.component'))
);
+const StoredProcedureDetailsPage = withSuspenseFallback(
+ React.lazy(() => import('pages/StoredProcedure/StoredProcedurePage'))
+);
+
const TableDetailsPageV1 = withSuspenseFallback(
React.lazy(() => import('pages/TableDetailsPageV1/TableDetailsPageV1'))
);
@@ -461,6 +465,23 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
component={DataModelDetailsPage}
path={ROUTES.DATA_MODEL_DETAILS_WITH_SUB_TAB}
/>
+
+
+
+
+
{
return `${path}${columnName ? `.${columnName}` : ''}`;
};
+export const getStoredProcedureDetailsPath = (
+ storedProcedureFQN: string,
+ columnName?: string
+) => {
+ let path = ROUTES.STORED_PROCEDURE_DETAILS;
+ path = path.replace(
+ PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN,
+ getEncodedFqn(storedProcedureFQN)
+ );
+
+ return `${path}${columnName ? `.${columnName}` : ''}`;
+};
+
export const getTagsDetailsPath = (entityFQN: string, columnName?: string) => {
let path = ROUTES.TAG_DETAILS;
const classification = getPartialNameFromFQN(entityFQN, ['service']);
@@ -660,6 +678,32 @@ export const getContainerDetailPath = (
return path;
};
+export const getStoredProcedureDetailPath = (
+ storedProcedureFQN: string,
+ tab?: string,
+ subTab = 'all'
+) => {
+ let path = tab
+ ? ROUTES.STORED_PROCEDURE_DETAILS_WITH_TAB
+ : ROUTES.STORED_PROCEDURE_DETAILS;
+
+ if (tab === EntityTabs.ACTIVITY_FEED) {
+ path = ROUTES.STORED_PROCEDURE_DETAILS_WITH_SUB_TAB;
+ path = path.replace(PLACEHOLDER_ROUTE_SUB_TAB, subTab);
+ }
+
+ if (tab) {
+ path = path.replace(PLACEHOLDER_ROUTE_TAB, tab);
+ }
+
+ path = path.replace(
+ PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN,
+ getEncodedFqn(storedProcedureFQN)
+ );
+
+ return path;
+};
+
export const getGlossaryTermDetailsPath = (
glossaryFQN: string,
tab?: string
@@ -784,6 +828,7 @@ export const ENTITY_PATH: Record = {
containers: 'container',
tags: 'tag',
glossaries: 'glossary',
+ storedprocedure: 'storedProcedure',
};
export const VALIDATION_MESSAGES = {
diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts
index 5b2e8a09a918..1ac016a00288 100644
--- a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts
@@ -26,4 +26,5 @@ export enum ExplorePageTabs {
GLOSSARY = 'glossaries',
TAG = 'tags',
DASHBOARD_DATA_MODEL = 'dashboardDataModel',
+ STORED_PROCEDURE = 'storedProcedure',
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
index b2ad0e6dc327..82d580f091d3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts
@@ -48,6 +48,7 @@ export enum EntityType {
USER_NAME = 'username',
CHART = 'chart',
SAMPLE_DATA = 'sampleData',
+ STORED_PROCEDURE = 'storedProcedure',
}
export enum AssetsType {
@@ -161,6 +162,8 @@ export enum EntityTabs {
INGESTIONS = 'ingestions',
CONNECTION = 'connection',
SQL = 'sql',
+ STORED_PROCEDURE = 'stored_procedure',
+ CODE = 'code',
}
export enum EntityAction {
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
index 895461e4449f..71ba160ccd0b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json
@@ -134,6 +134,7 @@
"closed-task-plural": "Closed Tasks",
"closed-this-task-lowercase": "closed this task",
"cloud-config-source": "Cloud Config Source",
+ "code": "Code",
"collapse-all": "Collapse All",
"column": "Column",
"column-entity": "Column {{entity}}",
@@ -877,6 +878,7 @@
"stopped": "Stopped",
"storage": "Storage",
"storage-plural": "Storages",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "Sub Teams",
"submit": "Submit",
"success": "Success",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
index 4f50d7f72359..2d616a5887ae 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json
@@ -134,6 +134,7 @@
"closed-task-plural": "Tareas cerradas",
"closed-this-task-lowercase": "cerró esta tarea",
"cloud-config-source": "Fuente de configuración en el cloud",
+ "code": "Code",
"collapse-all": "Contraer todo",
"column": "Columna",
"column-entity": "Columna {{entity}}",
@@ -877,6 +878,7 @@
"stopped": "Stopped",
"storage": "Storage",
"storage-plural": "Storages",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "Sub Equipos",
"submit": "Enviar",
"success": "Éxito",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
index 7b4ce3db0d6d..6aec2161d7a7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json
@@ -134,6 +134,7 @@
"closed-task-plural": "Tâches Clôturées",
"closed-this-task-lowercase": "fermer cette tâche",
"cloud-config-source": "Source de Config Cloud",
+ "code": "Code",
"collapse-all": "Tout Réduire",
"column": "Colonne",
"column-entity": "{{entity}} Colonnes",
@@ -877,6 +878,7 @@
"stopped": "Arrêté",
"storage": "Stockage",
"storage-plural": "Stockages",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "Sous Equipes",
"submit": "Envoyer",
"success": "Succès",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
index a59e1f7e71c8..7493190da311 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json
@@ -134,6 +134,7 @@
"closed-task-plural": "終了したタスク",
"closed-this-task-lowercase": "このタスクを終了する",
"cloud-config-source": "Cloud Config Source",
+ "code": "Code",
"collapse-all": "全て折り畳む",
"column": "カラム",
"column-entity": "カラム {{entity}}",
@@ -877,6 +878,7 @@
"stopped": "Stopped",
"storage": "Storage",
"storage-plural": "Storages",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "サブチーム",
"submit": "Submit",
"success": "成功",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
index 39f66ca9b193..fce5061f049a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json
@@ -134,6 +134,7 @@
"closed-task-plural": "Tarefas fechadas",
"closed-this-task-lowercase": "esta tarefa foi fechada",
"cloud-config-source": "Origem de configurações de Cloud",
+ "code": "Code",
"collapse-all": "Recolher todas",
"column": "Coluna",
"column-entity": "Coluna {{entity}}",
@@ -877,6 +878,7 @@
"stopped": "Parado",
"storage": "Storage",
"storage-plural": "Storages",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "Sub-equipes",
"submit": "Enviar",
"success": "Sucesso",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
index e65392f0587d..1bd134d1e8b5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json
@@ -134,6 +134,7 @@
"closed-task-plural": "Закрытые задачи",
"closed-this-task-lowercase": "закрыть задачу",
"cloud-config-source": "Источник облачной конфигурации",
+ "code": "Code",
"collapse-all": "Свернуть все",
"column": "Столбец",
"column-entity": "Столбец {{entity}}",
@@ -877,6 +878,7 @@
"stopped": "Остановлено",
"storage": "Хранилище",
"storage-plural": "Хранилища",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "Подгруппы",
"submit": "Подтвердить",
"success": "Успешно",
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
index c9b4c69afcc9..8595875eb0dc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
+++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json
@@ -134,6 +134,7 @@
"closed-task-plural": "已关闭任务",
"closed-this-task-lowercase": "关闭此任务",
"cloud-config-source": "云配置源",
+ "code": "Code",
"collapse-all": "全部折叠",
"column": "列",
"column-entity": "列{{entity}}",
@@ -877,6 +878,7 @@
"stopped": "已停止",
"storage": "存储",
"storage-plural": "存储",
+ "stored-procedure": "Stored Procedure",
"sub-team-plural": "子团队",
"submit": "提交",
"success": "成功",
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
index f92cf16fd948..812ac60c656e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
@@ -139,6 +139,8 @@ const CustomEntityDetailV1 = () => {
case ENTITY_PATH.containers:
return PAGE_HEADERS.CONTAINER_CUSTOM_ATTRIBUTES;
+ case ENTITY_PATH.storedprocedure:
+ return PAGE_HEADERS.STORED_PROCEDURE_CUSTOM_ATTRIBUTES;
default:
return PAGE_HEADERS.TABLES_CUSTOM_ATTRIBUTES;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx
index a4bb371b78a6..05dd76c12219 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx
@@ -40,6 +40,7 @@ import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel';
import { isEmpty, isString, isUndefined, toString } from 'lodash';
import { observer } from 'mobx-react';
import { EntityTags, PagingResponse } from 'Models';
+import StoredProcedureTab from 'pages/StoredProcedure/StoredProcedureTab';
import React, {
FunctionComponent,
useCallback,
@@ -50,12 +51,14 @@ import React, {
} from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
+import { ListDataModelParams } from 'rest/dashboardAPI';
import {
getDatabaseSchemaDetailsByFQN,
patchDatabaseSchemaDetails,
restoreDatabaseSchema,
} from 'rest/databaseAPI';
import { getFeedCount, postThread } from 'rest/feedsAPI';
+import { getStoredProceduresList } from 'rest/storedProceduresAPI';
import { getTableList, TableListParams } from 'rest/tableAPI';
import { getEntityMissingError } from 'utils/CommonUtils';
import { getDatabaseSchemaVersionPath } from 'utils/RouterUtils';
@@ -64,6 +67,7 @@ import { default as appState } from '../../AppState';
import {
getDatabaseSchemaDetailsPath,
INITIAL_PAGING_VALUE,
+ pagingObject,
} from '../../constants/constants';
import { EntityTabs, EntityType } from '../../enums/entity.enum';
import { CreateThread } from '../../generated/api/feed/createThread';
@@ -73,6 +77,7 @@ import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
+import { StoredProcedureData } from './DatabaseSchemaPage.interface';
import SchemaTablesTab from './SchemaTablesTab';
const DatabaseSchemaPage: FunctionComponent = () => {
@@ -109,11 +114,26 @@ const DatabaseSchemaPage: FunctionComponent = () => {
const [currentTablesPage, setCurrentTablesPage] =
useState(INITIAL_PAGING_VALUE);
+ const [storedProcedure, setStoredProcedure] = useState({
+ data: [],
+ isLoading: false,
+ deleted: false,
+ paging: pagingObject,
+ currentPage: INITIAL_PAGING_VALUE,
+ });
+
const handleShowDeletedTables = (value: boolean) => {
setShowDeletedTables(value);
setCurrentTablesPage(INITIAL_PAGING_VALUE);
};
+ const handleShowDeletedStoredProcedure = (value: boolean) => {
+ setStoredProcedure((prev) => ({
+ ...prev,
+ currentPage: INITIAL_PAGING_VALUE,
+ deleted: value,
+ }));
+ };
const { version: currentVersion } = useMemo(
() => databaseSchema,
[databaseSchema]
@@ -197,6 +217,28 @@ const DatabaseSchemaPage: FunctionComponent = () => {
}
}, [databaseSchemaFQN]);
+ const fetchStoreProcedureDetails = useCallback(
+ async (params?: ListDataModelParams) => {
+ try {
+ setStoredProcedure((prev) => ({ ...prev, isLoading: true }));
+ const { data, paging } = await getStoredProceduresList({
+ service: getDecodedFqn(databaseSchemaFQN),
+ fields: 'owner,tags,followers',
+ include: storedProcedure.deleted
+ ? Include.Deleted
+ : Include.NonDeleted,
+ ...params,
+ });
+ setStoredProcedure((prev) => ({ ...prev, data, paging }));
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ } finally {
+ setStoredProcedure((prev) => ({ ...prev, isLoading: false }));
+ }
+ },
+ [databaseSchemaFQN, storedProcedure.deleted]
+ );
+
const getSchemaTables = useCallback(
async (params?: TableListParams) => {
setTableDataLoading(true);
@@ -460,6 +502,25 @@ const DatabaseSchemaPage: FunctionComponent = () => {
[]
);
+ const storedProcedurePagingHandler = useCallback(
+ async (cursorType: string | number, activePage?: number) => {
+ const pagingString = {
+ [cursorType]:
+ storedProcedure.paging[
+ cursorType as keyof typeof storedProcedure.paging
+ ],
+ };
+
+ await fetchStoreProcedureDetails(pagingString);
+
+ setStoredProcedure((prev) => ({
+ ...prev,
+ currentPage: activePage ?? INITIAL_PAGING_VALUE,
+ }));
+ },
+ [storedProcedure.paging]
+ );
+
useEffect(() => {
fetchDatabaseSchemaPermission();
}, [databaseSchemaFQN]);
@@ -467,6 +528,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
useEffect(() => {
if (viewDatabaseSchemaPermission) {
fetchDatabaseSchemaDetails();
+ fetchStoreProcedureDetails({ limit: 0 });
getEntityFeedCount();
}
}, [viewDatabaseSchemaPermission, databaseSchemaFQN]);
@@ -580,6 +642,25 @@ const DatabaseSchemaPage: FunctionComponent = () => {
),
},
+ {
+ label: (
+
+ ),
+ key: EntityTabs.STORED_PROCEDURE,
+ children: (
+
+ ),
+ },
];
if (isPermissionsLoading) {
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.interface.ts
new file mode 100644
index 000000000000..c230eca0a407
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.interface.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 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 { Paging } from 'generated/type/paging';
+import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage';
+
+export interface StoredProcedureData {
+ isLoading: boolean;
+ deleted: boolean;
+ data: ServicePageData[];
+ paging: Paging;
+ currentPage: number;
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx
index 7293a34cacf4..56d50932c712 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx
@@ -54,6 +54,7 @@ import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
import { Database } from 'generated/entity/data/database';
import { Mlmodel } from 'generated/entity/data/mlmodel';
import { Pipeline } from 'generated/entity/data/pipeline';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { Topic } from 'generated/entity/data/topic';
import { DashboardConnection } from 'generated/entity/services/dashboardService';
import { DatabaseService } from 'generated/entity/services/databaseService';
@@ -123,7 +124,8 @@ export type ServicePageData =
| Mlmodel
| Pipeline
| Container
- | DashboardDataModel;
+ | DashboardDataModel
+ | StoredProcedure;
const ServiceDetailsPage: FunctionComponent = () => {
const { t } = useTranslation();
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx
new file mode 100644
index 000000000000..e7bed79f15de
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx
@@ -0,0 +1,688 @@
+/*
+ * Copyright 2023 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 { Card, Col, Row, Space, Tabs } from 'antd';
+import { AxiosError } from 'axios';
+import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
+import { ActivityFeedTab } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
+import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
+import { CustomPropertyTable } from 'components/common/CustomPropertyTable/CustomPropertyTable';
+import { CustomPropertyProps } from 'components/common/CustomPropertyTable/CustomPropertyTable.interface';
+import DescriptionV1 from 'components/common/description/DescriptionV1';
+import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
+import PageLayoutV1 from 'components/containers/PageLayoutV1';
+import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
+import EntityLineageComponent from 'components/Entity/EntityLineage/EntityLineage.component';
+import Loader from 'components/Loader/Loader';
+import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
+import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
+import {
+ OperationPermission,
+ ResourceEntity,
+} from 'components/PermissionProvider/PermissionProvider.interface';
+import { withActivityFeed } from 'components/router/withActivityFeed';
+import SchemaEditor from 'components/schema-editor/SchemaEditor';
+import { SourceType } from 'components/searched-data/SearchedData.interface';
+import TabsLabel from 'components/TabsLabel/TabsLabel.component';
+import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
+import { DisplayType } from 'components/Tag/TagsViewer/TagsViewer.interface';
+import {
+ getStoredProcedureDetailPath,
+ getVersionPath,
+} from 'constants/constants';
+import { CSMode } from 'enums/codemirror.enum';
+import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
+import { EntityTabs, EntityType } from 'enums/entity.enum';
+import { compare } from 'fast-json-patch';
+import { CreateThread, ThreadType } from 'generated/api/feed/createThread';
+import {
+ StoredProcedure,
+ StoredProcedureCodeObject,
+} from 'generated/entity/data/storedProcedure';
+import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel';
+import { EntityTags } from 'Models';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useHistory, useParams } from 'react-router-dom';
+import { postThread } from 'rest/feedsAPI';
+import {
+ addStoredProceduresFollower,
+ getStoredProceduresDetailsByFQN,
+ patchStoredProceduresDetails,
+ removeStoredProceduresFollower,
+ restoreStoredProcedures,
+} from 'rest/storedProceduresAPI';
+import {
+ getCurrentUserId,
+ getFeedCounts,
+ sortTagsCaseInsensitive,
+} from 'utils/CommonUtils';
+import { getEntityName } from 'utils/EntityUtils';
+import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
+import { STORED_PROCEDURE_DEFAULT_FIELDS } from 'utils/StoredProceduresUtils';
+import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
+import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
+
+const StoredProcedurePage = () => {
+ const { t } = useTranslation();
+ const USER_ID = getCurrentUserId();
+ const history = useHistory();
+ const { storedProcedureFQN, tab: activeTab = EntityTabs.CODE } =
+ useParams<{ storedProcedureFQN: string; tab: string }>();
+
+ const { getEntityPermissionByFqn } = usePermissionProvider();
+ const { postFeed, deleteFeed, updateFeed } = useActivityFeedProvider();
+
+ const [isLoading, setIsLoading] = useState(true);
+ const [storedProcedure, setStoredProcedure] = useState();
+ const [storedProcedurePermissions, setStoredProcedurePermissions] =
+ useState(DEFAULT_ENTITY_PERMISSION);
+ const [isEdit, setIsEdit] = useState(false);
+
+ const [feedCount, setFeedCount] = useState(0);
+ const [threadLink, setThreadLink] = useState('');
+
+ const [threadType, setThreadType] = useState(
+ ThreadType.Conversation
+ );
+
+ const {
+ id: storedProcedureId = '',
+ followers,
+ owner,
+ tags,
+ tier,
+ version,
+ code,
+ description,
+ deleted,
+ entityName,
+ entityFQN,
+ } = useMemo(() => {
+ return {
+ ...storedProcedure,
+ tier: getTierTags(storedProcedure?.tags ?? []),
+ tags: getTagsWithoutTier(storedProcedure?.tags ?? []),
+ entityName: getEntityName(storedProcedure),
+ entityFQN: storedProcedure?.fullyQualifiedName ?? '',
+ code:
+ (storedProcedure?.storedProcedureCode as StoredProcedureCodeObject)
+ ?.code ?? '',
+ };
+ }, [storedProcedure]);
+
+ const { isFollowing } = useMemo(() => {
+ return {
+ isFollowing: followers?.some(({ id }) => id === USER_ID),
+ };
+ }, [followers, USER_ID]);
+
+ const fetchResourcePermission = useCallback(async () => {
+ try {
+ const permission = await getEntityPermissionByFqn(
+ ResourceEntity.STORED_PROCEDURE,
+ storedProcedureFQN
+ );
+
+ setStoredProcedurePermissions(permission);
+ } catch (error) {
+ showErrorToast(
+ t('server.fetch-entity-permissions-error', {
+ entity: t('label.resource-permission-lowercase'),
+ })
+ );
+ } finally {
+ setIsLoading(false);
+ }
+ }, [getEntityPermissionByFqn]);
+
+ const getEntityFeedCount = () => {
+ getFeedCounts(
+ EntityType.STORED_PROCEDURE,
+ storedProcedureFQN,
+ setFeedCount
+ );
+ };
+
+ const fetchStoredProcedureDetails = async () => {
+ setIsLoading(true);
+ try {
+ const response = await getStoredProceduresDetailsByFQN(
+ storedProcedureFQN,
+ STORED_PROCEDURE_DEFAULT_FIELDS
+ );
+
+ setStoredProcedure(response);
+ } catch (error) {
+ // Error here
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const versionHandler = useCallback(() => {
+ version &&
+ history.push(
+ getVersionPath(
+ EntityType.STORED_PROCEDURE,
+ storedProcedureFQN,
+ version + ''
+ )
+ );
+ }, [storedProcedureFQN, version]);
+
+ const saveUpdatedStoredProceduresData = useCallback(
+ (updatedData: StoredProcedure) => {
+ if (!storedProcedure) {
+ return updatedData;
+ }
+ const jsonPatch = compare(storedProcedure ?? '', updatedData);
+
+ return patchStoredProceduresDetails(storedProcedureId ?? '', jsonPatch);
+ },
+ [storedProcedure]
+ );
+
+ const handleStoreProcedureUpdate = async (
+ updatedData: StoredProcedure,
+ key: keyof StoredProcedure
+ ) => {
+ try {
+ const res = await saveUpdatedStoredProceduresData(updatedData);
+
+ setStoredProcedure((previous) => {
+ if (!previous) {
+ return;
+ }
+ if (key === 'tags') {
+ return {
+ ...previous,
+ version: res.version,
+ [key]: sortTagsCaseInsensitive(res.tags ?? []),
+ };
+ }
+
+ return {
+ ...previous,
+ version: res.version,
+ [key]: res[key],
+ };
+ });
+
+ getEntityFeedCount();
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ }
+ };
+
+ const followEntity = useCallback(async () => {
+ try {
+ const res = await addStoredProceduresFollower(storedProcedureId, USER_ID);
+ const { newValue } = res.changeDescription.fieldsAdded[0];
+ const newFollowers = [...(followers ?? []), ...newValue];
+ setStoredProcedure((prev) => {
+ if (!prev) {
+ return prev;
+ }
+
+ return { ...prev, followers: newFollowers };
+ });
+ getEntityFeedCount();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.entity-follow-error', {
+ entity: getEntityName(storedProcedure),
+ })
+ );
+ }
+ }, [USER_ID, followers, storedProcedure, storedProcedureId]);
+
+ const unFollowEntity = useCallback(async () => {
+ try {
+ const res = await removeStoredProceduresFollower(
+ storedProcedureId,
+ USER_ID
+ );
+ const { oldValue } = res.changeDescription.fieldsDeleted[0];
+ setStoredProcedure((pre) => {
+ if (!pre) {
+ return pre;
+ }
+
+ return {
+ ...pre,
+ followers: pre.followers?.filter(
+ (follower) => follower.id !== oldValue[0].id
+ ),
+ };
+ });
+ getEntityFeedCount();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.entity-unfollow-error', {
+ entity: getEntityName(storedProcedure),
+ })
+ );
+ }
+ }, [USER_ID, storedProcedureId]);
+
+ const handleDisplayNameUpdate = async (data: EntityName) => {
+ if (!storedProcedure) {
+ return;
+ }
+ const updatedData = { ...storedProcedure, displayName: data.displayName };
+ await handleStoreProcedureUpdate(updatedData, 'displayName');
+ };
+
+ const handleFollow = useCallback(async () => {
+ isFollowing ? await unFollowEntity() : await followEntity();
+ }, [isFollowing]);
+
+ const handleUpdateOwner = useCallback(
+ async (newOwner?: StoredProcedure['owner']) => {
+ if (!storedProcedure) {
+ return;
+ }
+ const updatedEntityDetails = {
+ ...storedProcedure,
+ owner: newOwner
+ ? {
+ ...owner,
+ ...newOwner,
+ }
+ : undefined,
+ };
+ await handleStoreProcedureUpdate(updatedEntityDetails, 'owner');
+ },
+ [owner, storedProcedure]
+ );
+
+ const handleToggleDelete = () => {
+ setStoredProcedure((prev) => {
+ if (!prev) {
+ return prev;
+ }
+
+ return { ...prev, deleted: !prev?.deleted };
+ });
+ };
+
+ const handleRestoreStoredProcedures = async () => {
+ try {
+ await restoreStoredProcedures(storedProcedureId);
+ showSuccessToast(
+ t('message.restore-entities-success', {
+ entity: t('label.stored-procedure'),
+ }),
+ 2000
+ );
+ handleToggleDelete();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('message.restore-entities-error', {
+ entity: t('label.stored-procedure'),
+ })
+ );
+ }
+ };
+
+ const onTierUpdate = useCallback(
+ async (newTier?: string) => {
+ if (storedProcedure) {
+ const tierTag: StoredProcedure['tags'] = newTier
+ ? [
+ ...getTagsWithoutTier(tags ?? []),
+ {
+ tagFQN: newTier,
+ labelType: LabelType.Manual,
+ state: State.Confirmed,
+ },
+ ]
+ : getTagsWithoutTier(tags ?? []);
+ const updatedDetails = {
+ ...storedProcedure,
+ tags: tierTag,
+ };
+
+ await handleStoreProcedureUpdate(updatedDetails, 'tags');
+ }
+ },
+ [storedProcedure, tags]
+ );
+
+ const afterDeleteAction = useCallback(
+ (isSoftDelete?: boolean) =>
+ isSoftDelete ? handleToggleDelete() : history.push('/'),
+ []
+ );
+
+ const handleTabChange = (activeKey: EntityTabs) => {
+ if (activeKey !== activeTab) {
+ history.push(getStoredProcedureDetailPath(storedProcedureFQN, activeKey));
+ }
+ };
+
+ const onDescriptionEdit = (): void => {
+ setIsEdit(true);
+ };
+ const onCancel = () => {
+ setIsEdit(false);
+ };
+
+ const onDescriptionUpdate = async (updatedHTML: string) => {
+ if (description !== updatedHTML && storedProcedure) {
+ const updatedData = {
+ ...storedProcedure,
+ description: updatedHTML,
+ };
+ try {
+ await handleStoreProcedureUpdate(updatedData, 'description');
+ } catch (error) {
+ showErrorToast(error as AxiosError);
+ } finally {
+ setIsEdit(false);
+ }
+ } else {
+ setIsEdit(false);
+ }
+ };
+
+ const onThreadLinkSelect = (link: string, threadType?: ThreadType) => {
+ setThreadLink(link);
+ if (threadType) {
+ setThreadType(threadType);
+ }
+ };
+
+ const handleTagSelection = async (selectedTags: EntityTags[]) => {
+ const updatedTags: TagLabel[] | undefined = selectedTags?.map((tag) => ({
+ source: tag.source,
+ tagFQN: tag.tagFQN,
+ labelType: LabelType.Manual,
+ state: State.Confirmed,
+ }));
+
+ if (updatedTags && storedProcedure) {
+ const updatedTags = [...(tier ? [tier] : []), ...selectedTags];
+ const updatedData = { ...storedProcedure, tags: updatedTags };
+ await handleStoreProcedureUpdate(updatedData, 'tags');
+ }
+ };
+
+ const createThread = async (data: CreateThread) => {
+ try {
+ await postThread(data);
+ getEntityFeedCount();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.create-entity-error', {
+ entity: t('label.conversation'),
+ })
+ );
+ }
+ };
+
+ const onThreadPanelClose = () => {
+ setThreadLink('');
+ };
+
+ const onExtensionUpdate = async (updatedData: StoredProcedure) => {
+ await handleStoreProcedureUpdate(updatedData, 'extension');
+ };
+
+ const tabs = useMemo(
+ () => [
+ {
+ label: (
+
+ ),
+ key: EntityTabs.CODE,
+ children: (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+ },
+ {
+ label: (
+
+ ),
+ key: EntityTabs.ACTIVITY_FEED,
+ children: (
+
+ ),
+ },
+ {
+ label: ,
+ key: EntityTabs.LINEAGE,
+ children: (
+
+ ),
+ },
+ {
+ label: (
+
+ ),
+ key: EntityTabs.CUSTOM_PROPERTIES,
+ children: (
+
+ ),
+ },
+ ],
+ [
+ code,
+ tags,
+ isEdit,
+ deleted,
+ feedCount,
+ activeTab,
+ entityFQN,
+ entityName,
+ description,
+ storedProcedure,
+ storedProcedureFQN,
+ storedProcedurePermissions,
+ ]
+ );
+
+ useEffect(() => {
+ if (storedProcedureFQN) {
+ fetchResourcePermission();
+ }
+ }, [storedProcedureFQN]);
+
+ useEffect(() => {
+ if (
+ storedProcedurePermissions.ViewAll ||
+ storedProcedurePermissions.ViewBasic
+ ) {
+ fetchStoredProcedureDetails();
+ getEntityFeedCount();
+ }
+ }, [storedProcedureFQN, storedProcedurePermissions]);
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (
+ !(
+ storedProcedurePermissions.ViewAll || storedProcedurePermissions.ViewBasic
+ )
+ ) {
+ return ;
+ }
+
+ if (!storedProcedure) {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+
+ {/* Entity Tabs */}
+
+
+ handleTabChange(activeKey as EntityTabs)
+ }
+ />
+
+
+ {threadLink ? (
+
+ ) : null}
+
+
+ );
+};
+
+export default withActivityFeed(StoredProcedurePage);
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx
new file mode 100644
index 000000000000..92710f4cc3c4
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 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 { Col, Row, Switch, Table, Typography } from 'antd';
+import { ColumnsType } from 'antd/lib/table';
+import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
+import NextPrevious from 'components/common/next-previous/NextPrevious';
+import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
+import Loader from 'components/Loader/Loader';
+import { PAGE_SIZE } from 'constants/constants';
+import { EntityType } from 'enums/entity.enum';
+import { isEmpty } from 'lodash';
+import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage';
+import React, { useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
+import { getEntityName } from 'utils/EntityUtils';
+import { getEncodedFqn } from 'utils/StringsUtils';
+import { getEntityLink } from 'utils/TableUtils';
+import { StoredProcedureTabProps } from './storedProcedure.interface';
+
+const StoredProcedureTab = ({
+ storedProcedure,
+ pagingHandler,
+ fetchStoredProcedure,
+ onShowDeletedStoreProcedureChange,
+}: StoredProcedureTabProps) => {
+ const { t } = useTranslation();
+ const { data, isLoading, deleted, paging, currentPage } = storedProcedure;
+
+ const tableColumn: ColumnsType = useMemo(
+ () => [
+ {
+ title: t('label.name'),
+ dataIndex: 'name',
+ key: 'name',
+ width: 350,
+ render: (_, record) => (
+
+ {getEntityName(record)}
+
+ ),
+ },
+ {
+ title: t('label.description'),
+ dataIndex: 'description',
+ key: 'description',
+ render: (text: string) =>
+ isEmpty(text) ? (
+
+ {t('label.no-description')}
+
+ ) : (
+
+ ),
+ },
+ ],
+ []
+ );
+
+ useEffect(() => {
+ fetchStoredProcedure();
+ }, [deleted]);
+
+ return (
+
+
+
+
+ {t('label.deleted')}
+ {' '}
+
+
+ ,
+ }}
+ locale={{
+ emptyText: ,
+ }}
+ pagination={false}
+ rowKey="id"
+ size="small"
+ />
+
+
+
+ {paging && paging.total > PAGE_SIZE && (
+
+ )}
+
+
+ );
+};
+
+export default StoredProcedureTab;
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts
new file mode 100644
index 000000000000..b0758f16b019
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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 { StoredProcedureData } from 'pages/DatabaseSchemaPage/DatabaseSchemaPage.interface';
+
+export interface StoredProcedureTabProps {
+ storedProcedure: StoredProcedureData;
+ fetchStoredProcedure: () => void;
+ pagingHandler: (cursorValue: string | number, activePage?: number) => void;
+ onShowDeletedStoreProcedureChange: (value: boolean) => void;
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/TasksPage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/TasksPage.interface.ts
index c8e7112ed071..90fef6d2ea30 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/TasksPage.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/TasksPage.interface.ts
@@ -15,6 +15,7 @@ import { Container } from 'generated/entity/data/container';
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
import { Database } from 'generated/entity/data/database';
import { DatabaseSchema } from 'generated/entity/data/databaseSchema';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
import { Dashboard } from '../../generated/entity/data/dashboard';
import { Mlmodel } from '../../generated/entity/data/mlmodel';
import { Pipeline } from '../../generated/entity/data/pipeline';
@@ -28,6 +29,7 @@ export type EntityData =
| Pipeline
| Mlmodel
| Container
+ | StoredProcedure
| Database
| DatabaseSchema
| DashboardDataModel;
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts
new file mode 100644
index 000000000000..53271f5e34b8
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2023 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 { AxiosResponse } from 'axios';
+import { Operation } from 'fast-json-patch';
+import { StoredProcedure } from 'generated/entity/data/storedProcedure';
+import { EntityHistory } from 'generated/type/entityHistory';
+import { EntityReference } from 'generated/type/entityReference';
+import { Include } from 'generated/type/include';
+import { PagingResponse, RestoreRequestType } from 'Models';
+import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage';
+import { getURLWithQueryFields } from 'utils/APIUtils';
+import { ListDataModelParams } from './dashboardAPI';
+import APIClient from './index';
+
+const URL = '/storedProcedures';
+
+const configOptionsForPatch = {
+ headers: { 'Content-type': 'application/json-patch+json' },
+};
+
+const configOptions = {
+ headers: { 'Content-type': 'application/json' },
+};
+
+export const getStoredProceduresList = async (params?: ListDataModelParams) => {
+ const response = await APIClient.get>(URL, {
+ params,
+ });
+
+ return response.data;
+};
+
+export const getStoredProceduresDetails = async (
+ id: string,
+ arrQueryFields: string | string[]
+) => {
+ const url = getURLWithQueryFields(`${URL}/${id}`, arrQueryFields);
+
+ const response = await APIClient.get(url);
+
+ return response.data;
+};
+
+export const getStoredProceduresByName = async (
+ name: string,
+ fields: string | string[],
+ include: Include = Include.NonDeleted
+) => {
+ const response = await APIClient.get(
+ `${URL}/name/${name}?fields=${fields}`,
+ {
+ params: {
+ include,
+ },
+ }
+ );
+
+ return response.data;
+};
+
+export const getStoredProceduresDetailsByFQN = async (
+ storedProceduresName: string,
+ arrQueryFields?: string | string[],
+ include = Include.All
+) => {
+ const url = `${getURLWithQueryFields(
+ `${URL}/name/${storedProceduresName}`,
+ arrQueryFields,
+ `include=${include}`
+ )}`;
+
+ const response = await APIClient.get(url);
+
+ return response.data;
+};
+
+export const patchStoredProceduresDetails = async (
+ id: string,
+ data: Operation[]
+) => {
+ const response = await APIClient.patch<
+ Operation[],
+ AxiosResponse
+ >(`${URL}/${id}`, data, configOptionsForPatch);
+
+ return response.data;
+};
+
+export const addStoredProceduresFollower = async (
+ id: string,
+ userId: string
+) => {
+ const response = await APIClient.put<
+ string,
+ AxiosResponse<{
+ changeDescription: { fieldsAdded: { newValue: EntityReference[] }[] };
+ }>
+ >(`${URL}/${id}/followers`, userId, configOptions);
+
+ return response.data;
+};
+
+export const removeStoredProceduresFollower = async (
+ id: string,
+ userId: string
+) => {
+ const response = await APIClient.delete<
+ string,
+ AxiosResponse<{
+ changeDescription: { fieldsDeleted: { oldValue: EntityReference[] }[] };
+ }>
+ >(`${URL}/${id}/followers/${userId}`, configOptions);
+
+ return response.data;
+};
+
+export const getStoredProceduresVersionsList = async (id: string) => {
+ const url = `${URL}/${id}/versions`;
+
+ const response = await APIClient.get(url);
+
+ return response.data;
+};
+
+export const getStoredProceduresVersion = async (
+ id: string,
+ version: string
+) => {
+ const url = `${URL}/${id}/versions/${version}`;
+
+ const response = await APIClient.get(url);
+
+ return response.data;
+};
+
+export const restoreStoredProcedures = async (id: string) => {
+ const response = await APIClient.put<
+ RestoreRequestType,
+ AxiosResponse
+ >(`${URL}/restore`, { id });
+
+ return response.data;
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
index 917a661e2309..7438f4deffa2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
@@ -55,6 +55,7 @@ import {
getDataModelDetailsPath,
getMlModelDetailsPath,
getPipelineDetailsPath,
+ getStoredProcedureDetailPath,
getTableTabPath,
getTeamAndUserDetailsPath,
getTopicDetailsPath,
@@ -837,6 +838,11 @@ export const getEntityDetailLink = (
case EntityType.USER_NAME:
path = getUserPath(fqn, tab, subTab);
+ break;
+
+ case EntityType.STORED_PROCEDURE:
+ path = getStoredProcedureDetailPath(fqn, tab, subTab);
+
break;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
index ee338fecf2e7..f1699545473e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
@@ -20,7 +20,10 @@ import {
DataAssetHeaderInfo,
DataAssetsHeaderProps,
} from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
-import { getDashboardDetailsPath } from 'constants/constants';
+import {
+ getDashboardDetailsPath,
+ NO_DATA_PLACEHOLDER,
+} from 'constants/constants';
import { EntityType } from 'enums/entity.enum';
import { Container } from 'generated/entity/data/container';
import { Dashboard } from 'generated/entity/data/dashboard';
@@ -29,6 +32,10 @@ import { Database } from 'generated/entity/data/database';
import { DatabaseSchema } from 'generated/entity/data/databaseSchema';
import { Mlmodel } from 'generated/entity/data/mlmodel';
import { Pipeline } from 'generated/entity/data/pipeline';
+import {
+ StoredProcedure,
+ StoredProcedureCodeObject,
+} from 'generated/entity/data/storedProcedure';
import { Table } from 'generated/entity/data/table';
import { Topic } from 'generated/entity/data/topic';
import { DashboardService } from 'generated/entity/services/dashboardService';
@@ -39,7 +46,7 @@ import { MlmodelService } from 'generated/entity/services/mlmodelService';
import { PipelineService } from 'generated/entity/services/pipelineService';
import { StorageService } from 'generated/entity/services/storageService';
import { t } from 'i18next';
-import { isUndefined } from 'lodash';
+import { isObject, isUndefined } from 'lodash';
import React from 'react';
import {
getBreadcrumbForContainer,
@@ -324,6 +331,28 @@ export const getDataAssetsHeaderInfo = (
break;
+ case EntityType.STORED_PROCEDURE:
+ const storedProcedureDetails = dataAsset as StoredProcedure;
+
+ returnData.extraInfo = (
+ <>
+ {isObject(storedProcedureDetails.storedProcedureCode) && (
+
+ )}
+ >
+ );
+
+ returnData.breadcrumbs = getBreadcrumbForTable(dataAsset as Table);
+
+ break;
+
case EntityType.TABLE:
default:
const tableDetails = dataAsset as Table;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx
index 28b9cd075523..56a7fe682967 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx
@@ -1327,6 +1327,8 @@ export const getParamByEntityType = (entityType: EntityType): string => {
return 'databaseSchemaFQN';
case EntityType.DASHBOARD_DATA_MODEL:
return 'dashboardDataModelFQN';
+ case EntityType.STORED_PROCEDURE:
+ return 'storedProcedureFQN';
default:
return 'entityFQN';
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
index 49a938f242ad..e6d8bd98ace4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
@@ -38,10 +38,22 @@ import { Database } from 'generated/entity/data/database';
import { DatabaseSchema } from 'generated/entity/data/databaseSchema';
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
import { Mlmodel } from 'generated/entity/data/mlmodel';
+import {
+ StoredProcedure,
+ StoredProcedureCodeObject,
+} from 'generated/entity/data/storedProcedure';
import { Topic } from 'generated/entity/data/topic';
import i18next from 'i18next';
import { EntityFieldThreadCount } from 'interface/feed.interface';
-import { get, isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash';
+import {
+ get,
+ isEmpty,
+ isNil,
+ isObject,
+ isUndefined,
+ lowerCase,
+ startCase,
+} from 'lodash';
import { Bucket, EntityDetailUnion } from 'Models';
import React, { Fragment } from 'react';
import { Link } from 'react-router-dom';
@@ -129,6 +141,7 @@ export const getEntityTags = (
case EntityType.DASHBOARD:
case EntityType.TOPIC:
case EntityType.MLMODEL:
+ case EntityType.STORED_PROCEDURE:
case EntityType.DASHBOARD_DATA_MODEL: {
return entityDetail.tags || [];
}
@@ -558,6 +571,88 @@ export const getEntityOverview = (
return overview;
}
+ case ExplorePageTabs.STORED_PROCEDURE: {
+ const { fullyQualifiedName, owner, tags, storedProcedureCode } =
+ entityDetail as StoredProcedure;
+ const [service, database, schema] = getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ ).split(FQN_SEPARATOR_CHAR);
+
+ const tier = getTierFromTableTags(tags || []);
+
+ const overview = [
+ {
+ name: i18next.t('label.owner'),
+ value:
+ getOwnerNameWithProfilePic(owner) ||
+ i18next.t('label.no-entity', {
+ entity: i18next.t('label.owner'),
+ }),
+ url: getOwnerValue(owner as EntityReference),
+ isLink: owner?.name ? true : false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18next.t('label.service'),
+ value: service || NO_DATA,
+ url: getServiceDetailsPath(
+ service,
+ ServiceCategory.DATABASE_SERVICES
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18next.t('label.database'),
+ value: database || NO_DATA,
+ url: getDatabaseDetailsPath(
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18next.t('label.schema'),
+ value: schema || NO_DATA,
+ url: getDatabaseSchemaDetailsPath(
+ getPartialNameFromTableFQN(
+ fullyQualifiedName ?? '',
+ [FqnPart.Service, FqnPart.Database, FqnPart.Schema],
+ FQN_SEPARATOR_CHAR
+ )
+ ),
+ isLink: true,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ {
+ name: i18next.t('label.tier'),
+ value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : NO_DATA,
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ...(isObject(storedProcedureCode)
+ ? [
+ {
+ name: i18next.t('label.language'),
+ value:
+ (storedProcedureCode as StoredProcedureCodeObject).language ??
+ NO_DATA,
+ isLink: false,
+ visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
+ },
+ ]
+ : []),
+ ];
+
+ return overview;
+ }
+
default:
return [];
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
index e1c61361bbcd..3ef66686476b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsUtils.tsx
@@ -42,6 +42,7 @@ import { ReactComponent as TopicIcon } from '../../src/assets/svg/topic-grey.svg
import { ReactComponent as UsersIcon } from '../../src/assets/svg/user.svg';
import { ReactComponent as CustomLogoIcon } from '../assets/svg/ic-custom-logo.svg';
import { ReactComponent as StorageIcon } from '../assets/svg/ic-storage.svg';
+import { ReactComponent as StoredProcedureIcon } from '../assets/svg/ic-stored-procedure.svg';
import { userPermissions } from '../utils/PermissionsUtils';
export interface MenuListItem {
@@ -252,6 +253,12 @@ export const getGlobalSettingsMenuWithPermission = (
key: 'customAttributes.containers',
icon: ,
},
+ {
+ label: i18next.t('label.stored-procedure'),
+ isProtected: Boolean(isAdminUser),
+ key: 'customAttributes.storedProcedure',
+ icon: ,
+ },
],
},
{
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx
new file mode 100644
index 000000000000..5e79cac2022c
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2023 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 { TabSpecificField } from 'enums/entity.enum';
+
+export const STORED_PROCEDURE_DEFAULT_FIELDS = `${TabSpecificField.OWNER}, ${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.EXTENSION}`;
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
index 92f99610e433..7eea729798c1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx
@@ -53,6 +53,7 @@ import {
getMlModelPath,
getPipelineDetailsPath,
getServiceDetailsPath,
+ getStoredProcedureDetailsPath,
getTableDetailsPath,
getTableTabPath,
getTagsDetailsPath,
@@ -249,6 +250,9 @@ export const getEntityLink = (
case EntityType.DASHBOARD_DATA_MODEL:
return getDataModelDetailsPath(getDecodedFqn(fullyQualifiedName));
+ case EntityType.STORED_PROCEDURE:
+ return getStoredProcedureDetailsPath(getDecodedFqn(fullyQualifiedName));
+
case EntityType.TEST_CASE:
return `${getTableTabPath(
getTableFQNFromColumnFQN(fullyQualifiedName),
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts
index 35dcdf30d226..497f3f4f3af3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts
@@ -40,6 +40,7 @@ import { getUserSuggestions } from 'rest/miscAPI';
import { getMlModelByFQN } from 'rest/mlModelAPI';
import { getPipelineByFqn } from 'rest/pipelineAPI';
import { getContainerByFQN } from 'rest/storageAPI';
+import { getStoredProceduresDetailsByFQN } from 'rest/storedProceduresAPI';
import { getTableDetailsByFQN } from 'rest/tableAPI';
import { getTopicByFqn } from 'rest/topicsAPI';
import {
@@ -74,6 +75,7 @@ import { getEntityFQN, getEntityType } from './FeedUtils';
import { defaultFields as MlModelFields } from './MlModelDetailsUtils';
import { defaultFields as PipelineFields } from './PipelineDetailsUtils';
import { serviceTypeLogo } from './ServiceUtils';
+import { STORED_PROCEDURE_DEFAULT_FIELDS } from './StoredProceduresUtils';
import { getEntityLink } from './TableUtils';
import { showErrorToast } from './ToastUtils';
@@ -269,6 +271,7 @@ export const TASK_ENTITIES = [
EntityType.CONTAINER,
EntityType.DATABASE_SCHEMA,
EntityType.DASHBOARD_DATA_MODEL,
+ EntityType.STORED_PROCEDURE,
];
export const getBreadCrumbList = (
@@ -353,6 +356,15 @@ export const getBreadCrumbList = (
return [service(ServiceCategory.STORAGE_SERVICES), activeEntity];
}
+ case EntityType.STORED_PROCEDURE: {
+ return [
+ service(ServiceCategory.DATABASE_SERVICES),
+ database,
+ databaseSchema,
+ activeEntity,
+ ];
+ }
+
default:
return [];
}
@@ -447,6 +459,18 @@ export const fetchEntityDetail = (
break;
+ case EntityType.STORED_PROCEDURE:
+ getStoredProceduresDetailsByFQN(
+ entityFQN,
+ STORED_PROCEDURE_DEFAULT_FIELDS
+ )
+ .then((res) => {
+ setEntityData(res);
+ })
+ .catch((err: AxiosError) => showErrorToast(err));
+
+ break;
+
default:
break;
}