Skip to content
Snippets Groups Projects
Unverified Commit 79337de1 authored by Ariya Hidayat's avatar Ariya Hidayat Committed by GitHub
Browse files

Refactor and tests template tags extractions (#23524)

parent 6fb322ae
No related branches found
No related tags found
No related merge requests found
...@@ -25,6 +25,7 @@ import { DatabaseEngine, DatabaseId } from "metabase-types/types/Database"; ...@@ -25,6 +25,7 @@ import { DatabaseEngine, DatabaseId } from "metabase-types/types/Database";
import AtomicQuery from "metabase-lib/lib/queries/AtomicQuery"; import AtomicQuery from "metabase-lib/lib/queries/AtomicQuery";
import Dimension, { TemplateTagDimension, FieldDimension } from "../Dimension"; import Dimension, { TemplateTagDimension, FieldDimension } from "../Dimension";
import Variable, { TemplateTagVariable } from "../Variable"; import Variable, { TemplateTagVariable } from "../Variable";
import { createTemplateTag } from "metabase-lib/lib/queries/TemplateTag";
import DimensionOptions from "../DimensionOptions"; import DimensionOptions from "../DimensionOptions";
import { ValidationError } from "metabase-lib/lib/ValidationError"; import { ValidationError } from "metabase-lib/lib/ValidationError";
...@@ -422,21 +423,7 @@ export default class NativeQuery extends AtomicQuery { ...@@ -422,21 +423,7 @@ export default class NativeQuery extends AtomicQuery {
*/ */
_getUpdatedTemplateTags(queryText: string): TemplateTags { _getUpdatedTemplateTags(queryText: string): TemplateTags {
if (queryText && this.supportsNativeParameters()) { if (queryText && this.supportsNativeParameters()) {
let tags = []; const tags = recognizeTemplateTags(queryText);
// look for variable usage in the query (like '{{varname}}'). we only allow alphanumeric characters for the variable name
// a variable name can optionally end with :start or :end which is not considered part of the actual variable name
// expected pattern is like mustache templates, so we are looking for something like {{category}} or {{date:start}}
// anything that doesn't match our rule is ignored, so {{&foo!}} would simply be ignored
// variables referencing other questions, by their card ID, are also supported: {{#123}} references question with ID 123
let match;
const re = /\{\{\s*((snippet:\s*[^}]+)|[A-Za-z0-9_\.]+?|#[0-9]*)\s*\}\}/g;
while ((match = re.exec(queryText)) != null) {
tags.push(match[1]);
}
// eliminate any duplicates since it's allowed for a user to reference the same variable multiple times
tags = _.uniq(tags);
const existingTemplateTags = this.templateTagsMap(); const existingTemplateTags = this.templateTagsMap();
const existingTags = Object.keys(existingTemplateTags); const existingTags = Object.keys(existingTemplateTags);
...@@ -476,12 +463,7 @@ export default class NativeQuery extends AtomicQuery { ...@@ -476,12 +463,7 @@ export default class NativeQuery extends AtomicQuery {
// create new vars // create new vars
for (const tagName of newTags) { for (const tagName of newTags) {
templateTags[tagName] = { templateTags[tagName] = createTemplateTag(tagName);
id: Utils.uuid(),
name: tagName,
"display-name": humanize(tagName),
type: "text",
};
// parse card ID from tag name for card query template tags // parse card ID from tag name for card query template tags
if (isCardQueryName(tagName)) { if (isCardQueryName(tagName)) {
...@@ -533,3 +515,21 @@ export default class NativeQuery extends AtomicQuery { ...@@ -533,3 +515,21 @@ export default class NativeQuery extends AtomicQuery {
}); });
} }
} }
// look for variable usage in the query (like '{{varname}}'). we only allow alphanumeric characters for the variable name
// a variable name can optionally end with :start or :end which is not considered part of the actual variable name
// expected pattern is like mustache templates, so we are looking for something like {{category}} or {{date:start}}
// anything that doesn't match our rule is ignored, so {{&foo!}} would simply be ignored
// variables referencing other questions, by their card ID, are also supported: {{#123}} references question with ID 123
export function recognizeTemplateTags(queryText: string): string[] {
const tagNames = [];
let match;
const re = /\{\{\s*((snippet:\s*[^}]+)|[A-Za-z0-9_\.]+?|#[0-9]*)\s*\}\}/g;
while ((match = re.exec(queryText)) != null) {
tagNames.push(match[1]);
}
// eliminate any duplicates since it's allowed for a user to reference the same variable multiple times
return _.uniq(tagNames);
}
import Utils from "metabase/lib/utils";
import { humanize } from "metabase/lib/formatting";
import { TemplateTag } from "metabase-types/types/Query";
export const createTemplateTag = (tagName: string): TemplateTag => {
return {
id: Utils.uuid(),
name: tagName,
"display-name": humanize(tagName),
type: "text",
};
};
import { createTemplateTag } from "metabase-lib/lib/queries/TemplateTag";
describe("createTemplateTag", () => {
it("should create a proper template tag", () => {
const tag = createTemplateTag("stars");
expect(tag.name).toEqual("stars");
expect(tag.type).toEqual("text");
expect(typeof tag.id).toEqual("string");
expect(tag["display-name"]).toEqual("Stars");
});
});
...@@ -6,7 +6,9 @@ import { ...@@ -6,7 +6,9 @@ import {
MONGO_DATABASE, MONGO_DATABASE,
} from "__support__/sample_database_fixture"; } from "__support__/sample_database_fixture";
import NativeQuery from "metabase-lib/lib/queries/NativeQuery"; import NativeQuery, {
recognizeTemplateTags,
} from "metabase-lib/lib/queries/NativeQuery";
function makeDatasetQuery(queryText, templateTags, databaseId) { function makeDatasetQuery(queryText, templateTags, databaseId) {
return { return {
...@@ -339,4 +341,24 @@ describe("NativeQuery", () => { ...@@ -339,4 +341,24 @@ describe("NativeQuery", () => {
]); ]);
}); });
}); });
describe("recognizeTemplateTags", () => {
it("should handle standard variable names", () => {
expect(recognizeTemplateTags("SELECT * from {{products}}")).toEqual([
"products",
]);
});
it("should allow duplicated variables", () => {
expect(
recognizeTemplateTags("SELECT {{col}} FROM {{t}} ORDER BY {{col}} "),
).toEqual(["col", "t"]);
});
it("should ignore non-alphanumeric markers", () => {
expect(recognizeTemplateTags("SELECT * from X -- {{&universe}}")).toEqual(
[],
);
});
});
}); });
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