From 25a99bf39d4c78605233db46fcb3f39ea243dbde Mon Sep 17 00:00:00 2001 From: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:09:37 +0530 Subject: [PATCH] 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"] ```

File Walkthrough

Relevant files
Enhancement
useDashboardPanel.ts
Force table chart for VRL functions                                           

web/src/composables/useDashboardPanel.ts
  • Added VRL check in determineChartType
  • Default to table when VRL functions exist
+5/-0     
Index.vue
Propagate and handle VRL function queries                               

web/src/plugins/logs/Index.vue
  • Copy and clear vrlFunctionQuery on query update
  • Force table type and set table_dynamic_columns
  • Preserve vrlFunctionQuery in visualization flow
+70/-2   
SearchBar.vue
Enable VRL editor in visualize mode                                           

web/src/plugins/logs/SearchBar.vue
  • Always allow editing VRL function editor
  • Removed visualize-mode warning overlay
+1/-26   
TransformSelector.vue
Lift transform restrictions in visualize mode                       

web/src/plugins/logs/TransformSelector.vue
  • Removed disable state for transform toggle/save in visualize
  • Removed visualize-mode tooltip restrictions
+3/-13   
VisualizeLogsQuery.vue
Restrict chart types when VRL functions present                   

web/src/plugins/logs/VisualizeLogsQuery.vue
  • Block non-table chart selection if VRL functions exist
  • Show error and reset to table on invalid type switch
+14/-0   
___ ___ ### **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"] ```

File Walkthrough

Relevant files
Tests
5 files
useDashboardPanel.spec.ts
Add VRL chart type determination tests                                     
+110/-0 
Index.spec.ts
Add VRL support tests in Index.vue                                             
+189/-0 
SearchBar.spec.ts
Add VRL editor readOnly and banner tests                                 
+149/-1 
VisualizeLogsQuery.spec.ts
Test VRL chart type change restrictions                                   
+130/-0 
TransformSelector.spec.ts
Enable transform toggle in visualize mode                               
+247/-1 
Enhancement
5 files
useDashboardPanel.ts
Force table chart for VRL functions                                           
+5/-0     
Index.vue
Propagate and apply VRL function query                                     
+70/-2   
SearchBar.vue
Remove VRL editor read-only restriction                                   
+1/-26   
TransformSelector.vue
Allow transform selector in visualize mode                             
+3/-13   
VisualizeLogsQuery.vue
Restrict non-table charts when VRL present                             
+14/-0   
___ --- web/src/composables/useDashboardPanel.spec.ts | 110 ++++++++ web/src/composables/useDashboardPanel.ts | 5 + web/src/plugins/logs/Index.spec.ts | 189 +++++++++++++ web/src/plugins/logs/Index.vue | 72 ++++- web/src/plugins/logs/SearchBar.spec.ts | 150 ++++++++++- web/src/plugins/logs/SearchBar.vue | 27 +- web/src/plugins/logs/TransformSelector.vue | 16 +- .../plugins/logs/VisualizeLogsQuery.spec.ts | 130 +++++++++ web/src/plugins/logs/VisualizeLogsQuery.vue | 14 + .../plugins/logs/TransformSelector.spec.ts | 248 +++++++++++++++++- 10 files changed, 918 insertions(+), 43 deletions(-) diff --git a/web/src/composables/useDashboardPanel.spec.ts b/web/src/composables/useDashboardPanel.spec.ts index 1cf96f44dd..ba87180d8a 100644 --- a/web/src/composables/useDashboardPanel.spec.ts +++ b/web/src/composables/useDashboardPanel.spec.ts @@ -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); + }); + }); }); }); diff --git a/web/src/composables/useDashboardPanel.ts b/web/src/composables/useDashboardPanel.ts index e8445f9064..39a6c2a36d 100644 --- a/web/src/composables/useDashboardPanel.ts +++ b/web/src/composables/useDashboardPanel.ts @@ -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 diff --git a/web/src/plugins/logs/Index.spec.ts b/web/src/plugins/logs/Index.spec.ts index a2e5e2c8e5..64295678f8 100644 --- a/web/src/plugins/logs/Index.spec.ts +++ b/web/src/plugins/logs/Index.spec.ts @@ -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); + }); + }); + }); }); diff --git a/web/src/plugins/logs/Index.vue b/web/src/plugins/logs/Index.vue index 204f441441..2d11facf0c 100644 --- a/web/src/plugins/logs/Index.vue +++ b/web/src/plugins/logs/Index.vue @@ -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; diff --git a/web/src/plugins/logs/SearchBar.spec.ts b/web/src/plugins/logs/SearchBar.spec.ts index 738f2f6665..ef40cbd46f 100644 --- a/web/src/plugins/logs/SearchBar.spec.ts +++ b/web/src/plugins/logs/SearchBar.spec.ts @@ -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); + }); + }); }); \ No newline at end of file diff --git a/web/src/plugins/logs/SearchBar.vue b/web/src/plugins/logs/SearchBar.vue index de368dff9e..d2c54729ec 100644 --- a/web/src/plugins/logs/SearchBar.vue +++ b/web/src/plugins/logs/SearchBar.vue @@ -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" /> /> -
- - VRL Function Editor is not supported in visualize - mode. -