mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-02 19:48:17 +00:00
Feat: Added DBT Model Details page and Explore page tab (#1394)
* Feat: Added DBT Model tab to Explore page * Feat: Added DBTModel Details page * Changed dataset to dbtModel * Added support to add tier tag * Adding suggestions support for DBT Models. * Added ViewDefinition Tab UI Co-authored-by: Sachin-chaurasiya <sachinchaurasiyachotey87@gmail.com>
This commit is contained in:
parent
fdd6ca14b7
commit
dc1576cd91
@ -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<AxiosResponse> => {
|
||||
const url = getURLWithQueryFields(`/dbtmodels/${id}`, arrQueryFields);
|
||||
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getDBTModelDetailsByFQN: Function = (
|
||||
fqn: string,
|
||||
arrQueryFields: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const url = getURLWithQueryFields(`/dbtmodels/name/${fqn}`, arrQueryFields);
|
||||
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getDatabaseDBTModels: Function = (
|
||||
databaseName: string,
|
||||
paging: string,
|
||||
arrQueryFields?: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const url = `${getURLWithQueryFields(
|
||||
`/tables`,
|
||||
arrQueryFields
|
||||
)}&database=${databaseName}${paging ? paging : ''}`;
|
||||
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const patchDBTModelDetails: Function = (
|
||||
id: string,
|
||||
data: Dbtmodel
|
||||
): Promise<AxiosResponse> => {
|
||||
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<AxiosResponse> => {
|
||||
const configOptions = {
|
||||
headers: { 'Content-type': 'application/json' },
|
||||
};
|
||||
|
||||
return APIClient.put(
|
||||
`/dbtmodels/${dbtModelId}/followers`,
|
||||
userId,
|
||||
configOptions
|
||||
);
|
||||
};
|
||||
|
||||
export const removeFollower: Function = (
|
||||
dbtModelId: string,
|
||||
userId: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const configOptions = {
|
||||
headers: { 'Content-type': 'application/json' },
|
||||
};
|
||||
|
||||
return APIClient.delete(
|
||||
`/dbtmodels/${dbtModelId}/followers/${userId}`,
|
||||
configOptions
|
||||
);
|
||||
};
|
||||
@ -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<DBTModelDetailsProps> = ({
|
||||
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<User>) => {
|
||||
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<EntityTags>),
|
||||
{
|
||||
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 (
|
||||
<PageContainer>
|
||||
<div className="tw-px-4 tw-w-full tw-h-full tw-flex tw-flex-col">
|
||||
<EntityPageInfo
|
||||
entityName={entityName}
|
||||
extraInfo={extraInfo}
|
||||
followers={followersCount}
|
||||
followersList={followers}
|
||||
followHandler={followDBTModel}
|
||||
isFollowing={isFollowing}
|
||||
tags={dbtModelTags}
|
||||
tier={tier}
|
||||
titleLinks={slashedDBTModelName}
|
||||
version={version}
|
||||
versionHandler={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="tw-mt-1 tw-flex tw-flex-col tw-flex-grow">
|
||||
<TabsPane
|
||||
activeTab={activeTab}
|
||||
className="tw-flex-initial"
|
||||
setActiveTab={setActiveTabHandler}
|
||||
tabs={tabs}
|
||||
/>
|
||||
|
||||
<div className="tw-bg-white tw-flex-grow">
|
||||
{activeTab === 1 && (
|
||||
<div className="tw-grid tw-grid-cols-4 tw-gap-4 tw-w-full tw-mt-4 ">
|
||||
<div className="tw-col-span-4">
|
||||
<Description
|
||||
description={description}
|
||||
entityName={entityName}
|
||||
hasEditAccess={hasEditAccess()}
|
||||
isEdit={isEdit}
|
||||
owner={owner}
|
||||
onCancel={onCancel}
|
||||
onDescriptionEdit={onDescriptionEdit}
|
||||
onDescriptionUpdate={onDescriptionUpdate}
|
||||
/>
|
||||
</div>
|
||||
<div className="tw-col-span-full">
|
||||
<SchemaTab
|
||||
columnName={getPartialNameFromFQN(
|
||||
dbtModelFQN,
|
||||
['column'],
|
||||
'.'
|
||||
)}
|
||||
columns={columns}
|
||||
hasEditAccess={hasEditAccess()}
|
||||
joins={[]}
|
||||
owner={owner}
|
||||
onUpdate={onColumnsUpdate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 2 && (
|
||||
<div className="tw-my-4 tw-border tw-border-main tw-rounded-md tw-py-4 tw-h-full cm-h-full">
|
||||
<SchemaEditor
|
||||
className="tw-h-full"
|
||||
mode={{ name: CSMode.SQL }}
|
||||
value={viewDefinition}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 3 && (
|
||||
<div className="tw-mt-4">
|
||||
<ManageTab
|
||||
currentTier={tier}
|
||||
currentUser={owner?.id}
|
||||
hasEditAccess={hasEditAccess()}
|
||||
onSave={onSettingsUpdate}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DBTModelDetails;
|
||||
@ -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<User>;
|
||||
dbtModelDetails: Dbtmodel;
|
||||
dbtModelFQN: string;
|
||||
entityName: string;
|
||||
activeTab: number;
|
||||
owner: DatasetOwner;
|
||||
description: string;
|
||||
tier: string;
|
||||
columns: Dbtmodel['columns'];
|
||||
followers: Array<User>;
|
||||
dbtModelTags: Array<EntityTags>;
|
||||
slashedDBTModelName: TitleBreadcrumbProps['titleLinks'];
|
||||
viewDefinition: Dbtmodel['viewDefinition'];
|
||||
setActiveTabHandler: (value: number) => void;
|
||||
followDBTModelHandler: () => void;
|
||||
unfollowDBTModelHandler: () => void;
|
||||
settingsUpdateHandler: (updatedDBTModel: Dbtmodel) => Promise<void>;
|
||||
columnsUpdateHandler: (updatedDBTModel: Dbtmodel) => void;
|
||||
descriptionUpdateHandler: (updatedDBTModel: Dbtmodel) => void;
|
||||
}
|
||||
@ -73,6 +73,7 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
updateTableCount,
|
||||
updateTopicCount,
|
||||
updateDashboardCount,
|
||||
updateDbtModelCount,
|
||||
updatePipelineCount,
|
||||
}: ExploreProps) => {
|
||||
const location = useLocation();
|
||||
@ -197,6 +198,10 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
case SearchIndex.PIPELINE:
|
||||
updatePipelineCount(count);
|
||||
|
||||
break;
|
||||
case SearchIndex.DBT_MODEL:
|
||||
updateDbtModelCount(count);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -339,6 +344,8 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
return getCountBadge(tabCounts.dashboard);
|
||||
case SearchIndex.PIPELINE:
|
||||
return getCountBadge(tabCounts.pipeline);
|
||||
case SearchIndex.DBT_MODEL:
|
||||
return getCountBadge(tabCounts.dbtModel);
|
||||
default:
|
||||
return getCountBadge();
|
||||
}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<Props> = ({
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!isReadOnly ? (
|
||||
{!isReadOnly && !isNil(sampleData) ? (
|
||||
<div className="tw-col-span-2 tw-text-right tw-mb-4">
|
||||
<div
|
||||
className="tw-w-60 tw-inline-flex tw-border tw-border-main
|
||||
|
||||
@ -55,9 +55,18 @@ type PipelineSource = {
|
||||
pipeline_name: string;
|
||||
} & CommonSource;
|
||||
|
||||
type DBTModelSource = {
|
||||
dbt_model_id: string;
|
||||
dbt_model_name: string;
|
||||
} & CommonSource;
|
||||
|
||||
type Option = {
|
||||
_index: string;
|
||||
_source: TableSource & DashboardSource & TopicSource & PipelineSource;
|
||||
_source: TableSource &
|
||||
DashboardSource &
|
||||
TopicSource &
|
||||
PipelineSource &
|
||||
DBTModelSource;
|
||||
};
|
||||
|
||||
const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
|
||||
@ -67,10 +76,13 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
|
||||
const [dashboardSuggestions, setDashboardSuggestions] = useState<
|
||||
DashboardSource[]
|
||||
>([]);
|
||||
|
||||
const [pipelineSuggestions, setPipelineSuggestions] = useState<
|
||||
PipelineSource[]
|
||||
>([]);
|
||||
|
||||
const [DBTModelSuggestions, setDBTModelSuggestions] = useState<
|
||||
DBTModelSource[]
|
||||
>([]);
|
||||
const isMounting = useRef(true);
|
||||
|
||||
const setSuggestions = (options: Array<Option>) => {
|
||||
@ -94,6 +106,11 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
|
||||
.filter((option) => option._index === SearchIndex.PIPELINE)
|
||||
.map((option) => option._source)
|
||||
);
|
||||
setDBTModelSuggestions(
|
||||
options
|
||||
.filter((option) => option._index === SearchIndex.DBT_MODEL)
|
||||
.map((option) => option._source)
|
||||
);
|
||||
};
|
||||
|
||||
const getGroupLabel = (index: string) => {
|
||||
@ -114,6 +131,11 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
|
||||
label = 'Pipelines';
|
||||
icon = Icons.PIPELINE_GREY;
|
||||
|
||||
break;
|
||||
case SearchIndex.DBT_MODEL:
|
||||
label = 'DBT Models';
|
||||
icon = Icons.TABLE_GREY;
|
||||
|
||||
break;
|
||||
case SearchIndex.TABLE:
|
||||
default:
|
||||
@ -233,6 +255,24 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{DBTModelSuggestions.length > 0 && (
|
||||
<>
|
||||
{getGroupLabel(SearchIndex.DBT_MODEL)}
|
||||
|
||||
{DBTModelSuggestions.map((suggestion: DBTModelSource) => {
|
||||
const fqdn = suggestion.fqdn;
|
||||
const name = suggestion.dbt_model_name;
|
||||
const serviceType = suggestion.service_type;
|
||||
|
||||
return getSuggestionElement(
|
||||
fqdn,
|
||||
serviceType,
|
||||
name,
|
||||
SearchIndex.DBT_MODEL
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,30 +7,44 @@ import 'codemirror/addon/fold/foldgutter.js';
|
||||
import 'codemirror/addon/selection/active-line';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import 'codemirror/mode/sql/sql';
|
||||
import React, { useState } from 'react';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { JSON_TAB_SIZE } from '../../constants/constants';
|
||||
import { CSMode } from '../../enums/codemirror.enum';
|
||||
import { getSchemaEditorValue } from './SchemaEditor.utils';
|
||||
|
||||
const options = {
|
||||
tabSize: JSON_TAB_SIZE,
|
||||
indentUnit: JSON_TAB_SIZE,
|
||||
indentWithTabs: false,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
autoCloseBrackets: true,
|
||||
foldGutter: true,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
mode: {
|
||||
name: 'javascript',
|
||||
json: true,
|
||||
},
|
||||
readOnly: true,
|
||||
type Mode = {
|
||||
name: CSMode;
|
||||
json?: boolean;
|
||||
};
|
||||
|
||||
const SchemaEditor = ({ value }: { value: string }) => {
|
||||
const SchemaEditor = ({
|
||||
value,
|
||||
className = '',
|
||||
mode = {
|
||||
name: CSMode.JAVASCRIPT,
|
||||
json: true,
|
||||
},
|
||||
}: {
|
||||
value: string;
|
||||
className?: string;
|
||||
mode?: Mode;
|
||||
}) => {
|
||||
const options = {
|
||||
tabSize: JSON_TAB_SIZE,
|
||||
indentUnit: JSON_TAB_SIZE,
|
||||
indentWithTabs: false,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
autoCloseBrackets: true,
|
||||
foldGutter: true,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
mode,
|
||||
readOnly: true,
|
||||
};
|
||||
const [internalValue, setInternalValue] = useState(
|
||||
getSchemaEditorValue(value)
|
||||
);
|
||||
@ -43,7 +57,7 @@ const SchemaEditor = ({ value }: { value: string }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={className}>
|
||||
<CodeMirror
|
||||
options={options}
|
||||
value={internalValue}
|
||||
|
||||
@ -49,6 +49,7 @@ const ASSETS_NAME = [
|
||||
'topic_name',
|
||||
'dashboard_name',
|
||||
'pipeline_name',
|
||||
'dbt_model_name',
|
||||
];
|
||||
|
||||
const SearchedData: React.FC<SearchedDataProp> = ({
|
||||
|
||||
@ -37,6 +37,7 @@ export const ERROR500 = 'Something went wrong';
|
||||
const PLACEHOLDER_ROUTE_DATASET_FQN = ':datasetFQN';
|
||||
const PLACEHOLDER_ROUTE_TOPIC_FQN = ':topicFQN';
|
||||
const PLACEHOLDER_ROUTE_PIPELINE_FQN = ':pipelineFQN';
|
||||
const PLACEHOLDER_ROUTE_DBT_MODEL_FQN = ':dbtModelFQN';
|
||||
const PLACEHOLDER_ROUTE_DASHBOARD_FQN = ':dashboardFQN';
|
||||
const PLACEHOLDER_ROUTE_DATABASE_FQN = ':databaseFQN';
|
||||
const PLACEHOLDER_ROUTE_SERVICE_FQN = ':serviceFQN';
|
||||
@ -139,6 +140,8 @@ export const ROUTES = {
|
||||
DATABASE_DETAILS: `/database/${PLACEHOLDER_ROUTE_DATABASE_FQN}`,
|
||||
PIPELINE_DETAILS: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}`,
|
||||
PIPELINE_DETAILS_WITH_TAB: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
|
||||
DBT_MODEL_DETAILS: `/dbtmodel/${PLACEHOLDER_ROUTE_DBT_MODEL_FQN}`,
|
||||
DBT_MODEL_DETAILS_WITH_TAB: `/dbtmodel/${PLACEHOLDER_ROUTE_DBT_MODEL_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
|
||||
ONBOARDING: '/onboarding',
|
||||
INGESTION: '/ingestion',
|
||||
};
|
||||
@ -238,6 +241,17 @@ export const getPipelineDetailsPath = (pipelineFQN: string, tab?: string) => {
|
||||
return path;
|
||||
};
|
||||
|
||||
export const getDBTModelDetailsPath = (dbtModelFQN: string, tab?: string) => {
|
||||
let path = tab ? ROUTES.DBT_MODEL_DETAILS_WITH_TAB : ROUTES.DBT_MODEL_DETAILS;
|
||||
path = path.replace(PLACEHOLDER_ROUTE_DBT_MODEL_FQN, dbtModelFQN);
|
||||
|
||||
if (tab) {
|
||||
path = path.replace(PLACEHOLDER_ROUTE_TAB, tab);
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
export const getTeamDetailsPath = (teamName: string) => {
|
||||
let path = ROUTES.TEAM_DETAILS;
|
||||
path = path.replace(PLACEHOLDER_ROUTE_TEAM, teamName);
|
||||
|
||||
@ -104,6 +104,10 @@ export const getCurrentIndex = (tab: string) => {
|
||||
case 'pipelines':
|
||||
currentIndex = SearchIndex.PIPELINE;
|
||||
|
||||
break;
|
||||
case 'dbt_model':
|
||||
currentIndex = SearchIndex.DBT_MODEL;
|
||||
|
||||
break;
|
||||
|
||||
case 'tables':
|
||||
@ -132,6 +136,11 @@ export const getCurrentTab = (tab: string) => {
|
||||
|
||||
break;
|
||||
|
||||
case 'dbt_model':
|
||||
currentTab = 5;
|
||||
|
||||
break;
|
||||
|
||||
case 'tables':
|
||||
default:
|
||||
currentTab = 1;
|
||||
@ -179,4 +188,13 @@ export const tabsInfo = [
|
||||
path: 'pipelines',
|
||||
icon: Icons.PIPELINE_GREY,
|
||||
},
|
||||
{
|
||||
label: 'DBT Model',
|
||||
index: SearchIndex.DBT_MODEL,
|
||||
sortingFields: topicSortingFields,
|
||||
sortField: '',
|
||||
tab: 5,
|
||||
path: 'dbt_model',
|
||||
icon: Icons.PIPELINE_GREY,
|
||||
},
|
||||
];
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export enum CSMode {
|
||||
JAVASCRIPT = 'javascript',
|
||||
SQL = 'sql',
|
||||
}
|
||||
@ -21,4 +21,5 @@ export enum EntityType {
|
||||
TOPIC = 'topic',
|
||||
DASHBOARD = 'dashboard',
|
||||
PIPELINE = 'pipeline',
|
||||
DBT_MODEL = 'dbt_model',
|
||||
}
|
||||
|
||||
@ -26,4 +26,5 @@ export enum SearchIndex {
|
||||
TOPIC = 'topic_search_index',
|
||||
DASHBOARD = 'dashboard_search_index',
|
||||
PIPELINE = 'pipeline_search_index',
|
||||
DBT_MODEL = 'dbt_model_search_index',
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ declare module 'Models' {
|
||||
// topic interface end
|
||||
|
||||
interface RecentlyViewedData {
|
||||
entityType: 'dataset' | 'topic' | 'dashboard' | 'pipeline';
|
||||
entityType: 'dataset' | 'topic' | 'dashboard' | 'pipeline' | 'dbt_model';
|
||||
fqn: string;
|
||||
serviceType?: string;
|
||||
timestamp: number;
|
||||
|
||||
@ -0,0 +1,259 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { observer } from 'mobx-react';
|
||||
import { EntityTags } from 'Models';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import AppState from '../../AppState';
|
||||
import { getDatabase } from '../../axiosAPIs/databaseAPI';
|
||||
import {
|
||||
addFollower,
|
||||
getDBTModelDetailsByFQN,
|
||||
patchDBTModelDetails,
|
||||
removeFollower,
|
||||
} from '../../axiosAPIs/dbtModelAPI';
|
||||
import { getServiceById } from '../../axiosAPIs/serviceAPI';
|
||||
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||
import DBTModelDetails from '../../components/DBTModelDetails/DBTModelDetails.component';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import {
|
||||
getDatabaseDetailsPath,
|
||||
getDBTModelDetailsPath,
|
||||
getServiceDetailsPath,
|
||||
} from '../../constants/constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { ServiceCategory } from '../../enums/service.enum';
|
||||
import { Dbtmodel } from '../../generated/entity/data/dbtmodel';
|
||||
import { User } from '../../generated/entity/teams/user';
|
||||
import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils';
|
||||
import {
|
||||
dbtModelTabs,
|
||||
getCurrentDBTModelTab,
|
||||
} from '../../utils/DBTModelDetailsUtils';
|
||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
||||
import { getOwnerFromId, getTierFromTableTags } from '../../utils/TableUtils';
|
||||
import { getTableTags } from '../../utils/TagsUtils';
|
||||
|
||||
const DBTModelDetailsPage: FunctionComponent = () => {
|
||||
const history = useHistory();
|
||||
const USERId = getCurrentUserId();
|
||||
const { dbtModelFQN: dbtModelFQN, tab } = useParams() as Record<
|
||||
string,
|
||||
string
|
||||
>;
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [activeTab, setActiveTab] = useState<number>(
|
||||
getCurrentDBTModelTab(tab)
|
||||
);
|
||||
const [dbtModelDetails, setDbtModelDetails] = useState<Dbtmodel>(
|
||||
{} as Dbtmodel
|
||||
);
|
||||
const [, setCurrentVersion] = useState<string>();
|
||||
|
||||
const [dbtModelId, setDbtModelId] = useState('');
|
||||
const [tier, setTier] = useState<string>();
|
||||
const [name, setName] = useState('');
|
||||
const [followers, setFollowers] = useState<Array<User>>([]);
|
||||
const [slashedDBTModelName, setSlashedDBTModelName] = useState<
|
||||
TitleBreadcrumbProps['titleLinks']
|
||||
>([]);
|
||||
const [description, setDescription] = useState('');
|
||||
const [columns, setColumns] = useState<Dbtmodel['columns']>([]);
|
||||
const [dbtModelTags, setDBTModelTags] = useState<Array<EntityTags>>([]);
|
||||
const [dbtViewDefinition, setDbtViewDefinition] =
|
||||
useState<Dbtmodel['viewDefinition']>('');
|
||||
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<AxiosResponse> => {
|
||||
const jsonPatch = compare(dbtModelDetails, updatedData);
|
||||
|
||||
return patchDBTModelDetails(
|
||||
dbtModelId,
|
||||
jsonPatch
|
||||
) as unknown as Promise<AxiosResponse>;
|
||||
};
|
||||
|
||||
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<void> => {
|
||||
return new Promise<void>((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 ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<DBTModelDetails
|
||||
activeTab={activeTab}
|
||||
columns={columns}
|
||||
columnsUpdateHandler={columnsUpdateHandler}
|
||||
dbtModelDetails={dbtModelDetails}
|
||||
dbtModelFQN={dbtModelFQN}
|
||||
dbtModelTags={dbtModelTags}
|
||||
description={description}
|
||||
descriptionUpdateHandler={descriptionUpdateHandler}
|
||||
entityName={name}
|
||||
followDBTModelHandler={followDBTModel}
|
||||
followers={followers}
|
||||
owner={owner as Dbtmodel['owner'] & { displayName: string }}
|
||||
setActiveTabHandler={activeTabHandler}
|
||||
settingsUpdateHandler={settingsUpdateHandler}
|
||||
slashedDBTModelName={slashedDBTModelName}
|
||||
tier={tier as string}
|
||||
unfollowDBTModelHandler={unfollowDBTModel}
|
||||
users={AppState.users}
|
||||
viewDefinition={dbtViewDefinition}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default observer(DBTModelDetailsPage);
|
||||
@ -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<number>(0);
|
||||
const [dashboardCount, setDashboardCount] = useState<number>(0);
|
||||
const [pipelineCount, setPipelineCount] = useState<number>(0);
|
||||
const [dbtModelCount, setDbtModelCount] = useState<number>(0);
|
||||
const [searchResult, setSearchResult] = useState<ExploreSearchData>();
|
||||
const [initialSortField] = useState<string>(
|
||||
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<SearchResponse>[]) => {
|
||||
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}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
component={DBTModelDetailsPage}
|
||||
path={ROUTES.DBT_MODEL_DETAILS}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
component={DBTModelDetailsPage}
|
||||
path={ROUTES.DBT_MODEL_DETAILS_WITH_TAB}
|
||||
/>
|
||||
<Route component={Onboarding} path={ROUTES.ONBOARDING} />
|
||||
<Route
|
||||
exact
|
||||
|
||||
@ -4,3 +4,8 @@
|
||||
.cm-string.cm-property {
|
||||
color: #450de2 !important;
|
||||
}
|
||||
|
||||
.cm-h-full .react-codemirror2,
|
||||
.cm-h-full .react-codemirror2 > .CodeMirror {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -247,6 +247,15 @@ export const getEntityCountByType = (buckets: Array<Bucket>) => {
|
||||
return entityCounts;
|
||||
};
|
||||
|
||||
export const getTotalEntityCountByType = (buckets: Array<Bucket> = []) => {
|
||||
let entityCounts = 0;
|
||||
buckets.forEach((bucket) => {
|
||||
entityCounts += bucket.doc_count;
|
||||
});
|
||||
|
||||
return entityCounts;
|
||||
};
|
||||
|
||||
export const getEntityLineage = (
|
||||
oldVal: EntityLineage,
|
||||
newVal: EntityLineage,
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user