Skip to content
Snippets Groups Projects
Commit 97f910ad authored by Tom Robinson's avatar Tom Robinson
Browse files

Refactor qb/dash dataset_query parameterization

parent d3b7853b
No related branches found
No related tags found
No related merge requests found
......@@ -8,7 +8,7 @@ import { normalize, Schema, arrayOf } from "normalizr";
import MetabaseAnalytics from "metabase/lib/analytics";
import { getPositionForNewDashCard } from "metabase/lib/dashboard_grid";
import { applyParameters } from "metabase/meta/Card";
import { fetchDatabaseMetadata } from "metabase/redux/metadata";
const DATASET_SLOW_TIMEOUT = 15 * 1000;
......@@ -136,24 +136,7 @@ export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, d
let dashboard = dashboards[dashboardId];
let parameters = [];
if (dashboard && dashboard.parameters) {
for (const parameter of dashboard.parameters) {
let mapping = _.findWhere(dashcard && dashcard.parameter_mappings, { card_id: card.id, parameter_id: parameter.id });
if (parameterValues[parameter.id] != null) {
parameters.push({
type: parameter.type,
target: mapping && mapping.target,
value: parameterValues[parameter.id]
});
}
}
}
let datasetQuery = {
...card.dataset_query,
parameters
};
const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings);
let slowCardTimer = setTimeout(() => {
if (result === null) {
......
/* @flow */
import type { StructuredQueryObject, NativeQueryObject, TemplateTag } from "./types/Query";
import type { CardObject, StructuredDatasetQueryObject, NativeDatasetQueryObject } from "./types/Card";
import type { CardObject, DatasetQueryObject, StructuredDatasetQueryObject, NativeDatasetQueryObject } from "./types/Card";
import type { ParameterObject, ParameterId, ParameterMappingObject, ParameterMappingTarget } from "metabase/meta/types/Dashboard";
declare class Object {
static values<T>(object: { [key:string]: T }): Array<T>;
}
import * as Query from "./Query";
import QueryLib from "metabase/lib/query";
import _ from "underscore";
export const STRUCTURED_QUERY_TEMPLATE: StructuredDatasetQueryObject = {
type: "query",
......@@ -62,3 +65,46 @@ export function getTemplateTags(card: ?CardObject): Array<TemplateTag> {
Object.values(card.dataset_query.native.template_tags) :
[];
}
export function applyParameters(
card: CardObject,
parameters: Array<ParameterObject>,
parameterValues: { [key: ParameterId]: string } = {},
parameterMappings: Array<ParameterMappingObject> = []
): DatasetQueryObject {
const datasetQuery = JSON.parse(JSON.stringify(card.dataset_query));
// clean the query
if (datasetQuery.type === "query") {
datasetQuery.query = QueryLib.cleanQuery(datasetQuery.query);
}
datasetQuery.parameters = [];
for (const parameter of parameters || []) {
let value = parameterValues[parameter.id];
// dashboards
const mapping = _.findWhere(parameterMappings, { card_id: card.id, parameter_id: parameter.id });
if (value != null && mapping) {
datasetQuery.parameters.push({
type: parameter.type,
target: mapping.target,
value: value
});
}
// SQL parameters
if (datasetQuery.type === "native") {
let tag = _.findWhere(datasetQuery.native.template_tags, { id: parameter.id });
if (tag) {
datasetQuery.parameters.push({
type: parameter.type,
target: tag.type === "dimension" ?
["dimension", ["template-tag", tag.name]]:
["variable", ["template-tag", tag.name]],
value: value
});
}
}
}
return datasetQuery;
}
......@@ -2,6 +2,7 @@
import type { DatabaseId } from "./base";
import type { StructuredQueryObject, NativeQueryObject } from "./Query";
import type { ParameterInstance } from "./Dashboard";
export type CardId = number;
......@@ -17,13 +18,15 @@ export type CardObject = {
export type StructuredDatasetQueryObject = {
type: "query",
database: ?DatabaseId,
query: StructuredQueryObject
query: StructuredQueryObject,
parameters?: Array<ParameterInstance>
};
export type NativeDatasetQueryObject = {
type: "native",
database: ?DatabaseId,
native: NativeQueryObject,
parameters?: Array<ParameterInstance>
};
export type DatasetQueryObject = StructuredDatasetQueryObject | NativeDatasetQueryObject;
......@@ -52,3 +52,9 @@ export type ParameterOption = {
description?: string,
type: ParameterType
};
export type ParameterInstance = {
type: ParameterType,
target: ParameterMappingTarget,
value: string
};
......@@ -14,6 +14,7 @@ import Query from "metabase/lib/query";
import { createQuery } from "metabase/lib/query";
import { loadTableAndForeignKeys } from "metabase/lib/table";
import Utils from "metabase/lib/utils";
import { applyParameters } from "metabase/meta/Card";
import { getParameters } from "./selectors";
......@@ -310,19 +311,10 @@ export const updateTemplateTag = createThunkAction(UPDATE_TEMPLATE_TAG, (templat
});
export const SET_PARAMETER_VALUE = "SET_PARAMETER_VALUE";
export const setParameterValue = createThunkAction(SET_PARAMETER_VALUE, (parameterId, value) => {
return (dispatch, getState) => {
let { qb: { parameterValues } } = getState();
// apply this specific value
parameterValues = { ...parameterValues, [parameterId]: value};
// the return value from our action is still just the id/value of the parameter set
return {id: parameterId, value};
};
export const setParameterValue = createAction(SET_PARAMETER_VALUE, (parameterId, value) => {
return { id: parameterId, value };
});
export const NOTIFY_CARD_CREATED = "NOTIFY_CARD_CREATED";
export const notifyCardCreatedFn = createThunkAction(NOTIFY_CARD_CREATED, (card) => {
return (dispatch, getState) => {
......@@ -685,40 +677,24 @@ export const setQuerySort = createThunkAction(SET_QUERY_SORT, (column) => {
};
});
// runQuery
export const RUN_QUERY = "RUN_QUERY";
export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, paramValues) => {
export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, parameterValues) => {
return async (dispatch, getState) => {
const state = getState();
const parameters = getParameters(state);
// if we got a query directly on the action call then use it, otherwise take whatever is in our current state
card = card || state.qb.card;
card = JSON.parse(JSON.stringify(card));
let dataset_query = card.dataset_query,
cardIsDirty = isCardDirty(card, state.qb.originalCard);
parameterValues = parameterValues || state.qb.parameterValues || {};
if (dataset_query.query) {
// TODO: this needs to be immutable
dataset_query.query = Query.cleanQuery(dataset_query.query);
}
const cardIsDirty = isCardDirty(card, state.qb.originalCard);
// apply any pseudo-parameters, if specified
if (parameters && parameters.length > 0) {
let templateTags = card.dataset_query.native.template_tags || {};
let parameterValues = paramValues || state.qb.parameterValues || {};
dataset_query.parameters = parameters.map(parameter => {
let tag = _.findWhere(templateTags, { id: parameter.id });
let value = parameterValues[parameter.id];
if (value != null && tag) {
return {
type: parameter.type,
target: [tag.type === "dimension" ? "dimension" : "variable", ["template-tag", tag.name]],
value: value
};
}
}).filter(p => p);
}
card = {
...card,
dataset_query: applyParameters(card, parameters, parameterValues)
};
if (updateUrl) {
state.qb.updateUrl(card, cardIsDirty);
......@@ -728,14 +704,14 @@ export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, para
let startTime = new Date();
// make our api call
Metabase.dataset({ timeout: cancelQueryDeferred.promise }, dataset_query, function (queryResult) {
Metabase.dataset({ timeout: cancelQueryDeferred.promise }, card.dataset_query, function (queryResult) {
dispatch(queryCompleted(card, queryResult));
}, function (error) {
dispatch(queryErrored(startTime, error));
});
MetabaseAnalytics.trackEvent("QueryBuilder", "Run Query", dataset_query.type);
MetabaseAnalytics.trackEvent("QueryBuilder", "Run Query", card.dataset_query.type);
// HACK: prevent SQL editor from losing focus
try { ace.edit("id_sql").focus() } catch (e) {}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment