mirror of
https://github.com/apache/superset.git
synced 2024-09-12 08:39:45 -04:00
Added access check + Druid in endpoint (#1224)
* Explore control panel - Chart control, TimeFilter, GroupBy, Filters (#1205) * create structure for new forked explore view (#1099) * create structure for new forked explore view * update component name * add bootstrap data pattern * remove console.log * Associate version to entry files (#1060) * Associate version to entry files * Modified path joins for configs * Made changes based on comments * Created store and reducers (#1108) * Created store and reducers * Added spec * Modifications based on comments * Explore control panel components: Chart control, Time filter, SQL, GroupBy and Filters * Modifications based on comments * Added access check + Druid in endpoint * pull grains to constants * Switch explore.html to old version
This commit is contained in:
parent
a92190c3ae
commit
8ab5e5015a
@ -29,6 +29,7 @@ export const CHANGE_FILTER_OP = 'CHANGE_FILTER_OP';
|
|||||||
export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE';
|
export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE';
|
||||||
export const RESET_FORM_DATA = 'RESET_FORM_DATA';
|
export const RESET_FORM_DATA = 'RESET_FORM_DATA';
|
||||||
export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS';
|
export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS';
|
||||||
|
export const SET_DATASOURCE_CLASS = 'SET_DATASOURCE_CLASS';
|
||||||
|
|
||||||
export function setTimeColumnOpts(timeColumnOpts) {
|
export function setTimeColumnOpts(timeColumnOpts) {
|
||||||
return { type: SET_TIME_COLUMN_OPTS, timeColumnOpts };
|
return { type: SET_TIME_COLUMN_OPTS, timeColumnOpts };
|
||||||
@ -59,6 +60,10 @@ export function clearAllOpts() {
|
|||||||
return { type: CLEAR_ALL_OPTS };
|
return { type: CLEAR_ALL_OPTS };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setDatasourceClass(datasourceClass) {
|
||||||
|
return { type: SET_DATASOURCE_CLASS, datasourceClass };
|
||||||
|
}
|
||||||
|
|
||||||
export function setFormOpts(datasourceId, datasourceType) {
|
export function setFormOpts(datasourceId, datasourceType) {
|
||||||
return function (dispatch) {
|
return function (dispatch) {
|
||||||
const timeColumnOpts = [];
|
const timeColumnOpts = [];
|
||||||
@ -73,7 +78,7 @@ export function setFormOpts(datasourceId, datasourceType) {
|
|||||||
|
|
||||||
$.get(url, (data, status) => {
|
$.get(url, (data, status) => {
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
data.dttm_cols.forEach((d) => {
|
data.time_columns.forEach((d) => {
|
||||||
if (d) timeColumnOpts.push({ value: d, label: d });
|
if (d) timeColumnOpts.push({ value: d, label: d });
|
||||||
});
|
});
|
||||||
data.groupby_cols.forEach((d) => {
|
data.groupby_cols.forEach((d) => {
|
||||||
@ -89,6 +94,7 @@ export function setFormOpts(datasourceId, datasourceType) {
|
|||||||
if (d) timeGrainOpts.push({ value: d, label: d });
|
if (d) timeGrainOpts.push({ value: d, label: d });
|
||||||
});
|
});
|
||||||
// Repopulate options for controls
|
// Repopulate options for controls
|
||||||
|
dispatch(setDatasourceClass(data.datasource_class));
|
||||||
dispatch(setTimeColumnOpts(timeColumnOpts));
|
dispatch(setTimeColumnOpts(timeColumnOpts));
|
||||||
dispatch(setTimeGrainOpts(timeGrainOpts));
|
dispatch(setTimeGrainOpts(timeGrainOpts));
|
||||||
dispatch(setGroupByColumnOpts(groupByColumnOpts));
|
dispatch(setGroupByColumnOpts(groupByColumnOpts));
|
||||||
|
@ -7,6 +7,7 @@ import { sinceOptions, untilOptions } from '../constants';
|
|||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
actions: React.PropTypes.object,
|
actions: React.PropTypes.object,
|
||||||
|
datasourceClass: React.PropTypes.string,
|
||||||
timeColumnOpts: React.PropTypes.array,
|
timeColumnOpts: React.PropTypes.array,
|
||||||
timeColumn: React.PropTypes.string,
|
timeColumn: React.PropTypes.string,
|
||||||
timeGrainOpts: React.PropTypes.array,
|
timeGrainOpts: React.PropTypes.array,
|
||||||
@ -42,16 +43,20 @@ class TimeFilter extends React.Component {
|
|||||||
this.props.actions.setUntil(val);
|
this.props.actions.setUntil(val);
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
|
const timeColumnPlaceHolder =
|
||||||
|
(this.props.datasourceClass === 'SqlaTable') ? 'Time Column' : 'Time Granularity';
|
||||||
|
const timeGrainPlaceHolder =
|
||||||
|
(this.props.datasourceClass === 'SqlaTable') ? 'Time Grain' : 'Origin';
|
||||||
return (
|
return (
|
||||||
<div className="panel space-1">
|
<div className="panel space-1">
|
||||||
<div className="panel-header">Time Filter</div>
|
<div className="panel-header">Time Filter</div>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<h5 className="section-heading">Time Column & Grain</h5>
|
<h5 className="section-heading">Time Column & Grains</h5>
|
||||||
<Select
|
<Select
|
||||||
className="col-sm-6"
|
className="col-sm-6"
|
||||||
name="select-time-column"
|
name="select-time-column"
|
||||||
placeholder="Select a time column"
|
placeholder={`Select a ${timeColumnPlaceHolder}`}
|
||||||
options={this.props.timeColumnOpts}
|
options={this.props.timeColumnOpts}
|
||||||
value={this.props.timeColumn}
|
value={this.props.timeColumn}
|
||||||
autosize={false}
|
autosize={false}
|
||||||
@ -60,7 +65,7 @@ class TimeFilter extends React.Component {
|
|||||||
<Select
|
<Select
|
||||||
className="col-sm-6"
|
className="col-sm-6"
|
||||||
name="select-time-grain"
|
name="select-time-grain"
|
||||||
placeholder="Select a time grain"
|
placeholder={`Select a ${timeGrainPlaceHolder}`}
|
||||||
options={this.props.timeGrainOpts}
|
options={this.props.timeGrainOpts}
|
||||||
value={this.props.timeGrain}
|
value={this.props.timeGrain}
|
||||||
autosize={false}
|
autosize={false}
|
||||||
@ -99,6 +104,7 @@ TimeFilter.defaultProps = defaultProps;
|
|||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
|
datasourceClass: state.datasourceClass,
|
||||||
timeColumnOpts: state.timeColumnOpts,
|
timeColumnOpts: state.timeColumnOpts,
|
||||||
timeColumn: state.timeColumn,
|
timeColumn: state.timeColumn,
|
||||||
timeGrainOpts: state.timeGrainOpts,
|
timeGrainOpts: state.timeGrainOpts,
|
||||||
|
@ -17,12 +17,13 @@ const bootstrappedState = Object.assign(initialState, {
|
|||||||
datasources: bootstrapData.datasources,
|
datasources: bootstrapData.datasources,
|
||||||
datasourceId: parseInt(bootstrapData.datasource_id, 10),
|
datasourceId: parseInt(bootstrapData.datasource_id, 10),
|
||||||
datasourceType: bootstrapData.datasource_type,
|
datasourceType: bootstrapData.datasource_type,
|
||||||
|
datasourceClass: bootstrapData.datasource_class,
|
||||||
sliceName: bootstrapData.viz.form_data.slice_name,
|
sliceName: bootstrapData.viz.form_data.slice_name,
|
||||||
sliceId: bootstrapData.viz.form_data.slice_id,
|
sliceId: bootstrapData.viz.form_data.slice_id,
|
||||||
vizType: bootstrapData.viz.form_data.viz_type,
|
vizType: bootstrapData.viz.form_data.viz_type,
|
||||||
timeColumn: bootstrapData.viz.form_data.granularity_sqla,
|
timeColumn: bootstrapData.viz.form_data.granularity_sqla,
|
||||||
timeGrain: bootstrapData.viz.form_data.time_grain_sqla,
|
timeGrain: bootstrapData.viz.form_data.time_grain_sqla,
|
||||||
metrics: [bootstrapData.viz.form_data.metric].map((m) => ({ value: m, label: m })),
|
metrics: [bootstrapData.viz.form_data.metrics].map((m) => ({ value: m, label: m })),
|
||||||
since: bootstrapData.viz.form_data.since,
|
since: bootstrapData.viz.form_data.since,
|
||||||
until: bootstrapData.viz.form_data.until,
|
until: bootstrapData.viz.form_data.until,
|
||||||
havingClause: bootstrapData.viz.form_data.having,
|
havingClause: bootstrapData.viz.form_data.having,
|
||||||
|
@ -103,6 +103,9 @@ export const exploreReducer = function (state, action) {
|
|||||||
[actions.CLEAR_ALL_OPTS]() {
|
[actions.CLEAR_ALL_OPTS]() {
|
||||||
return Object.assign({}, state, defaultOpts);
|
return Object.assign({}, state, defaultOpts);
|
||||||
},
|
},
|
||||||
|
[actions.SET_DATASOURCE_CLASS]() {
|
||||||
|
return Object.assign({}, state, { datasourceClass: action.datasourceClass });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (action.type in actionHandlers) {
|
if (action.type in actionHandlers) {
|
||||||
return actionHandlers[action.type]();
|
return actionHandlers[action.type]();
|
||||||
|
@ -2,6 +2,7 @@ export const initialState = {
|
|||||||
datasources: null,
|
datasources: null,
|
||||||
datasourceId: null,
|
datasourceId: null,
|
||||||
datasourceType: null,
|
datasourceType: null,
|
||||||
|
datasourceClass: null,
|
||||||
vizType: null,
|
vizType: null,
|
||||||
timeColumnOpts: [],
|
timeColumnOpts: [],
|
||||||
timeColumn: null,
|
timeColumn: null,
|
||||||
|
@ -42,6 +42,10 @@ config = app.config
|
|||||||
log_this = models.Log.log_this
|
log_this = models.Log.log_this
|
||||||
can_access = utils.can_access
|
can_access = utils.can_access
|
||||||
QueryStatus = models.QueryStatus
|
QueryStatus = models.QueryStatus
|
||||||
|
DRUID_TIME_GRAINS = [
|
||||||
|
'all', '5 seconds', '30 seconds', '1 minute',
|
||||||
|
'5 minutes', '1 hour', '6 hour', '1 day', '7 days'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class BaseCaravelView(BaseView):
|
class BaseCaravelView(BaseView):
|
||||||
@ -1297,6 +1301,7 @@ class Caravel(BaseCaravelView):
|
|||||||
"datasources": [(d.id, d.full_name) for d in datasources],
|
"datasources": [(d.id, d.full_name) for d in datasources],
|
||||||
"datasource_id": datasource_id,
|
"datasource_id": datasource_id,
|
||||||
"datasource_type": datasource_type,
|
"datasource_type": datasource_type,
|
||||||
|
"datasource_class": datasource_class.__name__,
|
||||||
"user_id": g.user.get_id() if g.user else None,
|
"user_id": g.user.get_id() if g.user else None,
|
||||||
"viz": json.loads(viz_obj.get_json())
|
"viz": json.loads(viz_obj.get_json())
|
||||||
}
|
}
|
||||||
@ -1942,8 +1947,6 @@ class Caravel(BaseCaravelView):
|
|||||||
@expose("/fetch_datasource_metadata")
|
@expose("/fetch_datasource_metadata")
|
||||||
@log_this
|
@log_this
|
||||||
def fetch_datasource_metadata(self):
|
def fetch_datasource_metadata(self):
|
||||||
# TODO: check permissions
|
|
||||||
# TODO: check if datasource exits
|
|
||||||
session = db.session
|
session = db.session
|
||||||
datasource_type = request.args.get('datasource_type')
|
datasource_type = request.args.get('datasource_type')
|
||||||
datasource_class = SourceRegistry.sources[datasource_type]
|
datasource_class = SourceRegistry.sources[datasource_type]
|
||||||
@ -1952,17 +1955,34 @@ class Caravel(BaseCaravelView):
|
|||||||
.filter_by(id=request.args.get('datasource_id'))
|
.filter_by(id=request.args.get('datasource_id'))
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
# SUPPORT DRUID
|
|
||||||
# TODO: move this logic to the model (maybe)
|
# Check if datasource exists
|
||||||
datasource_grains = datasource.database.grains()
|
if not datasource:
|
||||||
grain_names = [str(grain.name) for grain in datasource_grains]
|
return json_error_response(DATASOURCE_MISSING_ERR)
|
||||||
|
# Check permission for datasource
|
||||||
|
if not self.datasource_access(datasource):
|
||||||
|
return json_error_response(DATASOURCE_ACCESS_ERR)
|
||||||
|
|
||||||
|
time_columns = []
|
||||||
|
grains_choices = []
|
||||||
|
datasource_class_name = datasource_class.__name__
|
||||||
|
if datasource_class_name == 'SqlaTable':
|
||||||
|
time_columns = datasource.dttm_cols
|
||||||
|
grains = datasource.database.grains()
|
||||||
|
grains_choices = [grain.name for grain in grains]
|
||||||
|
elif datasource_class_name == 'DruidDatasource':
|
||||||
|
time_columns = DRUID_TIME_GRAINS
|
||||||
|
grains_choices = ['now']
|
||||||
|
|
||||||
form_data = {
|
form_data = {
|
||||||
"dttm_cols": datasource.dttm_cols,
|
"datasource_class": datasource_class_name,
|
||||||
"time_grains": grain_names,
|
"time_columns": time_columns,
|
||||||
"groupby_cols": datasource.groupby_column_names,
|
"time_grains": grains_choices,
|
||||||
"metrics": datasource.metrics_combo,
|
"groupby_cols": datasource.groupby_column_names,
|
||||||
"filter_cols": datasource.filterable_column_names,
|
"metrics": datasource.metrics_combo,
|
||||||
}
|
"filter_cols": datasource.filterable_column_names,
|
||||||
|
}
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(form_data), mimetype="application/json")
|
json.dumps(form_data), mimetype="application/json")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user