Skip to content

Commit

Permalink
Improve table & columns description formatting (amundsen-io#98) (amun…
Browse files Browse the repository at this point in the history
…dsen-io#298)

* Add support for React-Markdown to editable text fields
* Add support for windows via cross-env
  • Loading branch information
Mikhail-Ivanov authored and Daniel committed Sep 25, 2019
1 parent 560b89d commit 1945ca6
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 60 deletions.
11 changes: 5 additions & 6 deletions amundsen_application/api/metadata/v0.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import logging

from http import HTTPStatus
Expand Down Expand Up @@ -259,13 +260,12 @@ def _log_put_table_description(*, table_key: str, description: str, source: str)
table_key = get_query_param(args, 'key')

description = get_query_param(args, 'description')
description = ' '.join(description.split())
src = get_query_param(args, 'source')

url = '{0}/{1}/description/{2}'.format(table_endpoint, table_key, description)
url = '{0}/{1}/description'.format(table_endpoint, table_key)
_log_put_table_description(table_key=table_key, description=description, source=src)

response = request_metadata(url=url, method='PUT')
response = request_metadata(url=url, method='PUT', json=json.dumps({'description': description}))
status_code = response.status_code

if status_code == HTTPStatus.OK:
Expand Down Expand Up @@ -295,14 +295,13 @@ def _log_put_column_description(*, table_key: str, column_name: str, description

column_name = get_query_param(args, 'column_name')
description = get_query_param(args, 'description')
description = ' '.join(description.split())

src = get_query_param(args, 'source')

url = '{0}/{1}/column/{2}/description/{3}'.format(table_endpoint, table_key, column_name, description)
url = '{0}/{1}/column/{2}/description'.format(table_endpoint, table_key, column_name)
_log_put_column_description(table_key=table_key, column_name=column_name, description=description, source=src)

response = request_metadata(url=url, method='PUT')
response = request_metadata(url=url, method='PUT', json=json.dumps({'description': description}))
status_code = response.status_code

if status_code == HTTPStatus.OK:
Expand Down
25 changes: 16 additions & 9 deletions amundsen_application/api/utils/request_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ def get_query_param(args: Dict, param: str, error_msg: str = None) -> str:
def request_metadata(*, # type: ignore
url: str,
method: str = 'GET',
timeout_sec: int = 0):
timeout_sec: int = 0,
json: str = '{}'):
"""
Helper function to make a request to metadata service.
Sets the client and header information based on the configuration
:param method: DELETE | GET | POST | PUT
:param url: The request URL
:param timeout_sec: Number of seconds before timeout is triggered.
:param json: Optional request payload
:return:
"""
if app.config['REQUEST_HEADERS_METHOD']:
Expand All @@ -32,19 +34,22 @@ def request_metadata(*, # type: ignore
url=url,
client=app.config['METADATASERVICE_REQUEST_CLIENT'],
headers=headers,
timeout_sec=timeout_sec)
timeout_sec=timeout_sec,
json=json)


def request_search(*, # type: ignore
url: str,
method: str = 'GET',
timeout_sec: int = 0):
timeout_sec: int = 0,
json: str = '{}'):
"""
Helper function to make a request to search service.
Sets the client and header information based on the configuration
:param method: DELETE | GET | POST | PUT
:param url: The request URL
:param timeout_sec: Number of seconds before timeout is triggered.
:param json: Optional request payload
:return:
"""
if app.config['REQUEST_HEADERS_METHOD']:
Expand All @@ -55,18 +60,20 @@ def request_search(*, # type: ignore
url=url,
client=app.config['SEARCHSERVICE_REQUEST_CLIENT'],
headers=headers,
timeout_sec=timeout_sec)
timeout_sec=timeout_sec,
json=json)


