From fd4f684a4214e166f4ce93fa70a01f04549cf58c Mon Sep 17 00:00:00 2001 From: Marina Samuel Date: Thu, 1 Nov 2018 12:11:32 -0400 Subject: [PATCH] Schema Improvements Part 2: Add data source config options. --- .../assets/less/redash/redash-newstyle.less | 4 + client/app/components/proptypes.js | 10 +- client/app/components/queries/SchemaData.jsx | 18 ++ .../components/queries/schema-browser.html | 5 +- .../app/components/queries/schema-browser.js | 14 +- .../schema-table-components/EditableTable.jsx | 86 ++++++ .../schema-table-components/SchemaTable.jsx | 264 ++++++++++++++++++ .../TableVisibilityCheckbox.jsx | 29 ++ .../schema-table-components/schema-table.css | 4 + client/app/services/data-source.js | 6 + migrations/versions/cf135a57332e_.py | 26 ++ redash/handlers/data_sources.py | 8 +- redash/models/__init__.py | 24 +- tests/models/test_data_sources.py | 16 +- tests/tasks/test_refresh_schemas.py | 3 + 15 files changed, 499 insertions(+), 18 deletions(-) create mode 100644 client/app/pages/data-sources/schema-table-components/EditableTable.jsx create mode 100644 client/app/pages/data-sources/schema-table-components/SchemaTable.jsx create mode 100644 client/app/pages/data-sources/schema-table-components/TableVisibilityCheckbox.jsx create mode 100644 client/app/pages/data-sources/schema-table-components/schema-table.css create mode 100644 migrations/versions/cf135a57332e_.py diff --git a/client/app/assets/less/redash/redash-newstyle.less b/client/app/assets/less/redash/redash-newstyle.less index 86baa41c14..5cf914c1da 100644 --- a/client/app/assets/less/redash/redash-newstyle.less +++ b/client/app/assets/less/redash/redash-newstyle.less @@ -78,6 +78,10 @@ body { } } +.admin-schema-editor { + padding: 50px 0; +} + .creation-container { h5 { color: #a7a7a7; diff --git a/client/app/components/proptypes.js b/client/app/components/proptypes.js index b569545aba..f276031791 100644 --- a/client/app/components/proptypes.js +++ b/client/app/components/proptypes.js @@ -16,10 +16,11 @@ export const DataSourceMetadata = PropTypes.shape({ name: PropTypes.string, type: PropTypes.string, example: PropTypes.string, + description: PropTypes.string, }); export const Table = PropTypes.shape({ - columns: PropTypes.arrayOf(PropTypes.string).isRequired, + columns: PropTypes.arrayOf(PropTypes.object).isRequired, }); export const Schema = PropTypes.arrayOf(Table); @@ -38,6 +39,13 @@ export const RefreshScheduleDefault = { until: null, }; +export const TableMetadata = PropTypes.shape({ + key: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + description: PropTypes.string, + visible: PropTypes.bool.isRequired, +}); + export const Field = PropTypes.shape({ name: PropTypes.string.isRequired, title: PropTypes.string, diff --git a/client/app/components/queries/SchemaData.jsx b/client/app/components/queries/SchemaData.jsx index 664ec27972..e1ad6bfab3 100644 --- a/client/app/components/queries/SchemaData.jsx +++ b/client/app/components/queries/SchemaData.jsx @@ -19,11 +19,13 @@ class SchemaData extends React.PureComponent { show: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, tableName: PropTypes.string, + tableDescription: PropTypes.string, tableMetadata: PropTypes.arrayOf(DataSourceMetadata), }; static defaultProps = { tableName: '', + tableDescription: '', tableMetadata: [], }; @@ -42,9 +44,22 @@ class SchemaData extends React.PureComponent { render: textWrapRenderer, }]; + const hasDescription = + this.props.tableMetadata.some(columnMetadata => columnMetadata.description); + const hasExample = this.props.tableMetadata.some(columnMetadata => columnMetadata.example); + if (hasDescription) { + columns.push({ + title: 'Description', + dataIndex: 'description', + width: 400, + key: 'description', + render: textWrapRenderer, + }); + } + if (hasExample) { columns.push({ title: 'Example', @@ -64,6 +79,9 @@ class SchemaData extends React.PureComponent { onClose={this.props.onClose} visible={this.props.show} > +
+ {this.props.tableDescription} +
-
+
@@ -27,7 +27,7 @@ ({{table.size}}) + ng-click="openSchemaInfo($event, table)">
@@ -43,6 +43,7 @@ diff --git a/client/app/components/queries/schema-browser.js b/client/app/components/queries/schema-browser.js index d6f692e753..c5ab533f8e 100644 --- a/client/app/components/queries/schema-browser.js +++ b/client/app/components/queries/schema-browser.js @@ -12,9 +12,10 @@ function SchemaBrowserCtrl($rootScope, $scope) { }; $scope.showSchemaInfo = false; - $scope.openSchemaInfo = ($event, tableName, tableMetadata) => { - $scope.tableName = tableName; - $scope.tableMetadata = tableMetadata; + $scope.openSchemaInfo = ($event, table) => { + $scope.tableName = table.name; + $scope.tableDescription = table.description; + $scope.tableMetadata = table.columns; $scope.showSchemaInfo = true; $event.stopPropagation(); }; @@ -45,6 +46,13 @@ function SchemaBrowserCtrl($rootScope, $scope) { } }; + this.itemExists = (item) => { + if ('visible' in item) { + return item.visible; + } + return false; + }; + this.itemSelected = ($event, hierarchy) => { $rootScope.$broadcast('query-editor.command', 'paste', hierarchy.join('.')); $event.preventDefault(); diff --git a/client/app/pages/data-sources/schema-table-components/EditableTable.jsx b/client/app/pages/data-sources/schema-table-components/EditableTable.jsx new file mode 100644 index 0000000000..006a18cd2f --- /dev/null +++ b/client/app/pages/data-sources/schema-table-components/EditableTable.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import Form from 'antd/lib/form'; +import Input from 'antd/lib/input'; +import PropTypes from 'prop-types'; +import { TableMetadata } from '@/components/proptypes'; +import TableVisibilityCheckbox from './TableVisibilityCheckbox'; +import './schema-table.css'; + +const FormItem = Form.Item; +const { TextArea } = Input; +export const EditableContext = React.createContext(); + +// eslint-disable-next-line react/prop-types +const EditableRow = ({ form, index, ...props }) => ( + +
+ +); + +export const EditableFormRow = Form.create()(EditableRow); + +export class EditableCell extends React.Component { + static propTypes = { + dataIndex: PropTypes.string, + input_type: PropTypes.string, + editing: PropTypes.bool, + record: TableMetadata, + }; + + static defaultProps = { + dataIndex: undefined, + input_type: undefined, + editing: false, + record: {}, + }; + + constructor(props) { + super(props); + this.state = { + visible: this.props.record ? this.props.record.visible : false, + }; + } + + onChange = () => { + this.setState({ visible: !this.state.visible }); + } + + getInput = () => { + if (this.props.input_type === 'visible') { + return ( + ); + } + return