mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 08:09:22 +00:00
feat: vrl support for visualization (#10237)
### **User description** Design at : https://github.com/openobserve/designs/tree/main/visualization-vrl-support ### **PR Type** Enhancement ___ ### **Description** - Always default to table chart with VRL functions - Preserve and propagate VRL function query across UI - Enable dynamic columns for VRL-based table charts - Remove visualization-mode restrictions for function editor ___ ### Diagram Walkthrough ```mermaid flowchart LR VRL["VRL Function Present?"] VRL -- "yes" --> Table["Force Table Chart"] Table -- "on table" --> DynCols["Enable Dynamic Columns"] VRL -- "no" --> OtherCharts["Allow Other Chart Types"] ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table> <tr> <td> <details> <summary><strong>useDashboardPanel.ts</strong><dd><code>Force table chart for VRL functions</code> </dd></summary> <hr> web/src/composables/useDashboardPanel.ts <ul><li>Added VRL check in determineChartType<br> <li> Default to table when VRL functions exist</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fbba1700750053b9eaf38ddd30dab84531bc44131c795ebb713811e4930cd7cb">+5/-0</a> </td> </tr> <tr> <td> <details> <summary><strong>Index.vue</strong><dd><code>Propagate and handle VRL function queries</code> </dd></summary> <hr> web/src/plugins/logs/Index.vue <ul><li>Copy and clear <code>vrlFunctionQuery</code> on query update<br> <li> Force table type and set <code>table_dynamic_columns</code><br> <li> Preserve <code>vrlFunctionQuery</code> in visualization flow</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0323c3da69b369843c5d72bf4df5b00ca6b5147192a213baf331659ecf92af62">+70/-2</a> </td> </tr> <tr> <td> <details> <summary><strong>SearchBar.vue</strong><dd><code>Enable VRL editor in visualize mode</code> </dd></summary> <hr> web/src/plugins/logs/SearchBar.vue <ul><li>Always allow editing VRL function editor<br> <li> Removed visualize-mode warning overlay</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-81893de4edf186b0556f610acb280c87baa2ffe67d3b48a60a866094c7635baf">+1/-26</a> </td> </tr> <tr> <td> <details> <summary><strong>TransformSelector.vue</strong><dd><code>Lift transform restrictions in visualize mode</code> </dd></summary> <hr> web/src/plugins/logs/TransformSelector.vue <ul><li>Removed disable state for transform toggle/save in visualize<br> <li> Removed visualize-mode tooltip restrictions</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0037d8dd9cfbacc26309b26e98ae953e7ccbd0891ac2fd01c6314ebd7eed9d2c">+3/-13</a> </td> </tr> <tr> <td> <details> <summary><strong>VisualizeLogsQuery.vue</strong><dd><code>Restrict chart types when VRL functions present</code> </dd></summary> <hr> web/src/plugins/logs/VisualizeLogsQuery.vue <ul><li>Block non-table chart selection if VRL functions exist<br> <li> Show error and reset to table on invalid type switch</ul> </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-979b01d943d58c5d70faeeac5948b7f02f0b2392a12b84ef07ba7f7393ab0261">+14/-0</a> </td> </tr> </table></td></tr></tr></tbody></table> </details> ___ ___ ### **PR Type** Enhancement, Tests ___ ### **Description** - Default to table chart when VRL functions exist - Propagate and encode VRL function query in UI - Enable dynamic columns for VRL-based table charts - Remove visualize-mode restrictions on VRL editor ___ ### Diagram Walkthrough ```mermaid flowchart LR VRL["VRL functions present?"] VRL -- "yes" --> Table["Force table chart"] Table --> DynCols["Enable dynamic columns"] DynCols --> Prop["Propagate VRL query"] VRL -- "no" --> Other["Allow other chart types"] ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><details><summary>5 files</summary><table> <tr> <td><strong>useDashboardPanel.spec.ts</strong><dd><code>Add VRL chart type determination tests</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-5dbd79fdde0cbbb7db569095fa062931286463da0b5e72b1fbe99f56116fa5e2">+110/-0</a> </td> </tr> <tr> <td><strong>Index.spec.ts</strong><dd><code>Add VRL support tests in Index.vue</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-a6ac1dd4a120625f1c0ed23803aa0cb63e8513b38abc975af72eefe3923b2c73">+189/-0</a> </td> </tr> <tr> <td><strong>SearchBar.spec.ts</strong><dd><code>Add VRL editor readOnly and banner tests</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-63ac75d3fe278dad233e7dd7c51b3b33076d54a776b89ae05190d8522130c78b">+149/-1</a> </td> </tr> <tr> <td><strong>VisualizeLogsQuery.spec.ts</strong><dd><code>Test VRL chart type change restrictions</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fdecf623f9a4bdfe679c6c2e4424f31cbce4a1234cb45e1de472189a46c9d4e2">+130/-0</a> </td> </tr> <tr> <td><strong>TransformSelector.spec.ts</strong><dd><code>Enable transform toggle in visualize mode</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-c850f07fc5e32a152df85ca63e6ed45c9aeac2965a5bf1238daf7353a8699195">+247/-1</a> </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>5 files</summary><table> <tr> <td><strong>useDashboardPanel.ts</strong><dd><code>Force table chart for VRL functions</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fbba1700750053b9eaf38ddd30dab84531bc44131c795ebb713811e4930cd7cb">+5/-0</a> </td> </tr> <tr> <td><strong>Index.vue</strong><dd><code>Propagate and apply VRL function query</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0323c3da69b369843c5d72bf4df5b00ca6b5147192a213baf331659ecf92af62">+70/-2</a> </td> </tr> <tr> <td><strong>SearchBar.vue</strong><dd><code>Remove VRL editor read-only restriction</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-81893de4edf186b0556f610acb280c87baa2ffe67d3b48a60a866094c7635baf">+1/-26</a> </td> </tr> <tr> <td><strong>TransformSelector.vue</strong><dd><code>Allow transform selector in visualize mode</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0037d8dd9cfbacc26309b26e98ae953e7ccbd0891ac2fd01c6314ebd7eed9d2c">+3/-13</a> </td> </tr> <tr> <td><strong>VisualizeLogsQuery.vue</strong><dd><code>Restrict non-table charts when VRL present</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/10237/files#diff-979b01d943d58c5d70faeeac5948b7f02f0b2392a12b84ef07ba7f7393ab0261">+14/-0</a> </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
This commit is contained in:
@@ -5740,5 +5740,115 @@ describe("useDashboardPanel", () => {
|
||||
const newField = panel.dashboardPanelData.data.queries[0].fields.y[panel.dashboardPanelData.data.queries[0].fields.y.length - 1];
|
||||
expect(newField.color).toBeDefined();
|
||||
});
|
||||
|
||||
/**
|
||||
* VRL Visualization Support Tests
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* Tests for the VRL function check added to determineChartType function.
|
||||
* When VRL functions produce derived fields, chart type should always be "table".
|
||||
*/
|
||||
describe("VRL Visualization Support - determineChartType", () => {
|
||||
it("should return 'table' when vrlFunctionFieldList has items (VRL functions present)", () => {
|
||||
// Set VRL function fields to simulate VRL function output
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "parsed_json", type: "Utf8" },
|
||||
{ name: "extracted_value", type: "Int64" },
|
||||
];
|
||||
|
||||
const extractedFields = {
|
||||
group_by: ["service"],
|
||||
projections: ["timestamp", "count", "parsed_json"],
|
||||
timeseries_field: "timestamp", // Has timeseries - would normally be "line"
|
||||
};
|
||||
|
||||
// With VRL functions present, should always return "table"
|
||||
const chartType = panel.determineChartType(extractedFields);
|
||||
expect(chartType).toBe("table");
|
||||
});
|
||||
|
||||
it("should return 'line' when vrlFunctionFieldList is empty and timeseries conditions met", () => {
|
||||
// Clear VRL function fields
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
|
||||
|
||||
const extractedFields = {
|
||||
group_by: ["service"],
|
||||
projections: ["timestamp", "count"],
|
||||
timeseries_field: "timestamp",
|
||||
};
|
||||
|
||||
// Without VRL functions, normal logic applies
|
||||
const chartType = panel.determineChartType(extractedFields);
|
||||
expect(chartType).toBe("line");
|
||||
});
|
||||
|
||||
it("should return 'table' with VRL functions even when perfect line chart conditions exist", () => {
|
||||
// Single VRL derived field
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "computed_field", type: "Float64" },
|
||||
];
|
||||
|
||||
// Perfect conditions for line chart: timeseries + single group_by
|
||||
const extractedFields = {
|
||||
group_by: ["service"],
|
||||
projections: ["timestamp", "count"],
|
||||
timeseries_field: "timestamp",
|
||||
};
|
||||
|
||||
// VRL check takes precedence
|
||||
const chartType = panel.determineChartType(extractedFields);
|
||||
expect(chartType).toBe("table");
|
||||
});
|
||||
|
||||
it("should return 'table' with VRL functions when no group_by fields", () => {
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "vrl_output", type: "Utf8" },
|
||||
];
|
||||
|
||||
const extractedFields = {
|
||||
group_by: [],
|
||||
projections: ["timestamp", "message", "vrl_output"],
|
||||
timeseries_field: "timestamp",
|
||||
};
|
||||
|
||||
const chartType = panel.determineChartType(extractedFields);
|
||||
expect(chartType).toBe("table");
|
||||
});
|
||||
|
||||
it("should return 'table' with multiple VRL derived fields", () => {
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "field1", type: "Utf8" },
|
||||
{ name: "field2", type: "Int64" },
|
||||
{ name: "field3", type: "Float64" },
|
||||
{ name: "field4", type: "Boolean" },
|
||||
];
|
||||
|
||||
const extractedFields = {
|
||||
group_by: [],
|
||||
projections: ["timestamp"],
|
||||
timeseries_field: "timestamp",
|
||||
};
|
||||
|
||||
const chartType = panel.determineChartType(extractedFields);
|
||||
expect(chartType).toBe("table");
|
||||
});
|
||||
|
||||
it("should handle empty vrlFunctionFieldList array correctly (length check)", () => {
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
|
||||
|
||||
// Verify the condition check
|
||||
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length).toBe(0);
|
||||
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle single item vrlFunctionFieldList array correctly (truthy check)", () => {
|
||||
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "single_field", type: "Utf8" },
|
||||
];
|
||||
|
||||
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length).toBe(1);
|
||||
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2859,6 +2859,11 @@ const useDashboardPanelData = (pageKey: string = "dashboard") => {
|
||||
projections: string[];
|
||||
timeseries_field: string | null;
|
||||
}): string => {
|
||||
// If VRL functions are present, always default to table chart
|
||||
if (dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0) {
|
||||
return "table";
|
||||
}
|
||||
|
||||
if (
|
||||
extractedFields.timeseries_field &&
|
||||
extractedFields.group_by.length <= 2
|
||||
|
||||
@@ -760,4 +760,193 @@ describe("Logs Index", async () => {
|
||||
const result2 = wrapper.vm.removeFieldByName(result, 'f2');
|
||||
expect(result2.find((c: any) => c.expr?.args?.expr?.column?.value === 'f2')).toBeUndefined();
|
||||
});
|
||||
|
||||
/**
|
||||
* VRL Visualization Support Tests - Index.vue
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* Tests for VRL function query propagation and visualization handling:
|
||||
* 1. VRL function query encoding with b64EncodeUnicode
|
||||
* 2. Force table chart type when VRL functions present
|
||||
* 3. Enable table_dynamic_columns for VRL table charts
|
||||
* 4. shouldUseHistogramQuery excludes VRL function queries
|
||||
*/
|
||||
describe("VRL Visualization Support", () => {
|
||||
describe("VRL function query propagation", () => {
|
||||
it("should encode VRL function content when transformType is function", () => {
|
||||
// Set up VRL function content
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".parsed = parse_json!(.message)";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
|
||||
// Verify the VRL content is set
|
||||
expect(wrapper.vm.searchObj.data.tempFunctionContent).toBe(".parsed = parse_json!(.message)");
|
||||
expect(wrapper.vm.searchObj.data.transformType).toBe("function");
|
||||
|
||||
// The actual encoding happens in the component during visualization toggle
|
||||
// Here we test the condition that triggers encoding
|
||||
const shouldEncodeVrl =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
expect(shouldEncodeVrl).toBe(true);
|
||||
});
|
||||
|
||||
it("should not encode VRL when tempFunctionContent is empty", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = "";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
|
||||
const shouldEncodeVrl =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
expect(shouldEncodeVrl).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should not encode VRL when transformType is not function", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = "some code";
|
||||
wrapper.vm.searchObj.data.transformType = "action";
|
||||
|
||||
const shouldEncodeVrl =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
expect(shouldEncodeVrl).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Force table chart type for VRL", () => {
|
||||
it("should identify when table chart should be forced for VRL", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
const shouldAutoSelectChartType = true;
|
||||
|
||||
// Logic that forces table chart
|
||||
const shouldForceTable =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function" &&
|
||||
shouldAutoSelectChartType;
|
||||
|
||||
expect(shouldForceTable).toBe(true);
|
||||
});
|
||||
|
||||
it("should not force table when auto-select is disabled", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
const shouldAutoSelectChartType = false;
|
||||
|
||||
const shouldForceTable =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function" &&
|
||||
shouldAutoSelectChartType;
|
||||
|
||||
expect(shouldForceTable).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Dynamic columns for VRL table charts", () => {
|
||||
it("should identify when dynamic columns should be enabled", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".new_field = 1";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
const chartType = "table";
|
||||
|
||||
// Logic that enables dynamic columns
|
||||
const shouldEnableDynamicColumns =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function" &&
|
||||
chartType === "table";
|
||||
|
||||
expect(shouldEnableDynamicColumns).toBe(true);
|
||||
});
|
||||
|
||||
it("should not enable dynamic columns for non-table charts", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".new_field = 1";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
const chartType = "line";
|
||||
|
||||
const shouldEnableDynamicColumns =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function" &&
|
||||
chartType === "table";
|
||||
|
||||
expect(shouldEnableDynamicColumns).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("shouldUseHistogramQuery with VRL functions", () => {
|
||||
it("should return false when VRL function is present", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
|
||||
wrapper.vm.searchObj.data.transformType = "function";
|
||||
const chartType = "line";
|
||||
const extractedFields = { group_by: [] };
|
||||
|
||||
// New logic: hasVrlFunction check
|
||||
const hasVrlFunction =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
const shouldUseHistogramQuery =
|
||||
chartType !== "table" &&
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length) &&
|
||||
!hasVrlFunction;
|
||||
|
||||
expect(hasVrlFunction).toBe(true);
|
||||
expect(shouldUseHistogramQuery).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true when no VRL function and conditions met", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = "";
|
||||
wrapper.vm.searchObj.data.transformType = "";
|
||||
const chartType = "line";
|
||||
const extractedFields = { group_by: [] };
|
||||
|
||||
const hasVrlFunction =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
const shouldUseHistogramQuery =
|
||||
chartType !== "table" &&
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length) &&
|
||||
!hasVrlFunction;
|
||||
|
||||
expect(hasVrlFunction).toBeFalsy();
|
||||
expect(shouldUseHistogramQuery).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for table chart type regardless of VRL", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = "";
|
||||
wrapper.vm.searchObj.data.transformType = "";
|
||||
const chartType = "table";
|
||||
const extractedFields = { group_by: [] };
|
||||
|
||||
const hasVrlFunction =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
const shouldUseHistogramQuery =
|
||||
chartType !== "table" &&
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length) &&
|
||||
!hasVrlFunction;
|
||||
|
||||
expect(shouldUseHistogramQuery).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when group_by fields exist", () => {
|
||||
wrapper.vm.searchObj.data.tempFunctionContent = "";
|
||||
wrapper.vm.searchObj.data.transformType = "";
|
||||
const chartType = "line";
|
||||
const extractedFields = { group_by: ["service", "level"] };
|
||||
|
||||
const hasVrlFunction =
|
||||
wrapper.vm.searchObj.data.tempFunctionContent &&
|
||||
wrapper.vm.searchObj.data.transformType === "function";
|
||||
|
||||
const shouldUseHistogramQuery =
|
||||
chartType !== "table" &&
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length) &&
|
||||
!hasVrlFunction;
|
||||
|
||||
expect(shouldUseHistogramQuery).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1604,6 +1604,22 @@ export default defineComponent({
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].customQuery = true;
|
||||
|
||||
// Copy VRL function query if present
|
||||
if (
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function"
|
||||
) {
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].vrlFunctionQuery = b64EncodeUnicode(
|
||||
searchObj.data.tempFunctionContent,
|
||||
);
|
||||
} else {
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].vrlFunctionQuery = "";
|
||||
}
|
||||
|
||||
// Store current config and chart type to preserve them during rebuild
|
||||
const queryParams = router.currentRoute.value.query;
|
||||
let preservedConfig = null;
|
||||
@@ -1694,6 +1710,17 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
// Force table chart if VRL functions are present
|
||||
if (
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function" &&
|
||||
shouldAutoSelectChartType
|
||||
) {
|
||||
dashboardPanelData.data.type = "table";
|
||||
// Enable dynamic columns for VRL table charts
|
||||
dashboardPanelData.data.config.table_dynamic_columns = true;
|
||||
}
|
||||
|
||||
// set logs page data to searchResponseForVisualization
|
||||
if (shouldUseHistogramQuery.value === true) {
|
||||
// only do it if is_histogram_eligible is true on logs page
|
||||
@@ -1721,6 +1748,8 @@ export default defineComponent({
|
||||
// assign to visualizeChartData as well
|
||||
visualizeChartData.value.queries[0].query =
|
||||
dashboardPanelData.data.queries[0].query;
|
||||
visualizeChartData.value.queries[0].vrlFunctionQuery =
|
||||
dashboardPanelData.data.queries[0].vrlFunctionQuery;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1783,6 +1812,15 @@ export default defineComponent({
|
||||
};
|
||||
}
|
||||
|
||||
// Enable dynamic columns for VRL table charts (after preservedConfig to ensure it's set)
|
||||
if (
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function" &&
|
||||
dashboardPanelData.data.type === "table"
|
||||
) {
|
||||
dashboardPanelData.data.config.table_dynamic_columns = true;
|
||||
}
|
||||
|
||||
// run query
|
||||
await copyDashboardDataToVisualize();
|
||||
|
||||
@@ -1831,6 +1869,22 @@ export default defineComponent({
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].customQuery = true;
|
||||
|
||||
// Update VRL function query if present
|
||||
if (
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function"
|
||||
) {
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].vrlFunctionQuery = b64EncodeUnicode(
|
||||
searchObj.data.tempFunctionContent,
|
||||
);
|
||||
} else {
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].vrlFunctionQuery = "";
|
||||
}
|
||||
|
||||
// reset old rendered chart
|
||||
visualizeChartData.value = {};
|
||||
|
||||
@@ -1842,6 +1896,15 @@ export default defineComponent({
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable dynamic columns for VRL table charts
|
||||
if (
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function" &&
|
||||
dashboardPanelData.data.type === "table"
|
||||
) {
|
||||
dashboardPanelData.data.config.table_dynamic_columns = true;
|
||||
}
|
||||
|
||||
// emit resize event
|
||||
// this will rerender/call resize method of already rendered chart to resize
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
@@ -2220,10 +2283,15 @@ export default defineComponent({
|
||||
|
||||
checkAbort();
|
||||
|
||||
/* Decide whether to use histogram query - don't use for table charts or when there are group_by fields */
|
||||
/* Decide whether to use histogram query - don't use for table charts, when there are group_by fields, or when VRL functions are present */
|
||||
const hasVrlFunction =
|
||||
searchObj.data.tempFunctionContent &&
|
||||
searchObj.data.transformType === "function";
|
||||
|
||||
shouldUseHistogramQuery.value =
|
||||
dashboardPanelData.data.type !== "table" &&
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length);
|
||||
!(extractedFields?.group_by && extractedFields.group_by.length) &&
|
||||
!hasVrlFunction;
|
||||
|
||||
const finalQuery = logsPageQuery;
|
||||
|
||||
|
||||
@@ -3402,7 +3402,7 @@ describe("SearchBar.vue Actual Component Methods", () => {
|
||||
componentInstance.updateQueryValue("SELECT * FROM logs");
|
||||
componentInstance.resetRegionFilter();
|
||||
componentInstance.createScheduleJob();
|
||||
|
||||
|
||||
// Validate final state
|
||||
expect(componentInstance.searchObj.data.query).toBe("test query");
|
||||
expect(componentInstance.searchObj.data.editorValue).toBe("SELECT * FROM logs");
|
||||
@@ -3410,4 +3410,152 @@ describe("SearchBar.vue Actual Component Methods", () => {
|
||||
expect(componentInstance.regionFilter).toBe("");
|
||||
expect(componentInstance.searchObj.meta.jobRecords).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* VRL Visualization Support Tests - SearchBar VRL Editor
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* Tests for the VRL editor changes:
|
||||
* 1. VRL editor is no longer read-only in visualize mode
|
||||
* 2. Warning banner removed for visualize mode
|
||||
*/
|
||||
describe("SearchBar.vue VRL Visualization Support", () => {
|
||||
let vrlInstance: any;
|
||||
|
||||
beforeEach(() => {
|
||||
vrlInstance = {
|
||||
searchObj: {
|
||||
data: {
|
||||
tempFunctionContent: "",
|
||||
transformType: "function",
|
||||
query: "SELECT * FROM logs",
|
||||
},
|
||||
meta: {
|
||||
logsVisualizeToggle: "search",
|
||||
functionEditorPlaceholderFlag: true,
|
||||
showTransformEditor: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("VRL Editor ReadOnly attribute changes", () => {
|
||||
// Test: BEFORE - readOnly was based on logsVisualizeToggle
|
||||
it("should understand the BEFORE behavior: readOnly was conditional on visualize mode", () => {
|
||||
// BEFORE: :readOnly="searchObj.meta.logsVisualizeToggle === 'visualize'"
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
|
||||
const beforeReadOnly = vrlInstance.searchObj.meta.logsVisualizeToggle === "visualize";
|
||||
expect(beforeReadOnly).toBe(true); // Would have been read-only
|
||||
});
|
||||
|
||||
// Test: AFTER - readOnly is always false
|
||||
it("should have VRL editor always editable (readOnly=false)", () => {
|
||||
// AFTER: :readOnly="false"
|
||||
const afterReadOnly = false; // New fixed value
|
||||
|
||||
// In search mode
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
|
||||
expect(afterReadOnly).toBe(false);
|
||||
|
||||
// In visualize mode - NOW ALSO EDITABLE
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
expect(afterReadOnly).toBe(false);
|
||||
});
|
||||
|
||||
it("should allow editing VRL function content in visualize mode", () => {
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
vrlInstance.searchObj.data.tempFunctionContent = "";
|
||||
|
||||
// User can now edit VRL function in visualize mode
|
||||
vrlInstance.searchObj.data.tempFunctionContent = ".parsed = parse_json!(.message)";
|
||||
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(".parsed = parse_json!(.message)");
|
||||
});
|
||||
|
||||
it("should allow editing VRL function content in search mode (unchanged)", () => {
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
|
||||
vrlInstance.searchObj.data.tempFunctionContent = "";
|
||||
|
||||
// This worked before and should still work
|
||||
vrlInstance.searchObj.data.tempFunctionContent = ".level = upcase!(.severity)";
|
||||
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(".level = upcase!(.severity)");
|
||||
});
|
||||
});
|
||||
|
||||
describe("VRL Warning Banner removal", () => {
|
||||
// Test: BEFORE - Warning banner was shown in visualize mode
|
||||
it("should understand the BEFORE behavior: warning banner was shown in visualize mode", () => {
|
||||
// BEFORE: v-if="searchObj.meta.logsVisualizeToggle === 'visualize'"
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
|
||||
const beforeShowBanner = vrlInstance.searchObj.meta.logsVisualizeToggle === "visualize";
|
||||
expect(beforeShowBanner).toBe(true); // Banner would have been shown
|
||||
});
|
||||
|
||||
// Test: AFTER - Warning banner is completely removed
|
||||
it("should NOT show VRL warning banner in visualize mode (banner removed)", () => {
|
||||
// AFTER: The entire warning banner div is removed from template
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
|
||||
// The banner element no longer exists in the template
|
||||
const afterShowBanner = false; // Banner div removed
|
||||
|
||||
expect(afterShowBanner).toBe(false);
|
||||
});
|
||||
|
||||
it("should NOT display 'VRL Function Editor is not supported in visualize mode' message", () => {
|
||||
// This message was inside the removed banner
|
||||
const warningMessage = "VRL Function Editor is not supported in visualize mode.";
|
||||
|
||||
// The message is no longer rendered since the banner was removed
|
||||
// Simulating that the banner render condition no longer exists
|
||||
const bannerRendered = false;
|
||||
|
||||
expect(bannerRendered).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("VRL Editor behavior in different modes", () => {
|
||||
it("should allow VRL editing when transformType is function in visualize mode", () => {
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
vrlInstance.searchObj.data.transformType = "function";
|
||||
|
||||
// VRL editor should be enabled
|
||||
const canEdit = vrlInstance.searchObj.data.transformType === "function";
|
||||
expect(canEdit).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle VRL function content changes in visualize mode", () => {
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
vrlInstance.searchObj.data.transformType = "function";
|
||||
|
||||
// Simulate user typing VRL code
|
||||
const vrlCode = `
|
||||
.timestamp = to_timestamp!(.time)
|
||||
.level = upcase!(.severity)
|
||||
.parsed = parse_json!(.body)
|
||||
`;
|
||||
|
||||
vrlInstance.searchObj.data.tempFunctionContent = vrlCode;
|
||||
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toContain("parse_json");
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toContain("to_timestamp");
|
||||
});
|
||||
|
||||
it("should maintain VRL function content when toggling between modes", () => {
|
||||
const vrlCode = ".field = 1";
|
||||
vrlInstance.searchObj.data.tempFunctionContent = vrlCode;
|
||||
|
||||
// Toggle to visualize
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(vrlCode);
|
||||
|
||||
// Toggle back to search
|
||||
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
|
||||
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(vrlCode);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1184,9 +1184,7 @@ class="q-pr-sm q-pt-xs" />
|
||||
? 'empty-function'
|
||||
: ''
|
||||
"
|
||||
:readOnly="
|
||||
searchObj.meta.logsVisualizeToggle === 'visualize'
|
||||
"
|
||||
:readOnly="false"
|
||||
@keydown="handleKeyDown"
|
||||
language="vrl"
|
||||
@focus="
|
||||
@@ -1198,29 +1196,6 @@ class="q-pr-sm q-pt-xs" />
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="searchObj.meta.logsVisualizeToggle === 'visualize'"
|
||||
:class="
|
||||
store.state.theme == 'dark'
|
||||
? 'tw:bg-white tw:bg-opacity-10'
|
||||
: 'tw:bg-black tw:bg-opacity-10'
|
||||
"
|
||||
class="tw:absolute tw:bottom-0 tw:w-full"
|
||||
style="margin-top: 12px; display: flex; align-items: center; flex"
|
||||
>
|
||||
<q-icon
|
||||
name="warning"
|
||||
color="warning"
|
||||
size="20px"
|
||||
class="q-mx-sm"
|
||||
/>
|
||||
<span
|
||||
class="text-negative q-pa-sm"
|
||||
style="font-weight: semibold; font-size: 14px"
|
||||
>VRL Function Editor is not supported in visualize
|
||||
mode.</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchObj.data.transformType === 'action'">
|
||||
<code-query-editor
|
||||
|
||||
@@ -22,10 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="o2-toggle-button-xs element-box-shadow"
|
||||
size="xs"
|
||||
flat
|
||||
:disable="
|
||||
!searchObj.data.transformType ||
|
||||
searchObj.meta.logsVisualizeToggle === 'visualize'
|
||||
"
|
||||
:disable="!searchObj.data.transformType"
|
||||
>
|
||||
<q-icon :name="transformIcon" class="toolbar-icon-in-toggle" :class="transformsLabel" />
|
||||
<q-tooltip class="tw:text-[12px]" :offset="[0, 2]">
|
||||
@@ -52,7 +49,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="btn-function no-case q-pl-sm q-pr-none no-border no-outline tw:border-none"
|
||||
:class="`${searchObj.data.transformType || 'transform'}-icon`"
|
||||
label-class="no-case"
|
||||
:disable="searchObj.meta.logsVisualizeToggle === 'visualize'"
|
||||
>
|
||||
<q-list data-test="logs-search-saved-function-list">
|
||||
<!-- Search Input -->
|
||||
@@ -135,7 +131,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
data-test="logs-search-bar-save-transform-btn"
|
||||
class=" save-transform-btn q-px-sm"
|
||||
icon="save"
|
||||
:disable="searchObj.data.transformType !== 'function' || searchObj.meta.logsVisualizeToggle === 'visualize'"
|
||||
:disable="searchObj.data.transformType !== 'function'"
|
||||
@click="fnSavedFunctionDialog"
|
||||
|
||||
>
|
||||
@@ -143,7 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
{{
|
||||
searchObj.data.transformType === "action"
|
||||
? t("search.saveActionDisabled")
|
||||
: searchObj.meta.logsVisualizeToggle === 'visualize' ? 'Not supported for visualization' : t("common.save")
|
||||
: t("common.save")
|
||||
}}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
@@ -244,8 +240,6 @@ const transformsLabel = computed(() => {
|
||||
return searchObj.data.selectedTransform.name;
|
||||
}
|
||||
|
||||
if (searchObj.meta.logsVisualizeToggle === 'visualize') return t("search.functionSelectionNotSupportedVisualization");
|
||||
|
||||
if (!isActionsEnabled.value) return t("search.functionLabel");
|
||||
|
||||
return searchObj.data.transformType === "action"
|
||||
@@ -314,10 +308,6 @@ const fnSavedFunctionDialog = () => {
|
||||
};
|
||||
|
||||
const getTransformLabelTooltip = computed(() => {
|
||||
|
||||
// function selection is not supported for visualization
|
||||
if (searchObj.meta.logsVisualizeToggle === 'visualize') return t("search.functionSelectionNotSupportedVisualization");
|
||||
|
||||
if (!isActionsEnabled.value) return t("search.toggleFunctionEditor");
|
||||
|
||||
const editorType = searchObj.data.transformType === "action"
|
||||
|
||||
@@ -1519,4 +1519,134 @@ describe("VisualizeLogsQuery Component", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* VRL Visualization Support Tests - handleChartTypeChange
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* Tests for the VRL function validation added to handleChartTypeChange.
|
||||
* When VRL functions are present, only table chart type is allowed.
|
||||
*/
|
||||
describe("VRL Visualization Support - handleChartTypeChange", () => {
|
||||
it("should allow switching to table when VRL functions are present", () => {
|
||||
// Set VRL function fields
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "parsed_field", type: "Utf8" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "line";
|
||||
|
||||
// Simulate handleChartTypeChange to table
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("table");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent switching to line chart when VRL functions are present", () => {
|
||||
// Set VRL function fields
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "vrl_output", type: "Utf8" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
// Simulate handleChartTypeChange to line (should be prevented)
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("line");
|
||||
// Should force back to table
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent switching to bar chart when VRL functions are present", () => {
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "derived", type: "Int64" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("bar");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent switching to area chart when VRL functions are present", () => {
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "computed", type: "Float64" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("area");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent switching to scatter chart when VRL functions are present", () => {
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "extracted", type: "Utf8" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("scatter");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent switching to h-bar chart when VRL functions are present", () => {
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "parsed_json", type: "Utf8" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("h-bar");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
|
||||
it("should allow switching to any chart type when no VRL functions", () => {
|
||||
// Clear VRL function fields
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("line");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("line");
|
||||
|
||||
wrapper.vm.handleChartTypeChange("bar");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("bar");
|
||||
|
||||
wrapper.vm.handleChartTypeChange("area");
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("area");
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle vrlFunctionFieldList length check correctly", () => {
|
||||
// Test empty array
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
|
||||
expect(wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(false);
|
||||
|
||||
// Test with items
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "field1", type: "Utf8" },
|
||||
];
|
||||
expect(wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle multiple VRL derived fields", () => {
|
||||
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
|
||||
{ name: "field1", type: "Utf8" },
|
||||
{ name: "field2", type: "Int64" },
|
||||
{ name: "field3", type: "Float64" },
|
||||
];
|
||||
wrapper.vm.dashboardPanelData.data.type = "table";
|
||||
|
||||
if (typeof wrapper.vm.handleChartTypeChange === "function") {
|
||||
wrapper.vm.handleChartTypeChange("line");
|
||||
// Should still be table due to VRL functions
|
||||
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -654,6 +654,20 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if VRL functions are present and trying to switch to non-table chart
|
||||
if (
|
||||
dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0 &&
|
||||
newType !== "table"
|
||||
) {
|
||||
showErrorNotification(
|
||||
"VRL functions are present. Only table chart is supported when using VRL functions.",
|
||||
);
|
||||
// Force table chart type
|
||||
dashboardPanelData.data.type = "table";
|
||||
resetAggregationFunction();
|
||||
return;
|
||||
}
|
||||
|
||||
// If validation passes, proceed with the change
|
||||
dashboardPanelData.data.type = newType;
|
||||
resetAggregationFunction();
|
||||
|
||||
@@ -410,8 +410,16 @@ describe("TransformSelector.vue", () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should disable everything when in visualize mode", () => {
|
||||
/**
|
||||
* VRL Visualization Support Tests - Updated behavior
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* BEFORE: Transform selector was disabled in visualize mode
|
||||
* AFTER: Transform selector is now enabled in visualize mode to support VRL functions
|
||||
*/
|
||||
it("should NOT disable toggle in visualize mode when transformType is set (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
@@ -435,6 +443,93 @@ describe("TransformSelector.vue", () => {
|
||||
},
|
||||
});
|
||||
|
||||
// With VRL support, toggle should be enabled when transformType is set
|
||||
// The disable condition is now only: !searchObj.data.transformType
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should still disable toggle when no transformType regardless of visualize mode", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = ""; // Empty - no transform type
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": {
|
||||
template: '<div class="toggle" :disable="$attrs.disable" />',
|
||||
},
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Toggle should be disabled when no transformType
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should NOT disable save button in visualize mode when transformType is function (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": {
|
||||
template: '<button :disable="$attrs.disable" />',
|
||||
},
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Save button should be enabled for function type even in visualize mode
|
||||
// The disable condition is now only: searchObj.data.transformType !== 'function'
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should NOT disable dropdown in visualize mode (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": {
|
||||
template: '<div class="dropdown" :disable="$attrs.disable"><slot /></div>',
|
||||
},
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Dropdown disable condition was completely removed
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -637,4 +732,155 @@ describe("TransformSelector.vue", () => {
|
||||
expect(btnGroup.classes()).toContain("dark-theme");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* VRL Visualization Support Tests - Computed Properties
|
||||
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
|
||||
*
|
||||
* Tests for the changes to transformsLabel and getTransformLabelTooltip computed properties
|
||||
* that removed the visualization mode restrictions.
|
||||
*/
|
||||
describe("VRL Visualization Support - Computed Properties", () => {
|
||||
it("should NOT show 'not supported for visualization' in transformsLabel (removed)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
mockSearchObj.data.selectedTransform = null;
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": {
|
||||
template: '<div :label="$attrs.label"><slot /></div>',
|
||||
},
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// transformsLabel should show "Function" not the visualization restriction message
|
||||
// The line "if (searchObj.meta.logsVisualizeToggle === 'visualize') return t(...)" was removed
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should show standard function label in visualize mode (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
mockSearchObj.data.selectedTransform = null;
|
||||
mockIsActionsEnabled.value = true;
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Label should be "Function" in visualize mode now
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should show standard action label in visualize mode (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "action";
|
||||
mockSearchObj.data.selectedTransform = null;
|
||||
mockIsActionsEnabled.value = true;
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Label should be "Action" in visualize mode now
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should show standard tooltip in visualize mode (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
mockSearchObj.meta.showTransformEditor = false;
|
||||
mockIsActionsEnabled.value = true;
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": { template: "<div class='tooltip'><slot /></div>" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Tooltip should show "Show Function Editor" not visualization restriction message
|
||||
// The line "if (searchObj.meta.logsVisualizeToggle === 'visualize') return t(...)" was removed
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should show 'Save' tooltip for function type in visualize mode (VRL support)", () => {
|
||||
mockSearchObj.meta.logsVisualizeToggle = "visualize";
|
||||
mockSearchObj.data.transformType = "function";
|
||||
|
||||
const wrapper = mount(TransformSelector, {
|
||||
props: defaultProps,
|
||||
global: {
|
||||
plugins: [store, i18n, Quasar],
|
||||
mocks: {
|
||||
$q,
|
||||
},
|
||||
stubs: {
|
||||
"q-toggle": true,
|
||||
"q-btn-group": { template: "<div><slot /></div>" },
|
||||
"q-btn-dropdown": { template: "<div><slot /></div>" },
|
||||
"q-btn": true,
|
||||
"q-icon": true,
|
||||
"q-tooltip": { template: "<div class='tooltip'><slot /></div>" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Save button tooltip should show "Save" not "Not supported for visualization"
|
||||
// The ternary check for logsVisualizeToggle === 'visualize' was removed
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user