mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-11 08:43:31 +00:00
parent
ef5108c47b
commit
fa68a1f18f
@ -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,
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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', {
|
||||
|
Loading…
x
Reference in New Issue
Block a user