diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/dbtModelAPI.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/dbtModelAPI.ts new file mode 100644 index 00000000000..571c4bfa7fa --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/dbtModelAPI.ts @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { Dbtmodel } from '../generated/entity/data/dbtmodel'; +import { getURLWithQueryFields } from '../utils/APIUtils'; +import APIClient from './index'; + +export const getDBTModelDetails: Function = ( + id: string, + arrQueryFields: string +): Promise => { + const url = getURLWithQueryFields(`/dbtmodels/${id}`, arrQueryFields); + + return APIClient.get(url); +}; + +export const getDBTModelDetailsByFQN: Function = ( + fqn: string, + arrQueryFields: string +): Promise => { + const url = getURLWithQueryFields(`/dbtmodels/name/${fqn}`, arrQueryFields); + + return APIClient.get(url); +}; + +export const getDatabaseDBTModels: Function = ( + databaseName: string, + paging: string, + arrQueryFields?: string +): Promise => { + const url = `${getURLWithQueryFields( + `/tables`, + arrQueryFields + )}&database=${databaseName}${paging ? paging : ''}`; + + return APIClient.get(url); +}; + +export const patchDBTModelDetails: Function = ( + id: string, + data: Dbtmodel +): Promise => { + const configOptions = { + headers: { 'Content-type': 'application/json-patch+json' }, + }; + + return APIClient.patch(`/dbtmodels/${id}`, data, configOptions); +}; + +export const addFollower: Function = ( + dbtModelId: string, + userId: string +): Promise => { + const configOptions = { + headers: { 'Content-type': 'application/json' }, + }; + + return APIClient.put( + `/dbtmodels/${dbtModelId}/followers`, + userId, + configOptions + ); +}; + +export const removeFollower: Function = ( + dbtModelId: string, + userId: string +): Promise => { + const configOptions = { + headers: { 'Content-type': 'application/json' }, + }; + + return APIClient.delete( + `/dbtmodels/${dbtModelId}/followers/${userId}`, + configOptions + ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.component.tsx new file mode 100644 index 00000000000..5564d62f96d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.component.tsx @@ -0,0 +1,284 @@ +import { isEqual } from 'lodash'; +import { EntityTags } from 'Models'; +import React, { useEffect, useState } from 'react'; +import { getTeamDetailsPath } from '../../constants/constants'; +import { CSMode } from '../../enums/codemirror.enum'; +import { Dbtmodel } from '../../generated/entity/data/dbtmodel'; +import { User } from '../../generated/entity/teams/user'; +import { LabelType, State } from '../../generated/type/tagLabel'; +import { useAuth } from '../../hooks/authHooks'; +import { + getCurrentUserId, + getPartialNameFromFQN, + getUserTeams, +} from '../../utils/CommonUtils'; +import { getTagsWithoutTier } from '../../utils/TableUtils'; +import Description from '../common/description/Description'; +import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo'; +import TabsPane from '../common/TabsPane/TabsPane'; +import PageContainer from '../containers/PageContainer'; +import ManageTab from '../ManageTab/ManageTab.component'; +import SchemaEditor from '../schema-editor/SchemaEditor'; +import SchemaTab from '../SchemaTab/SchemaTab.component'; +import { DBTModelDetailsProps } from './DBTModelDetails.interface'; + +const DBTModelDetails: React.FC = ({ + dbtModelDetails, + entityName, + dbtModelFQN, + activeTab, + setActiveTabHandler, + owner, + description, + columns, + followDBTModelHandler, + unfollowDBTModelHandler, + followers, + dbtModelTags, + slashedDBTModelName, + descriptionUpdateHandler, + columnsUpdateHandler, + settingsUpdateHandler, + users, + version, + viewDefinition = '', + tier, +}: DBTModelDetailsProps) => { + const { isAuthDisabled } = useAuth(); + const [isEdit, setIsEdit] = useState(false); + const [followersCount, setFollowersCount] = useState(0); + const [isFollowing, setIsFollowing] = useState(false); + + const hasEditAccess = () => { + if (owner?.type === 'user') { + return owner.id === getCurrentUserId(); + } else { + return getUserTeams().some((team) => team.id === owner?.id); + } + }; + const setFollowersData = (followers: Array) => { + setIsFollowing( + followers.some(({ id }: { id: string }) => id === getCurrentUserId()) + ); + setFollowersCount(followers?.length); + }; + const tabs = [ + { + name: 'Schema', + icon: { + alt: 'schema', + name: 'icon-schema', + title: 'Schema', + }, + isProtected: false, + position: 1, + }, + { + name: 'View Definition', + icon: { + alt: 'view_definition', + name: 'icon-profiler', + title: 'View Definition', + }, + isProtected: false, + position: 2, + }, + { + name: 'Manage', + icon: { + alt: 'manage', + name: 'icon-manage', + title: 'Manage', + }, + isProtected: true, + protectedState: !owner || hasEditAccess(), + position: 3, + }, + ]; + + const extraInfo: Array<{ + key?: string; + value: string | number | React.ReactNode; + isLink?: boolean; + placeholderText?: string; + openInNewTab?: boolean; + }> = [ + { + key: 'Owner', + value: + owner?.type === 'team' + ? getTeamDetailsPath(owner?.name || '') + : owner?.name || '', + placeholderText: owner?.displayName || '', + isLink: owner?.type === 'team', + openInNewTab: false, + }, + { key: 'Tier', value: tier ? tier.split('.')[1] : '' }, + ]; + + const onDescriptionEdit = (): void => { + setIsEdit(true); + }; + const onCancel = () => { + setIsEdit(false); + }; + + const onDescriptionUpdate = (updatedHTML: string) => { + if (description !== updatedHTML) { + const updatedDBTModelDetails = { + ...dbtModelDetails, + description: updatedHTML, + }; + descriptionUpdateHandler(updatedDBTModelDetails); + setIsEdit(false); + } else { + setIsEdit(false); + } + }; + + const onColumnsUpdate = (updateColumns: Dbtmodel['columns']) => { + if (!isEqual(columns, updateColumns)) { + const updatedDBTModelDetails = { + ...dbtModelDetails, + columns: updateColumns, + }; + columnsUpdateHandler(updatedDBTModelDetails); + } + }; + + const onSettingsUpdate = (newOwner?: Dbtmodel['owner'], newTier?: string) => { + if (newOwner || newTier) { + const tierTag: Dbtmodel['tags'] = newTier + ? [ + ...getTagsWithoutTier(dbtModelDetails.tags as Array), + { + tagFQN: newTier, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + ] + : dbtModelDetails.tags; + const updatedDBTModelDetails = { + ...dbtModelDetails, + owner: newOwner + ? { + ...dbtModelDetails.owner, + ...newOwner, + } + : dbtModelDetails.owner, + tags: tierTag, + }; + + return settingsUpdateHandler(updatedDBTModelDetails); + } else { + return Promise.reject(); + } + }; + + const followDBTModel = () => { + if (isFollowing) { + setFollowersCount((preValu) => preValu - 1); + setIsFollowing(false); + unfollowDBTModelHandler(); + } else { + setFollowersCount((preValu) => preValu + 1); + setIsFollowing(true); + followDBTModelHandler(); + } + }; + + useEffect(() => { + if (isAuthDisabled && users.length && followers.length) { + setFollowersData(followers); + } + }, [users, followers]); + + useEffect(() => { + setFollowersData(followers); + }, [followers]); + + return ( + +
+ { + return; + }} + /> + +
+ + +
+ {activeTab === 1 && ( +
+
+ +
+
+ +
+
+ )} + {activeTab === 2 && ( +
+ +
+ )} + {activeTab === 3 && ( +
+ +
+ )} +
+
+
+
+ ); +}; + +export default DBTModelDetails; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.interface.ts new file mode 100644 index 00000000000..23e8f5461a2 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DBTModelDetails/DBTModelDetails.interface.ts @@ -0,0 +1,32 @@ +import { EntityTags } from 'Models'; +import { Dbtmodel } from '../../generated/entity/data/dbtmodel'; +import { EntityReference } from '../../generated/entity/data/table'; +import { User } from '../../generated/entity/teams/user'; +import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface'; + +export interface DatasetOwner extends EntityReference { + displayName?: string; +} + +export interface DBTModelDetailsProps { + version?: string; + users: Array; + dbtModelDetails: Dbtmodel; + dbtModelFQN: string; + entityName: string; + activeTab: number; + owner: DatasetOwner; + description: string; + tier: string; + columns: Dbtmodel['columns']; + followers: Array; + dbtModelTags: Array; + slashedDBTModelName: TitleBreadcrumbProps['titleLinks']; + viewDefinition: Dbtmodel['viewDefinition']; + setActiveTabHandler: (value: number) => void; + followDBTModelHandler: () => void; + unfollowDBTModelHandler: () => void; + settingsUpdateHandler: (updatedDBTModel: Dbtmodel) => Promise; + columnsUpdateHandler: (updatedDBTModel: Dbtmodel) => void; + descriptionUpdateHandler: (updatedDBTModel: Dbtmodel) => void; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx index 14bbffaf891..aa2b9832062 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx @@ -73,6 +73,7 @@ const Explore: React.FC = ({ updateTableCount, updateTopicCount, updateDashboardCount, + updateDbtModelCount, updatePipelineCount, }: ExploreProps) => { const location = useLocation(); @@ -197,6 +198,10 @@ const Explore: React.FC = ({ case SearchIndex.PIPELINE: updatePipelineCount(count); + break; + case SearchIndex.DBT_MODEL: + updateDbtModelCount(count); + break; default: break; @@ -339,6 +344,8 @@ const Explore: React.FC = ({ return getCountBadge(tabCounts.dashboard); case SearchIndex.PIPELINE: return getCountBadge(tabCounts.pipeline); + case SearchIndex.DBT_MODEL: + return getCountBadge(tabCounts.dbtModel); default: return getCountBadge(); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.test.tsx index 3237901c38c..c1817cae06b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.test.tsx @@ -68,8 +68,10 @@ describe('Test Explore component', () => { topic: 2, dashboard: 8, pipeline: 5, + dbtModel: 7, }} updateDashboardCount={mockFunction} + updateDbtModelCount={mockFunction} updatePipelineCount={mockFunction} updateTableCount={mockFunction} updateTopicCount={mockFunction} 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 2c6b008e280..662ffd3e26b 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 @@ -35,6 +35,7 @@ export interface ExploreProps { topic: number; dashboard: number; pipeline: number; + dbtModel: number; }; searchText: string; sortValue: string; @@ -48,6 +49,7 @@ export interface ExploreProps { updateTopicCount: (count: number) => void; updateDashboardCount: (count: number) => void; updatePipelineCount: (count: number) => void; + updateDbtModelCount: (count: number) => void; fetchData: (value: SearchDataFunctionType[]) => void; searchResult: ExploreSearchData | undefined; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx index e0ccca0d482..fa9ee3a1a3f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import { isUndefined, lowerCase } from 'lodash'; +import { isNil, isUndefined, lowerCase } from 'lodash'; import { DatasetSchemaTableTab } from 'Models'; import React, { FunctionComponent, useEffect, useState } from 'react'; import { useHistory, useParams } from 'react-router'; @@ -121,7 +121,7 @@ const SchemaTab: FunctionComponent = ({ /> )} - {!isReadOnly ? ( + {!isReadOnly && !isNil(sampleData) ? (
{ + const history = useHistory(); + const USERId = getCurrentUserId(); + const { dbtModelFQN: dbtModelFQN, tab } = useParams() as Record< + string, + string + >; + + const [isLoading, setIsLoading] = useState(true); + const [activeTab, setActiveTab] = useState( + getCurrentDBTModelTab(tab) + ); + const [dbtModelDetails, setDbtModelDetails] = useState( + {} as Dbtmodel + ); + const [, setCurrentVersion] = useState(); + + const [dbtModelId, setDbtModelId] = useState(''); + const [tier, setTier] = useState(); + const [name, setName] = useState(''); + const [followers, setFollowers] = useState>([]); + const [slashedDBTModelName, setSlashedDBTModelName] = useState< + TitleBreadcrumbProps['titleLinks'] + >([]); + const [description, setDescription] = useState(''); + const [columns, setColumns] = useState([]); + const [dbtModelTags, setDBTModelTags] = useState>([]); + const [dbtViewDefinition, setDbtViewDefinition] = + useState(''); + const [owner, setOwner] = useState< + Dbtmodel['owner'] & { displayName?: string } + >(); + + const activeTabHandler = (tabValue: number) => { + const currentTabIndex = tabValue - 1; + if (dbtModelTabs[currentTabIndex].path !== tab) { + setActiveTab(getCurrentDBTModelTab(dbtModelTabs[currentTabIndex].path)); + history.push({ + pathname: getDBTModelDetailsPath( + dbtModelFQN, + dbtModelTabs[currentTabIndex].path + ), + }); + } + }; + + const saveUpdatedDBTModelData = ( + updatedData: Dbtmodel + ): Promise => { + const jsonPatch = compare(dbtModelDetails, updatedData); + + return patchDBTModelDetails( + dbtModelId, + jsonPatch + ) as unknown as Promise; + }; + + const descriptionUpdateHandler = (updatedDBTModel: Dbtmodel) => { + saveUpdatedDBTModelData(updatedDBTModel).then((res: AxiosResponse) => { + const { description, version } = res.data; + setCurrentVersion(version); + setDbtModelDetails(res.data); + setDescription(description); + }); + }; + + const columnsUpdateHandler = (updatedDBTModel: Dbtmodel) => { + saveUpdatedDBTModelData(updatedDBTModel).then((res: AxiosResponse) => { + const { columns, version } = res.data; + setCurrentVersion(version); + setDbtModelDetails(res.data); + setColumns(columns); + setDBTModelTags(getTableTags(columns || [])); + }); + }; + + const settingsUpdateHandler = (updatedDBTModel: Dbtmodel): Promise => { + return new Promise((resolve, reject) => { + saveUpdatedDBTModelData(updatedDBTModel) + .then((res) => { + const { version, owner, tags } = res.data; + setCurrentVersion(version); + setDbtModelDetails(res.data); + setOwner(getOwnerFromId(owner?.id)); + setTier(getTierFromTableTags(tags)); + resolve(); + }) + .catch(() => reject()); + }); + }; + + const followDBTModel = () => { + addFollower(dbtModelId, USERId).then((res: AxiosResponse) => { + const { newValue } = res.data.changeDescription.fieldsAdded[0]; + + setFollowers([...followers, ...newValue]); + }); + }; + const unfollowDBTModel = () => { + removeFollower(dbtModelId, USERId).then((res: AxiosResponse) => { + const { oldValue } = res.data.changeDescription.fieldsDeleted[0]; + + setFollowers( + followers.filter((follower) => follower.id !== oldValue[0].id) + ); + }); + }; + + useEffect(() => { + if (dbtModelTabs[activeTab - 1].path !== tab) { + setActiveTab(getCurrentDBTModelTab(tab)); + } + }, [tab]); + + useEffect(() => { + setIsLoading(true); + getDBTModelDetailsByFQN( + dbtModelFQN, + 'columns,owner,database,tags,followers,viewDefinition' + ) + .then((res: AxiosResponse) => { + const { + description, + id, + name, + columns, + database, + owner, + followers, + fullyQualifiedName, + version, + viewDefinition, + tags, + } = res.data; + setDbtModelDetails(res.data); + setDbtModelId(id); + setCurrentVersion(version); + setOwner(getOwnerFromId(owner?.id)); + setTier(getTierFromTableTags(tags)); + setFollowers(followers); + getDatabase(database.id, 'service').then((resDB: AxiosResponse) => { + getServiceById('databaseServices', resDB.data.service?.id).then( + (resService: AxiosResponse) => { + setSlashedDBTModelName([ + { + name: resService.data.name, + url: resService.data.name + ? getServiceDetailsPath( + resService.data.name, + resService.data.serviceType, + ServiceCategory.DATABASE_SERVICES + ) + : '', + imgSrc: resService.data.serviceType + ? serviceTypeLogo(resService.data.serviceType) + : undefined, + }, + { + name: resDB.data.name, + url: getDatabaseDetailsPath(resDB.data.fullyQualifiedName), + }, + { + name: name, + url: '', + activeTitle: true, + }, + ]); + + addToRecentViewed({ + entityType: EntityType.DBT_MODEL, + fqn: fullyQualifiedName, + serviceType: resService.data.serviceType, + timestamp: 0, + }); + } + ); + }); + setName(name); + + setDescription(description); + setColumns(columns || []); + setDBTModelTags(getTableTags(columns || [])); + setDbtViewDefinition(viewDefinition); + }) + .finally(() => { + setIsLoading(false); + }); + + setActiveTab(getCurrentDBTModelTab(tab)); + }, [dbtModelFQN]); + + return ( + <> + {isLoading ? ( + + ) : ( + + )} + + ); +}; + +export default observer(DBTModelDetailsPage); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx index 4c9e102cd10..0cd3a8c4cfe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx @@ -40,8 +40,8 @@ import { ZERO_SIZE, } from '../../constants/explore.constants'; import { SearchIndex } from '../../enums/search.enum'; +import { getTotalEntityCountByType } from '../../utils/EntityUtils'; import { getFilterString } from '../../utils/FilterUtils'; -import { getTotalEntityCountByService } from '../../utils/ServiceUtils'; const ExplorePage: FunctionComponent = () => { const initialFilter = getFilterString( @@ -58,6 +58,7 @@ const ExplorePage: FunctionComponent = () => { const [topicCount, setTopicCount] = useState(0); const [dashboardCount, setDashboardCount] = useState(0); const [pipelineCount, setPipelineCount] = useState(0); + const [dbtModelCount, setDbtModelCount] = useState(0); const [searchResult, setSearchResult] = useState(); const [initialSortField] = useState( searchQuery @@ -85,6 +86,10 @@ const ExplorePage: FunctionComponent = () => { setPipelineCount(count); }; + const handleDbtModelCount = (count: number) => { + setDbtModelCount(count); + }; + const handlePathChange = (path: string) => { AppState.explorePageTab = path; }; @@ -95,6 +100,7 @@ const ExplorePage: FunctionComponent = () => { SearchIndex.TOPIC, SearchIndex.DASHBOARD, SearchIndex.PIPELINE, + SearchIndex.DBT_MODEL, ]; const entityCounts = entities.map((entity) => @@ -116,35 +122,44 @@ const ExplorePage: FunctionComponent = () => { topic, dashboard, pipeline, + dbtModel, ]: PromiseSettledResult[]) => { setTableCount( table.status === 'fulfilled' - ? getTotalEntityCountByService( - table.value.data.aggregations?.['sterms#Service'] + ? getTotalEntityCountByType( + table.value.data.aggregations?.['sterms#EntityType'] ?.buckets as Bucket[] ) : 0 ); setTopicCount( topic.status === 'fulfilled' - ? getTotalEntityCountByService( - topic.value.data.aggregations?.['sterms#Service'] + ? getTotalEntityCountByType( + topic.value.data.aggregations?.['sterms#EntityType'] ?.buckets as Bucket[] ) : 0 ); setDashboardCount( dashboard.status === 'fulfilled' - ? getTotalEntityCountByService( - dashboard.value.data.aggregations?.['sterms#Service'] + ? getTotalEntityCountByType( + dashboard.value.data.aggregations?.['sterms#EntityType'] ?.buckets as Bucket[] ) : 0 ); setPipelineCount( pipeline.status === 'fulfilled' - ? getTotalEntityCountByService( - pipeline.value.data.aggregations?.['sterms#Service'] + ? getTotalEntityCountByType( + pipeline.value.data.aggregations?.['sterms#EntityType'] + ?.buckets as Bucket[] + ) + : 0 + ); + setDbtModelCount( + dbtModel.status === 'fulfilled' + ? getTotalEntityCountByType( + dbtModel.value.data.aggregations?.['sterms#EntityType'] ?.buckets as Bucket[] ) : 0 @@ -258,8 +273,10 @@ const ExplorePage: FunctionComponent = () => { topic: topicCount, dashboard: dashboardCount, pipeline: pipelineCount, + dbtModel: dbtModelCount, }} updateDashboardCount={handleDashboardCount} + updateDbtModelCount={handleDbtModelCount} updatePipelineCount={handlePipelineCount} updateTableCount={handleTableCount} updateTopicCount={handleTopicCount} diff --git a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx index fca37797f1e..d3a0bcc5103 100644 --- a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx @@ -24,6 +24,7 @@ import { ROUTES } from '../constants/constants'; import DashboardDetailsPage from '../pages/DashboardDetailsPage/DashboardDetailsPage.component'; import DatabaseDetails from '../pages/database-details/index'; import DatasetDetailsPage from '../pages/DatasetDetailsPage/DatasetDetailsPage.component'; +import DBTModelDetailsPage from '../pages/DBTModelDetailsPage/DBTModelDetailsPage.component'; import EntityVersionPage from '../pages/EntityVersionPage/EntityVersionPage.component'; import ExplorePage from '../pages/explore/ExplorePage.component'; import IngestionPage from '../pages/IngestionPage/IngestionPage.component'; @@ -105,6 +106,16 @@ const AuthenticatedAppRouter: FunctionComponent = () => { component={PipelineDetailsPage} path={ROUTES.PIPELINE_DETAILS_WITH_TAB} /> + + .CodeMirror { + height: 100%; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.js b/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.js index c673819ccd4..42296ad25d1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.js +++ b/openmetadata-ui/src/main/resources/ui/src/utils/APIUtils.js @@ -16,7 +16,8 @@ export const formatDataResponse = (hits) => { hit._source.table_name || hit._source.topic_name || hit._source.dashboard_name || - hit._source.pipeline_name; + hit._source.pipeline_name || + hit._source.dbt_model_name; newData.description = hit._source.description; newData.fullyQualifiedName = hit._source.fqdn; newData.tableType = hit._source.table_type; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DBTModelDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DBTModelDetailsUtils.ts new file mode 100644 index 00000000000..37d78d1f508 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DBTModelDetailsUtils.ts @@ -0,0 +1,36 @@ +export const dbtModelTabs = [ + { + name: 'Schema', + path: 'schema', + }, + { + name: 'View Definition', + path: 'view_definition', + }, + { + name: 'Manage', + path: 'manage', + }, +]; + +export const getCurrentDBTModelTab = (tab: string): number => { + let currentTab; + switch (tab) { + case 'view_definition': + currentTab = 2; + + break; + case 'manage': + currentTab = 3; + + break; + + case 'schema': + default: + currentTab = 1; + + break; + } + + return currentTab; +}; 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 0e7b83a31cb..c7eddaee709 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -247,6 +247,15 @@ export const getEntityCountByType = (buckets: Array) => { return entityCounts; }; +export const getTotalEntityCountByType = (buckets: Array = []) => { + let entityCounts = 0; + buckets.forEach((bucket) => { + entityCounts += bucket.doc_count; + }); + + return entityCounts; +}; + export const getEntityLineage = ( oldVal: EntityLineage, newVal: EntityLineage, 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 977ad2da0c4..0f5a8157741 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx @@ -5,6 +5,7 @@ import PopOver from '../components/common/popover/PopOver'; import { getDashboardDetailsPath, getDatasetDetailsPath, + getDBTModelDetailsPath, getPipelineDetailsPath, getTopicDetailsPath, } from '../constants/constants'; @@ -176,6 +177,10 @@ export const getEntityLink = ( case EntityType.PIPELINE: return getPipelineDetailsPath(fullyQualifiedName); + case SearchIndex.DBT_MODEL: + case EntityType.DBT_MODEL: + return getDBTModelDetailsPath(fullyQualifiedName); + case SearchIndex.TABLE: case EntityType.TABLE: default: