From f6b4c0f2ce19c31a9972143c12bda210f55681c7 Mon Sep 17 00:00:00 2001
From: Dalton <daltojohnso@users.noreply.github.com>
Date: Tue, 26 Oct 2021 08:54:49 -0700
Subject: [PATCH] move mapping options function into own utils file (#18638)

* move mapping options function into own utils file

* pull out option-building logic into own functions
---
 .../QuestionParameterTargetWidget.jsx         |   8 +-
 frontend/src/metabase/dashboard/selectors.js  |   2 +-
 frontend/src/metabase/meta/Dashboard.js       |  80 ---------
 .../parameters/utils/mapping-options.js       |  81 +++++++++
 .../utils/mapping-options.unit.spec.js        | 170 ++++++++++++++++++
 .../test/metabase/meta/Dashboard.unit.spec.js | 167 -----------------
 6 files changed, 254 insertions(+), 254 deletions(-)
 create mode 100644 frontend/src/metabase/parameters/utils/mapping-options.js
 create mode 100644 frontend/src/metabase/parameters/utils/mapping-options.unit.spec.js

diff --git a/enterprise/frontend/src/metabase-enterprise/sandboxes/containers/QuestionParameterTargetWidget.jsx b/enterprise/frontend/src/metabase-enterprise/sandboxes/containers/QuestionParameterTargetWidget.jsx
index 248b73adba6..43f80867faf 100644
--- a/enterprise/frontend/src/metabase-enterprise/sandboxes/containers/QuestionParameterTargetWidget.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/sandboxes/containers/QuestionParameterTargetWidget.jsx
@@ -4,7 +4,7 @@ import React from "react";
 import ParameterTargetWidget from "metabase/parameters/components/ParameterTargetWidget";
 import { QuestionLoaderHOC } from "metabase/containers/QuestionLoader";
 
-import * as Dashboard from "metabase/meta/Dashboard";
+import { getParameterMappingOptions } from "metabase/parameters/utils/mapping-options";
 
 import type { ParameterTarget } from "metabase-types/types/Parameter";
 
@@ -23,11 +23,7 @@ export default class QuestionParameterTargetWidget extends React.Component {
   render() {
     const { question, ...props } = this.props;
     const mappingOptions = question
-      ? Dashboard.getParameterMappingOptions(
-          question.metadata(),
-          null,
-          question.card(),
-        )
+      ? getParameterMappingOptions(question.metadata(), null, question.card())
       : [];
     return <ParameterTargetWidget {...props} mappingOptions={mappingOptions} />;
   }
diff --git a/frontend/src/metabase/dashboard/selectors.js b/frontend/src/metabase/dashboard/selectors.js
index 7b114a1a695..37faea69b1a 100644
--- a/frontend/src/metabase/dashboard/selectors.js
+++ b/frontend/src/metabase/dashboard/selectors.js
@@ -5,10 +5,10 @@ import { createSelector } from "reselect";
 import { getMetadata } from "metabase/selectors/metadata";
 
 import {
-  getParameterMappingOptions as _getParameterMappingOptions,
   getMappingsByParameter as _getMappingsByParameter,
   getDashboardParametersWithFieldMetadata,
 } from "metabase/meta/Dashboard";
+import { getParameterMappingOptions as _getParameterMappingOptions } from "metabase/parameters/utils/mapping-options";
 
 import { SIDEBAR_NAME } from "metabase/dashboard/constants";
 
diff --git a/frontend/src/metabase/meta/Dashboard.js b/frontend/src/metabase/meta/Dashboard.js
index 03eb252e49d..e895edcc2d3 100644
--- a/frontend/src/metabase/meta/Dashboard.js
+++ b/frontend/src/metabase/meta/Dashboard.js
@@ -3,23 +3,11 @@ import { setIn } from "icepick";
 
 import Question from "metabase-lib/lib/Question";
 
-import { ExpressionDimension } from "metabase-lib/lib/Dimension";
-
-import type Metadata from "metabase-lib/lib/metadata/Metadata";
-import type { Card } from "metabase-types/types/Card";
 import type {
   ParameterOption,
   Parameter,
-  ParameterMappingUIOption,
 } from "metabase-types/types/Parameter";
-
 import { getParameterTargetField } from "metabase/meta/Parameter";
-import {
-  dimensionFilterForParameter,
-  getTagOperatorFilterForParameter,
-  variableFilterForParameter,
-} from "metabase/parameters/utils/filters";
-
 import { slugify } from "metabase/lib/formatting";
 
 export type ParameterSection = {
@@ -29,74 +17,6 @@ export type ParameterSection = {
   options: ParameterOption[],
 };
 
-export function getParameterMappingOptions(
-  metadata: Metadata,
-  parameter: ?Parameter = null,
-  card: Card,
-): ParameterMappingUIOption[] {
-  const options = [];
-  if (card.display === "text") {
-    // text cards don't have parameters
-    return [];
-  }
-
-  const question = new Question(card, metadata);
-  const query = question.query();
-
-  if (question.isStructured()) {
-    options.push(
-      ...query
-        .dimensionOptions(
-          parameter ? dimensionFilterForParameter(parameter) : undefined,
-        )
-        .sections()
-        .flatMap(section =>
-          section.items.map(({ dimension }) => ({
-            sectionName: section.name,
-            name: dimension.displayName(),
-            icon: dimension.icon(),
-            target: ["dimension", dimension.mbql()],
-            // these methods don't exist on instances of ExpressionDimension
-            isForeign: !!(dimension instanceof ExpressionDimension
-              ? false
-              : dimension.fk() || dimension.joinAlias()),
-          })),
-        ),
-    );
-  } else {
-    options.push(
-      ...query
-        .variables(
-          parameter ? variableFilterForParameter(parameter) : undefined,
-        )
-        .map(variable => ({
-          name: variable.displayName(),
-          icon: variable.icon(),
-          isForeign: false,
-          target: ["variable", variable.mbql()],
-        })),
-    );
-    options.push(
-      ...query
-        .dimensionOptions(
-          parameter ? dimensionFilterForParameter(parameter) : undefined,
-          parameter ? getTagOperatorFilterForParameter(parameter) : undefined,
-        )
-        .sections()
-        .flatMap(section =>
-          section.items.map(({ dimension }) => ({
-            name: dimension.displayName(),
-            icon: dimension.icon(),
-            isForeign: false,
-            target: ["dimension", dimension.mbql()],
-          })),
-        ),
-    );
-  }
-
-  return options;
-}
-
 export function createParameter(
   option: ParameterOption,
   parameters: Parameter[] = [],
diff --git a/frontend/src/metabase/parameters/utils/mapping-options.js b/frontend/src/metabase/parameters/utils/mapping-options.js
new file mode 100644
index 00000000000..72d19dfb702
--- /dev/null
+++ b/frontend/src/metabase/parameters/utils/mapping-options.js
@@ -0,0 +1,81 @@
+import Question from "metabase-lib/lib/Question";
+
+import { ExpressionDimension } from "metabase-lib/lib/Dimension";
+
+import {
+  dimensionFilterForParameter,
+  getTagOperatorFilterForParameter,
+  variableFilterForParameter,
+} from "./filters";
+
+function buildStructuredQuerySectionOptions(section) {
+  return section.items.map(({ dimension }) => ({
+    sectionName: section.name,
+    name: dimension.displayName(),
+    icon: dimension.icon(),
+    target: ["dimension", dimension.mbql()],
+    // these methods don't exist on instances of ExpressionDimension
+    isForeign: !!(dimension instanceof ExpressionDimension
+      ? false
+      : dimension.fk() || dimension.joinAlias()),
+  }));
+}
+
+function buildNativeQuerySectionOptions(section) {
+  return section.items.map(({ dimension }) => ({
+    name: dimension.displayName(),
+    icon: dimension.icon(),
+    isForeign: false,
+    target: ["dimension", dimension.mbql()],
+  }));
+}
+
+function buildVariableOption(variable) {
+  return {
+    name: variable.displayName(),
+    icon: variable.icon(),
+    isForeign: false,
+    target: ["variable", variable.mbql()],
+  };
+}
+
+export function getParameterMappingOptions(metadata, parameter = null, card) {
+  const options = [];
+  if (card.display === "text") {
+    // text cards don't have parameters
+    return [];
+  }
+
+  const question = new Question(card, metadata);
+  const query = question.query();
+
+  if (question.isStructured()) {
+    options.push(
+      ...query
+        .dimensionOptions(
+          parameter ? dimensionFilterForParameter(parameter) : undefined,
+        )
+        .sections()
+        .flatMap(section => buildStructuredQuerySectionOptions(section)),
+    );
+  } else {
+    options.push(
+      ...query
+        .variables(
+          parameter ? variableFilterForParameter(parameter) : undefined,
+        )
+        .map(buildVariableOption),
+    );
+    options.push(
+      ...query
+        .dimensionOptions(
+          parameter ? dimensionFilterForParameter(parameter) : undefined,
+          parameter ? getTagOperatorFilterForParameter(parameter) : undefined,
+        )
+        .sections()
+        .flatMap(section => buildNativeQuerySectionOptions(section)),
+    );
+  }
+
+  return options;
+}
diff --git a/frontend/src/metabase/parameters/utils/mapping-options.unit.spec.js b/frontend/src/metabase/parameters/utils/mapping-options.unit.spec.js
new file mode 100644
index 00000000000..00910e13197
--- /dev/null
+++ b/frontend/src/metabase/parameters/utils/mapping-options.unit.spec.js
@@ -0,0 +1,170 @@
+import {
+  metadata,
+  SAMPLE_DATASET,
+  REVIEWS,
+  ORDERS,
+  PRODUCTS,
+} from "__support__/sample_dataset_fixture";
+
+import { getParameterMappingOptions } from "./mapping-options";
+
+function structured(query) {
+  return SAMPLE_DATASET.question(query).card();
+}
+
+function native(native) {
+  return SAMPLE_DATASET.nativeQuestion(native).card();
+}
+
+describe("parameters/utils/mapping-options", () => {
+  describe("getParameterMappingOptions", () => {
+    describe("Structured Query", () => {
+      it("should return field-id and fk-> dimensions", () => {
+        const options = getParameterMappingOptions(
+          metadata,
+          { type: "date/single" },
+          structured({
+            "source-table": REVIEWS.id,
+          }),
+        );
+        expect(options).toEqual([
+          {
+            sectionName: "Review",
+            icon: "calendar",
+            name: "Created At",
+            target: ["dimension", ["field", REVIEWS.CREATED_AT.id, null]],
+            isForeign: false,
+          },
+          {
+            sectionName: "Product",
+            name: "Created At",
+            icon: "calendar",
+            target: [
+              "dimension",
+              [
+                "field",
+                PRODUCTS.CREATED_AT.id,
+                { "source-field": REVIEWS.PRODUCT_ID.id },
+              ],
+            ],
+            isForeign: true,
+          },
+        ]);
+      });
+      it("should also return fields from explicitly joined tables", () => {
+        const options = getParameterMappingOptions(
+          metadata,
+          { type: "date/single" },
+          structured({
+            "source-table": REVIEWS.id,
+            joins: [
+              {
+                alias: "Joined Table",
+                "source-table": ORDERS.id,
+              },
+            ],
+          }),
+        );
+        expect(options).toEqual([
+          {
+            sectionName: "Review",
+            name: "Created At",
+            icon: "calendar",
+            target: ["dimension", ["field", 30, null]],
+            isForeign: false,
+          },
+          {
+            sectionName: "Joined Table",
+            name: "Created At",
+            icon: "calendar",
+            target: [
+              "dimension",
+              ["field", 1, { "join-alias": "Joined Table" }],
+            ],
+            isForeign: true,
+          },
+          {
+            sectionName: "Product",
+            name: "Created At",
+            icon: "calendar",
+            target: ["dimension", ["field", 22, { "source-field": 32 }]],
+            isForeign: true,
+          },
+        ]);
+      });
+      it("should return fields in nested query", () => {
+        const options = getParameterMappingOptions(
+          metadata,
+          { type: "date/single" },
+          structured({
+            "source-query": {
+              "source-table": ORDERS.id,
+            },
+          }),
+        );
+        expect(options).toEqual([
+          {
+            sectionName: null,
+            name: "Created At",
+            icon: "calendar",
+            target: [
+              "dimension",
+              ["field", "CREATED_AT", { "base-type": "type/DateTime" }],
+            ],
+            isForeign: false,
+          },
+        ]);
+      });
+    });
+
+    describe("NativeQuery", () => {
+      it("should return variables for non-dimension template-tags", () => {
+        const options = getParameterMappingOptions(
+          metadata,
+          { type: "date/single" },
+          native({
+            query: "select * from ORDERS where CREATED_AT = {{created}}",
+            "template-tags": {
+              created: {
+                type: "date",
+                name: "created",
+              },
+            },
+          }),
+        );
+        expect(options).toEqual([
+          {
+            name: "created",
+            icon: "calendar",
+            target: ["variable", ["template-tag", "created"]],
+            isForeign: false,
+          },
+        ]);
+      });
+    });
+    it("should return dimensions for dimension template-tags", () => {
+      const options = getParameterMappingOptions(
+        metadata,
+        { type: "date/single" },
+        native({
+          query: "select * from ORDERS where CREATED_AT = {{created}}",
+          "template-tags": {
+            created: {
+              type: "dimension",
+              name: "created",
+              dimension: ["field", ORDERS.CREATED_AT.id, null],
+            },
+          },
+        }),
+      );
+      expect(options).toEqual([
+        {
+          name: "Created At",
+          icon: "calendar",
+          target: ["dimension", ["template-tag", "created"]],
+          isForeign: false,
+        },
+      ]);
+    });
+  });
+});
diff --git a/frontend/test/metabase/meta/Dashboard.unit.spec.js b/frontend/test/metabase/meta/Dashboard.unit.spec.js
index f7f34396526..057ee4cbe5c 100644
--- a/frontend/test/metabase/meta/Dashboard.unit.spec.js
+++ b/frontend/test/metabase/meta/Dashboard.unit.spec.js
@@ -1,15 +1,7 @@
-import {
-  metadata,
-  SAMPLE_DATASET,
-  REVIEWS,
-  ORDERS,
-  PRODUCTS,
-} from "__support__/sample_dataset_fixture";
 import {
   createParameter,
   setParameterName,
   setParameterDefaultValue,
-  getParameterMappingOptions,
   hasMapping,
   isDashboardParameterWithoutMapping,
   getMappingsByParameter,
@@ -18,14 +10,6 @@ import DASHBOARD_WITH_BOOLEAN_PARAMETER from "./dashboard-with-boolean-parameter
 
 import Field from "metabase-lib/lib/metadata/Field";
 
-function structured(query) {
-  return SAMPLE_DATASET.question(query).card();
-}
-
-function native(native) {
-  return SAMPLE_DATASET.nativeQuestion(native).card();
-}
-
 describe("meta/Dashboard", () => {
   describe("createParameter", () => {
     it("should create a new parameter using the given parameter option", () => {
@@ -122,157 +106,6 @@ describe("meta/Dashboard", () => {
     });
   });
 
-  describe("getParameterMappingOptions", () => {
-    describe("Structured Query", () => {
-      it("should return field-id and fk-> dimensions", () => {
-        const options = getParameterMappingOptions(
-          metadata,
-          { type: "date/single" },
-          structured({
-            "source-table": REVIEWS.id,
-          }),
-        );
-        expect(options).toEqual([
-          {
-            sectionName: "Review",
-            icon: "calendar",
-            name: "Created At",
-            target: ["dimension", ["field", REVIEWS.CREATED_AT.id, null]],
-            isForeign: false,
-          },
-          {
-            sectionName: "Product",
-            name: "Created At",
-            icon: "calendar",
-            target: [
-              "dimension",
-              [
-                "field",
-                PRODUCTS.CREATED_AT.id,
-                { "source-field": REVIEWS.PRODUCT_ID.id },
-              ],
-            ],
-            isForeign: true,
-          },
-        ]);
-      });
-      it("should also return fields from explicitly joined tables", () => {
-        const options = getParameterMappingOptions(
-          metadata,
-          { type: "date/single" },
-          structured({
-            "source-table": REVIEWS.id,
-            joins: [
-              {
-                alias: "Joined Table",
-                "source-table": ORDERS.id,
-              },
-            ],
-          }),
-        );
-        expect(options).toEqual([
-          {
-            sectionName: "Review",
-            name: "Created At",
-            icon: "calendar",
-            target: ["dimension", ["field", 30, null]],
-            isForeign: false,
-          },
-          {
-            sectionName: "Joined Table",
-            name: "Created At",
-            icon: "calendar",
-            target: [
-              "dimension",
-              ["field", 1, { "join-alias": "Joined Table" }],
-            ],
-            isForeign: true,
-          },
-          {
-            sectionName: "Product",
-            name: "Created At",
-            icon: "calendar",
-            target: ["dimension", ["field", 22, { "source-field": 32 }]],
-            isForeign: true,
-          },
-        ]);
-      });
-      it("should return fields in nested query", () => {
-        const options = getParameterMappingOptions(
-          metadata,
-          { type: "date/single" },
-          structured({
-            "source-query": {
-              "source-table": ORDERS.id,
-            },
-          }),
-        );
-        expect(options).toEqual([
-          {
-            sectionName: null,
-            name: "Created At",
-            icon: "calendar",
-            target: [
-              "dimension",
-              ["field", "CREATED_AT", { "base-type": "type/DateTime" }],
-            ],
-            isForeign: false,
-          },
-        ]);
-      });
-    });
-
-    describe("NativeQuery", () => {
-      it("should return variables for non-dimension template-tags", () => {
-        const options = getParameterMappingOptions(
-          metadata,
-          { type: "date/single" },
-          native({
-            query: "select * from ORDERS where CREATED_AT = {{created}}",
-            "template-tags": {
-              created: {
-                type: "date",
-                name: "created",
-              },
-            },
-          }),
-        );
-        expect(options).toEqual([
-          {
-            name: "created",
-            icon: "calendar",
-            target: ["variable", ["template-tag", "created"]],
-            isForeign: false,
-          },
-        ]);
-      });
-    });
-    it("should return dimensions for dimension template-tags", () => {
-      const options = getParameterMappingOptions(
-        metadata,
-        { type: "date/single" },
-        native({
-          query: "select * from ORDERS where CREATED_AT = {{created}}",
-          "template-tags": {
-            created: {
-              type: "dimension",
-              name: "created",
-              dimension: ["field", ORDERS.CREATED_AT.id, null],
-            },
-          },
-        }),
-      );
-      expect(options).toEqual([
-        {
-          name: "Created At",
-          icon: "calendar",
-          target: ["dimension", ["template-tag", "created"]],
-          isForeign: false,
-        },
-      ]);
-    });
-  });
-
   describe("hasMapping", () => {
     const parameter = { id: "foo" };
 
-- 
GitLab