Unverified Commit 09de27a4 authored by friendtocephalopods's avatar friendtocephalopods Committed by GitHub

use schemas for metadata retrieval & update search field names (#382)

* use schemas for metadata retrieval

* - Re-add user object sanitization
- Add 'first_name' and 'last_name' fallback for 'full_name'

* upgrade common to 0.2.2
Co-authored-by: 's avatarDaniel <dwon@lyft.com>
parent 0bbbed00
...@@ -121,7 +121,7 @@ def _get_table_metadata(*, table_key: str, index: int, source: str) -> Dict[str, ...@@ -121,7 +121,7 @@ def _get_table_metadata(*, table_key: str, index: int, source: str) -> Dict[str,
return results_dict return results_dict
try: try:
table_data_raw = response.json() table_data_raw: dict = response.json()
# Ideally the response should include 'key' to begin with # Ideally the response should include 'key' to begin with
table_data_raw['key'] = table_key table_data_raw['key'] = table_key
......
...@@ -186,8 +186,8 @@ def _search_table(*, search_term: str, page_index: int) -> Dict[str, Any]: ...@@ -186,8 +186,8 @@ def _search_table(*, search_term: str, page_index: int) -> Dict[str, Any]:
'cluster': result.get('cluster', None), 'cluster': result.get('cluster', None),
'description': result.get('description', None), 'description': result.get('description', None),
'database': result.get('database', None), 'database': result.get('database', None),
'schema_name': result.get('schema_name', None), 'schema': result.get('schema', None),
'last_updated_epoch': result.get('last_updated_epoch', None), 'last_updated_timestamp': result.get('last_updated_timestamp', None),
} }
tables = { tables = {
......
from typing import Dict from typing import Any, Dict
from flask import current_app as app
from amundsen_common.models.popular_table import PopularTable, PopularTableSchema
from amundsen_common.models.table import Table, TableSchema
from amundsen_application.models.user import load_user, dump_user from amundsen_application.models.user import load_user, dump_user
from flask import current_app as app
def marshall_table_partial(table: Dict) -> Dict: def marshall_table_partial(table_dict: Dict) -> Dict:
""" """
Forms a short version of a table Dict, with selected fields and an added 'key' Forms a short version of a table Dict, with selected fields and an added 'key'
:param table: Dict of partial table object :param table: Dict of partial table object
...@@ -12,61 +14,41 @@ def marshall_table_partial(table: Dict) -> Dict: ...@@ -12,61 +14,41 @@ def marshall_table_partial(table: Dict) -> Dict:
TODO - Unify data format returned by search and metadata. TODO - Unify data format returned by search and metadata.
""" """
table_name = table.get('table_name', '') schema = PopularTableSchema(strict=True)
schema_name = table.get('schema', '') # TODO: consider migrating to validate() instead of roundtripping
cluster = table.get('cluster', '') table: PopularTable = schema.load(table_dict).data
db = table.get('database', '') results = schema.dump(table).data
return { # TODO: fix popular tables to provide these? remove if we're not using them?
'cluster': cluster, # TODO: Add the 'key' or 'id' to the base PopularTableSchema
'database': db, results['key'] = f'{table.database}://{table.cluster}.{table.schema}/{ table.name}'
'description': table.get('table_description', ''), results['last_updated_timestamp'] = None
'key': '{0}://{1}.{2}/{3}'.format(db, cluster, schema_name, table_name), results['type'] = 'table'
'name': table_name,
'schema_name': schema_name, return results
'type': 'table',
'last_updated_epoch': table.get('last_updated_epoch', None),
}
def marshall_table_full(table: Dict) -> Dict: def marshall_table_full(table_dict: Dict) -> Dict:
""" """
Forms the full version of a table Dict, with additional and sanitized fields Forms the full version of a table Dict, with additional and sanitized fields
:param table: Table Dict from metadata service :param table: Table Dict from metadata service
:return: Table Dict with sanitized fields :return: Table Dict with sanitized fields
""" """
# Filter and parse the response dictionary from the metadata service
fields = [ schema = TableSchema(strict=True)
'badges', # TODO: consider migrating to validate() instead of roundtripping
'columns', table: Table = schema.load(table_dict).data
'cluster', results: Dict[str, Any] = schema.dump(table).data
'database',
'is_view',
'key',
'owners',
'schema',
'source',
'table_description',
'table_name',
'table_readers',
'table_writer',
'tags',
'watermarks',
# 'last_updated_timestamp' Exists on the response from metadata but is not used.
# This should also be consolidated with 'last_updated_epoch' to have the same name and format.
]
results = {field: table.get(field, None) for field in fields}
is_editable = results['schema'] not in app.config['UNEDITABLE_SCHEMAS'] is_editable = results['schema'] not in app.config['UNEDITABLE_SCHEMAS']
results['is_editable'] = is_editable results['is_editable'] = is_editable
# In the list of owners, sanitize each entry # TODO - Cleanup https://github.com/lyft/amundsen/issues/296
# This code will try to supplement some missing data since the data here is incomplete.
# Once the metadata service response provides complete user objects we can remove this.
results['owners'] = [_map_user_object_to_schema(owner) for owner in results['owners']] results['owners'] = [_map_user_object_to_schema(owner) for owner in results['owners']]
# In the list of reader_objects, sanitize the reader value on each entry
readers = results['table_readers'] readers = results['table_readers']
for reader_object in readers: for reader_object in readers:
reader_object['reader'] = _map_user_object_to_schema(reader_object['reader']) reader_object['user'] = _map_user_object_to_schema(reader_object['user'])
# If order is provided, we sort the column based on the pre-defined order # If order is provided, we sort the column based on the pre-defined order
if app.config['COLUMN_STAT_ORDER']: if app.config['COLUMN_STAT_ORDER']:
...@@ -77,6 +59,8 @@ def marshall_table_full(table: Dict) -> Dict: ...@@ -77,6 +59,8 @@ def marshall_table_full(table: Dict) -> Dict:
get(x['stat_type'], len(app.config['COLUMN_STAT_ORDER']))) get(x['stat_type'], len(app.config['COLUMN_STAT_ORDER'])))
col['is_editable'] = is_editable col['is_editable'] = is_editable
# TODO: Add the 'key' or 'id' to the base TableSchema
results['key'] = f'{table.database}://{table.cluster}.{table.schema}/{ table.name}'
# Temp code to make 'partition_key' and 'partition_value' part of the table # Temp code to make 'partition_key' and 'partition_value' part of the table
results['partition'] = _get_partition_data(results['watermarks']) results['partition'] = _get_partition_data(results['watermarks'])
return results return results
......
...@@ -8,6 +8,10 @@ from flask import current_app as app ...@@ -8,6 +8,10 @@ from flask import current_app as app
""" """
TODO: Explore all internationalization use cases and TODO: Explore all internationalization use cases and
redesign how User handles names redesign how User handles names
TODO - Delete this file
Once all of the upstream services provide a complete User object we will no
longer need to supplement the User objects as done in `preprocess_data`
""" """
...@@ -81,8 +85,11 @@ class UserSchema(Schema): ...@@ -81,8 +85,11 @@ class UserSchema(Schema):
if app.config['GET_PROFILE_URL']: if app.config['GET_PROFILE_URL']:
data['profile_url'] = app.config['GET_PROFILE_URL'](data['user_id']) data['profile_url'] = app.config['GET_PROFILE_URL'](data['user_id'])
# Fallback since search and metadata use a different key for 'full_name' first_name = data.get('first_name')
data['full_name'] = data.get('full_name', data.get('name')) last_name = data.get('last_name')
if self._str_no_value(data.get('full_name')) and first_name and last_name:
data['full_name'] = f"{first_name} {last_name}"
if self. _str_no_value(data.get('display_name')): if self. _str_no_value(data.get('display_name')):
if self._str_no_value(data.get('full_name')): if self._str_no_value(data.get('full_name')):
......
...@@ -43,7 +43,7 @@ export class ColumnListItem extends React.Component<ColumnListItemProps, ColumnL ...@@ -43,7 +43,7 @@ export class ColumnListItem extends React.Component<ColumnListItemProps, ColumnL
logClick(e, { logClick(e, {
target_id: `column::${metadata.name}`, target_id: `column::${metadata.name}`,
target_type: 'column stats', target_type: 'column stats',
label: `${metadata.name} ${metadata.type}`, label: `${metadata.name} ${metadata.col_type}`,
}); });
} }
this.setState({ isExpanded: !this.state.isExpanded }); this.setState({ isExpanded: !this.state.isExpanded });
...@@ -122,7 +122,7 @@ export class ColumnListItem extends React.Component<ColumnListItemProps, ColumnL ...@@ -122,7 +122,7 @@ export class ColumnListItem extends React.Component<ColumnListItemProps, ColumnL
} }
</div> </div>
<div className="resource-type"> <div className="resource-type">
{ this.renderColumnType(this.props.index, metadata.type) } { this.renderColumnType(this.props.index, metadata.col_type) }
</div> </div>
<div className="badges"> <div className="badges">
{/* Placeholder */} {/* Placeholder */}
......
...@@ -18,7 +18,7 @@ describe('ColumnListItem', () => { ...@@ -18,7 +18,7 @@ describe('ColumnListItem', () => {
name: "test_column_name", name: "test_column_name",
description: "This is a test description of this table", description: "This is a test description of this table",
is_editable: true, is_editable: true,
type: "varchar(32)", col_type: "varchar(32)",
stats: [{ end_epoch: 1571616000, start_epoch: 1571616000, stat_type: "count", stat_val: "12345" }] stats: [{ end_epoch: 1571616000, start_epoch: 1571616000, stat_type: "count", stat_val: "12345" }]
}, },
index: 0, index: 0,
...@@ -86,7 +86,7 @@ describe('ColumnListItem', () => { ...@@ -86,7 +86,7 @@ describe('ColumnListItem', () => {
it('renders the correct resource type', () => { it('renders the correct resource type', () => {
const resourceType = wrapper.find('.resource-type'); const resourceType = wrapper.find('.resource-type');
expect(resourceType.text()).toBe(props.data.type.toLowerCase()); expect(resourceType.text()).toBe(props.data.col_type.toLowerCase());
}); });
it('renders the dropdown when notifications is enabled', () => { it('renders the dropdown when notifications is enabled', () => {
......
...@@ -77,7 +77,7 @@ export class DataPreviewButton extends React.Component<DataPreviewButtonProps, D ...@@ -77,7 +77,7 @@ export class DataPreviewButton extends React.Component<DataPreviewButtonProps, D
this.props.getPreviewData({ this.props.getPreviewData({
database: tableData.database, database: tableData.database,
schema: tableData.schema, schema: tableData.schema,
tableName: tableData.table_name, tableName: tableData.name,
}); });
} }
......
...@@ -19,10 +19,10 @@ export class ExploreButton extends React.Component<ExploreButtonProps> { ...@@ -19,10 +19,10 @@ export class ExploreButton extends React.Component<ExploreButtonProps> {
if (partition.is_partitioned) { if (partition.is_partitioned) {
return AppConfig.tableProfile.exploreUrlGenerator( return AppConfig.tableProfile.exploreUrlGenerator(
tableData.database, tableData.cluster, tableData.schema, tableData.table_name, partition.key, partition.value); tableData.database, tableData.cluster, tableData.schema, tableData.name, partition.key, partition.value);
} }
return AppConfig.tableProfile.exploreUrlGenerator( return AppConfig.tableProfile.exploreUrlGenerator(
tableData.database, tableData.cluster, tableData.schema, tableData.table_name); tableData.database, tableData.cluster, tableData.schema, tableData.name);
} }
render() { render() {
......
...@@ -12,7 +12,7 @@ export interface FrequentUsersProps { ...@@ -12,7 +12,7 @@ export interface FrequentUsersProps {
} }
export function renderReader(reader: TableReader, index: number, readers: TableReader[]) { export function renderReader(reader: TableReader, index: number, readers: TableReader[]) {
const user = reader.reader; const user = reader.user;
let link = user.profile_url; let link = user.profile_url;
let target = '_blank'; let target = '_blank';
if (AppConfig.indexUsers.enabled) { if (AppConfig.indexUsers.enabled) {
......
...@@ -13,8 +13,8 @@ const LineageLink: React.SFC<LineageLinkProps> = ({ tableData }) => { ...@@ -13,8 +13,8 @@ const LineageLink: React.SFC<LineageLinkProps> = ({ tableData }) => {
const config = AppConfig.tableLineage; const config = AppConfig.tableLineage;
if (!config.isEnabled) return null; if (!config.isEnabled) return null;
const { database, cluster, schema, table_name } = tableData; const { database, cluster, schema, name } = tableData;
const href = config.urlGenerator(database, cluster, schema, table_name); const href = config.urlGenerator(database, cluster, schema, name);
const label = 'Lineage'; const label = 'Lineage';
return ( return (
......
...@@ -102,15 +102,15 @@ export class RequestMetadataForm extends React.Component<RequestMetadataProps, R ...@@ -102,15 +102,15 @@ export class RequestMetadataForm extends React.Component<RequestMetadataProps, R
const descriptionRequested = formData.get('table-description') === "on"; const descriptionRequested = formData.get('table-description') === "on";
const fieldsRequested = formData.get('column-description') === "on"; const fieldsRequested = formData.get('column-description') === "on";
const comment = formData.get('comment') as string; const comment = formData.get('comment') as string;
const { cluster, database, schema, table_name } = this.props.tableMetadata; const { cluster, database, schema, name } = this.props.tableMetadata;
this.props.submitNotification( this.props.submitNotification(
recipients, recipients,
sender, sender,
NotificationType.METADATA_REQUESTED, NotificationType.METADATA_REQUESTED,
{ {
comment, comment,
resource_name: `${schema}.${table_name}`, resource_name: `${schema}.${name}`,
resource_path: `/table_detail/${cluster}/${database}/${schema}/${table_name}`, resource_path: `/table_detail/${cluster}/${database}/${schema}/${name}`,
description_requested: descriptionRequested, description_requested: descriptionRequested,
fields_requested: fieldsRequested, fields_requested: fieldsRequested,
} }
......
...@@ -114,7 +114,7 @@ describe('RequestMetadataForm', () => { ...@@ -114,7 +114,7 @@ describe('RequestMetadataForm', () => {
it('calls submitNotification', () => { it('calls submitNotification', () => {
const { props, wrapper } = setup(); const { props, wrapper } = setup();
const submitNotificationSpy = jest.spyOn(props, 'submitNotification'); const submitNotificationSpy = jest.spyOn(props, 'submitNotification');
const { cluster, database, schema, table_name } = props.tableMetadata; const { cluster, database, schema, name } = props.tableMetadata;
wrapper.instance().submitNotification({ preventDefault: jest.fn() }); wrapper.instance().submitNotification({ preventDefault: jest.fn() });
expect(submitNotificationSpy).toHaveBeenCalledWith( expect(submitNotificationSpy).toHaveBeenCalledWith(
mockFormData['recipients'].split(','), mockFormData['recipients'].split(','),
...@@ -122,8 +122,8 @@ describe('RequestMetadataForm', () => { ...@@ -122,8 +122,8 @@ describe('RequestMetadataForm', () => {
NotificationType.METADATA_REQUESTED, NotificationType.METADATA_REQUESTED,
{ {
comment: mockFormData['comment'], comment: mockFormData['comment'],
resource_name: `${schema}.${table_name}`, resource_name: `${schema}.${name}`,
resource_path: `/table_detail/${cluster}/${database}/${schema}/${table_name}`, resource_path: `/table_detail/${cluster}/${database}/${schema}/${name}`,
description_requested: true, description_requested: true,
fields_requested: false, fields_requested: false,
} }
......
...@@ -8,7 +8,7 @@ import EditableText, { ComponentProps, DispatchFromProps, StateFromProps } from ...@@ -8,7 +8,7 @@ import EditableText, { ComponentProps, DispatchFromProps, StateFromProps } from
export const mapStateToProps = (state: GlobalState) => { export const mapStateToProps = (state: GlobalState) => {
return { return {
refreshValue: state.tableMetadata.tableData.table_description, refreshValue: state.tableMetadata.tableData.description,
}; };
}; };
......
...@@ -170,7 +170,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps ...@@ -170,7 +170,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
<EditableSection title="Description"> <EditableSection title="Description">
<TableDescEditableText <TableDescEditableText
maxLength={ AppConfig.editableText.tableDescLength } maxLength={ AppConfig.editableText.tableDescLength }
value={ data.table_description } value={ data.description }
editable={ data.is_editable } editable={ data.is_editable }
/> />
</EditableSection> </EditableSection>
......
...@@ -26,7 +26,7 @@ describe('MyBookmarks', () => { ...@@ -26,7 +26,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmark-2', key: 'bookmark-2',
...@@ -35,7 +35,7 @@ describe('MyBookmarks', () => { ...@@ -35,7 +35,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmark-3', key: 'bookmark-3',
...@@ -44,7 +44,7 @@ describe('MyBookmarks', () => { ...@@ -44,7 +44,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmark-4', key: 'bookmark-4',
...@@ -53,7 +53,7 @@ describe('MyBookmarks', () => { ...@@ -53,7 +53,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmark-5', key: 'bookmark-5',
...@@ -62,7 +62,7 @@ describe('MyBookmarks', () => { ...@@ -62,7 +62,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmark-6', key: 'bookmark-6',
...@@ -71,7 +71,7 @@ describe('MyBookmarks', () => { ...@@ -71,7 +71,7 @@ describe('MyBookmarks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
], ],
isLoaded: true, isLoaded: true,
......
...@@ -21,13 +21,13 @@ class TableListItem extends React.Component<TableListItemProps, {}> { ...@@ -21,13 +21,13 @@ class TableListItem extends React.Component<TableListItemProps, {}> {
getDateLabel = () => { getDateLabel = () => {
const { table } = this.props; const { table } = this.props;
const dateTokens = new Date(table.last_updated_epoch * 1000).toDateString().split(' '); const dateTokens = new Date(table.last_updated_timestamp * 1000).toDateString().split(' ');
return `${dateTokens[1]} ${dateTokens[2]}, ${dateTokens[3]}`; return `${dateTokens[1]} ${dateTokens[2]}, ${dateTokens[3]}`;
}; };
getLink = () => { getLink = () => {
const { table, logging } = this.props; const { table, logging } = this.props;
return `/table_detail/${table.cluster}/${table.database}/${table.schema_name}/${table.name}` return `/table_detail/${table.cluster}/${table.database}/${table.schema}/${table.name}`
+ `?index=${logging.index}&source=${logging.source}`; + `?index=${logging.index}&source=${logging.source}`;
}; };
...@@ -37,7 +37,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> { ...@@ -37,7 +37,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> {
render() { render() {
const { table } = this.props; const { table } = this.props;
const hasLastUpdated = !!table.last_updated_epoch; const hasLastUpdated = !!table.last_updated_timestamp;
return ( return (
<li className="list-group-item"> <li className="list-group-item">
...@@ -47,7 +47,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> { ...@@ -47,7 +47,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> {
<div className="resource-info-text"> <div className="resource-info-text">
<div className="resource-name title-2"> <div className="resource-name title-2">
<div className="truncated"> <div className="truncated">
{ `${table.schema_name}.${table.name}`} { `${table.schema}.${table.name}`}
</div> </div>
<BookmarkIcon bookmarkKey={ this.props.table.key }/> <BookmarkIcon bookmarkKey={ this.props.table.key }/>
</div> </div>
......
...@@ -31,9 +31,9 @@ describe('TableListItem', () => { ...@@ -31,9 +31,9 @@ describe('TableListItem', () => {
database: 'testdb', database: 'testdb',
description: 'I am the description', description: 'I am the description',
key: '', key: '',
last_updated_epoch: 1553829681, last_updated_timestamp: 1553829681,
name: 'tableName', name: 'tableName',
schema_name: 'tableSchema', schema: 'tableSchema',
}, },
...propOverrides ...propOverrides
}; };
...@@ -53,7 +53,7 @@ describe('TableListItem', () => { ...@@ -53,7 +53,7 @@ describe('TableListItem', () => {
it('getLink returns correct string', () => { it('getLink returns correct string', () => {
const { props, wrapper } = setup(); const { props, wrapper } = setup();
const { table, logging } = props; const { table, logging } = props;
expect(wrapper.instance().getLink()).toEqual(`/table_detail/${table.cluster}/${table.database}/${table.schema_name}/${table.name}?index=${logging.index}&source=${logging.source}`); expect(wrapper.instance().getLink()).toEqual(`/table_detail/${table.cluster}/${table.database}/${table.schema}/${table.name}?index=${logging.index}&source=${logging.source}`);
}); });
}); });
...@@ -133,7 +133,7 @@ describe('TableListItem', () => { ...@@ -133,7 +133,7 @@ describe('TableListItem', () => {
expect(resourceBadges.exists()).toBe(true); expect(resourceBadges.exists()).toBe(true);
}); });
describe('if props.table has last_updated_epoch', () => { describe('if props.table has last_updated_timestamp', () => {
it('renders Last Updated title', () => { it('renders Last Updated title', () => {
expect(resourceBadges.children().at(0).children().at(0).text()).toEqual('Last Updated'); expect(resourceBadges.children().at(0).children().at(0).text()).toEqual('Last Updated');
}); });
...@@ -143,7 +143,7 @@ describe('TableListItem', () => { ...@@ -143,7 +143,7 @@ describe('TableListItem', () => {
}); });
}); });
describe('if props.table does not have last_updated_epoch', () => { describe('if props.table does not have last_updated_timestamp', () => {
it('does not render Last Updated section', () => { it('does not render Last Updated section', () => {
const { props, wrapper } = setup({ table: { const { props, wrapper } = setup({ table: {
type: ResourceType.table, type: ResourceType.table,
...@@ -151,9 +151,9 @@ describe('TableListItem', () => { ...@@ -151,9 +151,9 @@ describe('TableListItem', () => {
database: '', database: '',
description: 'I am the description', description: 'I am the description',
key: '', key: '',
last_updated_epoch: null, last_updated_timestamp: null,
name: 'tableName', name: 'tableName',
schema_name: 'tableSchema', schema: 'tableSchema',
}}); }});
expect(wrapper.find('.resource-badges').children()).toHaveLength(1); expect(wrapper.find('.resource-badges').children()).toHaveLength(1);
}); });
......
...@@ -93,7 +93,7 @@ export class InlineSearchResults extends React.Component<InlineSearchResultsProp ...@@ -93,7 +93,7 @@ export class InlineSearchResults extends React.Component<InlineSearchResultsProp
switch (resourceType) { switch (resourceType) {
case ResourceType.table: case ResourceType.table:
const table = result as TableResource; const table = result as TableResource;
return `/table_detail/${table.cluster}/${table.database}/${table.schema_name}/${table.name}?${logParams}`; return `/table_detail/${table.cluster}/${table.database}/${table.schema}/${table.name}?${logParams}`;
case ResourceType.user: case ResourceType.user:
const user = result as UserResource; const user = result as UserResource;
return `/user/${user.user_id}?${logParams}`; return `/user/${user.user_id}?${logParams}`;
...@@ -131,7 +131,7 @@ export class InlineSearchResults extends React.Component<InlineSearchResultsProp ...@@ -131,7 +131,7 @@ export class InlineSearchResults extends React.Component<InlineSearchResultsProp
switch (resourceType) { switch (resourceType) {
case ResourceType.table: case ResourceType.table:
const table = result as TableResource; const table = result as TableResource;
return `${table.schema_name}.${table.name}`; return `${table.schema}.${table.name}`;
case ResourceType.user: case ResourceType.user:
const user = result as UserResource; const user = result as UserResource;
return user.display_name; return user.display_name;
......
...@@ -183,9 +183,9 @@ describe('InlineSearchResults', () => { ...@@ -183,9 +183,9 @@ describe('InlineSearchResults', () => {
it('returns the correct href for ResourceType.table', () => { it('returns the correct href for ResourceType.table', () => {
const index = 0; const index = 0;
const givenTable = props.tables.results[index]; const givenTable = props.tables.results[index];
const { cluster, database, schema_name, name } = givenTable; const { cluster, database, schema, name } = givenTable;
const output = wrapper.instance().getSuggestedResultHref(ResourceType.table, givenTable, index); const output = wrapper.instance().getSuggestedResultHref(ResourceType.table, givenTable, index);
expect(output).toEqual(`/table_detail/${cluster}/${database}/${schema_name}/${name}?source=inline_search&index=${index}`); expect(output).toEqual(`/table_detail/${cluster}/${database}/${schema}/${name}?source=inline_search&index=${index}`);
}); });
it('returns the correct href for ResourceType.user', () => { it('returns the correct href for ResourceType.user', () => {
const index = 0; const index = 0;
...@@ -258,10 +258,10 @@ describe('InlineSearchResults', () => { ...@@ -258,10 +258,10 @@ describe('InlineSearchResults', () => {
props = setupResult.props; props = setupResult.props;
wrapper = setupResult.wrapper; wrapper = setupResult.wrapper;
}); });
it('returns the schema_name.name for ResourceType.table', () => { it('returns the schema.name for ResourceType.table', () => {
const givenTable = props.tables.results[0]; const givenTable = props.tables.results[0];
const output = wrapper.instance().getSuggestedResultTitle(ResourceType.table, givenTable); const output = wrapper.instance().getSuggestedResultTitle(ResourceType.table, givenTable);
expect(output).toEqual(`${givenTable.schema_name}.${givenTable.name}`); expect(output).toEqual(`${givenTable.schema}.${givenTable.name}`);
}); });
it('returns the display_name ResourceType.user', () => { it('returns the display_name ResourceType.user', () => {
const givenUser = props.users.results[0]; const givenUser = props.users.results[0];
......
...@@ -42,7 +42,7 @@ describe('bookmark ducks', () => { ...@@ -42,7 +42,7 @@ describe('bookmark ducks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
]; ];
}); });
...@@ -142,7 +142,7 @@ describe('bookmark ducks', () => { ...@@ -142,7 +142,7 @@ describe('bookmark ducks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
{ {
key: 'bookmarked_key_1', key: 'bookmarked_key_1',
...@@ -151,7 +151,7 @@ describe('bookmark ducks', () => { ...@@ -151,7 +151,7 @@ describe('bookmark ducks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
]; ];
testState = { testState = {
...@@ -182,7 +182,7 @@ describe('bookmark ducks', () => { ...@@ -182,7 +182,7 @@ describe('bookmark ducks', () => {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}], }],
}); });
}); });
......
...@@ -79,9 +79,9 @@ describe('search ducks', () => { ...@@ -79,9 +79,9 @@ describe('search ducks', () => {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName', key: 'testDatabase://testCluster.testSchema/testName',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName', name: 'testName',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
], ],
...@@ -104,9 +104,9 @@ describe('search ducks', () => { ...@@ -104,9 +104,9 @@ describe('search ducks', () => {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName', key: 'testDatabase://testCluster.testSchema/testName',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName', name: 'testName',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
], ],
......
...@@ -45,8 +45,8 @@ export function createOwnerNotificationData(payload: UpdateOwnerPayload, tableDa ...@@ -45,8 +45,8 @@ export function createOwnerNotificationData(payload: UpdateOwnerPayload, tableDa
return { return {
notificationType: payload.method === UpdateMethod.PUT ? NotificationType.OWNER_ADDED : NotificationType.OWNER_REMOVED, notificationType: payload.method === UpdateMethod.PUT ? NotificationType.OWNER_ADDED : NotificationType.OWNER_REMOVED,
options: { options: {
resource_name: `${tableData.schema}.${tableData.table_name}`, resource_name: `${tableData.schema}.${tableData.name}`,
resource_path: `/table_detail/${tableData.cluster}/${tableData.database}/${tableData.schema}/${tableData.table_name}` resource_path: `/table_detail/${tableData.cluster}/${tableData.database}/${tableData.schema}/${tableData.name}`
}, },
recipients: [payload.id], recipients: [payload.id],
}; };
......
...@@ -80,8 +80,8 @@ describe('helpers', () => { ...@@ -80,8 +80,8 @@ describe('helpers', () => {
beforeAll(() => { beforeAll(() => {
testData = globalState.tableMetadata.tableData; testData = globalState.tableMetadata.tableData;
testId = 'testId@test.com'; testId = 'testId@test.com';
expectedName = `${testData.schema}.${testData.table_name}`; expectedName = `${testData.schema}.${testData.name}`;
expectedPath = `/table_detail/${testData.cluster}/${testData.database}/${testData.schema}/${testData.table_name}`; expectedPath = `/table_detail/${testData.cluster}/${testData.database}/${testData.schema}/${testData.name}`;
}); });
it('creates correct request data for PUT', () => { it('creates correct request data for PUT', () => {
......
...@@ -62,7 +62,7 @@ export function getTableDescription(tableData: TableMetadata) { ...@@ -62,7 +62,7 @@ export function getTableDescription(tableData: TableMetadata) {
const tableParams = getTableQueryParams(tableData.key); const tableParams = getTableQueryParams(tableData.key);
return axios.get(`${API_PATH}/get_table_description?${tableParams}`) return axios.get(`${API_PATH}/get_table_description?${tableParams}`)
.then((response: AxiosResponse<DescriptionAPI>) => { .then((response: AxiosResponse<DescriptionAPI>) => {
tableData.table_description = response.data.description; tableData.description = response.data.description;
return tableData; return tableData;
}); });
} }
......
...@@ -187,8 +187,8 @@ export const initialTableDataState: TableMetadata = { ...@@ -187,8 +187,8 @@ export const initialTableDataState: TableMetadata = {
is_view: false, is_view: false,
key: '', key: '',
schema: '', schema: '',
table_name: '', name: '',
table_description: '', description: '',
table_writer: { application_url: '', description: '', id: '', name: '' }, table_writer: { application_url: '', description: '', id: '', name: '' },
partition: { is_partitioned: false }, partition: { is_partitioned: false },
table_readers: [], table_readers: [],
......
...@@ -23,7 +23,7 @@ const globalState: GlobalState = { ...@@ -23,7 +23,7 @@ const globalState: GlobalState = {
database: 'database', database: 'database',
description: 'description', description: 'description',
name: 'name', name: 'name',
schema_name: 'schema_name', schema: 'schema',
}, },
], ],
myBookmarksIsLoaded: false, myBookmarksIsLoaded: false,
...@@ -43,7 +43,7 @@ const globalState: GlobalState = { ...@@ -43,7 +43,7 @@ const globalState: GlobalState = {
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName', key: 'testDatabase://testCluster.testSchema/testName',
name: 'testName', name: 'testName',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
{ {
...@@ -52,7 +52,7 @@ const globalState: GlobalState = { ...@@ -52,7 +52,7 @@ const globalState: GlobalState = {
description: 'I also have a lot of users', description: 'I also have a lot of users',
key: 'testDatabase://testCluster.testSchema/otherName', key: 'testDatabase://testCluster.testSchema/otherName',
name: 'otherName', name: 'otherName',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
} }
], ],
...@@ -73,9 +73,9 @@ const globalState: GlobalState = { ...@@ -73,9 +73,9 @@ const globalState: GlobalState = {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName', key: 'testDatabase://testCluster.testSchema/testName',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName', name: 'testName',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
], ],
...@@ -117,8 +117,8 @@ const globalState: GlobalState = { ...@@ -117,8 +117,8 @@ const globalState: GlobalState = {
is_view: false, is_view: false,
key: '', key: '',
schema: '', schema: '',
table_name: '', name: '',
table_description: '', description: '',
table_writer: { application_url: '', description: '', id: '', name: '' }, table_writer: { application_url: '', description: '', id: '', name: '' },
partition: { is_partitioned: false }, partition: { is_partitioned: false },
table_readers: [], table_readers: [],
......
...@@ -38,9 +38,9 @@ export const allResourcesExample = { ...@@ -38,9 +38,9 @@ export const allResourcesExample = {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName1', key: 'testDatabase://testCluster.testSchema/testName1',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName1', name: 'testName1',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
{ {
...@@ -48,9 +48,9 @@ export const allResourcesExample = { ...@@ -48,9 +48,9 @@ export const allResourcesExample = {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName2', key: 'testDatabase://testCluster.testSchema/testName2',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName2', name: 'testName2',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
}, },
{ {
...@@ -58,9 +58,9 @@ export const allResourcesExample = { ...@@ -58,9 +58,9 @@ export const allResourcesExample = {
database: 'testDatabase', database: 'testDatabase',
description: 'I have a lot of users', description: 'I have a lot of users',
key: 'testDatabase://testCluster.testSchema/testName3', key: 'testDatabase://testCluster.testSchema/testName3',
last_updated_epoch: 946684799, last_updated_timestamp: 946684799,
name: 'testName3', name: 'testName3',
schema_name: 'testSchema', schema: 'testSchema',
type: ResourceType.table, type: ResourceType.table,
} }
], ],
......
...@@ -24,10 +24,10 @@ export interface TableResource extends Resource { ...@@ -24,10 +24,10 @@ export interface TableResource extends Resource {
database: string; database: string;
description: string; description: string;
key: string; key: string;
// 'popular_tables' currently does not support 'last_updated_epoch' // 'popular_tables' currently does not support 'last_updated_timestamp'
last_updated_epoch?: number; last_updated_timestamp?: number;
name: string; name: string;
schema_name: string; schema: string;
}; };
export interface UserResource extends Resource, PeopleUser { export interface UserResource extends Resource, PeopleUser {
......
...@@ -28,7 +28,7 @@ export interface TableColumnStats { ...@@ -28,7 +28,7 @@ export interface TableColumnStats {
export interface TableReader { export interface TableReader {
read_count: number; read_count: number;
reader: User; user: User;
} }
export interface TableSource { export interface TableSource {
...@@ -59,7 +59,7 @@ export interface TableColumn { ...@@ -59,7 +59,7 @@ export interface TableColumn {
name: string; name: string;
description: string; description: string;
is_editable: boolean; is_editable: boolean;
type: string; col_type: string;
stats: TableColumnStats[]; stats: TableColumnStats[];
} }
...@@ -77,8 +77,8 @@ export interface TableMetadata { ...@@ -77,8 +77,8 @@ export interface TableMetadata {
is_view: boolean; is_view: boolean;
key: string; key: string;
schema: string; schema: string;
table_name: string; name: string;
table_description: string; description: string;
table_writer: TableWriter; table_writer: TableWriter;
partition: PartitionData; partition: PartitionData;
table_readers: TableReader[]; table_readers: TableReader[];
......
...@@ -58,7 +58,14 @@ requests==2.20.0 ...@@ -58,7 +58,14 @@ requests==2.20.0
# Upstream url: https://github.com/marshmallow-code/marshmallow # Upstream url: https://github.com/marshmallow-code/marshmallow
marshmallow==2.15.3 marshmallow==2.15.3
# Allows declaring marshmallow schema through type annotations
# License: MIT
# Upstream url: https://github.com/justanr/marshmallow-annotations
marshmallow-annotations==2.4.0
# A utility library for mocking out the requests Python library. # A utility library for mocking out the requests Python library.
# License: Apache 2.0 # License: Apache 2.0
# Upstream url: https://pypi.org/project/responses/ # Upstream url: https://pypi.org/project/responses/
responses==0.9.0 responses==0.9.0
amundsen-common==0.2.2
\ No newline at end of file
...@@ -34,7 +34,7 @@ requirements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'r ...@@ -34,7 +34,7 @@ requirements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'r
with open(requirements_path) as requirements_file: with open(requirements_path) as requirements_file:
requirements = requirements_file.readlines() requirements = requirements_file.readlines()
__version__ = '1.2.1' __version__ = '2.0.0'
setup( setup(
......
...@@ -20,11 +20,10 @@ class MetadataTest(unittest.TestCase): ...@@ -20,11 +20,10 @@ class MetadataTest(unittest.TestCase):
{ {
'cluster': 'test_cluster', 'cluster': 'test_cluster',
'database': 'test_db', 'database': 'test_db',
'key': 'test_db://test_cluster.test_schema/test_table',
'schema': 'test_schema', 'schema': 'test_schema',
'table_description': 'This is a test', 'description': 'This is a test',
'table_name': 'test_table', 'name': 'test_table',
'type': 'table', 'key': 'test_db://test_cluster.test_schema/test_table',
} }
] ]
} }
...@@ -33,11 +32,11 @@ class MetadataTest(unittest.TestCase): ...@@ -33,11 +32,11 @@ class MetadataTest(unittest.TestCase):
'cluster': 'test_cluster', 'cluster': 'test_cluster',
'database': 'test_db', 'database': 'test_db',
'description': 'This is a test', 'description': 'This is a test',
'key': 'test_db://test_cluster.test_schema/test_table', 'schema': 'test_schema',
'schema_name': 'test_schema',
'type': 'table', 'type': 'table',
'name': 'test_table', 'name': 'test_table',
'last_updated_epoch': None, 'key': 'test_db://test_cluster.test_schema/test_table',
'last_updated_timestamp': None,
} }
] ]
self.mock_metadata = { self.mock_metadata = {
...@@ -46,7 +45,7 @@ class MetadataTest(unittest.TestCase): ...@@ -46,7 +45,7 @@ class MetadataTest(unittest.TestCase):
{ {
'name': 'column_1', 'name': 'column_1',
'description': 'This is a test', 'description': 'This is a test',
'type': 'bigint', 'col_type': 'bigint',
'sort_order': 0, 'sort_order': 0,
'stats': [ 'stats': [
{'stat_type': 'count', 'stat_val': '100', 'start_epoch': 1538352000, 'end_epoch': 1538352000}, {'stat_type': 'count', 'stat_val': '100', 'start_epoch': 1538352000, 'end_epoch': 1538352000},
...@@ -59,11 +58,12 @@ class MetadataTest(unittest.TestCase): ...@@ -59,11 +58,12 @@ class MetadataTest(unittest.TestCase):
'key': 'test_db://test_cluster.test_schema/test_table', 'key': 'test_db://test_cluster.test_schema/test_table',
'owners': [], 'owners': [],
'schema': 'test_schema', 'schema': 'test_schema',
'table_name': 'test_table', 'name': 'test_table',
'table_description': 'This is a test', 'description': 'This is a test',
'programmatic_descriptions': [],
'tags': [], 'tags': [],
'table_readers': [ 'table_readers': [
{'reader': {'email': 'test@test.com', 'first_name': None, 'last_name': None}, 'read_count': 100} {'user': {'email': 'test@test.com', 'first_name': None, 'last_name': None}, 'read_count': 100}
], ],
'watermarks': [ 'watermarks': [
{'watermark_type': 'low_watermark', 'partition_key': 'ds', 'partition_value': '', 'create_time': ''}, {'watermark_type': 'low_watermark', 'partition_key': 'ds', 'partition_value': '', 'create_time': ''},
...@@ -77,17 +77,18 @@ class MetadataTest(unittest.TestCase): ...@@ -77,17 +77,18 @@ class MetadataTest(unittest.TestCase):
}, },
} }
self.expected_parsed_metadata = { self.expected_parsed_metadata = {
'key': 'table_key',
'badges': [], 'badges': [],
'cluster': 'test_cluster', 'cluster': 'test_cluster',
'database': 'test_db', 'database': 'test_db',
'schema': 'test_schema', 'schema': 'test_schema',
'table_name': 'test_table', 'name': 'test_table',
'table_description': 'This is a test', 'key': 'test_db://test_cluster.test_schema/test_table',
'description': 'This is a test',
'programmatic_descriptions': [],
'tags': [], 'tags': [],
'table_readers': [ 'table_readers': [
{ {
'reader': { 'user': {
'email': 'test@test.com', 'email': 'test@test.com',
'first_name': None, 'first_name': None,
'last_name': None, 'last_name': None,
...@@ -108,7 +109,7 @@ class MetadataTest(unittest.TestCase): ...@@ -108,7 +109,7 @@ class MetadataTest(unittest.TestCase):
{ {
'name': 'column_1', 'name': 'column_1',
'description': 'This is a test', 'description': 'This is a test',
'type': 'bigint', 'col_type': 'bigint',
'sort_order': 0, 'sort_order': 0,
'stats': [ 'stats': [
{'stat_type': 'count', 'stat_val': '100', 'start_epoch': 1538352000, 'end_epoch': 1538352000}, {'stat_type': 'count', 'stat_val': '100', 'start_epoch': 1538352000, 'end_epoch': 1538352000},
...@@ -128,7 +129,8 @@ class MetadataTest(unittest.TestCase): ...@@ -128,7 +129,8 @@ class MetadataTest(unittest.TestCase):
{'watermark_type': 'high_watermark', 'partition_key': 'ds', 'partition_value': '', 'create_time': ''} {'watermark_type': 'high_watermark', 'partition_key': 'ds', 'partition_value': '', 'create_time': ''}
], ],
'source': '/source', 'source': '/source',
'is_editable': True 'is_editable': True,
'last_updated_timestamp': None,
} }
self.mock_tags = { self.mock_tags = {
'tag_usages': [ 'tag_usages': [
...@@ -209,15 +211,15 @@ class MetadataTest(unittest.TestCase): ...@@ -209,15 +211,15 @@ class MetadataTest(unittest.TestCase):
'cluster': 'cluster', 'cluster': 'cluster',
'database': 'database', 'database': 'database',
'schema': 'schema', 'schema': 'schema',
'table_name': 'table_name_0', 'name': 'table_name_0',
'table_description': 'description', 'description': 'description',
}, },
{ {
'cluster': 'cluster', 'cluster': 'cluster',
'database': 'database', 'database': 'database',
'schema': 'schema', 'schema': 'schema',
'table_name': 'table_name_1', 'name': 'table_name_1',
'table_description': 'description', 'description': 'description',
}, },
] ]
} }
...@@ -226,21 +228,21 @@ class MetadataTest(unittest.TestCase): ...@@ -226,21 +228,21 @@ class MetadataTest(unittest.TestCase):
'cluster': 'cluster', 'cluster': 'cluster',
'database': 'database', 'database': 'database',
'description': 'description', 'description': 'description',
'key': 'database://cluster.schema/table_name_0', 'last_updated_timestamp': None,
'last_updated_epoch': None,
'name': 'table_name_0', 'name': 'table_name_0',
'schema_name': 'schema', 'schema': 'schema',
'type': 'table', 'type': 'table',
'key': 'database://cluster.schema/table_name_0',
}, },
{ {
'cluster': 'cluster', 'cluster': 'cluster',
'database': 'database', 'database': 'database',
'description': 'description', 'description': 'description',
'key': 'database://cluster.schema/table_name_1', 'last_updated_timestamp': None,
'last_updated_epoch': None,
'name': 'table_name_1', 'name': 'table_name_1',
'schema_name': 'schema', 'schema': 'schema',
'type': 'table', 'type': 'table',
'key': 'database://cluster.schema/table_name_1',
}, },
] ]
......
...@@ -25,9 +25,9 @@ class SearchTest(unittest.TestCase): ...@@ -25,9 +25,9 @@ class SearchTest(unittest.TestCase):
'database': 'test_db', 'database': 'test_db',
'description': 'This is a test', 'description': 'This is a test',
'key': 'test_key', 'key': 'test_key',
'last_updated_epoch': 1527283287, 'last_updated_timestamp': 1527283287,
'name': 'test_table', 'name': 'test_table',
'schema_name': 'test_schema', 'schema': 'test_schema',
'tags': [], 'tags': [],
} }
] ]
...@@ -39,9 +39,9 @@ class SearchTest(unittest.TestCase): ...@@ -39,9 +39,9 @@ class SearchTest(unittest.TestCase):
'database': 'test_db', 'database': 'test_db',
'description': 'This is a test', 'description': 'This is a test',
'key': 'test_key', 'key': 'test_key',
'last_updated_epoch': 1527283287, 'last_updated_timestamp': 1527283287,
'name': 'test_table', 'name': 'test_table',
'schema_name': 'test_schema', 'schema': 'test_schema',
} }
] ]
self.mock_search_user_results = { self.mock_search_user_results = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment