From 031209d30ae28f049f53f3f8227a016edb931554 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Mon, 10 Oct 2022 19:25:20 +0300
Subject: [PATCH] Move FieldRemapping logic to metabase-lib (#25859)

---
 frontend/src/metabase-lib/lib/Dimension.ts    |  6 +++
 .../src/metabase-lib/lib/metadata/Field.ts    | 16 +++++++
 .../datamodel/components/FieldRemapping.jsx   | 42 ++++---------------
 .../admin/datamodel/containers/FieldApp.jsx   |  1 +
 4 files changed, 30 insertions(+), 35 deletions(-)

diff --git a/frontend/src/metabase-lib/lib/Dimension.ts b/frontend/src/metabase-lib/lib/Dimension.ts
index a3d491da9ad..21acf7c02b0 100644
--- a/frontend/src/metabase-lib/lib/Dimension.ts
+++ b/frontend/src/metabase-lib/lib/Dimension.ts
@@ -583,6 +583,12 @@ export default class Dimension {
     return this.withoutTemporalBucketing().withoutBinning();
   }
 
+  isValidFKRemappingTarget() {
+    return !(
+      this.defaultDimension() instanceof FieldDimension && this.temporalUnit()
+    );
+  }
+
   /**
    * The name to be shown when this dimension is being displayed as a sub-dimension of another.
    *
diff --git a/frontend/src/metabase-lib/lib/metadata/Field.ts b/frontend/src/metabase-lib/lib/metadata/Field.ts
index 31b95ed5419..c2a100a9f26 100644
--- a/frontend/src/metabase-lib/lib/metadata/Field.ts
+++ b/frontend/src/metabase-lib/lib/metadata/Field.ts
@@ -440,6 +440,22 @@ class FieldInner extends Base {
     });
   }
 
+  remappingOptions = () => {
+    const table = this.table;
+    if (!table) {
+      return [];
+    }
+
+    const { fks } = table.query().fieldOptions();
+    return fks
+      .filter(({ field }) => field.id === this.id)
+      .map(({ field, dimension, dimensions }) => ({
+        field,
+        dimension,
+        dimensions: dimensions.filter(d => d.isValidFKRemappingTarget()),
+      }));
+  };
+
   clone(fieldMetadata) {
     if (fieldMetadata instanceof Field) {
       throw new Error("`fieldMetadata` arg must be a plain object");
diff --git a/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx b/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
index 65ded2d0d95..7a482f31f80 100644
--- a/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
+++ b/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
@@ -12,7 +12,7 @@ import ButtonWithStatus from "metabase/components/ButtonWithStatus";
 import * as MetabaseAnalytics from "metabase/lib/analytics";
 
 import Dimension, { FieldDimension } from "metabase-lib/lib/Dimension";
-import Question from "metabase-lib/lib/Question";
+import { isEntityName, isFK } from "metabase-lib/lib/types/utils/isa";
 import SelectSeparator from "../components/SelectSeparator";
 import {
   FieldMappingContainer,
@@ -57,9 +57,9 @@ export default class FieldRemapping extends React.Component {
     throw new Error(t`Unrecognized mapping type`);
   };
 
-  hasForeignKeys = () =>
-    this.props.field.semantic_type === "type/FK" &&
-    this.getForeignKeys().length > 0;
+  hasForeignKeys = () => {
+    return isFK(this.props.field) && this.getForeignKeys().length > 0;
+  };
 
   hasMappableNumeralValues = () => {
     const { field } = this.props;
@@ -95,10 +95,7 @@ export default class FieldRemapping extends React.Component {
     const fkTargetFields = fks[0] && fks[0].dimensions.map(dim => dim.field());
 
     if (fkTargetFields) {
-      // TODO Atte Keinänen 7/11/17: Should there be `isName(field)` in Field.js?
-      const nameField = fkTargetFields.find(
-        field => field.semantic_type === "type/Name",
-      );
+      const nameField = fkTargetFields.find(field => isEntityName(field));
       return nameField ? nameField.id : null;
     } else {
       throw new Error(
@@ -217,34 +214,9 @@ export default class FieldRemapping extends React.Component {
     return updateFieldValues({ id: field.id }, Array.from(remappings));
   };
 
-  // TODO Atte Keinänen 7/11/17: Should we have stricter criteria for valid remapping targets?
-  isValidFKRemappingTarget = dimension =>
-    !(
-      dimension.defaultDimension() instanceof FieldDimension &&
-      dimension.temporalUnit()
-    );
-
   getForeignKeys = () => {
-    const { table, field } = this.props;
-
-    // this method has a little odd structure due to using fieldOptions(); basically filteredFKs should
-    // always be an array with a single value
-    const metadata = table.metadata;
-    const fieldOptions = Question.create({
-      metadata,
-      databaseId: table.db.id,
-      tableId: table.id,
-    })
-      .query()
-      .fieldOptions();
-    const unfilteredFks = fieldOptions.fks;
-    const filteredFKs = unfilteredFks.filter(fk => fk.field.id === field.id);
-
-    return filteredFKs.map(filteredFK => ({
-      field: filteredFK.field,
-      dimension: filteredFK.dimension,
-      dimensions: filteredFK.dimensions.filter(this.isValidFKRemappingTarget),
-    }));
+    const { field, metadata } = this.props;
+    return metadata.field(field.id).remappingOptions();
   };
 
   onFkPopoverDismiss = () => {
diff --git a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
index b1b808e1c2c..432fba91b1e 100644
--- a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
+++ b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
@@ -352,6 +352,7 @@ const FieldGeneralPane = ({
         field={field}
         table={table}
         fields={metadata.fields}
+        metadata={metadata}
         fieldsError={fieldsError}
         updateFieldProperties={onUpdateFieldProperties}
         updateFieldValues={onUpdateFieldValues}
-- 
GitLab