diff --git a/client/app/components/dynamic-form/DynamicForm.jsx b/client/app/components/dynamic-form/DynamicForm.jsx index 673b7ecb3d..98e585bad1 100644 --- a/client/app/components/dynamic-form/DynamicForm.jsx +++ b/client/app/components/dynamic-form/DynamicForm.jsx @@ -146,9 +146,21 @@ class DynamicForm extends React.Component { }; return getFieldDecorator(name, decoratorOptions)( - trigger.parentNode} + > + {options && options.map(option => ( + ))} , ); diff --git a/client/app/components/dynamic-form/dynamicFormHelper.js b/client/app/components/dynamic-form/dynamicFormHelper.js index c1d17997d3..15e4645566 100644 --- a/client/app/components/dynamic-form/dynamicFormHelper.js +++ b/client/app/components/dynamic-form/dynamicFormHelper.js @@ -1,5 +1,5 @@ import React from 'react'; -import { each, includes, isUndefined } from 'lodash'; +import { each, includes, isUndefined, isEmpty, map } from 'lodash'; function orderedInputs(properties, order, targetOptions) { const inputs = new Array(order.length); @@ -14,6 +14,11 @@ function orderedInputs(properties, order, targetOptions) { initialValue: targetOptions[key], }; + if (input.type === 'select') { + input.placeholder = 'Select an option'; + input.options = properties[key].options; + } + if (position > -1) { inputs[position] = input; } else { @@ -41,27 +46,45 @@ function normalizeSchema(configurationSchema) { prop.type = 'text'; } + if (!isEmpty(prop.enum)) { + prop.type = 'select'; + prop.options = map(prop.enum, value => ({ value, name: value })); + } + + if (!isEmpty(prop.extendedEnum)) { + prop.type = 'select'; + prop.options = prop.extendedEnum; + } + prop.required = includes(configurationSchema.required, name); }); configurationSchema.order = configurationSchema.order || []; } -function setDefaultValueForCheckboxes(configurationSchema, options = {}) { - if (Object.keys(options).length === 0) { - const properties = configurationSchema.properties; - Object.keys(properties).forEach((property) => { - if (!isUndefined(properties[property].default) && properties[property].type === 'checkbox') { - options[property] = properties[property].default; - } - }); - } +function setDefaultValueToFields(configurationSchema, options = {}) { + const properties = configurationSchema.properties; + Object.keys(properties).forEach((key) => { + const property = properties[key]; + // set default value for checkboxes + if (!isUndefined(property.default) && property.type === 'checkbox') { + options[key] = property.default; + } + // set default or first value when value has predefined options + if (property.type === 'select') { + const optionValues = map(property.options, option => option.value); + options[key] = includes(optionValues, property.default) ? property.default : optionValues[0]; + } + }); } function getFields(type = {}, target = { options: {} }) { const configurationSchema = type.configuration_schema; normalizeSchema(configurationSchema); - setDefaultValueForCheckboxes(configurationSchema, target.options); + const hasTargetObject = Object.keys(target.options).length > 0; + if (!hasTargetObject) { + setDefaultValueToFields(configurationSchema, target.options); + } const isNewTarget = !target.id; const inputs = [ diff --git a/client/app/components/users/UserEdit.jsx b/client/app/components/users/UserEdit.jsx index 0c0b086657..ae9a6f3984 100644 --- a/client/app/components/users/UserEdit.jsx +++ b/client/app/components/users/UserEdit.jsx @@ -35,7 +35,7 @@ export default class UserEdit extends React.Component { componentDidMount() { Group.query((groups) => { this.setState({ - groups: groups.map(({ id, name }) => ({ value: id, title: name })), + groups: groups.map(({ id, name }) => ({ value: id, name })), loadingGroups: false, }); }); @@ -165,7 +165,7 @@ export default class UserEdit extends React.Component {
{groups.filter(group => includes(user.groupIds, group.value)).map((group => ( - {group.title} + {group.name} )))}
diff --git a/redash/query_runner/impala_ds.py b/redash/query_runner/impala_ds.py index 90b63fb3d1..47f62f000d 100644 --- a/redash/query_runner/impala_ds.py +++ b/redash/query_runner/impala_ds.py @@ -48,7 +48,11 @@ def configuration_schema(cls): }, "protocol": { "type": "string", - "title": "Please specify beeswax or hiveserver2" + "extendedEnum": [ + {"value": "beeswax", "name": "Beeswax"}, + {"value": "hiveserver2", "name": "Hive Server 2"} + ], + "title": "Protocol" }, "database": { "type": "string" diff --git a/redash/utils/configuration.py b/redash/utils/configuration.py index 6473c02c6e..41625d6468 100644 --- a/redash/utils/configuration.py +++ b/redash/utils/configuration.py @@ -1,4 +1,5 @@ import jsonschema +import copy from jsonschema import ValidationError from sqlalchemy.ext.mutable import Mutable @@ -24,7 +25,13 @@ def __init__(self, config, schema=None): self.set_schema(schema) def set_schema(self, schema): - self._schema = schema + configuration_schema = copy.deepcopy(schema) + if isinstance(configuration_schema, dict): + for prop in configuration_schema.get('properties', {}).values(): + if 'extendedEnum' in prop: + prop['enum'] = map(lambda v: v['value'], prop['extendedEnum']) + del prop['extendedEnum'] + self._schema = configuration_schema @property def schema(self):