Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Add data source detail page #620

Merged
merged 20 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions registry/purview-registry/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ def get_project_datasources(project: str) -> list:
return list([to_camel(e.to_dict()) for e in sources])


@router.get("/projects/{project}/datasources/{datasource}",tags=["Project"])
def get_datasource(project: str, datasource: str) -> dict:
p = registry.get_entity(project,True)
for s in p.attributes.sources:
if str(s.id) == datasource:
return s
# If datasource is not found, raise 404 error
raise HTTPException(
status_code=404, detail=f"Data Source {datasource} not found")


@router.get("/projects/{project}/features",tags=["Project"])
def get_project_features(project: str, keyword: Optional[str] = None) -> list:
atlasEntities = registry.get_project_features(project, keywords=keyword)
Expand Down
13 changes: 12 additions & 1 deletion registry/sql-registry/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,25 @@ def get_project_datasources(project: str) -> list:
return list([e.to_dict() for e in sources])


@router.get("/projects/{project}/datasources/{datasource}")
def get_datasource(project: str, datasource: str) -> dict:
p = registry.get_entity(project)
for s in p.attributes.sources:
if str(s.id) == datasource:
return s
# If datasource is not found, raise 404 error
raise HTTPException(
status_code=404, detail=f"Data Source {datasource} not found")


@router.get("/projects/{project}/features")
def get_project_features(project: str, keyword: Optional[str] = None, page: Optional[int] = None, limit: Optional[int] = None) -> list:
if keyword:
start = None
size = None
if page is not None and limit is not None:
start = (page - 1) * limit
size = limit
size = limit
efs = registry.search_entity(
keyword, [EntityType.AnchorFeature, EntityType.DerivedFeature], project=project, start=start, size=size)
feature_ids = [ef.id for ef in efs]
Expand Down
4 changes: 4 additions & 0 deletions ui/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
REACT_APP_AZURE_CLIENT_ID=db8dc4b0-202e-450c-b38d-7396ad9631a5
ahlag marked this conversation as resolved.
Show resolved Hide resolved
REACT_APP_AZURE_TENANT_ID=common
REACT_APP_API_ENDPOINT=http://127.0.0.1:8000
REACT_APP_ENABLE_RBAC=false
17 changes: 17 additions & 0 deletions ui/src/api/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ export const fetchDataSources = async (project: string) => {
});
};

export const fetchDataSource = async (
project: string,
dataSourceId: string
) => {
const axios = await authAxios(msalInstance);
return axios
.get<DataSource>(
`${getApiBaseUrl()}/projects/${project}/datasources/${dataSourceId}`,
blrchen marked this conversation as resolved.
Show resolved Hide resolved
{
params: { project: project, datasource: dataSourceId },
}
)
.then((response) => {
return response.data;
});
};

export const fetchProjects = async () => {
const axios = await authAxios(msalInstance);
return axios
Expand Down
5 changes: 5 additions & 0 deletions ui/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Features from "./pages/feature/features";
import NewFeature from "./pages/feature/newFeature";
import FeatureDetails from "./pages/feature/featureDetails";
import DataSources from "./pages/dataSource/dataSources";
import DataSourceDetails from "./pages/dataSource/dataSourceDetails";
import Jobs from "./pages/jobs/jobs";
import Monitoring from "./pages/monitoring/monitoring";
import LineageGraph from "./pages/feature/lineageGraph";
Expand Down Expand Up @@ -44,6 +45,10 @@ const App = () => {
path="/projects/:project/features/:featureId"
element={<FeatureDetails />}
/>
<Route
path="/projects/:project/dataSources/:dataSourceId"
element={<DataSourceDetails />}
/>
<Route
path="/projects/:project/lineage"
element={<LineageGraph />}
Expand Down
64 changes: 60 additions & 4 deletions ui/src/components/dataSourceList.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import React, { useCallback, useEffect, useState } from "react";
import { Form, Select, Table } from "antd";
import { useNavigate, Link } from "react-router-dom";
import { Form, Select, Table, Button, Menu, Dropdown, Tooltip } from "antd";
import { DownOutlined } from "@ant-design/icons";
import { DataSource } from "../models/model";
import { fetchDataSources, fetchProjects } from "../api";

const DataSourceList = () => {
type Props = {
projectProp: string;
keywordProp: string;
};

const DataSourceList = ({ projectProp, keywordProp }: Props) => {
const navigate = useNavigate();
const columns = [
{
title: <div style={{ userSelect: "none" }}>Name</div>,
key: "name",
align: "center" as "center",
width: 120,
render: (row: DataSource) => {
return row.attributes.name;
return (
<Button
type="link"
onClick={() => {
navigate(`/projects/${project}/dataSources/${row.guid}`);
}}
>
{row.displayText}
</Button>
);
},
onCell: () => {
return {
Expand Down Expand Up @@ -54,7 +71,7 @@ const DataSourceList = () => {
},
},
{
title: <div>Pre Processing</div>,
title: <div>Preprocessing</div>,
key: "preprocessing",
align: "center" as "center",
width: 190,
Expand Down Expand Up @@ -101,6 +118,45 @@ const DataSourceList = () => {
};
},
},
{
title: (
<div>
Action{" "}
<Tooltip
title={
<Link style={{ color: "cyan" }} to="/help">
Learn more
</Link>
}
></Tooltip>
</div>
),
key: "action",
align: "center" as "center",
width: 120,
render: (name: string, row: DataSource) => (
<Dropdown
overlay={() => {
return (
<Menu>
<Menu.Item key="view">
<Button
type="link"
onClick={() => {
navigate(`/projects/${project}/dataSources/${row.guid}`);
}}
>
View Details
</Button>
</Menu.Item>
</Menu>
);
}}
>
<Button icon={<DownOutlined />}>action</Button>
</Dropdown>
),
},
];
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
Expand Down
12 changes: 7 additions & 5 deletions ui/src/components/userRoles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const UserRoles = () => {
sorter: {
compare: (a: UserRole, b: UserRole) => a.scope.localeCompare(b.scope),
multiple: 3,
}
},
},
{
title: <div style={{ userSelect: "none" }}>Role</div>,
Expand All @@ -53,9 +53,10 @@ const UserRoles = () => {
key: "userName",
align: "center" as "center",
sorter: {
compare: (a: UserRole, b: UserRole) => a.userName.localeCompare(b.userName),
compare: (a: UserRole, b: UserRole) =>
a.userName.localeCompare(b.userName),
multiple: 1,
}
},
},
{
title: <div>Permissions</div>,
Expand Down Expand Up @@ -93,9 +94,10 @@ const UserRoles = () => {
key: "createTime",
align: "center" as "center",
sorter: {
compare: (a: UserRole, b: UserRole) => a.createTime.localeCompare(b.createTime),
compare: (a: UserRole, b: UserRole) =>
a.createTime.localeCompare(b.createTime),
multiple: 2,
}
},
},
{
title: "Action",
Expand Down
112 changes: 112 additions & 0 deletions ui/src/pages/dataSource/dataSourceDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React from "react";
import { LoadingOutlined } from "@ant-design/icons";
import { useNavigate, useParams } from "react-router-dom";
import { Alert, Button, Card, Col, Row, Spin, Typography } from "antd";
import { QueryStatus, useQuery } from "react-query";
import { AxiosError } from "axios";
import { fetchDataSource } from "../../api";
import { DataSource, DataSourceAttributes } from "../../models/model";

const { Title } = Typography;

type DataSourceKeyProps = { dataSource: DataSource };
const DataSourceKey = ({ dataSource }: DataSourceKeyProps) => {
const keys = dataSource.attributes;
return (
<>
{keys && (
<Col span={24}>
<Card className="card">
<Title level={4}>Data Source Attributes</Title>
<div className="dataSource-container">
<p>Name: {keys.name}</p>
<p>Type: {keys.type}</p>
<p>Path: {keys.path}</p>
<p>Preprocessing: {keys.preprocessing}</p>
<p>Event Timestamp Column: {keys.eventTimestampColumn}</p>
<p>Timestamp Format: {keys.timestampFormat}</p>
<p>Qualified Name: {keys.qualifiedName}</p>
<p>Tags: {JSON.stringify(keys.tags)}</p>
</div>
</Card>
</Col>
)}
</>
);
};

type Params = {
project: string;
dataSourceId: string;
};

const DataSourceDetails = () => {
const { project, dataSourceId } = useParams() as Params;
const navigate = useNavigate();
const loadingIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
const { status, error, data } = useQuery<DataSource, AxiosError>(
["dataSourceId", dataSourceId],
() => fetchDataSource(project, dataSourceId)
);

const render = (status: QueryStatus): JSX.Element => {
switch (status) {
case "error":
return (
<Card>
<Alert
message="Error"
description={error?.message}
type="error"
showIcon
/>
</Card>
);
case "idle":
return (
<Card>
<Spin indicator={loadingIcon} />
</Card>
);
case "loading":
return (
<Card>
<Spin indicator={loadingIcon} />
</Card>
);
case "success":
if (data === undefined) {
return (
<Card>
<Alert
message="Error"
description="Data does not exist..."
type="error"
showIcon
/>
</Card>
);
} else {
return (
<>
<Button type="link" onClick={() => navigate(-1)}>
dataSource list {">"}
</Button>
<Card>
<Title level={3}>{data.attributes.name}</Title>
<div>
<Row>
<DataSourceKey dataSource={data} />
</Row>
</div>
</Card>
</>
);
}
}
};

return <div className="page">{render(status)}</div>;
};

export default DataSourceDetails;
8 changes: 6 additions & 2 deletions ui/src/pages/dataSource/dataSources.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React from "react";
import { Card, Typography } from "antd";
import { useSearchParams } from "react-router-dom";
import DataSourceList from "../../components/dataSourceList";

const { Title } = Typography;

const DataSources = () => {
const [searchParams] = useSearchParams();
const project = (searchParams.get("project") as string) ?? "";
const keyword = (searchParams.get("keyword") as string) ?? "";

return (
<div className="page">
<Card>
<Title level={3}>Data Sources</Title>
<DataSourceList />
<DataSourceList projectProp={project} keywordProp={keyword} />
</Card>
</div>
);
Expand Down
4 changes: 4 additions & 0 deletions ui/src/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@
.feature-container p {
break-inside: avoid-column;
}

.dataSource-container {
column-count: 1;
}