From d1ab62ee75ecd34d2fb2c18135c2ccebcb481039 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Tue, 29 Aug 2023 18:41:09 +0530 Subject: [PATCH 1/8] feat: supported stored procedure in database --- .../DataAssetsHeader.interface.ts | 6 + .../PermissionProvider.interface.ts | 1 + .../router/AuthenticatedAppRouter.tsx | 21 + .../resources/ui/src/constants/constants.ts | 18 + .../resources/ui/src/enums/entity.enum.ts | 2 + .../ui/src/interface/service.interface.ts | 5 +- .../ui/src/locale/languages/en-us.json | 1 + .../ui/src/locale/languages/es-es.json | 1 + .../ui/src/locale/languages/fr-fr.json | 1 + .../ui/src/locale/languages/ja-jp.json | 1 + .../ui/src/locale/languages/pt-br.json | 1 + .../ui/src/locale/languages/ru-ru.json | 1 + .../ui/src/locale/languages/zh-cn.json | 1 + .../DatabaseSchemaPage.component.tsx | 82 ++++ .../ServiceDetailsPage/ServiceDetailsPage.tsx | 4 +- .../StoredProcedures/StoredProceduresPage.tsx | 378 ++++++++++++++++++ .../StoredProcedures/StoredProceduresTab.tsx | 106 +++++ .../storedProcedures.interface.ts | 22 + .../ui/src/rest/storedProceduresAPI.ts | 159 ++++++++ .../ui/src/utils/StoredProceduresUtils.tsx | 15 + .../resources/ui/src/utils/TableUtils.tsx | 4 + 21 files changed, 827 insertions(+), 3 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresTab.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx 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..dc39bc1e91cb 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 @@ -99,6 +99,7 @@ export type DataAssetsHeaderProps = { | DataAssetMlModelService | DataAssetMetadataService | DataAssetStorageService + | DataAssetStoredProceduresService ); export interface DataAssetTable { @@ -172,6 +173,11 @@ export interface DataAssetStorageService { entityType: EntityType.STORAGE_SERVICE; } +export interface DataAssetStoredProceduresService { + dataAsset: ServicesType; + entityType: EntityType.STORED_PROCEDURE; +} + export interface DataAssetHeaderInfo { extraInfo: ReactNode; breadcrumbs: TitleBreadcrumbProps['titleLinks']; 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/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/router/AuthenticatedAppRouter.tsx index 80dcae8a1202..c0f7f190c17d 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 @@ -127,6 +127,10 @@ const DataModelDetailsPage = withSuspenseFallback( React.lazy(() => import('pages/DataModelPage/DataModelPage.component')) ); +const StoredProceduresDetailsPage = withSuspenseFallback( + React.lazy(() => import('pages/StoredProcedures/StoredProceduresPage')) +); + const TableDetailsPageV1 = withSuspenseFallback( React.lazy(() => import('pages/TableDetailsPageV1/TableDetailsPageV1')) ); @@ -432,6 +436,23 @@ const AuthenticatedAppRouter: FunctionComponent = () => { component={DataModelDetailsPage} path={ROUTES.DATA_MODEL_DETAILS_WITH_SUB_TAB} /> + + + + + { return `${path}${columnName ? `.${columnName}` : ''}`; }; +export const getStoredProceduresDetailsPath = ( + storedProceduresFQN: string, + columnName?: string +) => { + let path = ROUTES.STORED_PROCEDURE_DETAILS; + path = path.replace( + PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN, + getEncodedFqn(storedProceduresFQN) + ); + + return `${path}${columnName ? `.${columnName}` : ''}`; +}; + export const getTagsDetailsPath = (entityFQN: string, columnName?: string) => { let path = ROUTES.TAG_DETAILS; const classification = getPartialNameFromFQN(entityFQN, ['service']); 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..b2e2bb97f744 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,7 @@ export enum EntityTabs { INGESTIONS = 'ingestions', CONNECTION = 'connection', SQL = 'sql', + STORED_PROCEDURE = 'stored_procedure', } export enum EntityAction { diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts index f7661acc224d..ee134c91cb71 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts @@ -17,6 +17,7 @@ import { Pipeline, PipelineType, } from 'generated/api/services/ingestionPipelines/createIngestionPipeline'; +import { StoredProcedure } from 'generated/entity/data/storedProcedure'; import { StorageConnection, StorageService, @@ -90,8 +91,8 @@ export type ServicesType = | PipelineService | MlmodelService | MetadataService - | StorageService; - + | StorageService + | StoredProcedure; export interface ServiceResponse { data: Array; paging: Paging; 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 694849b52781..d9753fc29537 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 @@ -877,6 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", + "stored-procedures": "Stored Procedures", "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 56a9f98fd07d..be340ae599e7 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 @@ -877,6 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", + "stored-procedures": "Stored Procedures", "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 1cf0e51e151c..5aa2e0a10fe4 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 @@ -877,6 +877,7 @@ "stopped": "Arrêté", "storage": "Stockage", "storage-plural": "Stockages", + "stored-procedures": "Stored Procedures", "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 8425f55452db..d016e567e484 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 @@ -877,6 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", + "stored-procedures": "Stored Procedures", "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 5ef49325e903..50532033035b 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 @@ -877,6 +877,7 @@ "stopped": "Parado", "storage": "Storage", "storage-plural": "Storages", + "stored-procedures": "Stored Procedures", "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 89228138ce71..171f945c55bb 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 @@ -877,6 +877,7 @@ "stopped": "Остановлено", "storage": "Хранилище", "storage-plural": "Хранилища", + "stored-procedures": "Stored Procedures", "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 7853acc614af..8b99b9926fd3 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 @@ -877,6 +877,7 @@ "stopped": "已停止", "storage": "存储", "storage-plural": "存储", + "stored-procedures": "Stored Procedures", "sub-team-plural": "子团队", "submit": "提交", "success": "成功", 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 50076051714d..d435e055d9c2 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 @@ -36,10 +36,13 @@ import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum'; import { compare, Operation } from 'fast-json-patch'; import { ThreadType } from 'generated/entity/feed/thread'; import { Include } from 'generated/type/include'; +import { Paging } from 'generated/type/paging'; import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel'; import { isEmpty, isString, isUndefined } from 'lodash'; import { observer } from 'mobx-react'; import { EntityTags, PagingResponse } from 'Models'; +import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; +import StoredProceduresTab from 'pages/StoredProcedures/StoredProceduresTab'; import React, { FunctionComponent, useCallback, @@ -56,6 +59,10 @@ import { restoreDatabaseSchema, } from 'rest/databaseAPI'; import { getFeedCount, postThread } from 'rest/feedsAPI'; +import { + getStoredProceduresList, + ListDataModelParams, +} from 'rest/storedProceduresAPI'; import { getTableList, TableListParams } from 'rest/tableAPI'; import { getEntityMissingError } from 'utils/CommonUtils'; import { getDecodedFqn } from 'utils/StringsUtils'; @@ -63,6 +70,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'; @@ -112,6 +120,16 @@ const DatabaseSchemaPage: FunctionComponent = () => { const [currentTablesPage, setCurrentTablesPage] = useState(INITIAL_PAGING_VALUE); + const [storedProcedures, setStoredProcedures] = useState( + [] + ); + const [isStoredProceduresLoading, setIsStoredProceduresLoading] = + useState(false); + const [storedProceduresPaging, setStoredProceduresPaging] = + useState(pagingObject); + const [storedProceduresCurrentPage, setStoredProceduresCurrentPage] = + useState(1); + const handleShowDeletedTables = (value: boolean) => { setShowDeletedTables(value); setCurrentTablesPage(INITIAL_PAGING_VALUE); @@ -196,6 +214,27 @@ const DatabaseSchemaPage: FunctionComponent = () => { } }, [databaseSchemaFQN]); + const fetchStoreProceduresDetails = useCallback( + async (params?: ListDataModelParams) => { + try { + setIsStoredProceduresLoading(true); + const { data, paging } = await getStoredProceduresList({ + service: getDecodedFqn(databaseSchemaFQN), + fields: 'owner,tags,followers', + include: Include.NonDeleted, + ...params, + }); + setStoredProcedures(data); + setStoredProceduresPaging(paging); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsStoredProceduresLoading(false); + } + }, + [databaseSchemaFQN] + ); + const getSchemaTables = useCallback( async (params?: TableListParams) => { setTableDataLoading(true); @@ -449,6 +488,28 @@ const DatabaseSchemaPage: FunctionComponent = () => { [] ); + const storedProceduresPagingHandler = useCallback( + async (cursorType: string | number, activePage?: number) => { + const pagingString = { + [cursorType]: + storedProceduresPaging[ + cursorType as keyof typeof storedProceduresPaging + ], + }; + + await fetchStoreProceduresDetails(pagingString); + + setStoredProceduresCurrentPage(activePage ?? 1); + }, + [storedProceduresPaging] + ); + + useEffect(() => { + if (activeTab === EntityTabs.STORED_PROCEDURE) { + fetchStoreProceduresDetails(); + } + }, [activeTab]); + useEffect(() => { fetchDatabaseSchemaPermission(); }, [databaseSchemaFQN]); @@ -456,6 +517,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { useEffect(() => { if (viewDatabaseSchemaPermission) { fetchDatabaseSchemaDetails(); + fetchStoreProceduresDetails({ limit: 0 }); getEntityFeedCount(); } }, [viewDatabaseSchemaPermission, databaseSchemaFQN]); @@ -570,6 +632,26 @@ const DatabaseSchemaPage: FunctionComponent = () => { ), }, + { + label: ( + + ), + key: EntityTabs.STORED_PROCEDURE, + children: ( + + ), + }, ]; if (isPermissionsLoading) { 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/StoredProcedures/StoredProceduresPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx new file mode 100644 index 000000000000..0c5d1d885bf8 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx @@ -0,0 +1,378 @@ +/* + * 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 } from 'antd'; +import { AxiosError } from 'axios'; +import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; +import PageLayoutV1 from 'components/containers/PageLayoutV1'; +import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.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 { getVersionPath } from 'constants/constants'; +import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum'; +import { EntityType } from 'enums/entity.enum'; +import { compare } from 'fast-json-patch'; +import { StoredProcedure } from 'generated/entity/data/storedProcedure'; +import { LabelType, State } from 'generated/type/tagLabel'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useHistory, useParams } from 'react-router-dom'; +import { + addStoredProceduresFollower, + getStoredProceduresDetailsByFQN, + patchStoredProceduresDetails, + removeStoredProceduresFollower, + restoreStoredProcedures, +} from 'rest/storedProceduresAPI'; +import { getCurrentUserId, 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 USERId = getCurrentUserId(); + const history = useHistory(); + const { storedProceduresFQN } = useParams<{ storedProceduresFQN: string }>(); + const { getEntityPermissionByFqn } = usePermissionProvider(); + + const [isLoading, setIsLoading] = useState(true); + const [storedProcedures, setStoredProcedures] = useState(); + const [storedProceduresPermissions, setStoredProceduresPermissions] = + useState(DEFAULT_ENTITY_PERMISSION); + + const fetchResourcePermission = useCallback(async () => { + try { + const permission = await getEntityPermissionByFqn( + ResourceEntity.STORED_PROCEDURE, + storedProceduresFQN + ); + + setStoredProceduresPermissions(permission); + } catch (error) { + showErrorToast( + t('server.fetch-entity-permissions-error', { + entity: t('label.resource-permission-lowercase'), + }) + ); + } finally { + setIsLoading(false); + } + }, [getEntityPermissionByFqn]); + + const fetchStoredProceduresDetails = async () => { + setIsLoading(true); + try { + const response = await getStoredProceduresDetailsByFQN( + storedProceduresFQN, + STORED_PROCEDURE_DEFAULT_FIELDS + ); + + setStoredProcedures(response); + } catch (error) { + // Error here + } finally { + setIsLoading(false); + } + }; + + const { + id: storedProceduresId = '', + followers, + owner, + tags, + version, + } = useMemo(() => { + if (storedProcedures) { + return { + ...storedProcedures, + tier: getTierTags(storedProcedures.tags ?? []), + tags: getTagsWithoutTier(storedProcedures.tags || []), + }; + } + + return {} as StoredProcedure; + }, [storedProcedures]); + + const { isFollowing } = useMemo(() => { + return { + isFollowing: followers?.some(({ id }) => id === USERId), + }; + }, [followers, USERId]); + + const versionHandler = useCallback(() => { + version && + history.push( + getVersionPath( + EntityType.STORED_PROCEDURE, + storedProceduresFQN, + version + '' + ) + ); + }, [version]); + + const saveUpdatedStoredProceduresData = useCallback( + (updatedData: StoredProcedure) => { + if (!storedProcedures) { + return updatedData; + } + const jsonPatch = compare(storedProcedures ?? '', updatedData); + + return patchStoredProceduresDetails(storedProceduresId ?? '', jsonPatch); + }, + [storedProcedures] + ); + + const handleStoreProceduresUpdate = async ( + updatedData: StoredProcedure, + key: keyof StoredProcedure + ) => { + try { + const res = await saveUpdatedStoredProceduresData(updatedData); + + setStoredProcedures((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 followStoredProcedure = useCallback(async () => { + try { + const res = await addStoredProceduresFollower(storedProceduresId, USERId); + const { newValue } = res.changeDescription.fieldsAdded[0]; + const newFollowers = [...(followers ?? []), ...newValue]; + setStoredProcedures((prev) => { + if (!prev) { + return prev; + } + + return { ...prev, followers: newFollowers }; + }); + // getEntityFeedCount(); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-follow-error', { + entity: getEntityName(storedProcedures), + }) + ); + } + }, [USERId, storedProceduresId]); + + const unFollowStoredProcedure = useCallback(async () => { + try { + const res = await removeStoredProceduresFollower( + storedProceduresId, + USERId + ); + const { oldValue } = res.changeDescription.fieldsDeleted[0]; + setStoredProcedures((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(storedProcedures), + }) + ); + } + }, [USERId, storedProceduresId]); + + const handleDisplayNameUpdate = async (data: EntityName) => { + if (!storedProcedures) { + return; + } + const updatedData = { ...storedProcedures, displayName: data.displayName }; + await handleStoreProceduresUpdate(updatedData, 'displayName'); + }; + + const handleFollow = useCallback(async () => { + isFollowing + ? await unFollowStoredProcedure() + : await followStoredProcedure(); + }, [isFollowing, followStoredProcedure, unFollowStoredProcedure]); + + const handleUpdateOwner = useCallback( + async (newOwner?: StoredProcedure['owner']) => { + if (!storedProcedures) { + return; + } + const updatedTableDetails = { + ...storedProcedures, + owner: newOwner + ? { + ...owner, + ...newOwner, + } + : undefined, + }; + await handleStoreProceduresUpdate(updatedTableDetails, 'owner'); + }, + [owner, storedProcedures] + ); + + const handleToggleDelete = () => { + setStoredProcedures((prev) => { + if (!prev) { + return prev; + } + + return { ...prev, deleted: !prev?.deleted }; + }); + }; + + const handleRestoreStoredProcedures = async () => { + try { + await restoreStoredProcedures(storedProceduresId); + showSuccessToast( + t('message.restore-entities-success', { + entity: t('label.table'), + }), + 2000 + ); + handleToggleDelete(); + } catch (error) { + showErrorToast( + error as AxiosError, + t('message.restore-entities-error', { + entity: t('label.table'), + }) + ); + } + }; + + const onTierUpdate = useCallback( + async (newTier?: string) => { + if (storedProcedures) { + const tierTag: StoredProcedure['tags'] = newTier + ? [ + ...getTagsWithoutTier(tags ?? []), + { + tagFQN: newTier, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + ] + : getTagsWithoutTier(tags ?? []); + const updatedDetails = { + ...storedProcedures, + tags: tierTag, + }; + + await handleStoreProceduresUpdate(updatedDetails, 'tags'); + } + }, + [storedProcedures, tags] + ); + + const afterDeleteAction = useCallback( + (isSoftDelete?: boolean) => + isSoftDelete ? handleToggleDelete() : history.push('/'), + [] + ); + + useEffect(() => { + if (storedProceduresFQN) { + fetchResourcePermission(); + } + }, [storedProceduresFQN]); + + useEffect(() => { + if ( + storedProceduresPermissions.ViewAll || + storedProceduresPermissions.ViewBasic + ) { + fetchStoredProceduresDetails(); + // getEntityFeedCount(); + } + }, [storedProceduresFQN, storedProceduresPermissions]); + + if (isLoading) { + return ; + } + + if ( + !( + storedProceduresPermissions.ViewAll || + storedProceduresPermissions.ViewBasic + ) + ) { + return ; + } + + if (!storedProcedures) { + return ; + } + + return ( + + + {/* Entity Heading */} + + + + + + ); +}; + +export default StoredProcedurePage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresTab.tsx new file mode 100644 index 000000000000..d32fe3a98840 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresTab.tsx @@ -0,0 +1,106 @@ +/* + * 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, Table } 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 { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { getEntityName } from 'utils/EntityUtils'; +import { getEntityLink } from 'utils/TableUtils'; +import { StoredProcedureTabProps } from './storedProcedures.interface'; + +const StoredProceduresTab = ({ + data, + isLoading, + paging, + pagingHandler, + currentPage, +}: StoredProcedureTabProps) => { + const { t } = useTranslation(); + + const tableColumn: ColumnsType = useMemo( + () => [ + { + title: t('label.name'), + dataIndex: 'name', + key: 'name', + render: (_, record) => { + return ( + + {getEntityName(record)} + + ); + }, + className: 'truncate w-max-500', + }, + { + title: t('label.description'), + dataIndex: 'description', + key: 'description', + render: (text: string) => + text?.trim() ? ( + + ) : ( + {t('label.no-description')} + ), + }, + ], + [] + ); + + return ( + + + , + }} + locale={{ + emptyText: , + }} + pagination={false} + rowKey="id" + size="small" + /> + {paging && paging.total > PAGE_SIZE && ( + + )} + + + ); +}; + +export default StoredProceduresTab; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts new file mode 100644 index 000000000000..7b19a4e0f9c3 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.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 { Paging } from 'generated/type/paging'; +import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; + +export interface StoredProcedureTabProps { + data: Array; + isLoading: boolean; + paging: Paging; + currentPage: number; + pagingHandler: (cursorValue: string | number, activePage?: number) => void; +} 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..023efbcde039 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts @@ -0,0 +1,159 @@ +/* + * 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 APIClient from './index'; + +const URL = '/storedProcedures'; + +const configOptionsForPatch = { + headers: { 'Content-type': 'application/json-patch+json' }, +}; + +const configOptions = { + headers: { 'Content-type': 'application/json' }, +}; + +export type ListDataModelParams = { + service?: string; + fields?: string; + after?: string; + before?: string; + include?: Include; + limit?: number; +}; + +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[] +) => { + const url = `${getURLWithQueryFields( + `${URL}/name/${storedProceduresName}`, + arrQueryFields + )}`; + + 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/StoredProceduresUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx new file mode 100644 index 000000000000..71eef7f30474 --- /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}`; 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..0b4156cc54de 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, + getStoredProceduresDetailsPath, getTableDetailsPath, getTableTabPath, getTagsDetailsPath, @@ -249,6 +250,9 @@ export const getEntityLink = ( case EntityType.DASHBOARD_DATA_MODEL: return getDataModelDetailsPath(getDecodedFqn(fullyQualifiedName)); + case EntityType.STORED_PROCEDURE: + return getStoredProceduresDetailsPath(getDecodedFqn(fullyQualifiedName)); + case EntityType.TEST_CASE: return `${getTableTabPath( getTableFQNFromColumnFQN(fullyQualifiedName), From 4e5fb51c672d0709754d0fad98d1476edc0f226c Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Thu, 31 Aug 2023 17:23:48 +0530 Subject: [PATCH 2/8] minor fixes --- .../DataAssetsHeader.interface.ts | 14 +- .../router/AuthenticatedAppRouter.tsx | 10 +- .../resources/ui/src/constants/constants.ts | 14 +- .../ui/src/locale/languages/en-us.json | 2 +- .../ui/src/locale/languages/es-es.json | 2 +- .../ui/src/locale/languages/fr-fr.json | 2 +- .../ui/src/locale/languages/ja-jp.json | 2 +- .../ui/src/locale/languages/pt-br.json | 2 +- .../ui/src/locale/languages/ru-ru.json | 2 +- .../ui/src/locale/languages/zh-cn.json | 2 +- .../DatabaseSchemaPage.component.tsx | 60 +++---- .../StoredProcedurePage.tsx} | 165 ++++++++---------- .../StoredProcedureTab.tsx} | 100 ++++++----- .../storedProcedure.interface.ts} | 2 +- .../ui/src/rest/storedProceduresAPI.ts | 10 +- .../ui/src/utils/DataAssetsHeader.utils.tsx | 33 +++- .../resources/ui/src/utils/TableUtils.tsx | 4 +- 17 files changed, 217 insertions(+), 209 deletions(-) rename openmetadata-ui/src/main/resources/ui/src/pages/{StoredProcedures/StoredProceduresPage.tsx => StoredProcedure/StoredProcedurePage.tsx} (71%) rename openmetadata-ui/src/main/resources/ui/src/pages/{StoredProcedures/StoredProceduresTab.tsx => StoredProcedure/StoredProcedureTab.tsx} (55%) rename openmetadata-ui/src/main/resources/ui/src/pages/{StoredProcedures/storedProcedures.interface.ts => StoredProcedure/storedProcedure.interface.ts} (96%) 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 dc39bc1e91cb..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 @@ -99,7 +102,6 @@ export type DataAssetsHeaderProps = { | DataAssetMlModelService | DataAssetMetadataService | DataAssetStorageService - | DataAssetStoredProceduresService ); export interface DataAssetTable { @@ -136,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; @@ -173,11 +180,6 @@ export interface DataAssetStorageService { entityType: EntityType.STORAGE_SERVICE; } -export interface DataAssetStoredProceduresService { - dataAsset: ServicesType; - entityType: EntityType.STORED_PROCEDURE; -} - export interface DataAssetHeaderInfo { extraInfo: ReactNode; breadcrumbs: TitleBreadcrumbProps['titleLinks']; 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 c0f7f190c17d..f5e36548e3eb 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 @@ -127,8 +127,8 @@ const DataModelDetailsPage = withSuspenseFallback( React.lazy(() => import('pages/DataModelPage/DataModelPage.component')) ); -const StoredProceduresDetailsPage = withSuspenseFallback( - React.lazy(() => import('pages/StoredProcedures/StoredProceduresPage')) +const StoredProcedureDetailsPage = withSuspenseFallback( + React.lazy(() => import('pages/StoredProcedure/StoredProcedurePage')) ); const TableDetailsPageV1 = withSuspenseFallback( @@ -439,17 +439,17 @@ const AuthenticatedAppRouter: FunctionComponent = () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index be5310b8eebb..8e1b7abb19da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -137,7 +137,7 @@ export const LOG_ENTITY_NAME = ':logEntityName'; export const KPI_NAME = ':kpiName'; export const PLACEHOLDER_ACTION = ':action'; export const PLACEHOLDER_ROUTE_DATA_MODEL_FQN = ':dashboardDataModelFQN'; -export const PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN = ':storedProceduresFQN'; +export const PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN = ':storedProcedureFQN'; export const pagingObject = { after: '', before: '', total: 0 }; @@ -260,9 +260,9 @@ export const ROUTES = { CONTAINER_DETAILS_WITH_TAB: `/container/${PLACEHOLDER_ROUTE_ENTITY_FQN}/${PLACEHOLDER_ROUTE_TAB}`, CONTAINER_DETAILS_WITH_SUB_TAB: `/container/${PLACEHOLDER_ROUTE_ENTITY_FQN}/${PLACEHOLDER_ROUTE_TAB}/${PLACEHOLDER_ROUTE_SUB_TAB}`, - STORED_PROCEDURE_DETAILS: `/storedProcedures/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}`, - STORED_PROCEDURE_DETAILS_WITH_TAB: `/storedProcedures/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}/${PLACEHOLDER_ROUTE_TAB}`, - STORED_PROCEDURE_DETAILS_WITH_SUB_TAB: `/storedProcedures/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}/${PLACEHOLDER_ROUTE_TAB}/${PLACEHOLDER_ROUTE_SUB_TAB}`, + STORED_PROCEDURE_DETAILS: `/storedProcedure/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}`, + STORED_PROCEDURE_DETAILS_WITH_TAB: `/storedProcedure/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}/${PLACEHOLDER_ROUTE_TAB}`, + STORED_PROCEDURE_DETAILS_WITH_SUB_TAB: `/storedProcedure/${PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN}/${PLACEHOLDER_ROUTE_TAB}/${PLACEHOLDER_ROUTE_SUB_TAB}`, USER_LIST: '/user-list', CREATE_USER: '/create-user', @@ -352,14 +352,14 @@ export const getTableDetailsPath = (tableFQN: string, columnName?: string) => { return `${path}${columnName ? `.${columnName}` : ''}`; }; -export const getStoredProceduresDetailsPath = ( - storedProceduresFQN: string, +export const getStoredProcedureDetailsPath = ( + storedProcedureFQN: string, columnName?: string ) => { let path = ROUTES.STORED_PROCEDURE_DETAILS; path = path.replace( PLACEHOLDER_ROUTE_STORED_PROCEDURE_FQN, - getEncodedFqn(storedProceduresFQN) + getEncodedFqn(storedProcedureFQN) ); return `${path}${columnName ? `.${columnName}` : ''}`; 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 d9753fc29537..147c9b5e2fa3 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 @@ -877,7 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", - "stored-procedures": "Stored Procedures", + "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 be340ae599e7..88a2b48abff6 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 @@ -877,7 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", - "stored-procedures": "Stored Procedures", + "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 5aa2e0a10fe4..8be2654b3782 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 @@ -877,7 +877,7 @@ "stopped": "Arrêté", "storage": "Stockage", "storage-plural": "Stockages", - "stored-procedures": "Stored Procedures", + "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 d016e567e484..244f082e3321 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 @@ -877,7 +877,7 @@ "stopped": "Stopped", "storage": "Storage", "storage-plural": "Storages", - "stored-procedures": "Stored Procedures", + "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 50532033035b..03b68674bc12 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 @@ -877,7 +877,7 @@ "stopped": "Parado", "storage": "Storage", "storage-plural": "Storages", - "stored-procedures": "Stored Procedures", + "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 171f945c55bb..cbaba7a4f96f 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 @@ -877,7 +877,7 @@ "stopped": "Остановлено", "storage": "Хранилище", "storage-plural": "Хранилища", - "stored-procedures": "Stored Procedures", + "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 8b99b9926fd3..2cae8337b18f 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 @@ -877,7 +877,7 @@ "stopped": "已停止", "storage": "存储", "storage-plural": "存储", - "stored-procedures": "Stored Procedures", + "stored-procedure": "Stored Procedure", "sub-team-plural": "子团队", "submit": "提交", "success": "成功", 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 d435e055d9c2..f464e2626ee8 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 @@ -42,7 +42,7 @@ import { isEmpty, isString, isUndefined } from 'lodash'; import { observer } from 'mobx-react'; import { EntityTags, PagingResponse } from 'Models'; import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; -import StoredProceduresTab from 'pages/StoredProcedures/StoredProceduresTab'; +import StoredProcedureTab from 'pages/StoredProcedure/StoredProcedureTab'; import React, { FunctionComponent, useCallback, @@ -53,16 +53,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, - ListDataModelParams, -} from 'rest/storedProceduresAPI'; +import { getStoredProceduresList } from 'rest/storedProceduresAPI'; import { getTableList, TableListParams } from 'rest/tableAPI'; import { getEntityMissingError } from 'utils/CommonUtils'; import { getDecodedFqn } from 'utils/StringsUtils'; @@ -120,14 +118,12 @@ const DatabaseSchemaPage: FunctionComponent = () => { const [currentTablesPage, setCurrentTablesPage] = useState(INITIAL_PAGING_VALUE); - const [storedProcedures, setStoredProcedures] = useState( - [] - ); - const [isStoredProceduresLoading, setIsStoredProceduresLoading] = + const [storedProcedure, setStoredProcedure] = useState([]); + const [isStoredProcedureLoading, setIsStoredProcedureLoading] = useState(false); - const [storedProceduresPaging, setStoredProceduresPaging] = + const [storedProcedurePaging, setStoredProcedurePaging] = useState(pagingObject); - const [storedProceduresCurrentPage, setStoredProceduresCurrentPage] = + const [storedProcedureCurrentPage, setStoredProcedureCurrentPage] = useState(1); const handleShowDeletedTables = (value: boolean) => { @@ -214,22 +210,22 @@ const DatabaseSchemaPage: FunctionComponent = () => { } }, [databaseSchemaFQN]); - const fetchStoreProceduresDetails = useCallback( + const fetchStoreProcedureDetails = useCallback( async (params?: ListDataModelParams) => { try { - setIsStoredProceduresLoading(true); + setIsStoredProcedureLoading(true); const { data, paging } = await getStoredProceduresList({ service: getDecodedFqn(databaseSchemaFQN), fields: 'owner,tags,followers', include: Include.NonDeleted, ...params, }); - setStoredProcedures(data); - setStoredProceduresPaging(paging); + setStoredProcedure(data); + setStoredProcedurePaging(paging); } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsStoredProceduresLoading(false); + setIsStoredProcedureLoading(false); } }, [databaseSchemaFQN] @@ -488,25 +484,25 @@ const DatabaseSchemaPage: FunctionComponent = () => { [] ); - const storedProceduresPagingHandler = useCallback( + const storedProcedurePagingHandler = useCallback( async (cursorType: string | number, activePage?: number) => { const pagingString = { [cursorType]: - storedProceduresPaging[ - cursorType as keyof typeof storedProceduresPaging + storedProcedurePaging[ + cursorType as keyof typeof storedProcedurePaging ], }; - await fetchStoreProceduresDetails(pagingString); + await fetchStoreProcedureDetails(pagingString); - setStoredProceduresCurrentPage(activePage ?? 1); + setStoredProcedureCurrentPage(activePage ?? 1); }, - [storedProceduresPaging] + [storedProcedurePaging] ); useEffect(() => { if (activeTab === EntityTabs.STORED_PROCEDURE) { - fetchStoreProceduresDetails(); + fetchStoreProcedureDetails(); } }, [activeTab]); @@ -517,7 +513,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { useEffect(() => { if (viewDatabaseSchemaPermission) { fetchDatabaseSchemaDetails(); - fetchStoreProceduresDetails({ limit: 0 }); + fetchStoreProcedureDetails({ limit: 0 }); getEntityFeedCount(); } }, [viewDatabaseSchemaPermission, databaseSchemaFQN]); @@ -635,20 +631,20 @@ const DatabaseSchemaPage: FunctionComponent = () => { { label: ( ), key: EntityTabs.STORED_PROCEDURE, children: ( - ), }, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx similarity index 71% rename from openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx rename to openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx index 0c5d1d885bf8..b3f6e123c909 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/StoredProceduresPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -47,24 +47,44 @@ import { showErrorToast, showSuccessToast } from 'utils/ToastUtils'; const StoredProcedurePage = () => { const { t } = useTranslation(); - const USERId = getCurrentUserId(); + const USER_ID = getCurrentUserId(); const history = useHistory(); - const { storedProceduresFQN } = useParams<{ storedProceduresFQN: string }>(); + const { storedProcedureFQN } = useParams<{ storedProcedureFQN: string }>(); const { getEntityPermissionByFqn } = usePermissionProvider(); const [isLoading, setIsLoading] = useState(true); - const [storedProcedures, setStoredProcedures] = useState(); - const [storedProceduresPermissions, setStoredProceduresPermissions] = + const [storedProcedure, setStoredProcedure] = useState(); + const [storedProcedurePermissions, setStoredProcedurePermissions] = useState(DEFAULT_ENTITY_PERMISSION); + const { + id: storedProcedureId = '', + followers, + owner, + tags, + version, + } = useMemo(() => { + return { + ...storedProcedure, + tier: getTierTags(storedProcedure?.tags ?? []), + tags: getTagsWithoutTier(storedProcedure?.tags || []), + }; + }, [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, - storedProceduresFQN + storedProcedureFQN ); - setStoredProceduresPermissions(permission); + setStoredProcedurePermissions(permission); } catch (error) { showErrorToast( t('server.fetch-entity-permissions-error', { @@ -80,11 +100,11 @@ const StoredProcedurePage = () => { setIsLoading(true); try { const response = await getStoredProceduresDetailsByFQN( - storedProceduresFQN, + storedProcedureFQN, STORED_PROCEDURE_DEFAULT_FIELDS ); - setStoredProcedures(response); + setStoredProcedure(response); } catch (error) { // Error here } finally { @@ -92,36 +112,12 @@ const StoredProcedurePage = () => { } }; - const { - id: storedProceduresId = '', - followers, - owner, - tags, - version, - } = useMemo(() => { - if (storedProcedures) { - return { - ...storedProcedures, - tier: getTierTags(storedProcedures.tags ?? []), - tags: getTagsWithoutTier(storedProcedures.tags || []), - }; - } - - return {} as StoredProcedure; - }, [storedProcedures]); - - const { isFollowing } = useMemo(() => { - return { - isFollowing: followers?.some(({ id }) => id === USERId), - }; - }, [followers, USERId]); - const versionHandler = useCallback(() => { version && history.push( getVersionPath( EntityType.STORED_PROCEDURE, - storedProceduresFQN, + storedProcedureFQN, version + '' ) ); @@ -129,24 +125,24 @@ const StoredProcedurePage = () => { const saveUpdatedStoredProceduresData = useCallback( (updatedData: StoredProcedure) => { - if (!storedProcedures) { + if (!storedProcedure) { return updatedData; } - const jsonPatch = compare(storedProcedures ?? '', updatedData); + const jsonPatch = compare(storedProcedure ?? '', updatedData); - return patchStoredProceduresDetails(storedProceduresId ?? '', jsonPatch); + return patchStoredProceduresDetails(storedProcedureId ?? '', jsonPatch); }, - [storedProcedures] + [storedProcedure] ); - const handleStoreProceduresUpdate = async ( + const handleStoreProcedureUpdate = async ( updatedData: StoredProcedure, key: keyof StoredProcedure ) => { try { const res = await saveUpdatedStoredProceduresData(updatedData); - setStoredProcedures((previous) => { + setStoredProcedure((previous) => { if (!previous) { return; } @@ -164,43 +160,41 @@ const StoredProcedurePage = () => { [key]: res[key], }; }); - // getEntityFeedCount(); } catch (error) { showErrorToast(error as AxiosError); } }; - const followStoredProcedure = useCallback(async () => { + const followEntity = useCallback(async () => { try { - const res = await addStoredProceduresFollower(storedProceduresId, USERId); + const res = await addStoredProceduresFollower(storedProcedureId, USER_ID); const { newValue } = res.changeDescription.fieldsAdded[0]; const newFollowers = [...(followers ?? []), ...newValue]; - setStoredProcedures((prev) => { + setStoredProcedure((prev) => { if (!prev) { return prev; } return { ...prev, followers: newFollowers }; }); - // getEntityFeedCount(); } catch (error) { showErrorToast( error as AxiosError, t('server.entity-follow-error', { - entity: getEntityName(storedProcedures), + entity: getEntityName(storedProcedure), }) ); } - }, [USERId, storedProceduresId]); + }, [USER_ID, storedProcedureId]); - const unFollowStoredProcedure = useCallback(async () => { + const unFollowEntity = useCallback(async () => { try { const res = await removeStoredProceduresFollower( - storedProceduresId, - USERId + storedProcedureId, + USER_ID ); const { oldValue } = res.changeDescription.fieldsDeleted[0]; - setStoredProcedures((pre) => { + setStoredProcedure((pre) => { if (!pre) { return pre; } @@ -212,38 +206,35 @@ const StoredProcedurePage = () => { ), }; }); - // getEntityFeedCount(); } catch (error) { showErrorToast( error as AxiosError, t('server.entity-unfollow-error', { - entity: getEntityName(storedProcedures), + entity: getEntityName(storedProcedure), }) ); } - }, [USERId, storedProceduresId]); + }, [USER_ID, storedProcedureId]); const handleDisplayNameUpdate = async (data: EntityName) => { - if (!storedProcedures) { + if (!storedProcedure) { return; } - const updatedData = { ...storedProcedures, displayName: data.displayName }; - await handleStoreProceduresUpdate(updatedData, 'displayName'); + const updatedData = { ...storedProcedure, displayName: data.displayName }; + await handleStoreProcedureUpdate(updatedData, 'displayName'); }; const handleFollow = useCallback(async () => { - isFollowing - ? await unFollowStoredProcedure() - : await followStoredProcedure(); - }, [isFollowing, followStoredProcedure, unFollowStoredProcedure]); + isFollowing ? await unFollowEntity() : await followEntity(); + }, [isFollowing, followEntity, unFollowEntity]); const handleUpdateOwner = useCallback( async (newOwner?: StoredProcedure['owner']) => { - if (!storedProcedures) { + if (!storedProcedure) { return; } - const updatedTableDetails = { - ...storedProcedures, + const updatedEntityDetails = { + ...storedProcedure, owner: newOwner ? { ...owner, @@ -251,13 +242,13 @@ const StoredProcedurePage = () => { } : undefined, }; - await handleStoreProceduresUpdate(updatedTableDetails, 'owner'); + await handleStoreProcedureUpdate(updatedEntityDetails, 'owner'); }, - [owner, storedProcedures] + [owner, storedProcedure] ); const handleToggleDelete = () => { - setStoredProcedures((prev) => { + setStoredProcedure((prev) => { if (!prev) { return prev; } @@ -268,10 +259,10 @@ const StoredProcedurePage = () => { const handleRestoreStoredProcedures = async () => { try { - await restoreStoredProcedures(storedProceduresId); + await restoreStoredProcedures(storedProcedureId); showSuccessToast( t('message.restore-entities-success', { - entity: t('label.table'), + entity: t('label.stored-procedure'), }), 2000 ); @@ -280,7 +271,7 @@ const StoredProcedurePage = () => { showErrorToast( error as AxiosError, t('message.restore-entities-error', { - entity: t('label.table'), + entity: t('label.stored-procedure'), }) ); } @@ -288,7 +279,7 @@ const StoredProcedurePage = () => { const onTierUpdate = useCallback( async (newTier?: string) => { - if (storedProcedures) { + if (storedProcedure) { const tierTag: StoredProcedure['tags'] = newTier ? [ ...getTagsWithoutTier(tags ?? []), @@ -300,14 +291,14 @@ const StoredProcedurePage = () => { ] : getTagsWithoutTier(tags ?? []); const updatedDetails = { - ...storedProcedures, + ...storedProcedure, tags: tierTag, }; - await handleStoreProceduresUpdate(updatedDetails, 'tags'); + await handleStoreProcedureUpdate(updatedDetails, 'tags'); } }, - [storedProcedures, tags] + [storedProcedure, tags] ); const afterDeleteAction = useCallback( @@ -317,20 +308,19 @@ const StoredProcedurePage = () => { ); useEffect(() => { - if (storedProceduresFQN) { + if (storedProcedureFQN) { fetchResourcePermission(); } - }, [storedProceduresFQN]); + }, [storedProcedureFQN]); useEffect(() => { if ( - storedProceduresPermissions.ViewAll || - storedProceduresPermissions.ViewBasic + storedProcedurePermissions.ViewAll || + storedProcedurePermissions.ViewBasic ) { fetchStoredProceduresDetails(); - // getEntityFeedCount(); } - }, [storedProceduresFQN, storedProceduresPermissions]); + }, [storedProcedureFQN, storedProcedurePermissions]); if (isLoading) { return ; @@ -338,30 +328,25 @@ const StoredProcedurePage = () => { if ( !( - storedProceduresPermissions.ViewAll || - storedProceduresPermissions.ViewBasic + storedProcedurePermissions.ViewAll || storedProcedurePermissions.ViewBasic ) ) { return ; } - if (!storedProcedures) { - return ; + if (!storedProcedure) { + return ; } return ( - + - {/* Entity Heading */} { - return ( - - {getEntityName(record)} - - ); - }, - className: 'truncate w-max-500', + width: 350, + render: (_, record) => ( + + {getEntityName(record)} + + ), }, { title: t('label.description'), dataIndex: 'description', key: 'description', render: (text: string) => - text?.trim() ? ( - + isEmpty(text) ? ( + + {t('label.no-description')} + ) : ( - {t('label.no-description')} + ), }, ], @@ -70,37 +72,39 @@ const StoredProceduresTab = ({ ); return ( - - -
, - }} - locale={{ - emptyText: , - }} - pagination={false} - rowKey="id" - size="small" + +
, + }} + locale={{ + emptyText: , + }} + pagination={false} + rowKey="id" + size="small" + /> + {paging && paging.total > PAGE_SIZE && ( + - {paging && paging.total > PAGE_SIZE && ( - - )} - - + )} + ); }; -export default StoredProceduresTab; +export default StoredProcedureTab; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts similarity index 96% rename from openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts rename to openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts index 7b19a4e0f9c3..53421bf7a69c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedures/storedProcedures.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/storedProcedure.interface.ts @@ -14,7 +14,7 @@ import { Paging } from 'generated/type/paging'; import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; export interface StoredProcedureTabProps { - data: Array; + data: ServicePageData[]; isLoading: boolean; paging: Paging; currentPage: number; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts index 023efbcde039..46a68396bee9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts @@ -19,6 +19,7 @@ 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'; @@ -31,15 +32,6 @@ const configOptions = { headers: { 'Content-type': 'application/json' }, }; -export type ListDataModelParams = { - service?: string; - fields?: string; - after?: string; - before?: string; - include?: Include; - limit?: number; -}; - export const getStoredProceduresList = async (params?: ListDataModelParams) => { const response = await APIClient.get>(URL, { params, 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/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx index 0b4156cc54de..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,7 +53,7 @@ import { getMlModelPath, getPipelineDetailsPath, getServiceDetailsPath, - getStoredProceduresDetailsPath, + getStoredProcedureDetailsPath, getTableDetailsPath, getTableTabPath, getTagsDetailsPath, @@ -251,7 +251,7 @@ export const getEntityLink = ( return getDataModelDetailsPath(getDecodedFqn(fullyQualifiedName)); case EntityType.STORED_PROCEDURE: - return getStoredProceduresDetailsPath(getDecodedFqn(fullyQualifiedName)); + return getStoredProcedureDetailsPath(getDecodedFqn(fullyQualifiedName)); case EntityType.TEST_CASE: return `${getTableTabPath( From 8030e7b89a561a3816142db8162588e01d8ec0bf Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Thu, 31 Aug 2023 18:06:28 +0530 Subject: [PATCH 3/8] fix sonar --- .../src/main/resources/ui/src/interface/service.interface.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts index ee134c91cb71..f7661acc224d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/service.interface.ts @@ -17,7 +17,6 @@ import { Pipeline, PipelineType, } from 'generated/api/services/ingestionPipelines/createIngestionPipeline'; -import { StoredProcedure } from 'generated/entity/data/storedProcedure'; import { StorageConnection, StorageService, @@ -91,8 +90,8 @@ export type ServicesType = | PipelineService | MlmodelService | MetadataService - | StorageService - | StoredProcedure; + | StorageService; + export interface ServiceResponse { data: Array; paging: Paging; From 8d6b0220caff4d98bb4148f5505b688c0af9e215 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Fri, 1 Sep 2023 12:38:06 +0530 Subject: [PATCH 4/8] optimize stored procedure code and supported deleted flag for it --- .../DatabaseSchemaPage.component.tsx | 63 +++++++++------ .../DatabaseSchemaPage.interface.ts | 23 ++++++ .../StoredProcedure/StoredProcedureTab.tsx | 76 +++++++++++-------- .../storedProcedure.interface.ts | 2 + .../ui/src/rest/storedProceduresAPI.ts | 6 +- 5 files changed, 111 insertions(+), 59 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.interface.ts 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 f464e2626ee8..6203b1af734b 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 @@ -36,12 +36,10 @@ import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum'; import { compare, Operation } from 'fast-json-patch'; import { ThreadType } from 'generated/entity/feed/thread'; import { Include } from 'generated/type/include'; -import { Paging } from 'generated/type/paging'; import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel'; import { isEmpty, isString, isUndefined } from 'lodash'; import { observer } from 'mobx-react'; import { EntityTags, PagingResponse } from 'Models'; -import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; import StoredProcedureTab from 'pages/StoredProcedure/StoredProcedureTab'; import React, { FunctionComponent, @@ -79,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 = () => { @@ -118,19 +117,27 @@ const DatabaseSchemaPage: FunctionComponent = () => { const [currentTablesPage, setCurrentTablesPage] = useState(INITIAL_PAGING_VALUE); - const [storedProcedure, setStoredProcedure] = useState([]); - const [isStoredProcedureLoading, setIsStoredProcedureLoading] = - useState(false); - const [storedProcedurePaging, setStoredProcedurePaging] = - useState(pagingObject); - const [storedProcedureCurrentPage, setStoredProcedureCurrentPage] = - useState(1); + 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 { tags, tier } = useMemo( () => ({ tier: getTierTags(databaseSchema.tags ?? []), @@ -213,22 +220,23 @@ const DatabaseSchemaPage: FunctionComponent = () => { const fetchStoreProcedureDetails = useCallback( async (params?: ListDataModelParams) => { try { - setIsStoredProcedureLoading(true); + setStoredProcedure((prev) => ({ ...prev, isLoading: true })); const { data, paging } = await getStoredProceduresList({ service: getDecodedFqn(databaseSchemaFQN), fields: 'owner,tags,followers', - include: Include.NonDeleted, + include: storedProcedure.deleted + ? Include.Deleted + : Include.NonDeleted, ...params, }); - setStoredProcedure(data); - setStoredProcedurePaging(paging); + setStoredProcedure((prev) => ({ ...prev, data, paging })); } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsStoredProcedureLoading(false); + setStoredProcedure((prev) => ({ ...prev, isLoading: false })); } }, - [databaseSchemaFQN] + [databaseSchemaFQN, storedProcedure.deleted] ); const getSchemaTables = useCallback( @@ -488,23 +496,26 @@ const DatabaseSchemaPage: FunctionComponent = () => { async (cursorType: string | number, activePage?: number) => { const pagingString = { [cursorType]: - storedProcedurePaging[ - cursorType as keyof typeof storedProcedurePaging + storedProcedure.paging[ + cursorType as keyof typeof storedProcedure.paging ], }; await fetchStoreProcedureDetails(pagingString); - setStoredProcedureCurrentPage(activePage ?? 1); + setStoredProcedure((prev) => ({ + ...prev, + currentPage: activePage ?? INITIAL_PAGING_VALUE, + })); }, - [storedProcedurePaging] + [storedProcedure.paging] ); useEffect(() => { if (activeTab === EntityTabs.STORED_PROCEDURE) { fetchStoreProcedureDetails(); } - }, [activeTab]); + }, [activeTab, storedProcedure.deleted]); useEffect(() => { fetchDatabaseSchemaPermission(); @@ -631,7 +642,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { { label: ( { key: EntityTabs.STORED_PROCEDURE, children: ( ), }, 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/StoredProcedure/StoredProcedureTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx index 77098eb278e4..4bb7e6a5a1da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Space, Table, Typography } from 'antd'; +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'; @@ -32,8 +32,10 @@ const StoredProcedureTab = ({ data, isLoading, paging, + showDeletedStoredProcedure, pagingHandler, currentPage, + onShowDeletedStoreProcedureChange, }: StoredProcedureTabProps) => { const { t } = useTranslation(); @@ -72,38 +74,48 @@ const StoredProcedureTab = ({ ); return ( - -
, - }} - locale={{ - emptyText: , - }} - pagination={false} - rowKey="id" - size="small" - /> - {paging && paging.total > PAGE_SIZE && ( - + + - )} - + + {t('label.deleted')} + {' '} + + +
, + }} + locale={{ + emptyText: , + }} + pagination={false} + rowKey="id" + size="small" + /> + + + + {paging && paging.total > PAGE_SIZE && ( + + )} + + ); }; 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 index 53421bf7a69c..3e5d9cce633c 100644 --- 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 @@ -18,5 +18,7 @@ export interface StoredProcedureTabProps { isLoading: boolean; paging: Paging; currentPage: number; + showDeletedStoredProcedure: boolean; pagingHandler: (cursorValue: string | number, activePage?: number) => void; + onShowDeletedStoreProcedureChange: (value: boolean) => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts index 46a68396bee9..53271f5e34b8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts @@ -70,11 +70,13 @@ export const getStoredProceduresByName = async ( export const getStoredProceduresDetailsByFQN = async ( storedProceduresName: string, - arrQueryFields?: string | string[] + arrQueryFields?: string | string[], + include = Include.All ) => { const url = `${getURLWithQueryFields( `${URL}/name/${storedProceduresName}`, - arrQueryFields + arrQueryFields, + `include=${include}` )}`; const response = await APIClient.get(url); From bc92b282288f65b6f6b2f2d8ee4a7c955fb78f78 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Sat, 2 Sep 2023 16:20:09 +0530 Subject: [PATCH 5/8] feat(ui): supported code and activity tab in stored procedure (#13054) * supported code and activity tab in stored procedure * added support for task functionality --- .../resources/ui/src/constants/constants.ts | 26 ++ .../resources/ui/src/enums/entity.enum.ts | 1 + .../ui/src/locale/languages/en-us.json | 1 + .../ui/src/locale/languages/es-es.json | 1 + .../ui/src/locale/languages/fr-fr.json | 1 + .../ui/src/locale/languages/ja-jp.json | 1 + .../ui/src/locale/languages/pt-br.json | 1 + .../ui/src/locale/languages/ru-ru.json | 1 + .../ui/src/locale/languages/zh-cn.json | 1 + .../StoredProcedure/StoredProcedurePage.tsx | 305 +++++++++++++++++- .../pages/TasksPage/TasksPage.interface.ts | 2 + .../resources/ui/src/utils/CommonUtils.tsx | 6 + .../main/resources/ui/src/utils/TasksUtils.ts | 24 ++ 13 files changed, 358 insertions(+), 13 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index 8e1b7abb19da..b88e8796614f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -673,6 +673,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 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 b2e2bb97f744..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 @@ -163,6 +163,7 @@ export enum EntityTabs { 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 ced45e44bbe4..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}}", 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 b9ef8d72a669..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}}", 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 f620197208fe..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", 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 c0ca69819632..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}}", 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 b65f94104bfb..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}}", 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 108844db0726..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}}", 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 314cc3abc000..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}}", 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 index b3f6e123c909..786d82fe11b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -10,8 +10,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Col, Row } from 'antd'; +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 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'; @@ -22,15 +26,30 @@ import { OperationPermission, ResourceEntity, } from 'components/PermissionProvider/PermissionProvider.interface'; -import { getVersionPath } from 'constants/constants'; +import { withActivityFeed } from 'components/router/withActivityFeed'; +import SchemaEditor from 'components/schema-editor/SchemaEditor'; +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 { EntityType } from 'enums/entity.enum'; +import { EntityTabs, EntityType } from 'enums/entity.enum'; import { compare } from 'fast-json-patch'; -import { StoredProcedure } from 'generated/entity/data/storedProcedure'; -import { LabelType, State } from 'generated/type/tagLabel'; +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, @@ -38,7 +57,11 @@ import { removeStoredProceduresFollower, restoreStoredProcedures, } from 'rest/storedProceduresAPI'; -import { getCurrentUserId, sortTagsCaseInsensitive } from 'utils/CommonUtils'; +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'; @@ -49,25 +72,47 @@ const StoredProcedurePage = () => { const { t } = useTranslation(); const USER_ID = getCurrentUserId(); const history = useHistory(); - const { storedProcedureFQN } = useParams<{ storedProcedureFQN: string }>(); + 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]); @@ -96,7 +141,15 @@ const StoredProcedurePage = () => { } }, [getEntityPermissionByFqn]); - const fetchStoredProceduresDetails = async () => { + const getEntityFeedCount = () => { + getFeedCounts( + EntityType.STORED_PROCEDURE, + storedProcedureFQN, + setFeedCount + ); + }; + + const fetchStoredProcedureDetails = async () => { setIsLoading(true); try { const response = await getStoredProceduresDetailsByFQN( @@ -121,7 +174,7 @@ const StoredProcedurePage = () => { version + '' ) ); - }, [version]); + }, [storedProcedureFQN, version]); const saveUpdatedStoredProceduresData = useCallback( (updatedData: StoredProcedure) => { @@ -160,6 +213,8 @@ const StoredProcedurePage = () => { [key]: res[key], }; }); + + getEntityFeedCount(); } catch (error) { showErrorToast(error as AxiosError); } @@ -177,6 +232,7 @@ const StoredProcedurePage = () => { return { ...prev, followers: newFollowers }; }); + getEntityFeedCount(); } catch (error) { showErrorToast( error as AxiosError, @@ -185,7 +241,7 @@ const StoredProcedurePage = () => { }) ); } - }, [USER_ID, storedProcedureId]); + }, [USER_ID, followers, storedProcedure, storedProcedureId]); const unFollowEntity = useCallback(async () => { try { @@ -206,6 +262,7 @@ const StoredProcedurePage = () => { ), }; }); + getEntityFeedCount(); } catch (error) { showErrorToast( error as AxiosError, @@ -226,7 +283,7 @@ const StoredProcedurePage = () => { const handleFollow = useCallback(async () => { isFollowing ? await unFollowEntity() : await followEntity(); - }, [isFollowing, followEntity, unFollowEntity]); + }, [isFollowing]); const handleUpdateOwner = useCallback( async (newOwner?: StoredProcedure['owner']) => { @@ -307,6 +364,200 @@ const StoredProcedurePage = () => { [] ); + 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 tabs = useMemo( + () => [ + { + label: ( + + ), + key: EntityTabs.CODE, + children: ( + + +
+ + + + + +
+ + + + + + + + + + ), + }, + { + label: ( + + ), + key: EntityTabs.ACTIVITY_FEED, + children: ( + + ), + }, + ], + [ + code, + tags, + isEdit, + deleted, + feedCount, + activeTab, + entityFQN, + entityName, + description, + storedProcedure, + storedProcedureFQN, + storedProcedurePermissions, + ] + ); + useEffect(() => { if (storedProcedureFQN) { fetchResourcePermission(); @@ -318,7 +569,8 @@ const StoredProcedurePage = () => { storedProcedurePermissions.ViewAll || storedProcedurePermissions.ViewBasic ) { - fetchStoredProceduresDetails(); + fetchStoredProcedureDetails(); + getEntityFeedCount(); } }, [storedProcedureFQN, storedProcedurePermissions]); @@ -355,9 +607,36 @@ const StoredProcedurePage = () => { onVersionClick={versionHandler} /> + + {/* Entity Tabs */} + + + handleTabChange(activeKey as EntityTabs) + } + /> + + + {threadLink ? ( + + ) : null} ); }; -export default StoredProcedurePage; +export default withActivityFeed(StoredProcedurePage); 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/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx index daffe6c2eb13..c919d33e514a 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/TasksUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts index 473a17da0c5a..79507e620ebf 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; } From 1934b206ee3959992628b56d5b6ab8df224c743e Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Sat, 2 Sep 2023 16:33:18 +0530 Subject: [PATCH 6/8] feat(ui): supported lineage tab in stored procedure (#13060) * supported code and activity tab in stored procedure * added support for task functionality * feat: supported lineage tab in stored procedure * feat(ui): supported custom property tab in stored procedure and setting page (#13063) * feat: supported custom property tab in stored procedure and setting page * added extension field --- .../ui/src/assets/svg/ic-stored-procedure.svg | 19 +++ .../EntityInfoDrawer.component.tsx | 19 +++ .../StoredProcedureSummary.component.tsx | 152 ++++++++++++++++++ .../StoredProcedureSummary.interface.ts | 22 +++ .../components/Explore/explore.interface.ts | 4 +- .../CustomPropertyTable.interface.ts | 4 +- .../ui/src/constants/PageHeaders.constant.ts | 6 + .../resources/ui/src/constants/constants.ts | 1 + .../resources/ui/src/enums/Explore.enum.ts | 1 + .../CustomPropertiesPageV1.tsx | 2 + .../StoredProcedure/StoredProcedurePage.tsx | 46 ++++++ .../ui/src/utils/EntityLineageUtils.tsx | 2 + .../resources/ui/src/utils/EntityUtils.tsx | 97 ++++++++++- .../ui/src/utils/GlobalSettingsUtils.tsx | 7 + .../ui/src/utils/StoredProceduresUtils.tsx | 2 +- 15 files changed, 380 insertions(+), 4 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-stored-procedure.svg create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts 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/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..7a4eb1be6223 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx @@ -0,0 +1,152 @@ +/* + * 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 { ReactComponent as IconExternalLink } from 'assets/svg/external-links.svg'; +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 { + StoredProcedure, + StoredProcedureCodeObject, +} from 'generated/entity/data/storedProcedure'; +import { isEmpty, isObject } from 'lodash'; +import React, { useEffect, useMemo, useState } 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 [storedProcedureDetails, setStoredProcedureDetails] = + useState(entityDetails); + + const entityInfo = useMemo( + () => + getEntityOverview( + ExplorePageTabs.STORED_PROCEDURE, + storedProcedureDetails + ), + [storedProcedureDetails] + ); + + useEffect(() => { + if (!isEmpty(entityDetails)) { + setStoredProcedureDetails(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.isExternal ? ( + + ) : null} + + ) : ( + + {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..6cd0f8002180 --- /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/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/constants/PageHeaders.constant.ts b/openmetadata-ui/src/main/resources/ui/src/constants/PageHeaders.constant.ts index 659605505195..18d77374775a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/PageHeaders.constant.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/PageHeaders.constant.ts @@ -118,6 +118,12 @@ export const PAGE_HEADERS = { entity: i18n.t('label.container-plural'), }), }, + STORED_PROCEDURE_CUSTOM_ATTRIBUTES: { + header: i18n.t('label.stored-procedure'), + subHeader: i18n.t('message.define-custom-property-for-entity', { + entity: i18n.t('label.label.stored-procedure'), + }), + }, BOTS: { header: i18n.t('label.bot-plural'), subHeader: i18n.t('message.page-sub-header-for-bots'), diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index b88e8796614f..a2de8f9139af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -823,6 +823,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/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx index 4148a8157c47..74ac6be5d11a 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 @@ -142,6 +142,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/StoredProcedure/StoredProcedurePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx index 786d82fe11b6..f80a4c90fa23 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -15,10 +15,13 @@ 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'; @@ -28,6 +31,7 @@ import { } 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'; @@ -435,6 +439,10 @@ const StoredProcedurePage = () => { setThreadLink(''); }; + const onExtensionUpdate = async (updatedData: StoredProcedure) => { + await handleStoreProcedureUpdate(updatedData, 'extension'); + }; + const tabs = useMemo( () => [ { @@ -541,6 +549,44 @@ const StoredProcedurePage = () => { /> ), }, + { + label: , + key: EntityTabs.LINEAGE, + children: ( + + ), + }, + { + label: ( + + ), + key: EntityTabs.CUSTOM_PROPERTIES, + children: ( + + ), + }, ], [ code, 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 index 71eef7f30474..5e79cac2022c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx @@ -12,4 +12,4 @@ */ import { TabSpecificField } from 'enums/entity.enum'; -export const STORED_PROCEDURE_DEFAULT_FIELDS = `${TabSpecificField.OWNER}, ${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}`; +export const STORED_PROCEDURE_DEFAULT_FIELDS = `${TabSpecificField.OWNER}, ${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.EXTENSION}`; From 4f3a8d8213edf12a85d5987e8ab48da9b92bdb2c Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Mon, 4 Sep 2023 12:18:30 +0530 Subject: [PATCH 7/8] chanegs as per comments --- .../DatabaseSchemaPage.component.tsx | 13 ++----------- .../pages/StoredProcedure/StoredProcedureTab.tsx | 16 +++++++++------- .../StoredProcedure/storedProcedure.interface.ts | 10 +++------- 3 files changed, 14 insertions(+), 25 deletions(-) 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 facd418a88bc..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 @@ -521,12 +521,6 @@ const DatabaseSchemaPage: FunctionComponent = () => { [storedProcedure.paging] ); - useEffect(() => { - if (activeTab === EntityTabs.STORED_PROCEDURE) { - fetchStoreProcedureDetails(); - } - }, [activeTab, storedProcedure.deleted]); - useEffect(() => { fetchDatabaseSchemaPermission(); }, [databaseSchemaFQN]); @@ -660,12 +654,9 @@ const DatabaseSchemaPage: FunctionComponent = () => { key: EntityTabs.STORED_PROCEDURE, children: ( ), 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 index 4bb7e6a5a1da..92710f4cc3c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx @@ -20,7 +20,7 @@ import { PAGE_SIZE } from 'constants/constants'; import { EntityType } from 'enums/entity.enum'; import { isEmpty } from 'lodash'; import { ServicePageData } from 'pages/ServiceDetailsPage/ServiceDetailsPage'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { getEntityName } from 'utils/EntityUtils'; @@ -29,15 +29,13 @@ import { getEntityLink } from 'utils/TableUtils'; import { StoredProcedureTabProps } from './storedProcedure.interface'; const StoredProcedureTab = ({ - data, - isLoading, - paging, - showDeletedStoredProcedure, + storedProcedure, pagingHandler, - currentPage, + fetchStoredProcedure, onShowDeletedStoreProcedureChange, }: StoredProcedureTabProps) => { const { t } = useTranslation(); + const { data, isLoading, deleted, paging, currentPage } = storedProcedure; const tableColumn: ColumnsType = useMemo( () => [ @@ -73,11 +71,15 @@ const StoredProcedureTab = ({ [] ); + useEffect(() => { + fetchStoredProcedure(); + }, [deleted]); + return ( 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 index 3e5d9cce633c..b0758f16b019 100644 --- 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 @@ -10,15 +10,11 @@ * 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'; +import { StoredProcedureData } from 'pages/DatabaseSchemaPage/DatabaseSchemaPage.interface'; export interface StoredProcedureTabProps { - data: ServicePageData[]; - isLoading: boolean; - paging: Paging; - currentPage: number; - showDeletedStoredProcedure: boolean; + storedProcedure: StoredProcedureData; + fetchStoredProcedure: () => void; pagingHandler: (cursorValue: string | number, activePage?: number) => void; onShowDeletedStoreProcedureChange: (value: boolean) => void; } From 8c86dac3537ef1c9a4cd0c8dc3a20414d70fee44 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Mon, 4 Sep 2023 14:09:43 +0530 Subject: [PATCH 8/8] chanegs as per comments --- .../StoredProcedureSummary.component.tsx | 36 +++++-------------- .../StoredProcedureSummary.interface.ts | 2 +- .../StoredProcedure/StoredProcedurePage.tsx | 2 +- 3 files changed, 10 insertions(+), 30 deletions(-) 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 index 7a4eb1be6223..c24fe1456422 100644 --- 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 @@ -12,19 +12,15 @@ */ import { Col, Divider, Row, Typography } from 'antd'; -import { ReactComponent as IconExternalLink } from 'assets/svg/external-links.svg'; 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 { - StoredProcedure, - StoredProcedureCodeObject, -} from 'generated/entity/data/storedProcedure'; -import { isEmpty, isObject } from 'lodash'; -import React, { useEffect, useMemo, useState } from 'react'; +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 { @@ -40,27 +36,14 @@ const StoredProcedureSummary = ({ isLoading, }: StoredProcedureSummaryProps) => { const { t } = useTranslation(); - const [storedProcedureDetails, setStoredProcedureDetails] = - useState(entityDetails); const entityInfo = useMemo( - () => - getEntityOverview( - ExplorePageTabs.STORED_PROCEDURE, - storedProcedureDetails - ), - [storedProcedureDetails] + () => getEntityOverview(ExplorePageTabs.STORED_PROCEDURE, entityDetails), + [entityDetails] ); - useEffect(() => { - if (!isEmpty(entityDetails)) { - setStoredProcedureDetails(entityDetails); - } - }, [entityDetails]); - return ( - + <> @@ -86,12 +69,9 @@ const StoredProcedureSummary = ({ {info.isLink ? ( {info.value} - {info.isExternal ? ( - - ) : null} ) : ( 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 index 6cd0f8002180..f5ae6e8905a2 100644 --- 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 @@ -18,5 +18,5 @@ export interface StoredProcedureSummaryProps { entityDetails: StoredProcedure; componentType?: string; tags?: TagLabel[]; - isLoading?: boolean; + isLoading: boolean; } 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 index f80a4c90fa23..e7bed79f15de 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -111,7 +111,7 @@ const StoredProcedurePage = () => { return { ...storedProcedure, tier: getTierTags(storedProcedure?.tags ?? []), - tags: getTagsWithoutTier(storedProcedure?.tags || []), + tags: getTagsWithoutTier(storedProcedure?.tags ?? []), entityName: getEntityName(storedProcedure), entityFQN: storedProcedure?.fullyQualifiedName ?? '', code: