Fix #6754: Added User Details for Usage (#8449)

This commit is contained in:
Mayur Singal 2022-11-02 14:07:41 +05:30 committed by GitHub
parent ef5108c47b
commit fa68a1f18f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 21 deletions

View File

@ -74,6 +74,7 @@ def parse_sql_statement(record: TableQuery) -> Optional[ParsedData]:
databaseName=record.databaseName,
databaseSchema=record.databaseSchema,
sql=record.query,
userName=record.userName,
date=start_date.__root__.strftime("%Y-%m-%d"),
serviceName=record.serviceName,
)

View File

@ -23,9 +23,12 @@ from metadata.generated.schema.entity.data.table import SqlQuery
from metadata.generated.schema.entity.services.connections.metadata.openMetadataConnection import (
OpenMetadataConnection,
)
from metadata.generated.schema.entity.teams.user import User
from metadata.generated.schema.type.entityReference import EntityReference
from metadata.generated.schema.type.queryParserData import QueryParserData
from metadata.generated.schema.type.tableUsageCount import TableUsageCount
from metadata.ingestion.api.stage import Stage, StageStatus
from metadata.ingestion.ometa.ometa_api import OpenMetadata
from metadata.utils.constants import UTF_8
from metadata.utils.logger import ingestion_logger
@ -54,6 +57,7 @@ class TableUsageStage(Stage[QueryParserData]):
):
self.config = config
self.metadata_config = metadata_config
self.metadata = OpenMetadata(self.metadata_config)
self.status = StageStatus()
self.table_usage = {}
self.table_queries = {}
@ -70,11 +74,41 @@ class TableUsageStage(Stage[QueryParserData]):
config = TableStageConfig.parse_obj(config_dict)
return cls(config, metadata_config)
def _get_user_entity(self, username: str):
if username:
user = self.metadata.get_by_name(entity=User, fqn=username)
if user:
return [
EntityReference(
id=user.id,
type="user",
name=user.name.__root__,
fullyQualifiedName=user.fullyQualifiedName.__root__,
description=user.description,
displayName=user.displayName,
deleted=user.deleted,
href=user.href,
)
]
return []
def _add_sql_query(self, record, table):
if self.table_queries.get((table, record.date)):
self.table_queries[(table, record.date)].append(SqlQuery(query=record.sql))
self.table_queries[(table, record.date)].append(
SqlQuery(
query=record.sql,
users=self._get_user_entity(record.userName),
queryDate=record.date,
)
)
else:
self.table_queries[(table, record.date)] = [SqlQuery(query=record.sql)]
self.table_queries[(table, record.date)] = [
SqlQuery(
query=record.sql,
users=self._get_user_entity(record.userName),
queryDate=record.date,
)
]
def stage_record(self, record: QueryParserData) -> None:
if not record or not record.parsedData:

View File

@ -39,6 +39,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -380,6 +381,14 @@ public class TableRepository extends EntityRepository<Table> {
storedMapQueries.put(q.getChecksum(), q);
}
}
SQLQuery oldQuery = storedMapQueries.get(query.getChecksum());
if (oldQuery != null && query.getUsers() != null) {
// Merge old and new users
List<EntityReference> userList = query.getUsers();
userList.addAll(oldQuery.getUsers());
HashSet<EntityReference> userSet = new HashSet<>(userList);
query.setUsers(new ArrayList<>(userSet));
}
storedMapQueries.put(query.getChecksum(), query);
List<SQLQuery> updatedQueries = new ArrayList<>(storedMapQueries.values());
daoCollection

View File

@ -578,9 +578,12 @@
"description": "How long did the query took to run in seconds.",
"type": "number"
},
"user": {
"description": "User who ran this query.",
"$ref": "../../type/entityReference.json",
"users": {
"description": "List of users who ran this query.",
"type":"array",
"items": {
"$ref": "../../type/entityReference.json"
},
"default": null
},
"vote": {

View File

@ -38,6 +38,10 @@
"description": "Name that identifies this database service.",
"type": "string"
},
"userName": {
"description": "Name of the user that executed the SQL query",
"type": "string"
},
"date": {
"description": "Date of execution of SQL query",
"type": "string"

View File

@ -14,7 +14,7 @@
import {
findByTestId,
findByText,
getByTestId,
// getByTestId,
queryByTestId,
render,
} from '@testing-library/react';
@ -25,13 +25,15 @@ import QueryCard from './QueryCard';
const mockQueryData = {
query: 'select products from raw_product_catalog',
duration: 0.309,
user: {
id: 'd4785e53-bbdb-4dbd-b368-009fdb50c2c6',
type: 'user',
name: 'aaron_johnson0',
displayName: 'Aaron Johnson',
href: 'http://localhost:8585/api/v1/users/d4785e53-bbdb-4dbd-b368-009fdb50c2c6',
},
users: [
{
id: 'd4785e53-bbdb-4dbd-b368-009fdb50c2c6',
type: 'user',
name: 'aaron_johnson0',
displayName: 'Aaron Johnson',
href: 'http://localhost:8585/api/v1/users/d4785e53-bbdb-4dbd-b368-009fdb50c2c6',
},
],
vote: 1,
checksum: '0232b0368458aadb29230ccc531462c9',
};
@ -49,7 +51,7 @@ describe('Test QueryCard Component', () => {
const { container } = render(<QueryCard query={mockQueryData} />, {
wrapper: MemoryRouter,
});
const queryHeader = getByTestId(container, 'query-header');
// const queryHeader = getByTestId(container, 'query-header');
const query = await findByText(container, /SchemaEditor/i);
const copyQueryButton = await findByText(
container,
@ -61,7 +63,7 @@ describe('Test QueryCard Component', () => {
'expand-collapse-button'
);
expect(queryHeader).toBeInTheDocument();
// expect(queryHeader).toBeInTheDocument();
expect(query).toBeInTheDocument();
expect(copyQueryButton).toBeInTheDocument();
expect(expandButton).toBeInTheDocument();
@ -69,7 +71,7 @@ describe('Test QueryCard Component', () => {
it('Should not render header if user is undefined', async () => {
const { container } = render(
<QueryCard query={{ ...mockQueryData, user: undefined }} />,
<QueryCard query={{ ...mockQueryData, users: undefined }} />,
{
wrapper: MemoryRouter,
}

View File

@ -12,10 +12,10 @@
*/
import classNames from 'classnames';
import { isUndefined } from 'lodash';
// import { isUndefined } from 'lodash';
import React, { FC, HTMLAttributes, useState } from 'react';
import { Link } from 'react-router-dom';
import { getUserPath } from '../../constants/constants';
// import { Link } from 'react-router-dom';
// import { getUserPath } from '../../constants/constants';
import { CSMode } from '../../enums/codemirror.enum';
import { SQLQuery } from '../../generated/entity/data/table';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
@ -34,7 +34,7 @@ const QueryCard: FC<QueryCardProp> = ({ className, query }) => {
className
)}
onClick={() => setExpanded((pre) => !pre)}>
{!isUndefined(query.user) && !isUndefined(query.duration) ? (
{/* {!isUndefined(query.user) && !isUndefined(query.duration) ? (
<div data-testid="query-header">
<p>
Last run by{' '}
@ -49,7 +49,7 @@ const QueryCard: FC<QueryCardProp> = ({ className, query }) => {
<span className="tw-font-medium">{query.duration} seconds</span>
</p>
</div>
) : null}
) : null} */}
<div className="tw-border tw-border-main tw-rounded-md tw-p-px">
<div
className={classNames('tw-overflow-hidden tw-relative', {