# TODO: Define an interface for envoy_client
def request_wrapper(method: str, url: str, client, headers, timeout_sec: int): # type: ignore
def request_wrapper(method: str, url: str, client, headers, timeout_sec: int, json: str = '{}'): # type: ignore
"""
Wraps a request to use Envoy client and headers, if available
:param method: DELETE | GET | POST | PUT
:param url: The request URL
:param client: Optional Envoy client
:param headers: Optional Envoy request headers
:param timeout_sec: Number of seconds before timeout is triggered. Not used with Envoy
:param json: Optional request payload
:return:
"""
# If no timeout specified, use the one from the configurations.
Expand All @@ -78,9 +85,9 @@ def request_wrapper(method: str, url: str, client, headers, timeout_sec: int):
elif method == 'GET':
return client.get(url, headers=headers, raw_response=True)
elif method == 'POST':
return client.post(url, headers=headers, raw_response=True)
return client.post(url, headers=headers, raw_response=True, json=json)
elif method == 'PUT':
return client.put(url, headers=headers, raw_response=True)
return client.put(url, headers=headers, raw_response=True, json=json)
else:
raise Exception('Method not allowed: {}'.format(method))
else:
Expand All @@ -90,8 +97,8 @@ def request_wrapper(method: str, url: str, client, headers, timeout_sec: int):
elif method == 'GET':
return s.get(url, headers=headers, timeout=timeout_sec)
elif method == 'POST':
return s.post(url, headers=headers, timeout=timeout_sec)
return s.post(url, headers=headers, timeout=timeout_sec, json=json)
elif method == 'PUT':
return s.put(url, headers=headers, timeout=timeout_sec)
return s.put(url, headers=headers, timeout=timeout_sec, json=json)
else:
raise Exception('Method not allowed: {}'.format(method))
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,6 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone
!data.is_view && <WatermarkLabel watermarks={ data.watermarks }/>
}
<TableDescEditableText
maxLength={ 750 }
value={ data.table_description }
editable={ data.is_editable }
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import * as ReactMarkdown from 'react-markdown';
import { Overlay, Popover, Tooltip } from 'react-bootstrap';
import autosize from 'autosize';

// TODO: Use css-modules instead of 'import'
// TODO: Outdated approach (lines 148, 168). Replace with React.createRef(). See more at https://reactjs.org/docs/refs-and-the-dom.html
import './styles.scss';

export interface StateFromProps {
Expand Down Expand Up @@ -36,7 +39,7 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>

public static defaultProps: EditableTextProps = {
editable: true,
maxLength: 250,
maxLength: 4000,
onSubmitValue: null,
getLatestValue: null,
value: '',
Expand All @@ -61,6 +64,7 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
componentDidUpdate() {
const { isDisabled, inEditMode, refreshValue, value } = this.state;
if (inEditMode) {
autosize(this.textAreaTarget);
if (refreshValue && refreshValue !== value && !isDisabled) {
// disable the component if a refresh is needed
this.setState({ isDisabled: true })
Expand Down Expand Up @@ -113,7 +117,9 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
if (!this.state.editable) {
return (
<div id='editable-container' className='editable-container'>
<div id='editable-text' className='editable-text'>{ this.state.value }</div>
<div id='editable-text' className='editable-text'>
<ReactMarkdown source={this.state.value}/>
</div>
</div>
);
}
Expand All @@ -135,7 +141,7 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
</Tooltip>
</Overlay>
<div id='editable-text' className={"editable-text"}>
{ this.state.value }
<ReactMarkdown source={this.state.value}/>
<a className={ "edit-link" + (this.state.value ? "" : " no-value") }
href="JavaScript:void(0)"
onClick={ this.enterEditMode }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import * as ReactMarkdown from 'react-markdown';

import { shallow } from 'enzyme';

Expand All @@ -13,7 +14,7 @@ describe('EditableText', () => {
beforeEach(() => {
props = {
editable: true,
maxLength: 250,
maxLength: 4000,
onSubmitValue: jest.fn(),
getLatestValue: jest.fn(),
refreshValue: 'newValue',
Expand All @@ -27,15 +28,15 @@ describe('EditableText', () => {
props.editable = false;
/* Note: Do not copy this pattern, for some reason setProps is not updating the content in this case */
subject = shallow(<EditableText {...props} />);
expect(subject.find('div#editable-text').text()).toEqual(props.value);
expect(subject.find('div#editable-text').find(ReactMarkdown).prop('source')).toEqual(props.value);
});

describe('renders correctly if !this.state.inEditMode', () => {
beforeEach(() => {
subject.setState({ inEditMode: false });
});
it('renders value as first child', () => {
expect(subject.find('#editable-text').props().children[0]).toEqual(props.value);
expect(subject.find('#editable-text').children().first().prop('source')).toEqual(props.value);
});

it('renders edit link to enterEditMode', () => {
Expand Down
Loading

0 comments on commit 1945ca6

Please sign in to comment.