Files
redash/client/app/components/QueryBasedParameterInput.jsx
Omer Lachish 03f040da0e Unify query based dropdown population (#3337)
* stop testing `collect_query_parameters`, it's an implementation detail

* add tests for `missing_query_params`

* rename SQLQuery -> ParameterizedSqlQuery

* rename sql_query.py to parameterized_query.py

* split to parameterized queries and parameterized SQL queries, where
parameterized queries only do templating and parameterized SQL queries
add tree validation on top of it

* move missing parameter detection to ParameterizedQuery

* get rid of some old code

* fix tests

* set syntax to `custom`

* revert the max-age-related refactoring

* 👋 tree validations 😢

* BaseQueryRunner is no longer a factory for ParameterizedQuery, for now

* add an endpoint for running a query by its id and (optional) parameters
without having to provide the query text

* adds parameter schema to ParameterizedQuery

* adds parameter schema validation (currently for strings)

* validate number parameters

* validate date parameters

* validate parameters on POST /api/queries/<id>/results

* validate enum parameters

* validate date range parameters

* validate query-based dropdowns by preprocessing them at the handler
level and converting them to a populated enum

* change _is_date_range to be a tad more succinct

* a single assignment with a `map` is sufficiently explanatory

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/handlers/query_results.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* build error message inside the error

* support all types of numbers as number parameters

* check for permissions when populating query-based dropdowns

* check for access to query before running it

* check for empty rows when populating query-based enums

* don't bother loading query results if user doesn't have access

* 💥 on unexpected parameter types

* parameter schema default is a list, not a dictionary

* fix a totally unrelated typo

* remove redundant null guards

* introduce /dropdown.json endpoint with dummy data

* wire frontend to /dropdown.json

* always return name/value combos from /dropdown.json

* load actual data into /dropdown.json

* pluck correct values for `name` and `value`

* reuse dropdwon plucking logic in QueryResultResource

* simplify _get_dropdown_values

* when doing parameter validation, we only care about the value and not
the display name

* rename dropdown to dropdownOptions

* move dropdown_values to utils/parameterized_query.py

* stop converting queries to enums and encapsulate the work inside
ParameterizedQuery (almost - /dropdown.json would still access the
dropdown_values method)

* re-order arguments by importance

* test query parameter validation

* tests for dropdown_values logic

* remove `.json` suffix to the dropdown endpoint

* allow `BaseResource` to handle JSON stuff

* move _pluck_name_and_value outside its containing method

* case-insensitive lookup when plucking name and value

* separate concerns and simplify test isolation for `dropdown_values`

* pick the default column according to the order specified in the query
result columns attribute

* use `current_org` instead of passing `org`

* test that user has access to the query when calling the /dropdown
endpoint
2019-02-10 13:10:39 +02:00

86 lines
2.3 KiB
JavaScript

import { find, isFunction } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import Select from 'antd/lib/select';
import { Query } from '@/services/query';
const { Option } = Select;
export class QueryBasedParameterInput extends React.Component {
static propTypes = {
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
queryId: PropTypes.number,
onSelect: PropTypes.func,
className: PropTypes.string,
};
static defaultProps = {
value: null,
queryId: null,
onSelect: () => {},
className: '',
};
constructor(props) {
super(props);
this.state = {
options: [],
loading: false,
};
}
componentDidMount() {
this._loadOptions(this.props.queryId);
}
// eslint-disable-next-line no-unused-vars
componentWillReceiveProps(nextProps) {
if (nextProps.queryId !== this.props.queryId) {
this._loadOptions(nextProps.queryId, nextProps.value);
}
}
_loadOptions(queryId) {
if (queryId && (queryId !== this.state.queryId)) {
this.setState({ loading: true });
Query.dropdownOptions({ id: queryId }, (options) => {
if (this.props.queryId === queryId) {
this.setState({ options, loading: false });
const found = find(options, option => option.value === this.props.value) !== undefined;
if (!found && isFunction(this.props.onSelect)) {
this.props.onSelect(options[0].value);
}
}
});
}
}
render() {
const { className, value, onSelect } = this.props;
const { loading, options } = this.state;
return (
<span>
<Select
className={className}
disabled={loading || (options.length === 0)}
loading={loading}
defaultValue={value}
onChange={onSelect}
dropdownMatchSelectWidth={false}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
>
{options.map(option => (<Option value={option.value} key={option.value}>{option.name}</Option>))}
</Select>
</span>
);
}
}
export default function init(ngModule) {
ngModule.component('queryBasedParameterInput', react2angular(QueryBasedParameterInput));
}
init.init = true;