From 3e9f4ac5152f63fe829f4934143ff47843dc6a13 Mon Sep 17 00:00:00 2001
From: Kamil Mielnik <kamil@kamilmielnik.com>
Date: Fri, 10 May 2024 20:39:09 +0700
Subject: [PATCH] Fix - `diagnose-expression` throws when `Offset` is nested
 (#42497)

* Fix offset not working in case

* Make offset function return any

* Add a repro for #42377

* Fix order of adjustments

* Revert unrelated changes

* Remove commented code

* Revert unrelated changes
---
 e2e/test/scenarios/question/offset.cy.spec.ts | 43 +++++++++++++++++++
 .../v1/expressions/diagnostics.ts             |  2 +-
 .../v1/expressions/recursive-parser.js        |  2 +-
 .../metabase/lib/expressions/pratt/common.ts  |  2 +-
 4 files changed, 46 insertions(+), 3 deletions(-)
 create mode 100644 e2e/test/scenarios/question/offset.cy.spec.ts

diff --git a/e2e/test/scenarios/question/offset.cy.spec.ts b/e2e/test/scenarios/question/offset.cy.spec.ts
new file mode 100644
index 00000000000..f20370dc1e3
--- /dev/null
+++ b/e2e/test/scenarios/question/offset.cy.spec.ts
@@ -0,0 +1,43 @@
+import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
+import type { StructuredQuestionDetails } from "e2e/support/helpers";
+import {
+  createQuestion,
+  enterCustomColumnDetails,
+  getNotebookStep,
+  openNotebook,
+  popover,
+  restore,
+} from "e2e/support/helpers";
+
+const { ORDERS_ID } = SAMPLE_DATABASE;
+
+describe("scenarios > question > offset", () => {
+  beforeEach(() => {
+    restore();
+    cy.signInAsAdmin();
+  });
+
+  it("should allow using OFFSET as a CASE argument (metabase#42377)", () => {
+    const formula = "Sum(case([Total] > 0, Offset([Total], -1)))";
+    const name = "Aggregation";
+    const questionDetails: StructuredQuestionDetails = {
+      query: {
+        "source-table": ORDERS_ID,
+        limit: 5,
+      },
+    };
+    createQuestion(questionDetails, { visitQuestion: true });
+    openNotebook();
+
+    cy.icon("sum").click();
+    getNotebookStep("summarize")
+      .findByText("Pick the metric you want to see")
+      .click();
+    popover().contains("Custom Expression").click();
+    enterCustomColumnDetails({ formula, name });
+
+    cy.on("uncaught:exception", error => {
+      expect(error.message.includes("Error normalizing")).not.to.be.true;
+    });
+  });
+});
diff --git a/frontend/src/metabase-lib/v1/expressions/diagnostics.ts b/frontend/src/metabase-lib/v1/expressions/diagnostics.ts
index 167e35a5f47..0e44cb5a833 100644
--- a/frontend/src/metabase-lib/v1/expressions/diagnostics.ts
+++ b/frontend/src/metabase-lib/v1/expressions/diagnostics.ts
@@ -228,8 +228,8 @@ function prattCompiler({
     passes: [
       adjustOptions,
       useShorthands,
-      adjustCase,
       adjustOffset,
+      adjustCase,
       expression =>
         resolve({
           expression,
diff --git a/frontend/src/metabase-lib/v1/expressions/recursive-parser.js b/frontend/src/metabase-lib/v1/expressions/recursive-parser.js
index 6c3d0bbcb41..6b4a039197e 100644
--- a/frontend/src/metabase-lib/v1/expressions/recursive-parser.js
+++ b/frontend/src/metabase-lib/v1/expressions/recursive-parser.js
@@ -372,6 +372,6 @@ export const parse = pipe(
   recursiveParse,
   adjustOptions,
   useShorthands,
-  adjustCase,
   adjustOffset,
+  adjustCase,
 );
diff --git a/frontend/test/metabase/lib/expressions/pratt/common.ts b/frontend/test/metabase/lib/expressions/pratt/common.ts
index 1d7089eb63b..7739f219813 100644
--- a/frontend/test/metabase/lib/expressions/pratt/common.ts
+++ b/frontend/test/metabase/lib/expressions/pratt/common.ts
@@ -25,7 +25,7 @@ interface Opts {
 
 export function compile(source: string, type: Type, opts: Opts = {}) {
   const { throwOnError } = opts;
-  const passes = [adjustOptions, useShorthands, adjustCase, adjustOffset];
+  const passes = [adjustOptions, useShorthands, adjustOffset, adjustCase];
   return newCompile(
     parse(lexify(source), {
       throwOnError,
-- 
GitLab