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

feat: Enhance search logging #2280

Merged
merged 15 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 13 additions & 2 deletions frontend/amundsen_application/api/log/v0.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import logging

from typing import List
from http import HTTPStatus

from flask import Response, jsonify, make_response, request
Expand Down Expand Up @@ -38,7 +39,12 @@ def _log_generic_action(*,
label: str,
location: str,
value: str,
position: str) -> None:
position: str,
resource_href: str,
resource_type: str,
search_term: str,
search_page_index: int,
search_results: List[str]) -> None:
pass # pragma: no cover

try:
Expand All @@ -52,7 +58,12 @@ def _log_generic_action(*,
label=args.get('label', None),
location=args.get('location', None),
value=args.get('value', None),
position=args.get('position', None)
position=args.get('position', None),
resource_href=args.get('resource_href', None),
resource_type=args.get('resource_type', None),
search_term=args.get('search_term', None),
search_page_index=args.get('search_page_index', None),
search_results=args.get('search_results', None),
)
message = 'Logging of {} action successful'.format(command)
return make_response(jsonify({'msg': message}), HTTPStatus.OK)
Expand Down
8 changes: 3 additions & 5 deletions frontend/amundsen_application/static/.betterer.results
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,13 @@ exports[`eslint`] = {
[112, 6, 191, "Unexpected lexical declaration in case block.", "1433271452"],
[118, 6, 59, "Unexpected lexical declaration in case block.", "3993269111"]
],
"js/ducks/search/reducer.ts:1964208432": [
"js/ducks/search/reducer.ts:4202738843": [
[310, 6, 27, "Unexpected lexical declaration in case block.", "3336779920"],
[364, 6, 53, "Unexpected lexical declaration in case block.", "2684420572"],
[387, 6, 66, "Unexpected lexical declaration in case block.", "2160263677"],
[402, 6, 123, "Unexpected lexical declaration in case block.", "2271601490"],
[417, 6, 61, "Unexpected lexical declaration in case block.", "2053236172"]
],
"js/ducks/search/utils.ts:923002554": [
[20, 2, 248, "Expected a default case.", "1034339850"]
[417, 6, 61, "Unexpected lexical declaration in case block.", "2053236172"],
[444, 6, 60, "Unexpected lexical declaration in case block.", "3101482005"]
],
"js/ducks/tableMetadata/api/v0.ts:3333048528": [
[141, 23, 2, "Expected to return a value at the end of arrow function.", "5859494"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { dashboardSummary } from 'fixtures/metadata/dashboard';
import { NO_TIMESTAMP_TEXT } from '../../../constants';

import * as Constants from './constants';
import DashboardListItem, { DashboardListItemProps } from './index';
import { DashboardListItem, DashboardListItemProps } from './index';

const MOCK_DISPLAY_NAME = 'displayName';
const MOCK_ICON_CLASS = 'test-class';
Expand All @@ -37,6 +37,7 @@ const setup = (propOverrides?: Partial<DashboardListItemProps>) => {
name: dashboardSummary.name,
description: dashboardSummary.description,
},
logSearchEvent: jest.fn(),
...propOverrides,
};
const wrapper = shallow<DashboardListItem>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,45 @@ import { Link } from 'react-router-dom';
import BookmarkIcon from 'components/Bookmark/BookmarkIcon';

import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
import { logClick } from 'utils/analytics';
import { buildDashboardURL } from 'utils/navigation';
import { formatDate } from 'utils/date';

import { ResourceType, DashboardResource } from 'interfaces';

import { LogSearchEventRequest } from 'ducks/log/types';
import { connect } from 'react-redux';
import { logSearchEvent } from 'ducks/log/reducer';
import { bindActionCreators } from 'redux';
import { NO_TIMESTAMP_TEXT } from '../../../constants';
import { LoggingParams } from '../types';
import { HighlightedDashboard } from '../MetadataHighlightList/utils';
import MetadataHighlightList from '../MetadataHighlightList';
import * as Constants from './constants';

export interface DashboardListItemProps {
export interface OwnProps {
dashboard: DashboardResource;
logging: LoggingParams;
dashboardHighlights: HighlightedDashboard;
}

class DashboardListItem extends React.Component<DashboardListItemProps, {}> {
export interface DispatchFromProps {
logSearchEvent: (
resourceLink: string,
resourceType: ResourceType,
source: string,
index: number,
event: any,
inline: boolean,
extra?: { [key: string]: any }
) => LogSearchEventRequest;
}

export type DashboardListItemProps = OwnProps & DispatchFromProps;

export class DashboardListItem extends React.Component<
DashboardListItemProps,
{}
> {
getLink = () => {
const { dashboard, logging } = this.props;

Expand All @@ -41,19 +61,23 @@ class DashboardListItem extends React.Component<DashboardListItemProps, {}> {
`icon resource-icon ${getSourceIconClass(dashboardId, dashboardType)}`;

render() {
const { dashboard, logging, dashboardHighlights } = this.props;
const { dashboard, logging, dashboardHighlights, logSearchEvent } =
this.props;

return (
<li className="list-group-item clickable">
<Link
className="resource-list-item table-list-item"
to={this.getLink()}
onClick={(e) =>
logClick(e, {
target_id: 'dashboard_list_item',
value: logging.source,
position: logging.index.toString(),
})
logSearchEvent(
this.getLink(),
ResourceType.dashboard,
logging.source,
logging.index,
e,
false
)
}
>
<div className="resource-info">
Expand Down Expand Up @@ -120,4 +144,17 @@ class DashboardListItem extends React.Component<DashboardListItemProps, {}> {
}
}

export default DashboardListItem;
export const mapDispatchToProps = (dispatch: any): DispatchFromProps => {
const dispatchableActions: DispatchFromProps = bindActionCreators(
{
logSearchEvent,
},
dispatch
);

return dispatchableActions;
};
export default connect<{}, DispatchFromProps, OwnProps>(
null,
mapDispatchToProps
)(DashboardListItem);
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import * as ConfigUtils from 'config/config-utils';

import { featureSummary } from 'fixtures/metadata/feature';

import FeatureListItem, {
import {
FeatureListItem,
FeatureListItemProps,
} from 'components/ResourceListItem/FeatureListItem';
import { RightIcon } from 'components/SVGIcons';
Expand All @@ -33,6 +34,7 @@ describe('FeatureListItem', () => {
name: MOCK_DISPLAY_NAME,
description: 'I am an ML <em>feature</em>',
},
logSearchEvent: jest.fn(),
...propOverrides,
};
const wrapper = shallow<typeof FeatureListItem>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,38 @@ import * as React from 'react';
import { Link } from 'react-router-dom';

import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
import { logClick } from 'utils/analytics';

import BadgeList from 'features/BadgeList';
import { ResourceType, FeatureResource } from 'interfaces';

import { RightIcon } from 'components/SVGIcons';
import { LoggingParams } from '../types';
import { LogSearchEventRequest } from 'ducks/log/types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { logSearchEvent } from 'ducks/log/reducer';
import { HighlightedResource } from '../MetadataHighlightList/utils';
import { LoggingParams } from '../types';

export interface FeatureListItemProps {
export interface OwnProps {
feature: FeatureResource;
logging: LoggingParams;
featureHighlights: HighlightedResource;
}

export interface DispatchFromProps {
logSearchEvent: (
resourceLink: string,
resourceType: ResourceType,
source: string,
index: number,
event: any,
inline: boolean,
extra?: { [key: string]: any }
) => LogSearchEventRequest;
}

export type FeatureListItemProps = OwnProps & DispatchFromProps;

const getLink = (feature: FeatureResource, logging: LoggingParams) =>
`/feature/${feature.key}?index=${logging.index}&source=${logging.source}`;

Expand All @@ -28,10 +45,11 @@ const generateResourceIconClass = (
featureType: ResourceType
): string => `icon resource-icon ${getSourceIconClass(featureId, featureType)}`;

const FeatureListItem: React.FC<FeatureListItemProps> = ({
export const FeatureListItem: React.FC<FeatureListItemProps> = ({
feature,
logging,
featureHighlights,
logSearchEvent,
}: FeatureListItemProps) => {
const source =
feature.availability?.length > 0 ? feature.availability[0] : '';
Expand All @@ -43,11 +61,14 @@ const FeatureListItem: React.FC<FeatureListItemProps> = ({
to={getLink(feature, logging)}
data-type="feature_list_item"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) =>
logClick(e, {
target_id: 'feature_list_item',
value: logging.source,
position: logging.index.toString(),
})
logSearchEvent(
getLink(feature, logging),
ResourceType.feature,
logging.source,
logging.index,
e,
false
)
}
>
<div className="resource-info">
Expand Down Expand Up @@ -87,4 +108,17 @@ const FeatureListItem: React.FC<FeatureListItemProps> = ({
);
};

export default FeatureListItem;
export const mapDispatchToProps = (dispatch: any): DispatchFromProps => {
const dispatchableActions: DispatchFromProps = bindActionCreators(
{
logSearchEvent,
},
dispatch
);

return dispatchableActions;
};
export default connect<{}, DispatchFromProps, OwnProps>(
null,
mapDispatchToProps
)(FeatureListItem);
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { ResourceType } from 'interfaces';

import * as ConfigUtils from 'config/config-utils';
import BadgeList from 'features/BadgeList';
import TableListItem, {
import {
TableListItem,
TableListItemProps,
getLink,
generateResourceIconClass,
Expand Down Expand Up @@ -56,6 +57,7 @@ describe('TableListItem', () => {
name: 'tableName',
description: 'I am the description',
},
logSearchEvent: jest.fn(),
...propOverrides,
};
// eslint-disable-next-line react/jsx-props-no-spreading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,34 @@ import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';

import BadgeList from 'features/BadgeList';
import SchemaInfo from 'components/ResourceListItem/SchemaInfo';
import { logClick } from 'utils/analytics';
import { LoggingParams } from '../types';
import MetadataHighlightList from '../MetadataHighlightList';
import { LogSearchEventRequest } from 'ducks/log/types';
import { bindActionCreators } from 'redux';
import { logSearchEvent } from 'ducks/log/reducer';
import { connect } from 'react-redux';
import { HighlightedTable } from '../MetadataHighlightList/utils';
import MetadataHighlightList from '../MetadataHighlightList';
import { LoggingParams } from '../types';

export interface TableListItemProps {
export interface OwnProps {
table: TableResource;
logging: LoggingParams;
tableHighlights: HighlightedTable;
disabled?: boolean;
}

export interface DispatchFromProps {
logSearchEvent: (
resourceLink: string,
resourceType: ResourceType,
source: string,
index: number,
event: any,
inline: boolean,
extra?: { [key: string]: any }
) => LogSearchEventRequest;
}

export type TableListItemProps = OwnProps & DispatchFromProps;
/*
this function get's the table name from the key to preserve original
capitalization since search needs the names to be lowercase for analysis
Expand Down Expand Up @@ -53,11 +69,12 @@ export const getLink = (table, logging) => {
export const generateResourceIconClass = (databaseId: string): string =>
`icon resource-icon ${getSourceIconClass(databaseId, ResourceType.table)}`;

const TableListItem: React.FC<TableListItemProps> = ({
export const TableListItem: React.FC<TableListItemProps> = ({
table,
logging,
tableHighlights,
disabled,
logSearchEvent,
}) => (
<li className="list-group-item">
<Link
Expand All @@ -66,11 +83,14 @@ const TableListItem: React.FC<TableListItemProps> = ({
}`}
to={getLink(table, logging)}
onClick={(e) =>
logClick(e, {
target_id: 'table_list_item',
value: logging.source,
position: logging.index.toString(),
})
logSearchEvent(
getLink(table, logging),
ResourceType.table,
logging.source,
logging.index,
e,
false
)
}
>
<div className="resource-info">
Expand Down Expand Up @@ -132,4 +152,18 @@ const TableListItem: React.FC<TableListItemProps> = ({
</Link>
</li>
);
export default TableListItem;

export const mapDispatchToProps = (dispatch: any): DispatchFromProps => {
const dispatchableActions: DispatchFromProps = bindActionCreators(
{
logSearchEvent,
},
dispatch
);

return dispatchableActions;
};
export default connect<{}, DispatchFromProps, OwnProps>(
null,
mapDispatchToProps
)(TableListItem);
Loading
Loading