From 724855886fcc7adcdddf9be360bf1fc1c6fbbb7b Mon Sep 17 00:00:00 2001 From: Cam Saul <1455846+camsaul@users.noreply.github.com> Date: Tue, 25 Apr 2023 20:27:26 -0700 Subject: [PATCH] MLv2: use kebab-case keys in Clojure & camelCase in JS (#30248) * MLv2: use kebab-case keys in Clojure & camelCase in JS * Revert changes to tests --- .clj-kondo/config.edn | 25 +- .../advanced_config/file.clj | 6 +- .../src/metabase-lib/order_by.unit.spec.ts | 46 +- frontend/src/metabase-lib/types.ts | 25 +- .../QueryColumnPicker/QueryColumnPicker.tsx | 10 +- .../QueryColumnPicker.unit.spec.tsx | 2 +- .../notebook/steps/SortStep/SortStep.tsx | 2 +- .../steps/SortStep/SortStep.unit.spec.tsx | 12 +- .../metabase/test/data/bigquery_cloud_sdk.clj | 5 +- .../test/metabase/test/data/presto_jdbc.clj | 8 +- src/metabase/analytics/snowplow.clj | 4 +- src/metabase/cmd/env_var_dox.clj | 2 +- src/metabase/domain_entities/converters.cljs | 10 +- src/metabase/lib/aggregation.cljc | 6 +- src/metabase/lib/card.cljc | 44 +- src/metabase/lib/column_group.cljc | 20 +- src/metabase/lib/convert.cljc | 8 +- src/metabase/lib/expression.cljc | 27 +- src/metabase/lib/field.cljc | 38 +- src/metabase/lib/join.cljc | 6 +- src/metabase/lib/js.cljs | 7 +- src/metabase/lib/js/metadata.cljs | 62 +- src/metabase/lib/metadata.cljc | 155 +- .../lib/metadata/cached_provider.cljc | 17 +- src/metabase/lib/metadata/calculation.cljc | 44 +- src/metabase/lib/metadata/jvm.clj | 39 +- src/metabase/lib/metric.cljc | 2 +- src/metabase/lib/order_by.cljc | 2 +- src/metabase/lib/query.cljc | 89 +- src/metabase/lib/ref.cljc | 2 +- src/metabase/lib/segment.cljc | 2 +- src/metabase/lib/stage.cljc | 20 +- src/metabase/lib/table.cljc | 4 +- src/metabase/lib/temporal_bucket.cljc | 45 +- src/metabase/lib/types/constants.cljc | 30 +- src/metabase/lib/types/isa.cljc | 61 +- src/metabase/lib/util.cljc | 37 +- src/metabase/models/interface.clj | 10 +- src/metabase/models/metric.clj | 2 +- src/metabase/models/segment.clj | 2 +- src/metabase/util.cljc | 123 +- test/metabase/lib/aggregation_test.cljc | 33 +- test/metabase/lib/breakout_test.cljc | 162 +- test/metabase/lib/card_test.cljc | 54 +- test/metabase/lib/column_group_test.cljc | 180 +- test/metabase/lib/dev_test.cljc | 1 - test/metabase/lib/expression_test.cljc | 27 +- test/metabase/lib/field_test.cljc | 64 +- test/metabase/lib/filter_test.cljc | 1 - test/metabase/lib/join_test.cljc | 17 +- test/metabase/lib/metadata/jvm_test.clj | 13 +- test/metabase/lib/metadata_test.cljc | 8 +- test/metabase/lib/metric_test.cljc | 4 +- test/metabase/lib/order_by_test.cljc | 395 ++- test/metabase/lib/query_test.cljc | 15 +- test/metabase/lib/stage_test.cljc | 36 +- test/metabase/lib/temporal_bucket_test.cljc | 3 +- test/metabase/lib/test_metadata.cljc | 2262 +++++++++-------- test/metabase/lib/test_util.cljc | 32 +- test/metabase/lib/types/isa_test.cljc | 92 +- test/metabase/util_test.cljc | 39 +- 61 files changed, 2328 insertions(+), 2171 deletions(-) diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 9c43a27f149..29f439e79fd 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -162,8 +162,9 @@ toucan.models/model? {:message "Use mdb.u/toucan-model? instead of toucan.models/model?"}} :discouraged-namespace - {clojure.tools.logging {:message "Use metabase.util.log instead of clojure.tools.logging directly"} - metabase.util.jvm {:message "All of metabase.util.jvm is re-exported from metabase.util; prefer that"}} + {camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."} + clojure.tools.logging {:message "Use metabase.util.log instead of clojure.tools.logging directly"} + metabase.util.jvm {:message "All of metabase.util.jvm is re-exported from metabase.util; prefer that"}} :unresolved-var {:exclude @@ -712,10 +713,16 @@ ;; to the new stricter rules from day 1 metabase-lib {:linters - {:docstring-leading-trailing-whitespace {:level :warning} - :reduce-without-init {:level :warning} - :used-underscored-binding {:level :warning} - :single-key-in {:level :warning} - :keyword-binding {:level :warning} - :shadowed-var {:level :warning} - :metabase/deftest-not-marked-parallel-or-synchronized {:level :warning}}}}} + {:docstring-leading-trailing-whitespace {:level :warning} + :reduce-without-init {:level :warning} + :used-underscored-binding {:level :warning} + :single-key-in {:level :warning} + :keyword-binding {:level :warning} + :shadowed-var {:level :warning} + :metabase/deftest-not-marked-parallel-or-synchronized {:level :warning} + + ;; eventually we should do this for the whole codebase, but the args are in the opposite order so switching them + ;; all at once isn't trivial, at least we can stop using it in new code. + :discouraged-var + {medley.core/map-keys {:message "Use clojure.core/update-keys"} + medley.core/map-vals {:message "Use clojure.core/update-vals"}}}}}} diff --git a/enterprise/backend/src/metabase_enterprise/advanced_config/file.clj b/enterprise/backend/src/metabase_enterprise/advanced_config/file.clj index 02a459becc9..a8c0f346cbd 100644 --- a/enterprise/backend/src/metabase_enterprise/advanced_config/file.clj +++ b/enterprise/backend/src/metabase_enterprise/advanced_config/file.clj @@ -91,14 +91,14 @@ {{env user-dir}} ```" (:require - [camel-snake-kebab.core :as csk] [clojure.edn :as edn] [clojure.spec.alpha :as s] [clojure.string :as str] [clojure.walk :as walk] [environ.core :as env] [metabase-enterprise.advanced-config.file.databases] - [metabase-enterprise.advanced-config.file.interface :as advanced-config.file.i] + [metabase-enterprise.advanced-config.file.interface + :as advanced-config.file.i] [metabase-enterprise.advanced-config.file.settings] [metabase-enterprise.advanced-config.file.users] [metabase.driver.common.parameters] @@ -183,7 +183,7 @@ (defmethod expand-parsed-template-form 'env [[_template-type env-var-name]] - (get *env* (csk/->kebab-case-keyword env-var-name))) + (get *env* (keyword (u/->kebab-case-en env-var-name)))) (defmulti ^:private expand-template-str-part {:arglists '([part])} diff --git a/frontend/src/metabase-lib/order_by.unit.spec.ts b/frontend/src/metabase-lib/order_by.unit.spec.ts index 987a4fc68f9..c5a31660483 100644 --- a/frontend/src/metabase-lib/order_by.unit.spec.ts +++ b/frontend/src/metabase-lib/order_by.unit.spec.ts @@ -37,17 +37,17 @@ describe("order by", () => { expect(ML.displayInfo(query, ordersID)).toEqual( expect.objectContaining({ name: "ID", - display_name: "ID", - effective_type: "type/BigInteger", - semantic_type: "type/PK", - is_calculated: false, - is_from_join: false, - is_from_previous_stage: false, - is_implicitly_joinable: false, + displayName: "ID", + effectiveType: "type/BigInteger", + semanticType: "type/PK", + isCalculated: false, + isFromJoin: false, + isFromPreviousStage: false, + isImplicitlyJoinable: false, table: { name: "ORDERS", - display_name: "Orders", - is_source_table: true, + displayName: "Orders", + isSourceTable: true, }, }), ); @@ -59,17 +59,17 @@ describe("order by", () => { expect(ML.displayInfo(query, productsTitle)).toEqual( expect.objectContaining({ name: "TITLE", - display_name: "Title", - effective_type: "type/Text", - semantic_type: "type/Category", - is_calculated: false, - is_from_join: false, - is_from_previous_stage: false, - is_implicitly_joinable: true, + displayName: "Title", + effectiveType: "type/Text", + semanticType: "type/Category", + isCalculated: false, + isFromJoin: false, + isFromPreviousStage: false, + isImplicitlyJoinable: true, table: { name: "PRODUCTS", - display_name: "Products", - is_source_table: false, + displayName: "Products", + isSourceTable: false, }, }), ); @@ -104,8 +104,8 @@ describe("order by", () => { (columnMetadata: ML.ColumnMetadata) => { const displayInfo = ML.displayInfo(query, columnMetadata); return ( - displayInfo.display_name === "Title" && - displayInfo.table?.display_name === "Product Model" + displayInfo.displayName === "Title" && + displayInfo.table?.displayName === "Product Model" ); }, ); @@ -113,9 +113,9 @@ describe("order by", () => { expect(ML.displayInfo(query, productsTitle as ML.ColumnMetadata)).toEqual( expect.objectContaining({ name: field.name, - display_name: field.display_name, - effective_type: field.base_type, - table: { name: "Product Model", display_name: "Product Model" }, + displayName: field.display_name, + effectiveType: field.base_type, + table: { name: "Product Model", displayName: "Product Model" }, }), ); }); diff --git a/frontend/src/metabase-lib/types.ts b/frontend/src/metabase-lib/types.ts index 1d571628cb7..806769b0041 100644 --- a/frontend/src/metabase-lib/types.ts +++ b/frontend/src/metabase-lib/types.ts @@ -25,32 +25,33 @@ export type ColumnGroup = unknown & { _opaque: typeof ColumnGroup }; export type TableDisplayInfo = { name: string; - display_name: string; - is_source_table: boolean; - is_from_join: boolean; - is_implicitly_joinable: boolean; + displayName: string; + isSourceTable: boolean; + isFromJoin: boolean; + isImplicitlyJoinable: boolean; }; type TableInlineDisplayInfo = Pick< TableDisplayInfo, - "name" | "display_name" | "is_source_table" + "name" | "displayName" | "isSourceTable" >; export type ColumnDisplayInfo = { name: string; - display_name: string; - fk_reference_name?: string; - semantic_type?: string | null; - effective_type: string; + displayName: string; + fkReferenceName?: string; + // TODO -- pretty sure the types were removed? + semanticType?: string | null; + effectiveType: string; is_calculated: boolean; - is_from_join: boolean; - is_implicitly_joinable: boolean; + isFromJoin: boolean; + isImplicitlyJoinable: boolean; table?: TableInlineDisplayInfo; }; export type OrderByClauseDisplayInfo = Pick< ColumnDisplayInfo, - "name" | "display_name" | "effective_type" | "semantic_type" | "table" + "name" | "displayName" | "effectiveType" | "semanticType" | "table" > & { direction: OrderByDirection; }; diff --git a/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.tsx b/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.tsx index d3d399e8ca2..a4b5c2bee59 100644 --- a/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.tsx +++ b/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.tsx @@ -81,7 +81,7 @@ function QueryColumnPicker({ } function renderItemName(item: ColumnListItem) { - return item.display_name; + return item.displayName; } function omitItemDescription() { @@ -95,17 +95,17 @@ function renderItemIcon(item: ColumnListItem) { function getGroupName(groupInfo: Lib.ColumnDisplayInfo | Lib.TableDisplayInfo) { const columnInfo = groupInfo as Lib.ColumnDisplayInfo; const tableInfo = groupInfo as Lib.TableDisplayInfo; - return columnInfo.fk_reference_name || singularize(tableInfo.display_name); + return columnInfo.fkReferenceName || singularize(tableInfo.displayName); } function getGroupIcon(groupInfo: Lib.ColumnDisplayInfo | Lib.TableDisplayInfo) { - if ((groupInfo as Lib.TableDisplayInfo).is_source_table) { + if ((groupInfo as Lib.TableDisplayInfo).isSourceTable) { return "table"; } - if (groupInfo.is_from_join) { + if (groupInfo.isFromJoin) { return "join_left_outer"; } - if (groupInfo.is_implicitly_joinable) { + if (groupInfo.isImplicitlyJoinable) { return "connections"; } return; diff --git a/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.unit.spec.tsx b/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.unit.spec.tsx index 76177cb6345..35def95eec1 100644 --- a/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.unit.spec.tsx +++ b/frontend/src/metabase/common/components/QueryColumnPicker/QueryColumnPicker.unit.spec.tsx @@ -62,7 +62,7 @@ describe("QueryColumnPicker", () => { it("should allow picking a column", async () => { const { sampleColumn, sampleColumnInfo, onSelect, onClose } = setup(); - userEvent.click(screen.getByText(sampleColumnInfo.display_name)); + userEvent.click(screen.getByText(sampleColumnInfo.displayName)); expect(onSelect).toHaveBeenCalledWith(sampleColumn); expect(onClose).toHaveBeenCalled(); diff --git a/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.tsx b/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.tsx index 056fc8655d1..2795ff83d72 100644 --- a/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.tsx +++ b/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.tsx @@ -103,7 +103,7 @@ function SortDisplayName({ }} > <Icon name={icon} /> - <span>{displayInfo.display_name}</span> + <span>{displayInfo.displayName}</span> </SortDirectionButton> ); } diff --git a/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.unit.spec.tsx b/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.unit.spec.tsx index 9565e15afe7..8b99a9c9cd2 100644 --- a/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.unit.spec.tsx +++ b/frontend/src/metabase/query_builder/components/notebook/steps/SortStep/SortStep.unit.spec.tsx @@ -58,7 +58,7 @@ describe("SortStep", () => { setup(createMockNotebookStep({ topLevelQuery: query })); - expect(screen.getByText(columnInfo.display_name)).toBeInTheDocument(); + expect(screen.getByText(columnInfo.displayName)).toBeInTheDocument(); expect(getIcon("arrow_up")).toBeInTheDocument(); expect(queryIcon("arrow_down")).not.toBeInTheDocument(); }); @@ -68,7 +68,7 @@ describe("SortStep", () => { setup(createMockNotebookStep({ topLevelQuery: query })); - expect(screen.getByText(columnInfo.display_name)).toBeInTheDocument(); + expect(screen.getByText(columnInfo.displayName)).toBeInTheDocument(); expect(getIcon("arrow_down")).toBeInTheDocument(); expect(queryIcon("arrow_up")).not.toBeInTheDocument(); }); @@ -98,7 +98,7 @@ describe("SortStep", () => { userEvent.click(screen.getByText("Created At")); const orderBy = gerRecentOrderByClause(); - expect(orderBy.display_name).toBe("Created At"); + expect(orderBy.displayName).toBe("Created At"); expect(orderBy.direction).toBe("asc"); }); @@ -112,7 +112,7 @@ describe("SortStep", () => { const orderBy = gerRecentOrderByClause(); expect(orderBy.direction).toBe("desc"); - expect(orderBy.display_name).toBe(columnInfo.display_name); + expect(orderBy.displayName).toBe(columnInfo.displayName); }); it("should change ordered field", () => { @@ -121,11 +121,11 @@ describe("SortStep", () => { createMockNotebookStep({ topLevelQuery: query }), ); - userEvent.click(screen.getByText(columnInfo.display_name)); + userEvent.click(screen.getByText(columnInfo.displayName)); userEvent.click(screen.getByText("Created At")); const orderBy = gerRecentOrderByClause(); - expect(orderBy.display_name).toBe("Created At"); + expect(orderBy.displayName).toBe("Created At"); }); it("should remove an order by", () => { diff --git a/modules/drivers/bigquery-cloud-sdk/test/metabase/test/data/bigquery_cloud_sdk.clj b/modules/drivers/bigquery-cloud-sdk/test/metabase/test/data/bigquery_cloud_sdk.clj index e77c86f0202..14c778cbfa8 100644 --- a/modules/drivers/bigquery-cloud-sdk/test/metabase/test/data/bigquery_cloud_sdk.clj +++ b/modules/drivers/bigquery-cloud-sdk/test/metabase/test/data/bigquery_cloud_sdk.clj @@ -90,8 +90,9 @@ ;;; -------------------------------------------------- Loading Data -------------------------------------------------- -(defmethod ddl.i/format-name :bigquery-cloud-sdk [_ table-or-field-name] - (u/snake-key table-or-field-name)) +(defmethod ddl.i/format-name :bigquery-cloud-sdk + [_driver table-or-field-name] + (str/replace table-or-field-name #"-" "_")) (defn- create-dataset! [^String dataset-id] {:pre [(seq dataset-id)]} diff --git a/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj index 00d3906eeae..12707babfc5 100644 --- a/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj +++ b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj @@ -15,7 +15,6 @@ [metabase.test.data.sql-jdbc.execute :as execute] [metabase.test.data.sql-jdbc.load-data :as load-data] [metabase.test.data.sql.ddl :as ddl] - [metabase.util :as u] [metabase.util.log :as log]) (:import (java.sql Connection DriverManager PreparedStatement))) @@ -147,7 +146,7 @@ (defmethod sql.tx/qualified-name-components :presto-jdbc ;; use the default schema from the in-memory connector - ([_ _db-name] [test-catalog-name "default"]) + ([_ _db-name] [test-catalog-name "default"]) ([_ db-name table-name] [test-catalog-name "default" (tx/db-qualified-table-name db-name table-name)]) ([_ db-name table-name field-name] [test-catalog-name "default" (tx/db-qualified-table-name db-name table-name) field-name])) @@ -172,8 +171,9 @@ (is (= "CREATE TABLE \"test_data\".\"default\".\"categories\" (\"id\" INTEGER, \"name\" VARCHAR) ;" (sql.tx/create-table-sql :presto-jdbc db-def table-def)))))) -(defmethod ddl.i/format-name :presto-jdbc [_ table-or-field-name] - (u/snake-key table-or-field-name)) +(defmethod ddl.i/format-name :presto-jdbc + [_driver table-or-field-name] + (str/replace table-or-field-name #"-" "_")) ;; Presto doesn't support FKs, at least not adding them via DDL (defmethod sql.tx/add-fk-sql :presto-jdbc diff --git a/src/metabase/analytics/snowplow.clj b/src/metabase/analytics/snowplow.clj index dfe298c7009..41cbf222ec6 100644 --- a/src/metabase/analytics/snowplow.clj +++ b/src/metabase/analytics/snowplow.clj @@ -1,13 +1,13 @@ (ns metabase.analytics.snowplow "Functions for sending Snowplow analytics events" (:require + [clojure.string :as str] [java-time :as t] [medley.core :as m] [metabase.config :as config] [metabase.models.setting :as setting :refer [defsetting Setting]] [metabase.models.user :refer [User]] [metabase.public-settings :as public-settings] - [metabase.util :as u] [metabase.util.date-2 :as u.date] [metabase.util.i18n :refer [deferred-tru trs]] [metabase.util.log :as log] @@ -172,7 +172,7 @@ (defn- normalize-kw [kw] - (-> kw u/snake-key name)) + (-> kw name (str/replace #"-" "_"))) (defn- payload "A SelfDescribingJson object containing the provided event data, which can be included as the payload for an diff --git a/src/metabase/cmd/env_var_dox.clj b/src/metabase/cmd/env_var_dox.clj index a9ea7787276..b4bb0d9eae9 100644 --- a/src/metabase/cmd/env_var_dox.clj +++ b/src/metabase/cmd/env_var_dox.clj @@ -50,7 +50,7 @@ (defn- format-prefix "Used to build an environment variable." [env-var] - (str "MB_" (u/screaming-snake-case (name (:name env-var))))) + (str "MB_" (u/->SCREAMING_SNAKE_CASE_EN (name (:name env-var))))) (defn- format-heading "Takes an integer and a string and creates a Markdown heading of level n." diff --git a/src/metabase/domain_entities/converters.cljs b/src/metabase/domain_entities/converters.cljs index 14b60ad080b..6e73517b335 100644 --- a/src/metabase/domain_entities/converters.cljs +++ b/src/metabase/domain_entities/converters.cljs @@ -1,13 +1,13 @@ (ns metabase.domain-entities.converters (:require - [camel-snake-kebab.core :as csk] [malli.core :as mc] - [malli.transform :as mtx])) + [malli.transform :as mtx] + [metabase.util :as u])) (defn- decode-map [schema _] (let [by-prop (into {} (for [[map-key props] (mc/children schema)] [(or (get props :js/prop) - (csk/->snake_case_string map-key)) + (u/->snake_case_en (u/qualified-name map-key))) {:map-key map-key}]))] {:enter (fn [x] (cond @@ -16,7 +16,7 @@ (into {} (for [prop (js-keys x) :let [js-val (unchecked-get x prop) map-key (or (get-in by-prop [prop :map-key]) - (csk/->kebab-case-keyword prop))]] + (keyword (u/->kebab-case-en prop)))]] [map-key js-val])))) :leave (fn [x] (if (object? x) @@ -127,7 +127,7 @@ :when (:js/prop props)] [k (:js/prop props)])) keyenc (fn [k] (or (get js-props k) - (csk/->snake_case_string k)))] + (u/->snake_case_en (u/qualified-name k))))] {:leave #(encode-map % keyenc)}))} :map-of {:leave #(encode-map % name)}})})) diff --git a/src/metabase/lib/aggregation.cljc b/src/metabase/lib/aggregation.cljc index 54e7f792935..9e7c1dcf4b6 100644 --- a/src/metabase/lib/aggregation.cljc +++ b/src/metabase/lib/aggregation.cljc @@ -17,7 +17,7 @@ "Given `:metadata/field` column metadata for an aggregation, construct an `:aggregation` reference." [metadata :- lib.metadata/ColumnMetadata] (let [options {:lib/uuid (str (random-uuid)) - :effective-type ((some-fn :effective_type :base_type) metadata)} + :effective-type ((some-fn :effective-type :base-type) metadata)} index (::aggregation-index metadata)] (assert (integer? index) "Metadata for an aggregation reference should include ::aggregation-index") [:aggregation options index])) @@ -51,9 +51,9 @@ {:lib/source :source/aggregations ::aggregation-index index} (when base-type - {:base_type base-type}) + {:base-type base-type}) (when effective-type - {:effective_type effective-type})))) + {:effective-type effective-type})))) ;;; TODO -- merge this stuff into `defop` somehow. diff --git a/src/metabase/lib/card.cljc b/src/metabase/lib/card.cljc index 99c34a98277..bbb0f57b461 100644 --- a/src/metabase/lib/card.cljc +++ b/src/metabase/lib/card.cljc @@ -4,29 +4,37 @@ [metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata.calculation :as lib.metadata.calculation] [metabase.lib.query :as lib.query] - [metabase.lib.schema :as lib.schema] [metabase.lib.schema.id :as lib.schema.id] + [metabase.util :as u] [metabase.util.humanization :as u.humanization] [metabase.util.malli :as mu])) (defmethod lib.metadata.calculation/display-name-method :metadata/card [_query _stage-number card-metadata _style] - ((some-fn :display_name :name) card-metadata)) + ((some-fn :display-name :name) card-metadata)) (defmethod lib.metadata.calculation/metadata-method :metadata/card - [_query _stage-number {card-name :name, display-name :display_name, :as card-metadata}] + [_query _stage-number {card-name :name, :keys [display-name], :as card-metadata}] (cond-> card-metadata - (not display-name) (assoc :display_name (u.humanization/name->human-readable-name :simple card-name)))) + (not display-name) (assoc :display-name (u.humanization/name->human-readable-name :simple card-name)))) -(defn- infer-results-metadata [metadata-provider card-query] - (lib.metadata.calculation/metadata (lib.query/query metadata-provider (lib.convert/->pMBQL card-query)))) +(mu/defn ^:private infer-results-metadata + [metadata-providerable :- lib.metadata/MetadataProviderable + card-query :- :map] + (lib.metadata.calculation/metadata (lib.query/query metadata-providerable (lib.convert/->pMBQL card-query)))) -(defn- card-metadata-columns - [query card] - (when-let [result-metadata (or (:result_metadata card) +(def ^:private Card + [:map + {:error/message "Card with :dataset-query"} + [:dataset-query :map]]) + +(mu/defn ^:private card-metadata-columns + [metadata-providerable :- lib.metadata/MetadataProviderable + card :- Card] + (when-let [result-metadata (or (:result-metadata card) (:fields card) - (infer-results-metadata (:lib/metadata query) (:dataset_query card)))] - ;; Card `result_metadata` SHOULD be a sequence of column infos, but just to be safe handle a map that + (infer-results-metadata metadata-providerable (:dataset-query card)))] + ;; Card `result-metadata` SHOULD be a sequence of column infos, but just to be safe handle a map that ;; contains` :columns` as well. (when-let [cols (not-empty (cond (map? result-metadata) (:columns result-metadata) @@ -34,8 +42,8 @@ (mapv (fn [col] (merge (when-let [field-id (:id col)] - (lib.metadata/field query field-id)) - col + (lib.metadata/field metadata-providerable field-id)) + (update-keys col u/->kebab-case-en) {:lib/type :metadata/field :lib/source :source/card :lib/card-id (:id card) @@ -44,12 +52,12 @@ (mu/defn saved-question-metadata :- [:maybe [:sequential {:min 1} lib.metadata.calculation/ColumnMetadataWithSource]] "Metadata associated with a Saved Question with `card-id`." - [query :- ::lib.schema/query - card-id :- ::lib.schema.id/card] - ;; it seems like in some cases (unit tests) the FE is renaming `:result_metadata` to `:fields`, not 100% sure why + [metadata-providerable :- lib.metadata/MetadataProviderable + card-id :- ::lib.schema.id/card] + ;; it seems like in some cases (unit tests) the FE is renaming `:result-metadata` to `:fields`, not 100% sure why ;; but handle that case anyway. (#29739) - (when-let [card (lib.metadata/card query card-id)] - (card-metadata-columns query card))) + (when-let [card (lib.metadata/card metadata-providerable card-id)] + (card-metadata-columns metadata-providerable card))) (defmethod lib.metadata.calculation/default-columns-method :metadata/card [query _stage-number card unique-name-fn] diff --git a/src/metabase/lib/column_group.cljc b/src/metabase/lib/column_group.cljc index a8350ddc316..134bcafc72f 100644 --- a/src/metabase/lib/column_group.cljc +++ b/src/metabase/lib/column_group.cljc @@ -53,20 +53,20 @@ (lib.metadata.calculation/display-info query stage-number table))) ;; for multi-stage queries return an empty string (#30108) (when (next (:stages query)) - {:display_name ""}) + {:display-name ""}) ;; if this is a native query or something else that doesn't have a source Table or source Card then use the ;; stage display name. - {:display_name (lib.metadata.calculation/display-name query stage-number stage)})) - {:is_from_join false - :is_implicitly_joinable false}) + {:display-name (lib.metadata.calculation/display-name query stage-number stage)})) + {:is-from-join false + :is-implicitly-joinable false}) :group-type/join.explicit (merge (let [join-alias (:join-alias column-group)] (when-let [join (lib.join/resolve-join query stage-number join-alias)] (lib.metadata.calculation/display-info query stage-number join))) - {:is_from_join true - :is_implicitly_joinable false}) + {:is-from-join true + :is-implicitly-joinable false}) :group-type/join.implicit (merge @@ -78,16 +78,16 @@ ;; This is very intentional: one table might have several FKs to one foreign table, each with different ;; meaning (eg. ORDERS.customer_id vs. ORDERS.supplier_id both linking to a PEOPLE table). ;; See #30109 for more details. - (assoc field-info :fk_reference_name (lib.util/strip-id (:display_name field-info)))))) - {:is_from_join false - :is_implicitly_joinable true}))) + (assoc field-info :fk-reference-name (lib.util/strip-id (:display-name field-info)))))) + {:is-from-join false + :is-implicitly-joinable true}))) (mu/defn ^:private column-group-info :- [:map [::group-type GroupType]] "The value we should use to `group-by` inside [[group-columns]]." [{source :lib/source, :as column-metadata} :- lib.metadata/ColumnMetadata] (case source :source/implicitly-joinable - {::group-type :group-type/join.implicit, :fk-field-id (:fk_field_id column-metadata)} + {::group-type :group-type/join.implicit, :fk-field-id (:fk-field-id column-metadata)} :source/joins {::group-type :group-type/join.explicit, :join-alias (lib.join/current-join-alias column-metadata)} diff --git a/src/metabase/lib/convert.cljc b/src/metabase/lib/convert.cljc index d09f32d83c8..8b3763e52cf 100644 --- a/src/metabase/lib/convert.cljc +++ b/src/metabase/lib/convert.cljc @@ -254,6 +254,12 @@ [input] (aggregation->legacy-MBQL input)) +(defn- stage-metadata->legacy-metadata [stage-metadata] + (into [] + (comp (map #(update-keys % u/->snake_case_en)) + (map ->legacy-MBQL)) + (:columns stage-metadata))) + (defn- chain-stages [{:keys [stages]}] ;; :source-metadata aka :lib/stage-metadata is handled differently in the two formats. ;; In legacy, an inner query might have both :source-query, and :source-metadata giving the metadata for that nested @@ -264,7 +270,7 @@ (let [inner-query (first (reduce (fn [[inner stage-metadata] stage] [(cond-> (->legacy-MBQL stage) inner (assoc :source-query inner) - stage-metadata (assoc :source-metadata (mapv ->legacy-MBQL (:columns stage-metadata)))) + stage-metadata (assoc :source-metadata (stage-metadata->legacy-metadata stage-metadata))) ;; Get the :lib/stage-metadata off the original pMBQL stage, not the converted one. (:lib/stage-metadata stage)]) nil diff --git a/src/metabase/lib/expression.cljc b/src/metabase/lib/expression.cljc index 3bc6c3d7ae1..1dd22f978ec 100644 --- a/src/metabase/lib/expression.cljc +++ b/src/metabase/lib/expression.cljc @@ -14,6 +14,7 @@ [metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] + [metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.util :as lib.util] [metabase.shared.util.i18n :as i18n] [metabase.types :as types] @@ -23,8 +24,8 @@ "Given `:metadata/field` column metadata for an expression, construct an `:expression` reference." [metadata :- lib.metadata/ColumnMetadata] (let [options {:lib/uuid (str (random-uuid)) - :base-type (:base_type metadata) - :effective-type ((some-fn :effective_type :base_type) metadata)}] + :base-type (:base-type metadata) + :effective-type ((some-fn :effective-type :base-type) metadata)}] [:expression options (:name metadata)])) (mu/defn resolve-expression :- ::lib.schema.expression/expression @@ -49,8 +50,8 @@ [query stage-number [_expression _opts expression-name, :as expression-ref]] {:lib/type :metadata/field :name expression-name - :display_name (lib.metadata.calculation/display-name query stage-number expression-ref) - :base_type (lib.metadata.calculation/type-of query stage-number expression-ref) + :display-name (lib.metadata.calculation/display-name query stage-number expression-ref) + :base-type (lib.metadata.calculation/type-of query stage-number expression-ref) :lib/source :source/expressions}) (defmethod lib.metadata.calculation/display-name-method :dispatch-type/integer @@ -133,17 +134,13 @@ (for [arg args] (lib.metadata.calculation/type-of query stage-number arg)))) +;;; TODO -- this stuff should probably be moved into [[metabase.lib.temporal-bucket]] + (defn- interval-unit-str [amount unit] - (clojure.core/case unit - :millisecond (i18n/trun "millisecond" "milliseconds" (clojure.core/abs amount)) - :second (i18n/trun "second" "seconds" (clojure.core/abs amount)) - :minute (i18n/trun "minute" "minutes" (clojure.core/abs amount)) - :hour (i18n/trun "hour" "hours" (clojure.core/abs amount)) - :day (i18n/trun "day" "days" (clojure.core/abs amount)) - :week (i18n/trun "week" "weeks" (clojure.core/abs amount)) - :month (i18n/trun "month" "months" (clojure.core/abs amount)) - :quarter (i18n/trun "quarter" "quarters" (clojure.core/abs amount)) - :year (i18n/trun "year" "years" (clojure.core/abs amount)))) + ;; this uses [[clojure.string/lower-case]] so its in the user's locale in the browser rather than always using + ;; English lower-casing rules. + #_{:clj-kondo/ignore [:discouraged-var]} + (str/lower-case (lib.temporal-bucket/describe-temporal-unit amount unit))) (mu/defn ^:private interval-display-name :- ::lib.schema.common/non-blank-string "e.g. something like \"- 2 days\"" @@ -253,7 +250,7 @@ (-> (lib.metadata.calculation/metadata query stage-number expression-definition) (assoc :lib/source :source/expressions :name expression-name - :display_name expression-name))))))) + :display-name expression-name))))))) (defmethod lib.ref/ref-method :expression [expression-clause] diff --git a/src/metabase/lib/field.cljc b/src/metabase/lib/field.cljc index cf62c6535af..0ccbca9adb1 100644 --- a/src/metabase/lib/field.cljc +++ b/src/metabase/lib/field.cljc @@ -93,9 +93,9 @@ [_field {:keys [join-alias], :as opts} id-or-name, :as _field-clause] :- :mbql.clause/field] (merge (when-let [base-type (:base-type opts)] - {:base_type base-type}) + {:base-type base-type}) (when-let [effective-type ((some-fn :effective-type :base-type) opts)] - {:effective_type effective-type}) + {:effective-type effective-type}) ;; TODO -- some of the other stuff in `opts` probably ought to be merged in here as well. Also, if the Field is ;; temporally bucketed, the base-type/effective-type would probably be affected, right? We should probably be ;; taking that into consideration? @@ -113,9 +113,9 @@ "If this is a nested column, add metadata about the parent column." [query :- ::lib.schema/query metadata :- lib.metadata/ColumnMetadata] - (let [parent-metadata (lib.metadata/field query (:parent_id metadata)) + (let [parent-metadata (lib.metadata/field query (:parent-id metadata)) {parent-name :name} (cond->> parent-metadata - (:parent_id parent-metadata) (add-parent-column-metadata query))] + (:parent-id parent-metadata) (add-parent-column-metadata query))] (update metadata :name (fn [field-name] (str parent-name \. field-name))))) @@ -127,7 +127,7 @@ (if (and temporal-unit (contains? lib.schema.temporal-bucketing/datetime-extraction-units temporal-unit)) :type/Integer - ((some-fn :effective_type :base_type) column-metadata))) + ((some-fn :effective-type :base-type) column-metadata))) (defmethod lib.metadata.calculation/type-of-method :metadata/field [_query _stage-number column-metadata] @@ -150,30 +150,30 @@ metadata (merge {:lib/type :metadata/field} field-metadata - {:display_name (or (:display-name opts) + {:display-name (or (:display-name opts) (lib.metadata.calculation/display-name query stage-number field-ref))} (when effective-type - {:effective_type effective-type}) + {:effective-type effective-type}) (when base-type - {:base_type base-type}) + {:base-type base-type}) (when temporal-unit {::temporal-unit temporal-unit}) (when join-alias {::join-alias join-alias}) (when source-field - {:fk_field_id source-field}))] + {:fk-field-id source-field}))] (cond->> metadata - (:parent_id metadata) (add-parent-column-metadata query)))) + (:parent-id metadata) (add-parent-column-metadata query)))) ;;; this lives here as opposed to [[metabase.lib.metadata]] because that namespace is more of an interface namespace ;;; and moving this there would cause circular references. (defmethod lib.metadata.calculation/display-name-method :metadata/field - [query stage-number {field-display-name :display_name + [query stage-number {field-display-name :display-name field-name :name temporal-unit :unit join-alias :source_alias - fk-field-id :fk_field_id - table-id :table_id + fk-field-id :fk-field-id + table-id :table-id :as _field-metadata} style] (let [field-display-name (or field-display-name (u.humanization/name->human-readable-name :simple field-name)) @@ -200,7 +200,7 @@ (if-let [field-metadata (cond-> (resolve-field-metadata query stage-number field-clause) join-alias (assoc :source_alias join-alias) temporal-unit (assoc :unit temporal-unit) - source-field (assoc :fk_field_id source-field))] + source-field (assoc :fk-field-id source-field))] (lib.metadata.calculation/display-name query stage-number field-metadata style) ;; mostly for the benefit of JS, which does not enforce the Malli schemas. (i18n/tru "[Unknown Field]"))) @@ -225,7 +225,7 @@ (when (= (:lib/source field-metadata) :source/card) (when-let [card-id (:lib/card-id field-metadata)] (when-let [card (lib.metadata/card query card-id)] - {:table {:name (:name card), :display_name (:name card)}}))))) + {:table {:name (:name card), :display-name (:name card)}}))))) (defmethod lib.temporal-bucket/temporal-bucket-method :field [[_tag opts _id-or-name]] @@ -274,7 +274,7 @@ (defmethod lib.temporal-bucket/available-temporal-buckets-method :metadata/field [_query _stage-number field-metadata] - (let [effective-type ((some-fn :effective_type :base_type) field-metadata)] + (let [effective-type ((some-fn :effective-type :base-type) field-metadata)] (cond (isa? effective-type :type/DateTime) lib.schema.temporal-bucketing/datetime-bucketing-units (isa? effective-type :type/Date) lib.schema.temporal-bucketing/date-bucketing-units @@ -310,13 +310,13 @@ :source/expressions (lib.expression/column-metadata->expression-ref metadata) (let [options (merge {:lib/uuid (str (random-uuid)) - :base-type (:base_type metadata) + :base-type (:base-type metadata) :effective-type (column-metadata-effective-type metadata)} (when-let [join-alias (::join-alias metadata)] {:join-alias join-alias}) (when-let [temporal-unit (::temporal-unit metadata)] {:temporal-unit temporal-unit}) - (when-let [source-field-id (:fk_field_id metadata)] + (when-let [source-field-id (:fk-field-id metadata)] {:source-field source-field-id}) ;; TODO -- binning options. ) @@ -325,7 +325,7 @@ (:name metadata) (or (:id metadata) (:name metadata)))]))) -(defn- implicit-join-name [query {fk-field-id :fk_field_id, table-id :table_id, :as _field-metadata}] +(defn- implicit-join-name [query {:keys [fk-field-id table-id], :as _field-metadata}] (when (and fk-field-id table-id) (when-let [table-metadata (lib.metadata/table query table-id)] (let [table-name (:name table-metadata) diff --git a/src/metabase/lib/join.cljc b/src/metabase/lib/join.cljc index b87015678aa..96355fec9ac 100644 --- a/src/metabase/lib/join.cljc +++ b/src/metabase/lib/join.cljc @@ -69,7 +69,7 @@ [query _stage-number {[first-stage] :stages, :as _join} _style] (if-let [source-table (:source-table first-stage)] (if (integer? source-table) - (:display_name (lib.metadata/table query source-table)) + (:display-name (lib.metadata/table query source-table)) ;; handle card__<id> source tables. (let [card-id (lib.util/string-table-id->card-id source-table)] (i18n/tru "Saved Question #{0}" card-id))) @@ -78,7 +78,7 @@ (defmethod lib.metadata.calculation/display-info-method :mbql/join [query stage-number join] (let [display-name (lib.metadata.calculation/display-name query stage-number join)] - {:name (or (:alias join) display-name), :display_name display-name})) + {:name (or (:alias join) display-name), :display-name display-name})) (mu/defn ^:private column-from-join-fields :- lib.metadata.calculation/ColumnMetadataWithSource "For a column that comes from a join `:fields` list, add or update metadata as needed, e.g. include join name in the @@ -89,7 +89,7 @@ join-alias :- ::lib.schema.common/non-blank-string] (let [column-metadata (assoc column-metadata :source_alias join-alias) col (-> (assoc column-metadata - :display_name (lib.metadata.calculation/display-name query stage-number column-metadata) + :display-name (lib.metadata.calculation/display-name query stage-number column-metadata) :lib/source :source/joins) (with-join-alias join-alias))] (assert (= (current-join-alias col) join-alias)) diff --git a/src/metabase/lib/js.cljs b/src/metabase/lib/js.cljs index c61d51f372e..4705e470f0a 100644 --- a/src/metabase/lib/js.cljs +++ b/src/metabase/lib/js.cljs @@ -91,7 +91,7 @@ (defn ^:export legacy-query "Coerce a CLJS pMBQL query back to (1) a legacy query (2) in vanilla JS." [query-map] - (-> query-map convert/->legacy-MBQL fix-namespaced-values clj->js)) + (-> query-map convert/->legacy-MBQL fix-namespaced-values (clj->js :keyword-fn u/qualified-name))) (defn ^:export orderable-columns "Return a sequence of Column metadatas about the columns you can add order bys for in a given stage of `a-query.` To @@ -103,11 +103,14 @@ (defn ^:export display-info "Given an opaque Cljs object, return a plain JS object with info you'd need to implement UI for it. - See `:metabase.lib.metadata.calculation/display-info` for the keys this might contain." + See `:metabase.lib.metadata.calculation/display-info` for the keys this might contain. Note that the JS versions of + the keys are converted to the equivalent `camelCase` strings from the original `:kebab-case`." ([a-query x] (display-info a-query -1 x)) ([a-query stage-number x] (-> (lib.core/display-info a-query stage-number x) + (update-keys u/->camelCaseEn) + (update :table update-keys u/->camelCaseEn) (clj->js :keyword-fn u/qualified-name)))) (defn ^:export order-by-clause diff --git a/src/metabase/lib/js/metadata.cljs b/src/metabase/lib/js/metadata.cljs index 7da2ab71315..da57b755909 100644 --- a/src/metabase/lib/js/metadata.cljs +++ b/src/metabase/lib/js/metadata.cljs @@ -1,5 +1,6 @@ (ns metabase.lib.js.metadata (:require + [clojure.core.protocols] [clojure.string :as str] [clojure.walk :as walk] [goog] @@ -82,9 +83,9 @@ (let [excluded-keys-set (excluded-keys object-type) parse-field (parse-field-fn object-type)] (comp - ;; convert keys to keywords + ;; convert keys to kebab-case keywords (map (fn [[k v]] - [(keyword k) v])) + [(keyword (u/->kebab-case-en k)) v])) ;; remove [[excluded-keys]] (if (empty? excluded-keys-set) identity @@ -138,9 +139,9 @@ [_object-type] (fn [k v] (case k - :dbms_version (js->clj v :keywordize-keys true) + :dbms-version (js->clj v :keywordize-keys true) :features (into #{} (map keyword) v) - :native_permissions (keyword v) + :native-permissions (keyword v) v))) (defmethod parse-objects-default-key :database @@ -153,16 +154,16 @@ (defmethod excluded-keys :table [_object-type] - #{:database :fields :segments :metrics :dimension_options}) + #{:database :fields :segments :metrics :dimension-options}) (defmethod parse-field-fn :table [_object-type] (fn [k v] (case k - :entity_type (keyword v) - :field_order (keyword v) - :initial_sync_status (keyword v) - :visibility_type (keyword v) + :entity-type (keyword v) + :field-order (keyword v) + :initial-sync-status (keyword v) + :visibility-type (keyword v) v))) (defmethod parse-objects :table @@ -182,8 +183,8 @@ [_object-type] #{:_comesFromEndpoint :database - :default_dimension_option - :dimension_options + :default-dimension-option + :dimension-options :dimensions :metrics :table}) @@ -192,13 +193,13 @@ [_object-type] (fn [k v] (case k - :base_type (keyword v) - :coercion_strategy (keyword v) - :effective_type (keyword v) + :base-type (keyword v) + :coercion-strategy (keyword v) + :effective-type (keyword v) :fingerprint (walk/keywordize-keys v) - :has_field_values (keyword v) - :semantic_type (keyword v) - :visibility_type (keyword v) + :has-field-values (keyword v) + :semantic-type (keyword v) + :visibility-type (keyword v) v))) (defmethod parse-objects-default-key :field @@ -222,12 +223,12 @@ [_object-type] (fn [k v] (case k - :result_metadata (if ((some-fn sequential? array?) v) + :result-metadata (if ((some-fn sequential? array?) v) (parse-fields v) (js->clj v :keywordize-keys true)) :fields (parse-fields v) - :visibility_type (keyword v) - :dataset_query (js->clj v :keywordize-keys true) + :visibility-type (keyword v) + :dataset-query (js->clj v :keywordize-keys true) ;; this is not complete, add more stuff as needed. v))) @@ -323,19 +324,19 @@ (defn- tables [metadata database-id] (for [[_id table-delay] (some-> metadata :tables deref) :let [a-table (some-> table-delay deref)] - :when (and a-table (= (:db_id a-table) database-id))] + :when (and a-table (= (:db-id a-table) database-id))] a-table)) (defn- fields [metadata table-id] (for [[_id field-delay] (some-> metadata :fields deref) :let [a-field (some-> field-delay deref)] - :when (and a-field (= (:table_id a-field) table-id))] + :when (and a-field (= (:table-id a-field) table-id))] a-field)) (defn metadata-provider "Use a `metabase-lib/metadata/Metadata` as a [[metabase.lib.metadata.protocols/MetadataProvider]]." - [database-id metadata] - (let [metadata (parse-metadata metadata)] + [database-id unparsed-metadata] + (let [metadata (parse-metadata unparsed-metadata)] (log/debug "Created metadata provider for metadata") (reify lib.metadata.protocols/MetadataProvider (database [_this] (database metadata database-id)) @@ -345,4 +346,15 @@ (segment [_this segment-id] (segment metadata segment-id)) (card [_this card-id] (card metadata card-id)) (tables [_this] (tables metadata database-id)) - (fields [_this table-id] (fields metadata table-id))))) + (fields [_this table-id] (fields metadata table-id)) + + ;; for debugging: call [[clojure.datafy/datafy]] on one of these to parse all of our metadata and see the whole + ;; thing at once. + clojure.core.protocols/Datafiable + (datafy [_this] + (walk/postwalk + (fn [form] + (if (delay? form) + (deref form) + form)) + metadata))))) diff --git a/src/metabase/lib/metadata.cljc b/src/metabase/lib/metadata.cljc index 7af8aa81c72..a7cca573ec9 100644 --- a/src/metabase/lib/metadata.cljc +++ b/src/metabase/lib/metadata.cljc @@ -1,7 +1,5 @@ (ns metabase.lib.metadata (:require - [metabase.lib.dispatch :as lib.dispatch] - [metabase.lib.hierarchy :as lib.hierarchy] [metabase.lib.metadata.protocols :as lib.metadata.protocols] [metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.id :as lib.schema.id] @@ -70,19 +68,19 @@ [:lib/type [:= :metadata/field]] ; TODO -- should this be changed to `:metadata/column`? [:name ::lib.schema.common/non-blank-string] ;; TODO -- ignore `base_type` and make `effective_type` required; see #29707 - [:base_type ::lib.schema.common/base-type] + [:base-type ::lib.schema.common/base-type] [:id {:optional true} ::lib.schema.id/field] - [:display_name {:optional true} [:maybe ::lib.schema.common/non-blank-string]] - [:effective_type {:optional true} [:maybe ::lib.schema.common/base-type]] + [:display-name {:optional true} [:maybe ::lib.schema.common/non-blank-string]] + [:effective-type {:optional true} [:maybe ::lib.schema.common/base-type]] ;; if this is a field from another table (implicit join), this is the field in the current table that should be ;; used to perform the implicit join. e.g. if current table is `VENUES` and this field is `CATEGORIES.ID`, then the ;; `fk_field_id` would be `VENUES.CATEGORY_ID`. In a `:field` reference this is saved in the options map as ;; `:source-field`. - [:fk_field_id {:optional true} [:maybe ::lib.schema.id/field]] + [:fk-field-id {:optional true} [:maybe ::lib.schema.id/field]] ;; Join alias of the table we're joining against, if any. Not really 100% clear why we would need this on top ;; of [[metabase.lib.join/current-join-alias]], which stores the same info under a namespaced key. I think we can ;; remove it. - [:source_alias {:optional true} [:maybe ::lib.schema.common/non-blank-string]] + [:source-alias {:optional true} [:maybe ::lib.schema.common/non-blank-string]] ;; what top-level clause in the query this metadata originated from, if it is calculated (i.e., if this metadata ;; was generated by [[metabase.lib.metadata.calculation/metadata]]) [:lib/source {:optional true} [:ref ::column-source]] @@ -98,35 +96,43 @@ [:lib/desired-column-alias {:optional true} [:maybe [:string {:min 1, :max 60}]]]]) (def ^:private CardMetadata + "More or less the same as a [[metabase.models.card]], but with kebab-case keys. Note that the `:dataset-query` is not + necessarily converted to pMBQL yet. Probably safe to assume it is normalized however. Likewise, `:result-metadata` + is probably not quite massaged into a sequence of `ColumnMetadata`s just yet. + See [[metabase.lib.card/card-metadata-columns]] that converts these as needed." [:map [:lib/type [:= :metadata/card]] - [:id ::lib.schema.id/card] - [:name ::lib.schema.common/non-blank-string]]) + [:id ::lib.schema.id/card] + [:name ::lib.schema.common/non-blank-string] + ;; saved query. This is possibly still a legacy query, but should already be normalized. + ;; Call [[metabase.lib.convert/->pMBQL]] on it as needed + [:dataset-query {:optional true} :map] + ;; vector of column metadata maps; these are ALMOST the correct shape to be [[ColumnMetadata]], but they're + ;; probably missing `:lib/type` and probably using `:snake_case` keys. + [:result-metadata {:optional true} [:maybe [:sequential :map]]]]) (def ^:private SegmentMetadata + "More or less the same as a [[metabase.models.segment]], but with kebab-case keys." [:map [:lib/type [:= :metadata/segment]] - [:id ::lib.schema.id/segment] - [:name ::lib.schema.common/non-blank-string]]) + [:id ::lib.schema.id/segment] + [:name ::lib.schema.common/non-blank-string]]) (def ^:private MetricMetadata + "More or less the same as a [[metabase.models.metric]], but with kebab-case keys." [:map [:lib/type [:= :metadata/metric]] - [:id ::lib.schema.id/metric] - [:name ::lib.schema.common/non-blank-string]]) + [:id ::lib.schema.id/metric] + [:name ::lib.schema.common/non-blank-string]]) (def ^:private TableMetadata + "More or less the same as a [[metabase.models.table]], but with kebab-case keys." [:map [:lib/type [:= :metadata/table]] - [:id ::lib.schema.id/table] - [:name ::lib.schema.common/non-blank-string] - [:display_name {:optional true} [:maybe ::lib.schema.common/non-blank-string]] - [:schema {:optional true} [:maybe ::lib.schema.common/non-blank-string]] - ;; This is now optional! If the [[MetadataProvider]] provides it, great, but if not we can always make the - ;; subsequent request to fetch fields separately. - [:fields {:optional true} [:maybe [:sequential ColumnMetadata]]] - [:segments {:optional true} [:maybe [:sequential SegmentMetadata]]] - [:metrics {:optional true} [:maybe [:sequential MetricMetadata]]]]) + [:id ::lib.schema.id/table] + [:name ::lib.schema.common/non-blank-string] + [:display-name {:optional true} [:maybe ::lib.schema.common/non-blank-string]] + [:schema {:optional true} [:maybe ::lib.schema.common/non-blank-string]]]) (def DatabaseMetadata "Malli schema for the DatabaseMetadata as returned by `GET /api/database/:id/metadata` -- what should be available to @@ -141,60 +147,57 @@ "Schema for something that satisfies the [[lib.metadata.protocols/MetadataProvider]] protocol." [:fn lib.metadata.protocols/metadata-provider?]) -(defmulti ^:private ->metadata-provider* - {:arglists '([x])} - lib.dispatch/dispatch-value - :hierarchy lib.hierarchy/hierarchy) - -(defmethod ->metadata-provider* :default - [x] - x) - -(defmethod ->metadata-provider* :mbql/query - [query] - (->metadata-provider* (:lib/metadata query))) - -(mu/defn ^:private ->metadata-provider :- MetadataProvider - [x :- some?] - (if (lib.metadata.protocols/metadata-provider? x) - x - (->metadata-provider* x))) +(def MetadataProviderable + "Something that can be used to get a MetadataProvider. Either a MetadataProvider, or a map with a MetadataProvider in + the key `:lib/metadata` (i.e., a query)." + [:or + MetadataProvider + [:map + {:error/message "map with a MetadataProvider in the key :lib/metadata (i.e. a query)"} + [:lib/metadata MetadataProvider]]]) + +(mu/defn ->metadata-provider :- MetadataProvider + "Get a MetadataProvider from something that can provide one." + [metadata-providerable :- MetadataProviderable] + (if (lib.metadata.protocols/metadata-provider? metadata-providerable) + metadata-providerable + (:lib/metadata metadata-providerable))) (mu/defn database :- DatabaseMetadata "Get metadata about the Database we're querying." - [metadata-provider] - (lib.metadata.protocols/database (->metadata-provider metadata-provider))) + [metadata-providerable :- MetadataProviderable] + (lib.metadata.protocols/database (->metadata-provider metadata-providerable))) (mu/defn tables :- [:sequential TableMetadata] "Get metadata about all Tables for the Database we're querying." - [metadata-provider] - (lib.metadata.protocols/tables (->metadata-provider metadata-provider))) + [metadata-providerable :- MetadataProviderable] + (lib.metadata.protocols/tables (->metadata-provider metadata-providerable))) (mu/defn table :- [:or TableMetadata CardMetadata] "Find metadata for a specific Table, either by string `table-name`, and optionally `schema`, or by ID." - ([metadata-provider + ([metadata-providerable :- MetadataProviderable table-id :- [:or ::lib.schema.id/table ::lib.schema.id/table-card-id-string]] (if-let [card-id (lib.util/string-table-id->card-id table-id)] - (lib.metadata.protocols/card (->metadata-provider metadata-provider) card-id) - (lib.metadata.protocols/table (->metadata-provider metadata-provider) table-id))) + (lib.metadata.protocols/card (->metadata-provider metadata-providerable) card-id) + (lib.metadata.protocols/table (->metadata-provider metadata-providerable) table-id))) - ([metadata-provider - table-schema :- [:maybe ::lib.schema.common/non-blank-string] - table-name :- ::lib.schema.common/non-blank-string] + ([metadata-providerable :- MetadataProviderable + table-schema :- [:maybe ::lib.schema.common/non-blank-string] + table-name :- ::lib.schema.common/non-blank-string] (some (fn [table-metadata] (when (and (or (nil? table-schema) (= (:schema table-metadata) table-schema)) (= (:name table-metadata) table-name)) table-metadata)) - (tables metadata-provider)))) + (tables metadata-providerable)))) (mu/defn fields :- [:sequential ColumnMetadata] "Get metadata about all the Fields belonging to a specific Table." - ([metadata-provider - table-id :- ::lib.schema.id/table] - (lib.metadata.protocols/fields (->metadata-provider metadata-provider) table-id)) + ([metadata-providerable :- MetadataProviderable + table-id :- ::lib.schema.id/table] + (lib.metadata.protocols/fields (->metadata-provider metadata-providerable) table-id)) ([metadata-provider table-schema :- [:maybe ::lib.schema.common/non-blank-string] @@ -204,25 +207,25 @@ (mu/defn field :- ColumnMetadata "Get metadata about a specific Field in the Database we're querying." - ([metadata-provider - field-id :- ::lib.schema.id/field] - (lib.metadata.protocols/field (->metadata-provider metadata-provider) field-id)) + ([metadata-providerable :- MetadataProviderable + field-id :- ::lib.schema.id/field] + (lib.metadata.protocols/field (->metadata-provider metadata-providerable) field-id)) ;; TODO -- we need to figure out how to deal with nested fields... should field-name be a varargs thing? - ([metadata-provider - table-id :- ::lib.schema.id/table - field-name :- ::lib.schema.common/non-blank-string] + ([metadata-providerable :- MetadataProviderable + table-id :- ::lib.schema.id/table + field-name :- ::lib.schema.common/non-blank-string] (some (fn [field-metadata] (when (= (:name field-metadata) field-name) field-metadata)) - (fields metadata-provider table-id))) + (fields metadata-providerable table-id))) - ([metadata-provider - table-schema :- [:maybe ::lib.schema.common/non-blank-string] - table-name :- ::lib.schema.common/non-blank-string - field-name :- ::lib.schema.common/non-blank-string] - (let [table-metadata (table metadata-provider table-schema table-name)] - (field metadata-provider (:id table-metadata) field-name)))) + ([metadata-providerable :- MetadataProviderable + table-schema :- [:maybe ::lib.schema.common/non-blank-string] + table-name :- ::lib.schema.common/non-blank-string + field-name :- ::lib.schema.common/non-blank-string] + (let [table-metadata (table metadata-providerable table-schema table-name)] + (field metadata-providerable (:id table-metadata) field-name)))) ;;;; Stage metadata @@ -295,18 +298,18 @@ (mu/defn card :- [:maybe CardMetadata] "Get metadata for a Card, aka Saved Question, with `card-id`, if it can be found." - [metadata-provider - card-id :- ::lib.schema.id/card] - (lib.metadata.protocols/card (->metadata-provider metadata-provider) card-id)) + [metadata-providerable :- MetadataProviderable + card-id :- ::lib.schema.id/card] + (lib.metadata.protocols/card (->metadata-provider metadata-providerable) card-id)) (mu/defn segment :- [:maybe SegmentMetadata] "Get metadata for the Segment with `segment-id`, if it can be found." - [metadata-provider - segment-id :- ::lib.schema.id/segment] - (lib.metadata.protocols/segment (->metadata-provider metadata-provider) segment-id)) + [metadata-providerable :- MetadataProviderable + segment-id :- ::lib.schema.id/segment] + (lib.metadata.protocols/segment (->metadata-provider metadata-providerable) segment-id)) (mu/defn metric :- [:maybe MetricMetadata] "Get metadata for the Metric with `metric-id`, if it can be found." - [metadata-provider - metric-id :- ::lib.schema.id/metric] - (lib.metadata.protocols/metric (->metadata-provider metadata-provider) metric-id)) + [metadata-providerable :- MetadataProviderable + metric-id :- ::lib.schema.id/metric] + (lib.metadata.protocols/metric (->metadata-provider metadata-providerable) metric-id)) diff --git a/src/metabase/lib/metadata/cached_provider.cljc b/src/metabase/lib/metadata/cached_provider.cljc index 2d62bc896ce..505f6730e52 100644 --- a/src/metabase/lib/metadata/cached_provider.cljc +++ b/src/metabase/lib/metadata/cached_provider.cljc @@ -2,6 +2,7 @@ (:require [clojure.set :as set] [metabase.lib.metadata.protocols :as lib.metadata.protocols] + [metabase.util :as u] [metabase.util.log :as log] #?@(:clj ([pretty.core :as pretty])))) @@ -15,6 +16,18 @@ (swap! cache assoc-in ks value) value)) +(defn- store-database! [cache database-metadata] + (let [database-metadata (-> database-metadata + (update-keys u/->kebab-case-en) + (assoc :lib/type :metadata/database))] + (store-in-cache! cache [:metadata/database] database-metadata))) + +(defn- store-metadata! [cache metadata-type id metadata] + (let [metadata (-> metadata + (update-keys u/->kebab-case-en) + (assoc :lib/type metadata-type))] + (store-in-cache! cache [metadata-type id] metadata))) + (defn- get-in-cache-or-fetch [cache ks fetch-thunk] (if-some [cached-value (get-in @cache ks)] (when-not (= cached-value ::nil) @@ -49,8 +62,8 @@ lib.metadata.protocols/CachedMetadataProvider (cached-database [_this] (get-in-cache cache [:metadata/database])) (cached-metadata [_this metadata-type id] (get-in-cache cache [metadata-type id])) - (store-database! [_this database-metadata] (store-in-cache! cache [:metadata/database] (assoc database-metadata :lib/type :metadata/database))) - (store-metadata! [_this metadata-type id metadata] (store-in-cache! cache [metadata-type id] (assoc metadata :lib/type metadata-type))) + (store-database! [_this database-metadata] (store-database! cache database-metadata)) + (store-metadata! [_this metadata-type id metadata] (store-metadata! cache metadata-type id metadata)) ;; these only work if the underlying metadata provider is also a [[BulkMetadataProvider]]. lib.metadata.protocols/BulkMetadataProvider diff --git a/src/metabase/lib/metadata/calculation.cljc b/src/metabase/lib/metadata/calculation.cljc index 35cc385b6b7..739d6d151f1 100644 --- a/src/metabase/lib/metadata/calculation.cljc +++ b/src/metabase/lib/metadata/calculation.cljc @@ -198,9 +198,9 @@ (try {:lib/type :metadata/field ;; TODO -- effective-type - :base_type (type-of query stage-number x) + :base-type (type-of query stage-number x) :name (column-name query stage-number x) - :display_name (display-name query stage-number x)} + :display-name (display-name query stage-number x)} (catch #?(:clj Throwable :cljs js/Error) e (throw (ex-info (i18n/tru "Error calculating metadata {0}" (ex-message e)) {:query query, :stage-number stage-number, :x x} @@ -254,7 +254,7 @@ (mr/register! ::display-info [:map - [:display_name :string] + [:display-name :string] ;; for things that have a Table, e.g. a Field [:table {:optional true} [:maybe [:ref ::display-info]]] ;; these are derived from the `:lib/source`/`:metabase.lib.metadata/column-source`, but instead of using that value @@ -262,16 +262,16 @@ ;; e.g. if we consolidate or split some of those keys. This is all the FE really needs to know. ;; ;; if this is a Column, does it come from a previous stage? - [:is_from_previous_stage {:optional true} [:maybe :boolean]] + [:is-from-previous-stage {:optional true} [:maybe :boolean]] ;; if this is a Column, does it come from a join in this stage? - [:is_from_join {:optional true} [:maybe :boolean]] + [:is-from-join {:optional true} [:maybe :boolean]] ;; if this is a Column, is it 'calculated', i.e. does it come from an expression in this stage? - [:is_calculated {:optional true} [:maybe :boolean]] + [:is-calculated {:optional true} [:maybe :boolean]] ;; if this is a Column, is it an implicitly joinable one? I.e. is it from a different table that we have not ;; already joined, but could implicitly join against? - [:is_implicitly_joinable {:optional true} [:maybe :boolean]] + [:is-implicitly-joinable {:optional true} [:maybe :boolean]] ;; For the `:table` field of a Column, is this the source table, or a joined table? - [:is_source_table {:optional true} [:maybe :boolean]]]) + [:is-source-table {:optional true} [:maybe :boolean]]]) (mu/defn display-info :- ::display-info "Given some sort of Cljs object, return a map with the info you'd need to implement UI for it. This is mostly meant to @@ -286,7 +286,9 @@ (try (display-info-method query stage-number x) (catch #?(:clj Throwable :cljs js/Error) e - (throw (ex-info (i18n/tru "Error calculating display info for {0}: {1}" (lib.dispatch/dispatch-value x) (ex-message e)) + (throw (ex-info (i18n/tru "Error calculating display info for {0}: {1}" + (lib.dispatch/dispatch-value x) + (ex-message e)) {:query query, :stage-number stage-number, :x x} e)))))) @@ -296,20 +298,20 @@ [query stage-number x] (let [x-metadata (metadata query stage-number x)] (merge - ;; TODO -- not 100% convinced the FE should actually have access to `:name`, can't it use `:display_name` + ;; TODO -- not 100% convinced the FE should actually have access to `:name`, can't it use `:display-name` ;; everywhere? Determine whether or not this is the case. - (select-keys x-metadata [:name :display_name :semantic_type]) - ;; don't return `:base_type`, FE should just use `:effective_type` everywhere and not even need to know - ;; `:base_type` exists. - (when-let [effective-type ((some-fn :effective_type :base_type) x-metadata)] - {:effective_type effective-type}) - (when-let [table-id (:table_id x-metadata)] + (select-keys x-metadata [:name :display-name :semantic-type]) + ;; don't return `:base-type`, FE should just use `:effective-type` everywhere and not even need to know + ;; `:base-type` exists. + (when-let [effective-type ((some-fn :effective-type :base-type) x-metadata)] + {:effective-type effective-type}) + (when-let [table-id (:table-id x-metadata)] {:table (display-info query stage-number (lib.metadata/table query table-id))}) (when-let [source (:lib/source x-metadata)] - {:is_from_previous_stage (= source :source/previous-stage) - :is_from_join (= source :source/joins) - :is_calculated (= source :source/expressions) - :is_implicitly_joinable (= source :source/implicitly-joinable)})))) + {:is-from-previous-stage (= source :source/previous-stage) + :is-from-join (= source :source/joins) + :is-calculated (= source :source/expressions) + :is-implicitly-joinable (= source :source/implicitly-joinable)})))) (defmethod display-info-method :default [query stage-number x] @@ -318,7 +320,7 @@ (defmethod display-info-method :metadata/table [query stage-number table] (merge (default-display-info query stage-number table) - {:is_source_table (= (lib.util/source-table query) (:id table))})) + {:is-source-table (= (lib.util/source-table query) (:id table))})) (defmulti default-columns-method "Impl for [[default-columns]]. This should mostly be similar to the implementation for [[metadata-method]], but needs diff --git a/src/metabase/lib/metadata/jvm.clj b/src/metabase/lib/metadata/jvm.clj index fb45032f539..287b0c06ccd 100644 --- a/src/metabase/lib/metadata/jvm.clj +++ b/src/metabase/lib/metadata/jvm.clj @@ -3,6 +3,7 @@ (:require [metabase.lib.metadata.cached-provider :as lib.metadata.cached-provider] [metabase.lib.metadata.protocols :as lib.metadata.protocols] + [metabase.util :as u] [metabase.util.log :as log] [potemkin :as p] [pretty.core :as pretty] @@ -17,18 +18,37 @@ :metadata/metric :metabase.models.metric/Metric :metadata/segment :metabase.models.segment/Segment)) +(defn- instance->metadata [instance metadata-type] + (some-> instance + (update-keys u/->kebab-case-en) + (assoc :lib/type metadata-type))) + (defn- fetch-instance [metadata-type id] {:pre [(integer? id)]} (let [model (metadata-type->model metadata-type)] (log/debugf "Fetching %s %d" model id) - (when-some [instance (t2/select-one model :id id)] - (assoc instance :lib/type metadata-type)))) + (instance->metadata (t2/select-one model :id id) metadata-type))) (defn- bulk-instances [metadata-type ids] (let [model (metadata-type->model metadata-type)] (log/debugf "Fetching instances of %s with ID in %s" model (pr-str (sort ids))) (for [instance (t2/select model :id [:in ids])] - (assoc instance :lib/type metadata-type)))) + (instance->metadata instance metadata-type)))) + +(defn- tables [database-id] + (when-not database-id + (throw (ex-info (format "Cannot use %s with %s with a nil Database ID" + `lib.metadata.protocols/tables + `UncachedApplicationDatabaseMetadataProvider) + {}))) + (log/debugf "Fetching all Tables for Database %d" database-id) + (mapv #(instance->metadata % :metadata/table) + (t2/select :metabase.models.table/Table :db_id database-id))) + +(defn- fields [table-id] + (log/debugf "Fetching all Fields for Table %d" table-id) + (mapv #(instance->metadata % :metadata/field) + (t2/select :metabase.models.field/Field :table_id table-id))) (p/deftype+ UncachedApplicationDatabaseMetadataProvider [database-id] lib.metadata.protocols/MetadataProvider @@ -47,19 +67,10 @@ (segment [_this segment-id] (fetch-instance :metadata/segment segment-id)) (tables [_this] - (when-not database-id - (throw (ex-info (format "Cannot use %s with %s with a nil Database ID" - `lib.metadata.protocols/tables - `UncachedApplicationDatabaseMetadataProvider) - {}))) - (log/debugf "Fetching all Tables for Database %d" database-id) - (mapv #(assoc % :lib/type :metadata/table) - (t2/select :metabase.models.table/Table :db_id database-id))) + (tables database-id)) (fields [_this table-id] - (log/debugf "Fetching all Fields for Table %d" table-id) - (mapv #(assoc % :lib/type :metadata/field) - (t2/select :metabase.models.field/Field :table_id table-id))) + (fields table-id)) lib.metadata.protocols/BulkMetadataProvider (bulk-metadata [_this metadata-type ids] diff --git a/src/metabase/lib/metric.cljc b/src/metabase/lib/metric.cljc index d062d3c302f..89ce5c3e8f2 100644 --- a/src/metabase/lib/metric.cljc +++ b/src/metabase/lib/metric.cljc @@ -43,7 +43,7 @@ (defmethod lib.metadata.calculation/display-name-method :metadata/metric [_query _stage-number metric-metadata _style] - (or ((some-fn :display_name :name) metric-metadata) + (or ((some-fn :display-name :name) metric-metadata) (i18n/tru "[Unknown Metric]"))) (defmethod lib.metadata.calculation/display-name-method :metric diff --git a/src/metabase/lib/order_by.cljc b/src/metabase/lib/order_by.cljc index 8d4cb30238c..ca19b80b04e 100644 --- a/src/metabase/lib/order_by.cljc +++ b/src/metabase/lib/order_by.cljc @@ -118,7 +118,7 @@ stage-number :- :int] (not-empty (get (lib.util/query-stage query stage-number) :order-by)))) -(defn- orderable-column? [{base-type :base_type, :as _column-metadata}] +(defn- orderable-column? [{:keys [base-type], :as _column-metadata}] (some (fn [orderable-base-type] (isa? base-type orderable-base-type)) lib.schema.expression/orderable-types)) diff --git a/src/metabase/lib/query.cljc b/src/metabase/lib/query.cljc index 0d62ec97c19..e3cd672105f 100644 --- a/src/metabase/lib/query.cljc +++ b/src/metabase/lib/query.cljc @@ -11,6 +11,8 @@ [metabase.lib.schema :as lib.schema] [metabase.lib.schema.id :as lib.schema.id] [metabase.lib.util :as lib.util] + [metabase.shared.util.i18n :as i18n] + [metabase.util.log :as log] [metabase.util.malli :as mu])) (defmethod lib.normalize/normalize :mbql/query @@ -29,49 +31,55 @@ [query stage-number x style] (lib.metadata.calculation/display-name query stage-number (lib.util/query-stage x stage-number) style)) -(defn query-with-stages +(mu/defn query-with-stages :- ::lib.schema/query "Create a query from a sequence of stages." - ([metadata-provider stages] - (query-with-stages (:id (lib.metadata/database metadata-provider)) metadata-provider stages)) + ([metadata-providerable stages] + (query-with-stages (:id (lib.metadata/database metadata-providerable)) metadata-providerable stages)) - ([database-id metadata-provider stages] + ([database-id :- ::lib.schema.id/database + metadata-providerable :- lib.metadata/MetadataProviderable + stages] {:lib/type :mbql/query - :lib/metadata metadata-provider + :lib/metadata (lib.metadata/->metadata-provider metadata-providerable) :database database-id - :stages (mapv lib.options/ensure-uuid stages)})) + :stages stages})) -(defn query-with-stage +(mu/defn query-with-stage "Create a query from a specific stage." - ([metadata-provider stage] - (query-with-stages metadata-provider [stage])) + ([metadata-providerable stage] + (query-with-stages metadata-providerable [stage])) - ([database-id metadata-provider stage] - (query-with-stages database-id metadata-provider [stage]))) + ([database-id :- ::lib.schema.id/database + metadata-providerable :- lib.metadata/MetadataProviderable + stage] + (query-with-stages database-id metadata-providerable [stage]))) -(defn- query-from-existing [metadata-provider query] +(mu/defn ^:private query-from-existing :- ::lib.schema/query + [metadata-providerable :- lib.metadata/MetadataProviderable + query :- lib.util/LegacyOrPMBQLQuery] (let [query (lib.util/pipeline query)] - (query-with-stages metadata-provider (:stages query)))) + (query-with-stages metadata-providerable (:stages query)))) (defmulti ^:private ->query "Implementation for [[query]]." - {:arglists '([metadata-provider x])} - (fn [_metadata-provider x] + {:arglists '([metadata-providerable x])} + (fn [_metadata-providerable x] (lib.dispatch/dispatch-value x)) :hierarchy lib.hierarchy/hierarchy) (defmethod ->query :dispatch-type/map - [metadata-provider query] - (query-from-existing metadata-provider query)) + [metadata-providerable query] + (query-from-existing metadata-providerable query)) ;;; this should already be a query in the shape we want, but let's make sure it has the database metadata that was ;;; passed in (defmethod ->query :mbql/query - [metadata-provider query] - (assoc query :lib/metadata metadata-provider)) + [metadata-providerable query] + (assoc query :lib/metadata (lib.metadata/->metadata-provider metadata-providerable))) (defmethod ->query :metadata/table - [metadata-provider table-metadata] - (query-with-stages metadata-provider + [metadata-providerable table-metadata] + (query-with-stages metadata-providerable [{:lib/type :mbql.stage/mbql :source-table (:id table-metadata)}])) @@ -79,23 +87,25 @@ "Create a new MBQL query from anything that could conceptually be an MBQL query, like a Database or Table or an existing MBQL query or saved question or whatever. If the thing in question does not already include metadata, pass it in separately -- metadata is needed for most query manipulation operations." - [metadata-provider :- lib.metadata/MetadataProvider + [metadata-providerable :- lib.metadata/MetadataProviderable x] - (->query metadata-provider x)) + (->query metadata-providerable x)) ;;; TODO -- the stuff below will probably change in the near future, please don't read too much in to it. (mu/defn native-query :- ::lib.schema/query "Create a new native query. Native in this sense means a pMBQL query with a first stage that is a native query." - ([metadata-provider :- lib.metadata/MetadataProvider + ([metadata-providerable :- lib.metadata/MetadataProviderable inner-query] - (native-query metadata-provider nil inner-query)) + (native-query metadata-providerable nil inner-query)) - ([metadata-provider :- lib.metadata/MetadataProvider - results-metadata :- lib.metadata/StageMetadata + ;; TODO not sure if `results-metadata` should be StageMetadata (i.e., a map roughly matching the shape you get from + ;; the QP) or a sequence of ColumnMetadatas, like what would be saved in Card `result_metadata`. + ([metadata-providerable :- lib.metadata/MetadataProviderable + results-metadata :- lib.metadata/StageMetadata inner-query] - (query-with-stages metadata-provider + (query-with-stages metadata-providerable [(-> {:lib/type :mbql.stage/native :lib/stage-metadata results-metadata :native inner-query} @@ -103,19 +113,24 @@ (mu/defn saved-question-query :- ::lib.schema/query "Convenience for creating a query from a Saved Question (i.e., a Card)." - [metadata-provider :- lib.metadata/MetadataProvider - {mbql-query :dataset_query, metadata :result_metadata}] + [metadata-providerable :- lib.metadata/MetadataProviderable + {mbql-query :dataset-query, metadata :result-metadata, :as saved-question}] + (assert mbql-query (i18n/tru "Saved Question is missing query")) + (when-not metadata + (log/warn (i18n/trs "Saved Question {0} {1} is missing result metadata" + (:id saved-question) + (pr-str (:name saved-question-query))))) (let [mbql-query (cond-> (assoc (lib.convert/->pMBQL mbql-query) - :lib/metadata metadata-provider) + :lib/metadata (lib.metadata/->metadata-provider metadata-providerable)) metadata - (lib.util/update-query-stage -1 assoc :lib/stage-metadata metadata))] - (query metadata-provider mbql-query))) + (lib.util/update-query-stage -1 assoc :lib/stage-metadata (lib.util/->stage-metadata metadata)))] + (query metadata-providerable mbql-query))) (mu/defn query-from-legacy-inner-query :- ::lib.schema/query "Create a pMBQL query from a legacy inner query." - [metadata-provider :- lib.metadata/MetadataProvider - database-id :- ::lib.schema.id/database - inner-query :- :map] + [metadata-providerable :- lib.metadata/MetadataProviderable + database-id :- ::lib.schema.id/database + inner-query :- :map] (->> (lib.convert/legacy-query-from-inner-query database-id inner-query) lib.convert/->pMBQL - (query metadata-provider))) + (query metadata-providerable))) diff --git a/src/metabase/lib/ref.cljc b/src/metabase/lib/ref.cljc index 2038026e1ba..acde87f12d9 100644 --- a/src/metabase/lib/ref.cljc +++ b/src/metabase/lib/ref.cljc @@ -14,5 +14,5 @@ (mu/defn ref :- ::lib.schema.ref/ref "Create a fresh ref that can be added to a query, e.g. a `:field`, `:aggregation`, or `:expression` reference. Will create a new UUID every time this is called." - [x] + [x :- some?] (ref-method x)) diff --git a/src/metabase/lib/segment.cljc b/src/metabase/lib/segment.cljc index f799a95e2bf..3f3f908ba96 100644 --- a/src/metabase/lib/segment.cljc +++ b/src/metabase/lib/segment.cljc @@ -7,7 +7,7 @@ (defmethod lib.metadata.calculation/display-name-method :metadata/segment [_query _stage-number segment-metadata _style] - (or ((some-fn :display_name :name) segment-metadata) + (or ((some-fn :display-name :name) segment-metadata) (i18n/tru "[Unknown Segment]"))) (defmethod lib.metadata.calculation/display-name-method :segment diff --git a/src/metabase/lib/stage.cljc b/src/metabase/lib/stage.cljc index d422148af5e..e0ce4031361 100644 --- a/src/metabase/lib/stage.cljc +++ b/src/metabase/lib/stage.cljc @@ -258,7 +258,7 @@ (defn- implicitly-joinable-columns "Columns that are implicitly joinable from some other columns in `column-metadatas`. To be joinable, the column has to - have appropriate FK metadata, i.e. have an `:fk_target_field_id` pointing to another Field. (I think we only include + have appropriate FK metadata, i.e. have an `:fk-target-field-id` pointing to another Field. (I think we only include this information for Databases that support FKs and joins, so I don't think we need to do an additional DB feature check here.) @@ -269,20 +269,20 @@ Does not include columns that would be implicitly joinable via multiple hops." [query stage-number column-metadatas unique-name-fn] - (let [existing-table-ids (into #{} (map :table_id) column-metadatas)] + (let [existing-table-ids (into #{} (map :table-id) column-metadatas)] (into [] - (comp (filter :fk_target_field_id) - (m/distinct-by :fk_target_field_id) - (map (fn [{source-field-id :id, target-field-id :fk_target_field_id}] - (-> (lib.metadata/field query target-field-id) + (comp (filter :fk-target-field-id) + (m/distinct-by :fk-target-field-id) + (map (fn [{source-field-id :id, :keys [fk-target-field-id]}] + (-> (lib.metadata/field query fk-target-field-id) (assoc ::source-field-id source-field-id)))) - (remove #(contains? existing-table-ids (:table_id %))) - (m/distinct-by :table_id) - (mapcat (fn [{table-id :table_id, ::keys [source-field-id]}] + (remove #(contains? existing-table-ids (:table-id %))) + (m/distinct-by :table-id) + (mapcat (fn [{:keys [table-id], ::keys [source-field-id]}] (let [table-metadata (lib.metadata/table query table-id)] (for [field (lib.metadata.calculation/default-columns query stage-number table-metadata unique-name-fn) :let [field (assoc field - :fk_field_id source-field-id + :fk-field-id source-field-id :lib/source :source/implicitly-joinable :lib/source-column-alias (:name field))]] (assoc field :lib/desired-column-alias (unique-name-fn diff --git a/src/metabase/lib/table.cljc b/src/metabase/lib/table.cljc index b9f166057db..e953d8eed31 100644 --- a/src/metabase/lib/table.cljc +++ b/src/metabase/lib/table.cljc @@ -11,7 +11,7 @@ (defmethod lib.metadata.calculation/display-name-method :metadata/table [_query _stage-number table-metadata _style] - (or (:display_name table-metadata) + (or (:display-name table-metadata) (some->> (:name table-metadata) (u.humanization/name->human-readable-name :simple)))) @@ -45,7 +45,7 @@ "Remove Fields that shouldn't be visible from the default Fields for a source Table. See [[metabase.query-processor.middleware.add-implicit-clauses/table->sorted-fields*]]." [field-metadatas] - (remove (fn [{visibility-type :visibility_type, active? :active, :as _field-metadata}] + (remove (fn [{:keys [visibility-type], active? :active, :as _field-metadata}] (or (false? active?) (#{:sensitive :retired} (some-> visibility-type keyword)))) field-metadatas)) diff --git a/src/metabase/lib/temporal_bucket.cljc b/src/metabase/lib/temporal_bucket.cljc index 0dc45576963..b39147302e6 100644 --- a/src/metabase/lib/temporal_bucket.cljc +++ b/src/metabase/lib/temporal_bucket.cljc @@ -22,28 +22,29 @@ unit :- [:maybe :keyword]] (if-not unit "" - (case (keyword unit) - :default (i18n/trun "Default period" "Default periods" n) - :millisecond (i18n/trun "Millisecond" "Milliseconds" n) - :second (i18n/trun "Second" "Seconds" n) - :minute (i18n/trun "Minute" "Minutes" n) - :hour (i18n/trun "Hour" "Hours" n) - :day (i18n/trun "Day" "Days" n) - :week (i18n/trun "Week" "Weeks" n) - :month (i18n/trun "Month" "Months" n) - :quarter (i18n/trun "Quarter" "Quarters" n) - :year (i18n/trun "Year" "Years" n) - :minute-of-hour (i18n/trun "Minute of hour" "Minutes of hour" n) - :hour-of-day (i18n/trun "Hour of day" "Hours of day" n) - :day-of-week (i18n/trun "Day of week" "Days of week" n) - :day-of-month (i18n/trun "Day of month" "Days of month" n) - :day-of-year (i18n/trun "Day of year" "Days of year" n) - :week-of-year (i18n/trun "Week of year" "Weeks of year" n) - :month-of-year (i18n/trun "Month of year" "Months of year" n) - :quarter-of-year (i18n/trun "Quarter of year" "Quarters of year" n) - ;; e.g. :unknown-unit => "Unknown unit" - (let [[unit & more] (str/split (name unit) #"-")] - (str/join \space (cons (str/capitalize unit) more))))))) + (let [n (abs n)] + (case (keyword unit) + :default (i18n/trun "Default period" "Default periods" n) + :millisecond (i18n/trun "Millisecond" "Milliseconds" n) + :second (i18n/trun "Second" "Seconds" n) + :minute (i18n/trun "Minute" "Minutes" n) + :hour (i18n/trun "Hour" "Hours" n) + :day (i18n/trun "Day" "Days" n) + :week (i18n/trun "Week" "Weeks" n) + :month (i18n/trun "Month" "Months" n) + :quarter (i18n/trun "Quarter" "Quarters" n) + :year (i18n/trun "Year" "Years" n) + :minute-of-hour (i18n/trun "Minute of hour" "Minutes of hour" n) + :hour-of-day (i18n/trun "Hour of day" "Hours of day" n) + :day-of-week (i18n/trun "Day of week" "Days of week" n) + :day-of-month (i18n/trun "Day of month" "Days of month" n) + :day-of-year (i18n/trun "Day of year" "Days of year" n) + :week-of-year (i18n/trun "Week of year" "Weeks of year" n) + :month-of-year (i18n/trun "Month of year" "Months of year" n) + :quarter-of-year (i18n/trun "Quarter of year" "Quarters of year" n) + ;; e.g. :unknown-unit => "Unknown unit" + (let [[unit & more] (str/split (name unit) #"-")] + (str/join \space (cons (str/capitalize unit) more)))))))) (def ^:private TemporalIntervalAmount [:or [:enum :current :last :next] :int]) diff --git a/src/metabase/lib/types/constants.cljc b/src/metabase/lib/types/constants.cljc index 0f78e121b09..ac4e0ce822a 100644 --- a/src/metabase/lib/types/constants.cljc +++ b/src/metabase/lib/types/constants.cljc @@ -34,25 +34,25 @@ (def type-hierarchies "A front-end specific type hierarchy used by [[metabase.lib.types.isa/field-type?]]. It is not meant to be used directly." - {::temporal {:effective_type [:type/Temporal] - :semantic_type [:type/Temporal]} - ::number {:effective_type [:type/Number] - :semantic_type [:type/Number]} - ::string {:effective_type [:type/Text] - :semantic_type [:type/Text :type/Category]} - ::string_like {:effective_type [:type/TextLike]} - ::boolean {:effective_type [:type/Boolean]} - ::coordinate {:semantic_type [:type/Coordinate]} - ::location {:semantic_type [:type/Address]} - ::entity {:semantic_type [:type/FK :type/PK :type/Name]} - ::foreign_key {:semantic_type [:type/FK]} - ::primary_key {:semantic_type [:type/PK]} + {::temporal {:effective-type [:type/Temporal] + :semantic-type [:type/Temporal]} + ::number {:effective-type [:type/Number] + :semantic-type [:type/Number]} + ::string {:effective-type [:type/Text] + :semantic-type [:type/Text :type/Category]} + ::string_like {:effective-type [:type/TextLike]} + ::boolean {:effective-type [:type/Boolean]} + ::coordinate {:semantic-type [:type/Coordinate]} + ::location {:semantic-type [:type/Address]} + ::entity {:semantic-type [:type/FK :type/PK :type/Name]} + ::foreign_key {:semantic-type [:type/FK]} + ::primary_key {:semantic-type [:type/PK]} ::summable {:include [::number] :exclude [::entity ::location ::temporal]} ::scope {:include [::number ::temporal ::category ::entity ::string] :exclude [::location]} - ::category {:effective_type [:type/Boolean] - :semantic_type [:type/Category] + ::category {:effective-type [:type/Boolean] + :semantic-type [:type/Category] :include [::location]} ;; NOTE: this is defunct right now. see definition of metabase.lib.types.isa/dimension?. ::dimension {:include [::temporal ::category ::entity]}}) diff --git a/src/metabase/lib/types/isa.cljc b/src/metabase/lib/types/isa.cljc index cb4daf689ee..65482c7b235 100644 --- a/src/metabase/lib/types/isa.cljc +++ b/src/metabase/lib/types/isa.cljc @@ -3,15 +3,18 @@ (:require [medley.core :as m] [metabase.lib.types.constants :as lib.types.constants] - [metabase.lib.util :as lib.u]) + [metabase.lib.util :as lib.u] + [metabase.types]) (:refer-clojure :exclude [isa? any? boolean? number? string?])) +(comment metabase.types/keep-me) + (defn ^:export isa? "Decide if `_column` is a subtype of the type denoted by the keyword `type-kw`. Both effective and semantic types are taken into account." - [{:keys [effective_type semantic_type] :as _column} type-kw] - (or (clojure.core/isa? effective_type type-kw) - (clojure.core/isa? semantic_type type-kw))) + [{:keys [effective-type semantic-type] :as _column} type-kw] + (or (clojure.core/isa? effective-type type-kw) + (clojure.core/isa? semantic-type type-kw))) (defn ^:export field-type? "Returns if `column` is of category `category`. @@ -23,7 +26,7 @@ ;; check field types (some (fn [[type-type types]] - (and (#{:effective_type :semantic_type} type-type) + (and (#{:effective-type :semantic-type} type-type) (some #(clojure.core/isa? (type-type column) %) types))) type-definition) true @@ -96,7 +99,7 @@ (defn ^:export description? "Is `column` a description?" [column] - (clojure.core/isa? (:semantic_type column) :type/Description)) + (clojure.core/isa? (:semantic-type column) :type/Description)) (defn ^:export dimension? "Is `column` a dimension?" @@ -114,17 +117,17 @@ (defn ^:export foreign-key? "Is `column` a foreign-key?" [column] - (clojure.core/isa? (:semantic_type column) :type/FK)) + (clojure.core/isa? (:semantic-type column) :type/FK)) (defn ^:export primary-key? "Is `column` a primary-key?" [column] - (clojure.core/isa? (:semantic_type column) :type/PK)) + (clojure.core/isa? (:semantic-type column) :type/PK)) (defn ^:export entity-name? "Is `column` an entity name?" [column] - (clojure.core/isa? (:semantic_type column) :type/Name)) + (clojure.core/isa? (:semantic-type column) :type/Name)) (defn ^:export any? "Is this `_column` whatever (including nil)?" @@ -134,19 +137,19 @@ (defn ^:export numeric-base-type? "Is `column` a numneric base type?" [column] - (clojure.core/isa? (:effective_type column) :type/Number)) + (clojure.core/isa? (:effective-type column) :type/Number)) (defn ^:export date-without-time? "Is `column` a date without time?" [column] - (clojure.core/isa? (:effective_type column) :type/Date)) + (clojure.core/isa? (:effective-type column) :type/Date)) ;; ZipCode, ID, etc derive from Number but should not be formatted as numbers (defn ^:export number? "Is `column` a number without some other semantic type (like ZIP code)?" [column] (and (numeric-base-type? column) - (let [semantic-type (:semantic_type column)] + (let [semantic-type (:semantic-type column)] (or (nil? semantic-type) ;; this is a precaution, :type/Number is not a semantic type (clojure.core/isa? semantic-type :type/Number))))) @@ -154,83 +157,83 @@ (defn ^:export time? "Is `column` a time?" [column] - (clojure.core/isa? (:effective_type column) :type/Time)) + (clojure.core/isa? (:effective-type column) :type/Time)) (defn ^:export address? "Is `column` an address?" [column] - (clojure.core/isa? (:semantic_type column) :type/Address)) + (clojure.core/isa? (:semantic-type column) :type/Address)) (defn ^:export city? "Is `column` a city?" [column] - (clojure.core/isa? (:semantic_type column) :type/City)) + (clojure.core/isa? (:semantic-type column) :type/City)) (defn ^:export state? "Is `column` a state?" [column] - (clojure.core/isa? (:semantic_type column) :type/State)) + (clojure.core/isa? (:semantic-type column) :type/State)) (defn ^:export zip-code? "Is `column` a zip-code?" [column] - (clojure.core/isa? (:semantic_type column) :type/ZipCode)) + (clojure.core/isa? (:semantic-type column) :type/ZipCode)) (defn ^:export country? "Is `column` a country?" [column] - (clojure.core/isa? (:semantic_type column) :type/Country)) + (clojure.core/isa? (:semantic-type column) :type/Country)) (defn ^:export coordinate? "Is `column` a coordinate?" [column] - (clojure.core/isa? (:semantic_type column) :type/Coordinate)) + (clojure.core/isa? (:semantic-type column) :type/Coordinate)) (defn ^:export latitude? "Is `column` a latitude?" [column] - (clojure.core/isa? (:semantic_type column) :type/Latitude)) + (clojure.core/isa? (:semantic-type column) :type/Latitude)) (defn ^:export longitude? "Is `column` a longitude?" [column] - (clojure.core/isa? (:semantic_type column) :type/Longitude)) + (clojure.core/isa? (:semantic-type column) :type/Longitude)) (defn ^:export currency? "Is `column` a currency?" [column] - (clojure.core/isa? (:semantic_type column) :type/Currency)) + (clojure.core/isa? (:semantic-type column) :type/Currency)) (defn ^:export comment? "Is `column` a comment?" [column] - (clojure.core/isa? (:semantic_type column) :type/Comment)) + (clojure.core/isa? (:semantic-type column) :type/Comment)) (defn ^:export id? "Is `column` an ID?" [column] - (or (clojure.core/isa? (:semantic_type column) :type/FK) - (clojure.core/isa? (:semantic_type column) :type/PK))) + (or (clojure.core/isa? (:semantic-type column) :type/FK) + (clojure.core/isa? (:semantic-type column) :type/PK))) (defn ^:export URL? "Is `column` a URL?" [column] - (clojure.core/isa? (:semantic_type column) :type/URL)) + (clojure.core/isa? (:semantic-type column) :type/URL)) (defn ^:export email? "Is `column` an email?" [column] - (clojure.core/isa? (:semantic_type column) :type/Email)) + (clojure.core/isa? (:semantic-type column) :type/Email)) (defn ^:export avatar-URL? "Is `column` an avatar URL?" [column] - (clojure.core/isa? (:semantic_type column) :type/AvatarURL)) + (clojure.core/isa? (:semantic-type column) :type/AvatarURL)) (defn ^:export image-URL? "Is `column` an image URL?" [column] - (clojure.core/isa? (:semantic_type column) :type/ImageURL)) + (clojure.core/isa? (:semantic-type column) :type/ImageURL)) (defn ^:export has-latitude-and-longitude? "Does the collection `columns` contain both a latitude and a longitude column?" diff --git a/src/metabase/lib/util.cljc b/src/metabase/lib/util.cljc index d96b3030ba5..c17dd74833b 100644 --- a/src/metabase/lib/util.cljc +++ b/src/metabase/lib/util.cljc @@ -116,18 +116,26 @@ (defn- joins->pipeline [joins] (mapv join->pipeline joins)) +(defn ->stage-metadata + "Convert legacy `:source-metadata` to [[metabase.lib.metadata/StageMetadata]]." + [source-metadata] + (when source-metadata + (-> (if (vector? source-metadata) + {:columns source-metadata} + source-metadata) + (update :columns (fn [columns] + (mapv (fn [column] + (-> column + (update-keys u/->kebab-case-en) + (assoc :lib/type :metadata/field))) + columns))) + (assoc :lib/type :metadata/results)))) + (defn- inner-query->stages [{:keys [source-query source-metadata], :as inner-query}] (let [previous-stages (if source-query (inner-query->stages source-query) []) - source-metadata (when source-metadata - (-> (if (vector? source-metadata) - {:columns source-metadata} - source-metadata) - (update :columns (fn [columns] - (for [column columns] - (assoc column :lib/type :metadata/field)))) - (assoc :lib/type :metadata/results))) + source-metadata (->stage-metadata source-metadata) previous-stage (dec (count previous-stages)) previous-stages (cond-> previous-stages (and source-metadata @@ -157,13 +165,14 @@ :stages (inner-query->stages (:query query))} (dissoc query :type :query))) -(def ^:private AnyQuery +(def LegacyOrPMBQLQuery + "Schema for a map that is either a legacy query OR a pMBQL query." [:or [:map - {:error/fn "legacy query"} + {:error/message "legacy query"} [:type [:enum :native :query]]] [:map - {:error/fn "pMBQL query"} + {:error/message "pMBQL query"} [:lib/type [:= :mbql/query]]]]) (mu/defn pipeline @@ -171,7 +180,7 @@ goal here is just to make sure we have `:stages` in the correct place and the like. See [[metabase.lib.convert]] for functions that actually ensure all parts of the query match the pMBQL schema (they use this function as part of that process.)" - [query :- AnyQuery] + [query :- LegacyOrPMBQLQuery] (if (= (:lib/type query) :mbql/query) query (case (:type query) @@ -212,7 +221,7 @@ (mu/defn query-stage :- ::lib.schema/stage "Fetch a specific `stage` of a query. This handles negative indices as well, e.g. `-1` will return the last stage of the query." - [query :- AnyQuery + [query :- LegacyOrPMBQLQuery stage-number :- :int] (let [{:keys [stages]} (pipeline query)] (get (vec stages) (non-negative-stage-index stages stage-number)))) @@ -229,7 +238,7 @@ (apply f stage args) `stage-number` can be a negative index, e.g. `-1` will update the last stage of the query." - [query :- AnyQuery + [query :- LegacyOrPMBQLQuery stage-number :- :int f & args] (let [{:keys [stages], :as query} (pipeline query) diff --git a/src/metabase/models/interface.clj b/src/metabase/models/interface.clj index 332ca9a7b66..3d89980e03b 100644 --- a/src/metabase/models/interface.clj +++ b/src/metabase/models/interface.clj @@ -1,8 +1,6 @@ (ns metabase.models.interface (:require [buddy.core.codecs :as codecs] - [camel-snake-kebab.core :as csk] - [camel-snake-kebab.internals.macros :as csk.macros] [cheshire.core :as json] [cheshire.generate :as json.generate] [clojure.core.memoize :as memoize] @@ -447,10 +445,6 @@ (methodical/prefer-method! #'t2.before-insert/before-insert :hook/timestamped? :hook/entity-id) -;;; define a custom CSK conversion function so we don't run into problems if the system locale is Turkish -(declare ^:private ->kebab-case-en) ; so Kondo doesn't complain -(csk.macros/defconversion "kebab-case-en" u/lower-case-en u/lower-case-en "-") - (methodical/defmethod t2.model/resolve-model :before :default "Ensure the namespace for given model is loaded. This is a safety mechanism as we moving to toucan2 and we don't need to require the model namespaces in order to use it." @@ -460,7 +454,7 @@ ;; don't try to require if it's already registered as a :metabase/model; this way ones defined in EE ;; namespaces won't break if they are loaded in some other way. (not (isa? x :metabase/model))) - (let [model-namespace (str "metabase.models." (->kebab-case-en (name x)))] + (let [model-namespace (str "metabase.models." (u/->kebab-case-en (name x)))] ;; use `classloader/require` which is thread-safe and plays nice with our plugins system (classloader/require model-namespace))) x) @@ -693,4 +687,4 @@ (methodical/defmethod t2.hydrate/fk-keys-for-automagic-hydration :default "In Metabase the FK key used for automagic hydration should use underscores (work around upstream Toucan 2 issue)." [_original-model dest-key _hydrated-key] - [(csk/->snake_case (keyword (str (name dest-key) "_id")))]) + [(u/->snake_case_en (keyword (str (name dest-key) "_id")))]) diff --git a/src/metabase/models/metric.clj b/src/metabase/models/metric.clj index 3100253640f..b5b6f6b07af 100644 --- a/src/metabase/models/metric.clj +++ b/src/metabase/models/metric.clj @@ -56,7 +56,7 @@ [metadata-provider :- lib.metadata/MetadataProvider {:keys [definition], table-id :table_id, :as _metric}] (when (seq definition) - (when-let [{database-id :db_id} (when table-id + (when-let [{database-id :db-id} (when table-id (lib.metadata.protocols/table metadata-provider table-id))] (try (let [definition (merge {:source-table table-id} diff --git a/src/metabase/models/segment.clj b/src/metabase/models/segment.clj index 0e20b265f88..e96d3b2052e 100644 --- a/src/metabase/models/segment.clj +++ b/src/metabase/models/segment.clj @@ -56,7 +56,7 @@ [metadata-provider :- lib.metadata/MetadataProvider {table-id :table_id, :keys [definition], :as _segment}] (when (seq definition) - (when-let [{database-id :db_id} (when table-id (lib.metadata.protocols/table metadata-provider table-id))] + (when-let [{database-id :db-id} (when table-id (lib.metadata.protocols/table metadata-provider table-id))] (try (let [definition (merge {:source-table table-id} definition) diff --git a/src/metabase/util.cljc b/src/metabase/util.cljc index 38bee573e7e..255f1b555e9 100644 --- a/src/metabase/util.cljc +++ b/src/metabase/util.cljc @@ -1,7 +1,7 @@ (ns metabase.util "Common utility functions useful throughout the codebase." (:require - [camel-snake-kebab.core :as csk] + [camel-snake-kebab.internals.macros :as csk.macros] [clojure.pprint :as pprint] [clojure.set :as set] [clojure.string :as str] @@ -24,7 +24,8 @@ (java.text Normalizer Normalizer$Form) (java.util Locale) (org.apache.commons.validator.routines RegexValidator UrlValidator))) - #?(:cljs (:require-macros [metabase.util]))) + #?(:cljs (:require-macros [camel-snake-kebab.internals.macros :as csk.macros] + [metabase.util]))) (u.ns/import-fns [u.format colorize format-bytes format-color format-milliseconds format-nanoseconds format-seconds]) @@ -125,30 +126,6 @@ [m] (m/filter-vals some? m)) -(defn normalize-map - "Given any map-like object, return it as a Clojure map with :kebab-case keyword keys. - The input map can be a: - - Clojure map with string or keyword keys, - - JS object (with string keys) - The keys are converted to `kebab-case` from `camelCase` or `snake_case` as necessary, and turned into keywords. - Namespaces keywords are rejected with an exception. - - Returns an empty map if nil is input (like [[update-keys]])." - [m] - (let [base #?(:clj m - ;; If we're running in CLJS, convert to a ClojureScript map as needed. - :cljs (if (object? m) - (js->clj m) - m))] - (update-keys base csk/->kebab-case-keyword))) - -(defn snake-key - "Convert a keyword or string `k` from `lisp-case` to `snake-case`." - [k] - (if (keyword? k) - (keyword (snake-key (name k))) - (str/replace k #"-" "_"))) - (defn recursive-map-keys "Recursively replace the keys in a map with the value of `(f key)`." [f m] @@ -158,11 +135,6 @@ %) m)) -(defn snake-keys - "Convert the keys in a map from `lisp-case` to `snake_case`." - [m] - (recursive-map-keys snake-key m)) - (defn add-period "Fixes strings that don't terminate in a period; also accounts for strings that end in `:`. Used for formatting docs." @@ -176,11 +148,9 @@ (str text "."))))) (defn lower-case-en - "Locale-agnostic version of `clojure.string/lower-case`. - `clojure.string/lower-case` uses the default locale in conversions, turning - `ID` into `ıd`, in the Turkish locale. This function always uses the - `en-US` locale." - [^CharSequence s] + "Locale-agnostic version of [[clojure.string/lower-case]]. [[clojure.string/lower-case]] uses the default locale in + conversions, turning `ID` into `ıd`, in the Turkish locale. This function always uses the `en-US` locale." + ^String [^CharSequence s] #?(:clj (.. s toString (toLowerCase (Locale/US))) :cljs (.toLowerCase s))) @@ -189,14 +159,61 @@ `clojure.string/upper-case` uses the default locale in conversions, turning `id` into `İD`, in the Turkish locale. This function always uses the `en-US` locale." - [^CharSequence s] + ^String [^CharSequence s] #?(:clj (.. s toString (toUpperCase (Locale/US))) :cljs (.toUpperCase s))) -(defn screaming-snake-case - "Turns `strings-that-look-like-deafening-vipers` into `STRINGS_THAT_LOOK_LIKE_DEAFENING_VIPERS`." - [s] - (upper-case-en (str/replace s "-" "_"))) +(defn capitalize-en + "Locale-agnostic version of [[clojure.string/capitalize]]." + ^String [^CharSequence s] + (when-let [s (some-> s str)] + (if (< (count s) 2) + (upper-case-en s) + (str (upper-case-en (subs s 0 1)) + (lower-case-en (subs s 1)))))) + +;;; define custom CSK conversion functions so we don't run into problems if the system locale is Turkish + +;; so Kondo doesn't complain +(declare ^:private ->kebab-case-en*) +(declare ^:private ->camelCaseEn*) +(declare ^:private ->snake_case_en*) +(declare ^:private ->SCREAMING_SNAKE_CASE_EN*) + +(csk.macros/defconversion "kebab-case-en*" lower-case-en lower-case-en "-") +(csk.macros/defconversion "camelCaseEn*" lower-case-en capitalize-en "") +(csk.macros/defconversion "snake_case_en*" lower-case-en lower-case-en "_") +(csk.macros/defconversion "SCREAMING_SNAKE_CASE_EN*" upper-case-en upper-case-en "_") + +(defn- wrap-csk-conversion-fn-to-handle-nil-and-namespaced-keywords + "Wrap a CSK defconversion function so that it handles nil and namespaced keywords, which it doesn't support out of the + box for whatever reason." + [f] + (fn [x] + (when x + (if (qualified-keyword? x) + (keyword (f (namespace x)) (f (name x))) + (f x))))) + +(def ^{:arglists '([x])} ->kebab-case-en + "Like [[camel-snake-kebab.core/->kebab-case]], but always uses English for lower-casing, supports keywords with + namespaces, and returns `nil` when passed `nil` (rather than throwing an exception)." + (wrap-csk-conversion-fn-to-handle-nil-and-namespaced-keywords ->kebab-case-en*)) + +(def ^{:arglists '([x])} ->snake_case_en + "Like [[camel-snake-kebab.core/->snake_case]], but always uses English for lower-casing, supports keywords with + namespaces, and returns `nil` when passed `nil` (rather than throwing an exception)." + (wrap-csk-conversion-fn-to-handle-nil-and-namespaced-keywords ->snake_case_en*)) + +(def ^{:arglists '([x])} ->camelCaseEn + "Like [[camel-snake-kebab.core/->camelCase]], but always uses English for upper- and lower-casing, supports keywords + with namespaces, and returns `nil` when passed `nil` (rather than throwing an exception)." + (wrap-csk-conversion-fn-to-handle-nil-and-namespaced-keywords ->camelCaseEn*)) + +(def ^{:arglists '([x])} ->SCREAMING_SNAKE_CASE_EN + "Like [[camel-snake-kebab.core/->SCREAMING_SNAKE_CASE]], but always uses English for upper- and lower-casing, supports + keywords with namespaces, and returns `nil` when passed `nil` (rather than throwing an exception)." + (wrap-csk-conversion-fn-to-handle-nil-and-namespaced-keywords ->SCREAMING_SNAKE_CASE_EN*)) (defn capitalize-first-char "Like string/capitalize, only it ignores the rest of the string @@ -207,6 +224,27 @@ (str (upper-case-en (subs s 0 1)) (subs s 1)))) +(defn snake-keys + "Convert the keys in a map from `kebab-case` to `snake_case`." + [m] + (recursive-map-keys ->snake_case_en m)) + +(defn normalize-map + "Given any map-like object, return it as a Clojure map with :kebab-case keyword keys. + The input map can be a: + - Clojure map with string or keyword keys, + - JS object (with string keys) + The keys are converted to `kebab-case` from `camelCase` or `snake_case` as necessary, and turned into keywords. + + Returns an empty map if nil is input (like [[update-keys]])." + [m] + (let [base #?(:clj m + ;; If we're running in CLJS, convert to a ClojureScript map as needed. + :cljs (if (object? m) + (js->clj m) + m))] + (update-keys base (comp keyword ->kebab-case-en)))) + ;; Log the maximum memory available to the JVM at launch time as well since it is very handy for debugging things #?(:clj (when-not *compile-files* @@ -550,8 +588,7 @@ (defn lower-case-map-keys "Changes the keys of a given map to lower case." [m] - (into {} (for [[k v] m] - [(-> k name lower-case-en keyword) v]))) + (update-keys m #(-> % name lower-case-en keyword))) (defn pprint-to-str "Returns the output of pretty-printing `x` as a string. diff --git a/test/metabase/lib/aggregation_test.cljc b/test/metabase/lib/aggregation_test.cljc index 1cdba752651..33835861b12 100644 --- a/test/metabase/lib/aggregation_test.cljc +++ b/test/metabase/lib/aggregation_test.cljc @@ -142,29 +142,29 @@ (col-info-for-aggregation-clause clause)) ;; :count, no field [:/ {} [:count {}] 2] - {:base_type :type/Float + {:base-type :type/Float :name "count_divided_by_2" - :display_name "Count ÷ 2"} + :display-name "Count ÷ 2"} ;; :sum [:sum {} [:+ {} (lib.tu/field-clause :venues :price) 1]] - {:base_type :type/Integer + {:base-type :type/Integer :name "sum_PRICE_plus_1" - :display_name "Sum of Price + 1"} + :display-name "Sum of Price + 1"} ;; options map [:sum {:name "sum_2", :display-name "My custom name", :base-type :type/BigInteger} (lib.tu/field-clause :venues :price)] - {:base_type :type/BigInteger + {:base-type :type/BigInteger :name "sum_2" - :display_name "My custom name"})) + :display-name "My custom name"})) (deftest ^:parallel col-info-named-aggregation-test (testing "col info for an `expression` aggregation w/ a named expression should work as expected" - (is (=? {:base_type :type/Integer + (is (=? {:base-type :type/Integer :name "sum_double-price" - :display_name "Sum of double-price"} + :display-name "Sum of double-price"} (col-info-for-aggregation-clause (lib.tu/venues-query-with-last-stage {:expressions {"double-price" [:* @@ -182,7 +182,6 @@ :database (meta/id) :stages [{:lib/type :mbql.stage/mbql :source-table (meta/id :venues) - :lib/options {:lib/uuid string?} :aggregation [[:sum {:lib/uuid string?} [:field {:base-type :type/Integer, :lib/uuid string?} @@ -238,9 +237,9 @@ (lib/expression "double-price" (lib/* (lib/field (meta/id :venues :price)) 2)) (lib/aggregate (lib/sum [:expression {:lib/uuid (str (random-uuid))} "double-price"])))] (is (=? [{:lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "sum_double-price" - :display_name "Sum of double-price"}] + :display-name "Sum of double-price"}] (lib/aggregations query))) (is (= :type/Integer (lib/type-of query (first (lib/aggregations query))))))) @@ -251,9 +250,9 @@ (lib/aggregate (lib/sum (lib/field (meta/id :venues :price)))))] (is (=? {:settings {:is_priceless true} :lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "sum_PRICE" - :display_name "Sum of Price" + :display-name "Sum of Price" :lib/source :source/aggregations} (lib.metadata.calculation/metadata query (first (lib/aggregations query -1)))))))) @@ -275,12 +274,12 @@ (lib.metadata.calculation/display-name query ag-ref))) (is (=? {:lib/type :metadata/field :lib/source :source/aggregations - :display_name "Average of Price + 1" - :effective_type :type/Float + :display-name "Average of Price + 1" + :effective-type :type/Float :metabase.lib.aggregation/aggregation-index 0} (lib.metadata.calculation/metadata query ag-ref))) - (is (=? {:display_name "Average of Price + 1" - :effective_type :type/Float} + (is (=? {:display-name "Average of Price + 1" + :effective-type :type/Float} (lib.metadata.calculation/display-info query ag-ref))))) (deftest ^:parallel aggregate-should-drop-invalid-parts diff --git a/test/metabase/lib/breakout_test.cljc b/test/metabase/lib/breakout_test.cljc index e104c1122b2..7d9d42e68cf 100644 --- a/test/metabase/lib/breakout_test.cljc +++ b/test/metabase/lib/breakout_test.cljc @@ -70,65 +70,65 @@ (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) (is (=? [{:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :id (meta/id :venues :id) - :table_id (meta/id :venues) - :base_type :type/BigInteger + :table-id (meta/id :venues) + :base-type :type/BigInteger :lib/source-column-alias "ID" :lib/desired-column-alias "ID"} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :id (meta/id :venues :name) - :table_id (meta/id :venues) - :base_type :type/Text + :table-id (meta/id :venues) + :base-type :type/Text :lib/source-column-alias "NAME" :lib/desired-column-alias "NAME"} {:lib/type :metadata/field :name "CATEGORY_ID" - :display_name "Category ID" + :display-name "Category ID" :id (meta/id :venues :category-id) - :table_id (meta/id :venues) + :table-id (meta/id :venues) :lib/source-column-alias "CATEGORY_ID" :lib/desired-column-alias "CATEGORY_ID"} {:lib/type :metadata/field :name "LATITUDE" - :display_name "Latitude" + :display-name "Latitude" :id (meta/id :venues :latitude) - :table_id (meta/id :venues) - :base_type :type/Float + :table-id (meta/id :venues) + :base-type :type/Float :lib/source-column-alias "LATITUDE" :lib/desired-column-alias "LATITUDE"} {:lib/type :metadata/field :name "LONGITUDE" - :display_name "Longitude" + :display-name "Longitude" :id (meta/id :venues :longitude) - :table_id (meta/id :venues) - :base_type :type/Float + :table-id (meta/id :venues) + :base-type :type/Float :lib/source-column-alias "LONGITUDE" :lib/desired-column-alias "LONGITUDE"} {:lib/type :metadata/field :name "PRICE" - :display_name "Price" + :display-name "Price" :id (meta/id :venues :price) - :table_id (meta/id :venues) - :base_type :type/Integer + :table-id (meta/id :venues) + :base-type :type/Integer :lib/source-column-alias "PRICE" :lib/desired-column-alias "PRICE"} {:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :id (meta/id :categories :id) - :table_id (meta/id :categories) - :base_type :type/BigInteger + :table-id (meta/id :categories) + :base-type :type/BigInteger :lib/source-column-alias "ID" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__ID"} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :id (meta/id :categories :name) - :table_id (meta/id :categories) - :base_type :type/Text + :table-id (meta/id :categories) + :base-type :type/Text :lib/source-column-alias "NAME" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__NAME"}] (lib/breakoutable-columns query)))))) @@ -145,9 +145,9 @@ {:id (meta/id :venues :longitude) :name "LONGITUDE"} {:id (meta/id :venues :price) :name "PRICE"} {:lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "Category ID + 1" - :display_name "Category ID + 1" + :display-name "Category ID + 1" :lib/source :source/expressions} {:id (meta/id :categories :id) :name "ID"} {:id (meta/id :categories :name) :name "NAME"}] @@ -172,18 +172,18 @@ {:id (meta/id :venues :price) :name "PRICE"} {:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :source_alias "Cat" :id (meta/id :categories :id) - :table_id (meta/id :categories) - :base_type :type/BigInteger} + :table-id (meta/id :categories) + :base-type :type/BigInteger} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :source_alias "Cat" :id (meta/id :categories :name) - :table_id (meta/id :categories) - :base_type :type/Text}] + :table-id (meta/id :categories) + :base-type :type/Text}] (lib/breakoutable-columns query))))))) (deftest ^:parallel breakoutable-columns-source-card-test @@ -193,57 +193,57 @@ (testing (str (pr-str varr) \newline (lib.util/format "Query =\n%s" (u/pprint-to-str query))) (let [columns (lib/breakoutable-columns query)] (is (=? [{:name "USER_ID" - :display_name "User ID" - :base_type :type/Integer + :display-name "User ID" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "USER_ID"} {:name "count" - :display_name "Count" - :base_type :type/Integer + :display-name "Count" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "count"} {:name "ID" - :display_name "ID" - :base_type :type/BigInteger + :display-name "ID" + :base-type :type/BigInteger :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__ID"} {:name "NAME" - :display_name "Name" - :base_type :type/Text + :display-name "Name" + :base-type :type/Text :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__NAME"} {:name "LAST_LOGIN" - :display_name "Last Login" - :base_type :type/DateTime + :display-name "Last Login" + :base-type :type/DateTime :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__LAST_LOGIN"}] columns)) (testing `lib/display-info (is (=? [{:name "USER_ID" - :display_name "User ID" - :table {:name "My Card", :display_name "My Card"} - :is_from_previous_stage false - :is_implicitly_joinable false} + :display-name "User ID" + :table {:name "My Card", :display-name "My Card"} + :is-from-previous-stage false + :is-implicitly-joinable false} {:name "count" - :display_name "Count" - :table {:name "My Card", :display_name "My Card"} - :is_from_previous_stage false - :is_implicitly_joinable false} + :display-name "Count" + :table {:name "My Card", :display-name "My Card"} + :is-from-previous-stage false + :is-implicitly-joinable false} {:name "ID" - :display_name "ID" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true} + :display-name "ID" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true} {:name "NAME" - :display_name "Name" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true} + :display-name "Name" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true} {:name "LAST_LOGIN" - :display_name "Last Login" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true}] + :display-name "Last Login" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true}] (for [col columns] (lib/display-info query col))))))))) @@ -258,8 +258,7 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :source-table (meta/id :venues) - :lib/options {:lib/uuid string?}}]} + :source-table (meta/id :venues)}]} query)) (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) (let [breakoutable-columns (lib/breakoutable-columns query) @@ -269,7 +268,6 @@ :database (meta/id) :stages [{:lib/type :mbql.stage/mbql :source-table (meta/id :venues) - :lib/options {:lib/uuid string?} :breakout [[:field {:lib/uuid string? :base-type :type/Text} (meta/id :venues :name)]]}]} query')) (is (=? [[:field {:lib/uuid string? :base-type :type/Text} (meta/id :venues :name)]] @@ -295,12 +293,12 @@ query')) (is (= "Venues, Grouped by Categories → Name and Price" (lib/describe-query query'))) - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/implicitly-joinable}] (lib/breakoutable-columns query')))))) (deftest ^:parallel breakoutable-columns-with-source-card-e2e-test @@ -310,7 +308,7 @@ (let [name-col (m/find-first #(= (:name %) "USER_ID") (lib/breakoutable-columns query))] (is (=? {:name "USER_ID" - :base_type :type/Integer} + :base-type :type/Integer} name-col)) (let [query' (lib/breakout query name-col)] (is (=? {:stages @@ -328,15 +326,15 @@ (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (lib/expression "expr" (lib/absolute-datetime "2020" :month)) (lib/with-fields [(lib/field "VENUES" "ID")]))] - (is (=? [{:id (meta/id :venues :id), :name "ID", :display_name "ID", :lib/source :source/table-defaults} - {:id (meta/id :venues :name), :name "NAME", :display_name "Name", :lib/source :source/table-defaults} - {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display_name "Category ID", :lib/source :source/table-defaults} - {:id (meta/id :venues :latitude), :name "LATITUDE", :display_name "Latitude", :lib/source :source/table-defaults} - {:id (meta/id :venues :longitude), :name "LONGITUDE", :display_name "Longitude", :lib/source :source/table-defaults} - {:id (meta/id :venues :price), :name "PRICE", :display_name "Price", :lib/source :source/table-defaults} - {:name "expr", :display_name "expr", :lib/source :source/expressions} - {:id (meta/id :categories :id), :name "ID", :display_name "ID", :lib/source :source/implicitly-joinable} - {:id (meta/id :categories :name), :name "NAME", :display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:id (meta/id :venues :id), :name "ID", :display-name "ID", :lib/source :source/table-defaults} + {:id (meta/id :venues :name), :name "NAME", :display-name "Name", :lib/source :source/table-defaults} + {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display-name "Category ID", :lib/source :source/table-defaults} + {:id (meta/id :venues :latitude), :name "LATITUDE", :display-name "Latitude", :lib/source :source/table-defaults} + {:id (meta/id :venues :longitude), :name "LONGITUDE", :display-name "Longitude", :lib/source :source/table-defaults} + {:id (meta/id :venues :price), :name "PRICE", :display-name "Price", :lib/source :source/table-defaults} + {:name "expr", :display-name "expr", :lib/source :source/expressions} + {:id (meta/id :categories :id), :name "ID", :display-name "ID", :lib/source :source/implicitly-joinable} + {:id (meta/id :categories :name), :name "NAME", :display-name "Name", :lib/source :source/implicitly-joinable}] (lib/breakoutable-columns query))) (let [expr (m/find-first #(= (:name %) "expr") (lib/breakoutable-columns query))] (is (=? {:lib/type :metadata/field @@ -357,8 +355,8 @@ (lib/with-fields [(lib/field "VENUES" "ID") (lib.dev/expression-ref "expr")]) (lib/append-stage))] - (is (=? [{:id (meta/id :venues :id), :name "ID", :display_name "ID", :lib/source :source/previous-stage} - {:name "expr", :display_name "expr", :lib/source :source/previous-stage}] + (is (=? [{:id (meta/id :venues :id), :name "ID", :display-name "ID", :lib/source :source/previous-stage} + {:name "expr", :display-name "expr", :lib/source :source/previous-stage}] (lib/breakoutable-columns query))) (let [expr (m/find-first #(= (:name %) "expr") (lib/breakoutable-columns query))] (is (=? {:lib/type :metadata/field diff --git a/test/metabase/lib/card_test.cljc b/test/metabase/lib/card_test.cljc index 40860f71122..a581388a293 100644 --- a/test/metabase/lib/card_test.cljc +++ b/test/metabase/lib/card_test.cljc @@ -27,36 +27,38 @@ (lib.metadata.calculation/metadata query))) (testing `lib/display-info (is (=? [{:name "USER_ID" - :display_name "User ID" + :display-name "User ID" :table {:name "My Card" - :display_name "My Card"} - :effective_type :type/Integer - :semantic_type :type/FK - :is_calculated false - :is_from_previous_stage false - :is_implicitly_joinable false - :is_from_join false} + :display-name "My Card"} + :effective-type :type/Integer + :semantic-type :type/FK + :is-calculated false + :is-from-previous-stage false + :is-implicitly-joinable false + :is-from-join false} {:name "count" - :display_name "Count" + :display-name "Count" :table {:name "My Card" - :display_name "My Card"} - :effective_type :type/Integer - :is_from_previous_stage false - :is_from_join false - :is_calculated false - :is_implicitly_joinable false}] + :display-name "My Card"} + :effective-type :type/Integer + :is-from-previous-stage false + :is-from-join false + :is-calculated false + :is-implicitly-joinable false}] (for [col (lib.metadata.calculation/metadata query)] (lib/display-info query col)))))))) (deftest ^:parallel card-source-query-metadata-test (doseq [metadata [{:id 1 :name "My Card" - :result_metadata meta/results-metadata} - ;; in some cases the FE is transforming the metadata like this, not sure why but handle it anyway + :result-metadata meta/card-results-metadata + :dataset-query {}} + ;; in some cases [the FE unit tests are broken] the FE is transforming the metadata like this, not sure why but handle it anyway ;; (#29739) - {:id 1 - :name "My Card" - :fields (:columns meta/results-metadata)}]] + {:id 1 + :name "My Card" + :fields meta/card-results-metadata + :dataset-query {}}]] (testing (str "metadata = \n" (u/pprint-to-str metadata)) (let [query {:lib/type :mbql/query :lib/metadata (lib.tu/mock-metadata-provider @@ -65,19 +67,19 @@ :stages [{:lib/type :mbql.stage/mbql :lib/options {:lib/uuid (str (random-uuid))} :source-table "card__1"}]}] - (is (=? (for [col (:columns meta/results-metadata)] + (is (=? (for [col meta/card-results-metadata] (assoc col :lib/source :source/card)) (lib.metadata.calculation/metadata query))))))) (deftest ^:parallel card-results-metadata-merge-metadata-provider-metadata-test - (testing "Merge metadata from the metadata provider into result_metadata (#30046)" + (testing "Merge metadata from the metadata provider into result-metadata (#30046)" (let [query (lib.tu/query-with-card-source-table-with-result-metadata)] (is (=? [{:lib/type :metadata/field :id (meta/id :checkins :user-id) - :table_id (meta/id :checkins) - :semantic_type :type/FK - ;; this comes from the metadata provider, it's not present in `result_metadata` - :fk_target_field_id (meta/id :users :id) + :table-id (meta/id :checkins) + :semantic-type :type/FK + ;; this comes from the metadata provider, it's not present in `result-metadata` + :fk-target-field-id (meta/id :users :id) :lib/desired-column-alias "USER_ID"} {:lib/type :metadata/field :name "count"}] diff --git a/test/metabase/lib/column_group_test.cljc b/test/metabase/lib/column_group_test.cljc index ac667d23ed7..eb78f0e1455 100644 --- a/test/metabase/lib/column_group_test.cljc +++ b/test/metabase/lib/column_group_test.cljc @@ -17,27 +17,27 @@ (is (not (mc/explain [:sequential @#'lib.column-group/ColumnGroup] groups))) (is (=? [{::lib.column-group/group-type :group-type/main :lib/type :metadata/column-group - ::lib.column-group/columns [{:name "ID", :display_name "ID"} - {:name "NAME", :display_name "Name"} - {:name "CATEGORY_ID", :display_name "Category ID"} - {:name "LATITUDE", :display_name "Latitude"} - {:name "LONGITUDE", :display_name "Longitude"} - {:name "PRICE", :display_name "Price"}]} + ::lib.column-group/columns [{:name "ID", :display-name "ID"} + {:name "NAME", :display-name "Name"} + {:name "CATEGORY_ID", :display-name "Category ID"} + {:name "LATITUDE", :display-name "Latitude"} + {:name "LONGITUDE", :display-name "Longitude"} + {:name "PRICE", :display-name "Price"}]} {::lib.column-group/group-type :group-type/join.implicit :lib/type :metadata/column-group - ::lib.column-group/columns [{:name "ID", :display_name "ID"} - {:name "NAME", :display_name "Name"}]}] + ::lib.column-group/columns [{:name "ID", :display-name "ID"} + {:name "NAME", :display-name "Name"}]}] groups)) (testing `lib/display-info - (is (=? [{:is_from_join false - :is_implicitly_joinable false + (is (=? [{:is-from-join false + :is-implicitly-joinable false :name "VENUES" - :display_name "Venues"} - {:is_from_join false - :is_implicitly_joinable true + :display-name "Venues"} + {:is-from-join false + :is-implicitly-joinable true :name "CATEGORY_ID" - :display_name "Category ID" - :fk_reference_name "Category"}] + :display-name "Category ID" + :fk-reference-name "Category"}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -51,14 +51,14 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "Name", :lib/source :source/breakouts} - {:display_name "Sum of ID", :lib/source :source/aggregations}]}] + ::lib.column-group/columns [{:display-name "Name", :lib/source :source/breakouts} + {:display-name "Sum of ID", :lib/source :source/aggregations}]}] groups)) (testing `lib/display-info - (is (=? [{:is_from_join false - :is_implicitly_joinable false + (is (=? [{:is-from-join false + :is-implicitly-joinable false :name "VENUES" - :display_name "Venues"}] + :display-name "Venues"}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -73,13 +73,13 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "Name", :lib/source :source/previous-stage} - {:display_name "Sum of ID", :lib/source :source/previous-stage}]}] + ::lib.column-group/columns [{:display-name "Name", :lib/source :source/previous-stage} + {:display-name "Sum of ID", :lib/source :source/previous-stage}]}] groups)) (testing `lib/display-info - (is (=? [{:display_name "" - :is_from_join false - :is_implicitly_joinable false}] + (is (=? [{:display-name "" + :is-from-join false + :is-implicitly-joinable false}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -91,24 +91,24 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "User ID", :lib/source :source/card} - {:display_name "Count", :lib/source :source/card}]} + ::lib.column-group/columns [{:display-name "User ID", :lib/source :source/card} + {:display-name "Count", :lib/source :source/card}]} {::lib.column-group/group-type :group-type/join.implicit :fk-field-id (meta/id :checkins :user-id) - ::lib.column-group/columns [{:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable} - {:display_name "Last Login", :lib/source :source/implicitly-joinable}]}] + ::lib.column-group/columns [{:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable} + {:display-name "Last Login", :lib/source :source/implicitly-joinable}]}] groups)) (testing `lib/display-info (is (=? [{:name "My Card" - :display_name "My Card" - :is_from_join false - :is_implicitly_joinable false} + :display-name "My Card" + :is-from-join false + :is-implicitly-joinable false} {:name "USER_ID" - :display_name "User ID" - :fk_reference_name "User" - :is_from_join false - :is_implicitly_joinable true}] + :display-name "User ID" + :fk-reference-name "User" + :is-from-join false + :is-implicitly-joinable true}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -120,26 +120,26 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:name "ID", :display_name "ID"} - {:name "NAME", :display_name "Name"} - {:name "CATEGORY_ID", :display_name "Category ID"} - {:name "LATITUDE", :display_name "Latitude"} - {:name "LONGITUDE", :display_name "Longitude"} - {:name "PRICE", :display_name "Price"}]} + ::lib.column-group/columns [{:name "ID", :display-name "ID"} + {:name "NAME", :display-name "Name"} + {:name "CATEGORY_ID", :display-name "Category ID"} + {:name "LATITUDE", :display-name "Latitude"} + {:name "LONGITUDE", :display-name "Longitude"} + {:name "PRICE", :display-name "Price"}]} {::lib.column-group/group-type :group-type/join.explicit :join-alias "Cat" - ::lib.column-group/columns [{:display_name "ID", :lib/source :source/joins} - {:display_name "Name", :lib/source :source/joins}]}] + ::lib.column-group/columns [{:display-name "ID", :lib/source :source/joins} + {:display-name "Name", :lib/source :source/joins}]}] groups)) (testing `lib/display-info - (is (=? [{:is_from_join false - :is_implicitly_joinable false + (is (=? [{:is-from-join false + :is-implicitly-joinable false :name "VENUES" - :display_name "Venues"} - {:is_from_join true - :is_implicitly_joinable false + :display-name "Venues"} + {:is-from-join true + :is-implicitly-joinable false :name "Cat" - :display_name "Categories"}] + :display-name "Categories"}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -151,28 +151,28 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:name "ID", :display_name "ID"} - {:name "NAME", :display_name "Name"} - {:name "CATEGORY_ID", :display_name "Category ID"} - {:name "LATITUDE", :display_name "Latitude"} - {:name "LONGITUDE", :display_name "Longitude"} - {:name "PRICE", :display_name "Price"} - {:display_name "expr", :lib/source :source/expressions}]} + ::lib.column-group/columns [{:name "ID", :display-name "ID"} + {:name "NAME", :display-name "Name"} + {:name "CATEGORY_ID", :display-name "Category ID"} + {:name "LATITUDE", :display-name "Latitude"} + {:name "LONGITUDE", :display-name "Longitude"} + {:name "PRICE", :display-name "Price"} + {:display-name "expr", :lib/source :source/expressions}]} {::lib.column-group/group-type :group-type/join.implicit :lib/type :metadata/column-group - ::lib.column-group/columns [{:name "ID", :display_name "ID"} - {:name "NAME", :display_name "Name"}]}] + ::lib.column-group/columns [{:name "ID", :display-name "ID"} + {:name "NAME", :display-name "Name"}]}] groups)) (testing `lib/display-info - (is (=? [{:is_from_join false - :is_implicitly_joinable false + (is (=? [{:is-from-join false + :is-implicitly-joinable false :name "VENUES" - :display_name "Venues"} - {:is_from_join false - :is_implicitly_joinable true + :display-name "Venues"} + {:is-from-join false + :is-implicitly-joinable true :name "CATEGORY_ID" - :display_name "Category ID" - :fk_reference_name "Category"}] + :display-name "Category ID" + :fk-reference-name "Category"}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -185,25 +185,25 @@ columns (lib/orderable-columns query) groups (lib/group-columns columns)] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "User ID", :lib/source :source/card} - {:display_name "Count", :lib/source :source/card} - {:display_name "expr", :lib/source :source/expressions}]} + ::lib.column-group/columns [{:display-name "User ID", :lib/source :source/card} + {:display-name "Count", :lib/source :source/card} + {:display-name "expr", :lib/source :source/expressions}]} {::lib.column-group/group-type :group-type/join.implicit :fk-field-id (meta/id :checkins :user-id) - ::lib.column-group/columns [{:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable} - {:display_name "Last Login", :lib/source :source/implicitly-joinable}] }] + ::lib.column-group/columns [{:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable} + {:display-name "Last Login", :lib/source :source/implicitly-joinable}] }] groups)) (testing `lib/display-info (is (=? [{:name "My Card" - :display_name "My Card" - :is_from_join false - :is_implicitly_joinable false} + :display-name "My Card" + :is-from-join false + :is-implicitly-joinable false} {:name "USER_ID" - :display_name "User ID" - :fk_reference_name "User" - :is_from_join false - :is_implicitly_joinable true}] + :display-name "User ID" + :fk-reference-name "User" + :is-from-join false + :is-implicitly-joinable true}] (for [group groups] (lib/display-info query group))))) (testing `lib/columns-group-columns @@ -214,13 +214,13 @@ (let [query (lib.tu/native-query) groups (lib/group-columns (lib/orderable-columns query))] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "another Field", :lib/source :source/native} - {:display_name "sum of User ID", :lib/source :source/native}]}] + ::lib.column-group/columns [{:display-name "another Field", :lib/source :source/native} + {:display-name "sum of User ID", :lib/source :source/native}]}] groups)) (testing `lib/display-info - (is (=? [{:display_name "Native query" - :is_from_join false - :is_implicitly_joinable false}] + (is (=? [{:display-name "Native query" + :is-from-join false + :is-implicitly-joinable false}] (for [group groups] (lib/display-info query group))))))) @@ -229,12 +229,12 @@ lib/append-stage) groups (lib/group-columns (lib/orderable-columns query))] (is (=? [{::lib.column-group/group-type :group-type/main - ::lib.column-group/columns [{:display_name "another Field", :lib/source :source/previous-stage} - {:display_name "sum of User ID", :lib/source :source/previous-stage}]}] + ::lib.column-group/columns [{:display-name "another Field", :lib/source :source/previous-stage} + {:display-name "sum of User ID", :lib/source :source/previous-stage}]}] groups)) (testing `lib/display-info - (is (=? [{:display_name "" - :is_from_join false - :is_implicitly_joinable false}] + (is (=? [{:display-name "" + :is-from-join false + :is-implicitly-joinable false}] (for [group groups] (lib/display-info query group))))))) diff --git a/test/metabase/lib/dev_test.cljc b/test/metabase/lib/dev_test.cljc index 39d09dd34be..ec36b3be844 100644 --- a/test/metabase/lib/dev_test.cljc +++ b/test/metabase/lib/dev_test.cljc @@ -21,7 +21,6 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :venues)}]} (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (dissoc :lib/metadata))))) diff --git a/test/metabase/lib/expression_test.cljc b/test/metabase/lib/expression_test.cljc index 2724a0d8b93..c1751ca7bb8 100644 --- a/test/metabase/lib/expression_test.cljc +++ b/test/metabase/lib/expression_test.cljc @@ -20,7 +20,6 @@ :database (meta/id) :stages [{:lib/type :mbql.stage/mbql :source-table (meta/id :venues) - :lib/options {:lib/uuid string?} :expressions {"myadd" [:+ {:lib/uuid string?} 1 [:field {:base-type :type/Integer, :lib/uuid string?} (meta/id :venues :category-id)]]}}]} @@ -85,9 +84,9 @@ (is (= typ (lib.schema.expression/type-of resolved))))))))) (deftest ^:parallel col-info-expression-ref-test - (is (=? {:base_type :type/Integer + (is (=? {:base-type :type/Integer :name "double-price" - :display_name "double-price" + :display-name "double-price" :lib/source :source/expressions} (lib.metadata.calculation/metadata (lib.tu/venues-query-with-last-stage @@ -106,8 +105,8 @@ [:interval {:lib/uuid (str (random-uuid))} -1 :month]]} :fields [[:expression {:base-type :type/DateTime, :lib/uuid (str (random-uuid))} "prev_month"]]})] (is (=? [{:name "prev_month" - :display_name "prev_month" - :base_type :type/DateTime + :display-name "prev_month" + :base-type :type/DateTime :lib/source :source/expressions}] (lib.metadata.calculation/metadata query))))) @@ -162,8 +161,8 @@ (testing "Uses the first clause" (testing "Gets the type information from the field" (is (=? {:name "expr" - :display_name "expr" - :base_type :type/Text} + :display-name "expr" + :base-type :type/Text} (infer-first [:coalesce {:lib/uuid (str (random-uuid))} (lib.tu/field-clause :venues :name) @@ -172,9 +171,9 @@ (is (not (contains? (infer-first [:coalesce {:lib/uuid (str (random-uuid))} (lib.tu/field-clause :venues :name) "bar"]) :id))))) (testing "Gets the type information from the literal" - (is (=? {:base_type :type/Text + (is (=? {:base-type :type/Text :name "expr" - :display_name "expr"} + :display-name "expr"} (infer-first [:coalesce {:lib/uuid (str (random-uuid))} "bar" (lib.tu/field-clause :venues :name)]))))))) (deftest ^:parallel infer-case-test @@ -182,8 +181,8 @@ (testing "Uses first available type information" (testing "From a field" (is (=? {:name "expr" - :display_name "expr" - :base_type :type/Text} + :display-name "expr" + :base-type :type/Text} (infer-first [:coalesce {:lib/uuid (str (random-uuid))} (lib.tu/field-clause :venues :name) @@ -194,9 +193,9 @@ :id)))))))) (deftest ^:parallel col-info-for-temporal-expression-test - (is (=? {:base_type :type/DateTime + (is (=? {:base-type :type/DateTime :name "last-login-plus-2" - :display_name "last-login-plus-2" + :display-name "last-login-plus-2" :lib/source :source/expressions} (lib.metadata.calculation/metadata (lib.tu/venues-query-with-last-stage @@ -245,7 +244,7 @@ (deftest ^:parallel expressions-names-test (testing "expressions should include the original expression name" (is (=? [{:name "expr" - :display_name "expr"}] + :display-name "expr"}] (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (lib/expression "expr" (lib/absolute-datetime "2020" :month)) lib/expressions))))) diff --git a/test/metabase/lib/field_test.cljc b/test/metabase/lib/field_test.cljc index 322438748c9..93a2b69eeee 100644 --- a/test/metabase/lib/field_test.cljc +++ b/test/metabase/lib/field_test.cljc @@ -37,23 +37,23 @@ (let [grandparent {:lib/type :metadata/field :name "grandparent" :id (grandparent-parent-child-id :grandparent) - :base_type :type/Text} + :base-type :type/Text} parent {:lib/type :metadata/field :name "parent" - :parent_id (grandparent-parent-child-id :grandparent) + :parent-id (grandparent-parent-child-id :grandparent) :id (grandparent-parent-child-id :parent) - :base_type :type/Text} + :base-type :type/Text} child {:lib/type :metadata/field :name "child" - :parent_id (grandparent-parent-child-id :parent) + :parent-id (grandparent-parent-child-id :parent) :id (grandparent-parent-child-id :child) - :base_type :type/Text}] + :base-type :type/Text}] (lib.tu/mock-metadata-provider {:database meta/metadata :tables [(meta/table-metadata :venues)] :fields (mapv (fn [field-metadata] - (merge {:visibility_type :normal - :table_id (meta/id :venues)} + (merge {:visibility-type :normal + :table-id (meta/id :venues)} field-metadata)) [grandparent parent child])}))) @@ -69,26 +69,26 @@ -1 a-field-clause))] (testing "For fields with parents we should return them with a combined name including parent's name" - (is (=? {:table_id (meta/id :venues) + (is (=? {:table-id (meta/id :venues) :name "grandparent.parent" - :parent_id (grandparent-parent-child-id :grandparent) + :parent-id (grandparent-parent-child-id :grandparent) :id (grandparent-parent-child-id :parent) - :visibility_type :normal} + :visibility-type :normal} (col-info [:field {:lib/uuid (str (random-uuid))} (grandparent-parent-child-id :parent)])))) (testing "nested-nested fields should include grandparent name (etc)" - (is (=? {:table_id (meta/id :venues) + (is (=? {:table-id (meta/id :venues) :name "grandparent.parent.child" - :parent_id (grandparent-parent-child-id :parent) + :parent-id (grandparent-parent-child-id :parent) :id (grandparent-parent-child-id :child) - :visibility_type :normal} + :visibility-type :normal} (col-info [:field {:lib/uuid (str (random-uuid))} (grandparent-parent-child-id :child)])))))) (deftest ^:parallel col-info-field-literals-test (testing "field literals should get the information from the matching `:lib/stage-metadata` if it was supplied" (is (=? {:name "sum" - :display_name "sum of User ID" - :base_type :type/Integer - :semantic_type :type/FK} + :display-name "sum of User ID" + :base-type :type/Integer + :semantic-type :type/FK} (lib.metadata.calculation/metadata (lib.tu/native-query) -1 @@ -125,7 +125,7 @@ (lib/display-name query -1 field style)) :default "Name" :long "Categories → Name") - (is (=? {:display_name "Name"} + (is (=? {:display-name "Name"} (lib.metadata.calculation/metadata query -1 field))))) (deftest ^:parallel unresolved-lib-field-with-temporal-bucket-test @@ -147,17 +147,17 @@ (def ^:private temporal-bucketing-mock-metadata "Mock metadata for testing temporal bucketing stuff. - * Includes a date field where the `:base_type` is `:type/Text`, but `:effective_type` is `:type/Date` because of a - `:Coercion/ISO8601->Date`, so we can test that `:effective_type` is preserved properly + * Includes a date field where the `:base-type` is `:type/Text`, but `:effective-type` is `:type/Date` because of a + `:Coercion/ISO8601->Date`, so we can test that `:effective-type` is preserved properly * Includes a mocked Field with `:type/Time`" (let [date-field (assoc (meta/field-metadata :people :birth-date) - :base_type :type/Text - :effective_type :type/Date - :coercion_strategy :Coercion/ISO8601->Date) + :base-type :type/Text + :effective-type :type/Date + :coercion-strategy :Coercion/ISO8601->Date) time-field (assoc (meta/field-metadata :orders :created-at) - :base_type :type/Time - :effective_type :type/Time) + :base-type :type/Time + :effective-type :type/Time) metadata-provider (lib.tu/composed-metadata-provider (lib.tu/mock-metadata-provider {:fields [date-field @@ -211,7 +211,7 @@ :expected-units lib.schema.temporal-bucketing/datetime-bucketing-units} {:metadata (get-in temporal-bucketing-mock-metadata [:fields :time]) :expected-units lib.schema.temporal-bucketing/time-bucketing-units}]] - (testing (str (:base_type metadata) " Field") + (testing (str (:base-type metadata) " Field") (doseq [[what x] {"column metadata" metadata, "field ref" (lib/ref metadata)}] (testing (str what "\n\n" (u/pprint-to-str x)) (is (= expected-units @@ -222,7 +222,7 @@ (lib/with-temporal-bucket x :month-of-year)))))))))) (deftest ^:parallel joined-field-column-name-test - (let [card {:dataset_query {:database (meta/id) + (let [card {:dataset-query {:database (meta/id) :type :query :query {:source-table (meta/id :venues) :joins [{:fields :all @@ -273,19 +273,19 @@ (deftest ^:parallel source-card-table-display-info-test (let [query (assoc lib.tu/venues-query :lib/metadata lib.tu/metadata-provider-with-card) field (lib.metadata.calculation/metadata query (assoc (lib.metadata/field query (meta/id :venues :name)) - :table_id "card__1"))] + :table-id "card__1"))] (is (=? {:name "NAME" - :display_name "Name" - :semantic_type :type/Name - :effective_type :type/Text - :table {:name "My Card", :display_name "My Card"}} + :display-name "Name" + :semantic-type :type/Name + :effective-type :type/Text + :table {:name "My Card", :display-name "My Card"}} (lib/display-info query field))))) (deftest ^:parallel resolve-column-name-in-join-test (testing ":field refs with string names should work if the Field comes from a :join" (let [card-1 {:name "My Card" :id 1 - :dataset_query {:database (meta/id) + :dataset-query {:database (meta/id) :type :query :query {:source-table (meta/id :checkins) :aggregation [[:count]] diff --git a/test/metabase/lib/filter_test.cljc b/test/metabase/lib/filter_test.cljc index f9b9f2a04e0..c546fbe9584 100644 --- a/test/metabase/lib/filter_test.cljc +++ b/test/metabase/lib/filter_test.cljc @@ -119,7 +119,6 @@ :database (meta/id) :stages [{:lib/type :mbql.stage/mbql :source-table (meta/id :categories) - :lib/options {:lib/uuid string?} :filters [original-filter]}]}] (testing "no filter" (is (nil? (lib/filters q2)))) diff --git a/test/metabase/lib/join_test.cljc b/test/metabase/lib/join_test.cljc index 264556a8e5c..0e346d98bac 100644 --- a/test/metabase/lib/join_test.cljc +++ b/test/metabase/lib/join_test.cljc @@ -30,12 +30,10 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :venues) :joins [{:lib/type :mbql/join :lib/options {:lib/uuid string?} :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :categories)}] :conditions [[:= {:lib/uuid string?} @@ -53,7 +51,6 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :categories) :joins [{:lib/type :mbql/join :lib/options {:lib/uuid string?} @@ -101,7 +98,7 @@ (dissoc :lib/metadata))))))) (deftest ^:parallel col-info-implicit-join-test - (testing (str "when a `:field` with `:source-field` (implicit join) is used, we should add in `:fk_field_id` " + (testing (str "when a `:field` with `:source-field` (implicit join) is used, we should add in `:fk-field-id` " "info about the source Field") (let [query (lib/query meta/metadata-provider @@ -112,7 +109,7 @@ :fields [[:field (meta/id :categories :name) {:source-field (meta/id :venues :category-id)}]]}}))] (is (=? [{:name "NAME" :id (meta/id :categories :name) - :fk_field_id (meta/id :venues :category-id) + :fk-field-id (meta/id :venues :category-id) :lib/source :source/fields}] (lib.metadata.calculation/metadata query -1 query)))))) @@ -142,7 +139,7 @@ :lib/metadata meta/metadata-provider}] (let [metadata (lib.metadata.calculation/metadata query)] (is (=? [(merge (meta/field-metadata :categories :name) - {:display_name "Name" + {:display-name "Name" :lib/source :source/fields :metabase.lib.field/join-alias "CATEGORIES__via__CATEGORY_ID"})] metadata)) @@ -156,7 +153,7 @@ (deftest ^:parallel join-against-source-card-metadata-test (let [card-1 {:name "My Card" :id 1 - :dataset_query {:database (meta/id) + :dataset-query {:database (meta/id) :type :query :query {:source-table (meta/id :checkins) :aggregation [[:count]] @@ -257,15 +254,15 @@ :lib/options {:lib/uuid "e8888108-22a7-4f97-8315-ff63503634d7"} :source-table (meta/id :categories)}]}]}]}] (is (=? [{:name "ID" - :display_name "ID" + :display-name "ID" :lib/source-column-alias "ID" :lib/desired-column-alias "ID"} {:name "NAME" - :display_name "Name" + :display-name "Name" :lib/source-column-alias "NAME" :lib/desired-column-alias "NAME"} {:name "ID" - :display_name "ID" + :display-name "ID" :lib/source-column-alias "ID" :lib/desired-column-alias "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY_bfaf4e7b"}] (lib.metadata.calculation/metadata query))))) diff --git a/test/metabase/lib/metadata/jvm_test.clj b/test/metabase/lib/metadata/jvm_test.clj index fc73d59e228..ee0278363d7 100644 --- a/test/metabase/lib/metadata/jvm_test.clj +++ b/test/metabase/lib/metadata/jvm_test.clj @@ -1,13 +1,20 @@ (ns metabase.lib.metadata.jvm-test (:require [clojure.test :refer :all] + [malli.core :as mc] + [malli.error :as me] [metabase.lib.core :as lib] + [metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata.calculation :as lib.metadata.calculation] - [metabase.lib.metadata.jvm :as sut] + [metabase.lib.metadata.jvm :as lib.metadata.jvm] [metabase.test :as mt])) +(deftest ^:parallel fetch-field-test + (let [field (#'lib.metadata.jvm/fetch-instance :metadata/field (mt/id :categories :id))] + (is (not (me/humanize (mc/validate lib.metadata/ColumnMetadata field)))))) + (deftest ^:parallel saved-question-metadata-test - (let [card {:dataset_query {:database (mt/id) + (let [card {:dataset-query {:database (mt/id) :type :query :query {:source-table (mt/id :venues) :joins [{:fields :all @@ -17,7 +24,7 @@ [:field (mt/id :categories :id) {:join-alias "Cat"}]] :alias "Cat"}]}}} query (lib/saved-question-query - (metabase.lib.metadata.jvm/application-database-metadata-provider (mt/id)) + (lib.metadata.jvm/application-database-metadata-provider (mt/id)) card)] (is (=? [{:lib/desired-column-alias "ID"} {:lib/desired-column-alias "NAME"} diff --git a/test/metabase/lib/metadata_test.cljc b/test/metabase/lib/metadata_test.cljc index 5655f62e601..63c3f824847 100644 --- a/test/metabase/lib/metadata_test.cljc +++ b/test/metabase/lib/metadata_test.cljc @@ -33,11 +33,11 @@ (deftest ^:parallel stage-column-metadata-test (let [query (lib/saved-question-query meta/metadata-provider meta/saved-question)] (are [x] (=? {:lib/type :metadata/field - :display_name "CATEGORY_ID" + :display-name "CATEGORY_ID" :name "CATEGORY_ID" - :base_type :type/Integer - :effective_type :type/Integer - :semantic_type nil} + :base-type :type/Integer + :effective-type :type/Integer + :semantic-type nil} x) (lib.metadata/stage-column query "CATEGORY_ID") (lib.metadata/stage-column query -1 "CATEGORY_ID")))) diff --git a/test/metabase/lib/metric_test.cljc b/test/metabase/lib/metric_test.cljc index b71f66dcbe5..b2612f25eb9 100644 --- a/test/metabase/lib/metric_test.cljc +++ b/test/metabase/lib/metric_test.cljc @@ -40,6 +40,6 @@ (lib/aggregate [:metric {:lib/uuid (str (random-uuid))} "ga:totalEvents"]))] (is (=? [{:lib/type :metadata/field :name "metric" - :display_name "[Unknown Metric]" - :base_type :type/*}] + :display-name "[Unknown Metric]" + :base-type :type/*}] (lib.metadata.calculation/metadata query -1 query)))))) diff --git a/test/metabase/lib/order_by_test.cljc b/test/metabase/lib/order_by_test.cljc index bb83ca3d827..49027dcee67 100644 --- a/test/metabase/lib/order_by_test.cljc +++ b/test/metabase/lib/order_by_test.cljc @@ -51,15 +51,12 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :venues)} - {:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} - :order-by [[:asc - {:lib/uuid string?} - [:field {:lib/uuid string?} (meta/id :venues :id)]]]} - {:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?}}]} + {:lib/type :mbql.stage/mbql + :order-by [[:asc + {:lib/uuid string?} + [:field {:lib/uuid string?} (meta/id :venues :id)]]]} + {:lib/type :mbql.stage/mbql}]} (-> (lib/query meta/metadata-provider {:database (meta/id) :type :query :query {:source-query {:source-query {:source-table (meta/id :venues)}}}}) @@ -130,27 +127,27 @@ (lib/aggregate (lib/avg (lib/+ (lib/field "VENUES" "PRICE") 1))) (lib/breakout (lib/field "VENUES" "CATEGORY_ID")))] (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) - (is (=? [{:database_type "INTEGER" - :semantic_type :type/FK + (is (=? [{:database-type "INTEGER" + :semantic-type :type/FK :lib/type :metadata/field - :table_id (meta/id :venues) + :table-id (meta/id :venues) :name "CATEGORY_ID" - :has_field_values :none + :has-field-values :none :lib/source :source/breakouts - :fk_target_field_id (meta/id :categories :id) - :effective_type :type/Integer + :fk-target-field-id (meta/id :categories :id) + :effective-type :type/Integer :id (meta/id :venues :category-id) - :display_name "Category ID" - :base_type :type/Integer} + :display-name "Category ID" + :base-type :type/Integer} {:lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "sum_PRICE" - :display_name "Sum of Price" + :display-name "Sum of Price" :lib/source :source/aggregations} {:lib/type :metadata/field - :base_type :type/Float + :base-type :type/Float :name "avg_PRICE_plus_1" - :display_name "Average of Price + 1" + :display-name "Average of Price + 1" :lib/source :source/aggregations}] (lib/orderable-columns query))))))) @@ -162,8 +159,8 @@ (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) (is (=? [{:lib/type :metadata/field :name "Category ID + 1" - :display_name "Category ID + 1" - :base_type :type/Integer + :display-name "Category ID + 1" + :base-type :type/Integer :lib/source :source/breakouts}] (lib/orderable-columns query))))))) @@ -172,65 +169,65 @@ (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) (is (=? [{:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :id (meta/id :venues :id) - :table_id (meta/id :venues) - :base_type :type/BigInteger + :table-id (meta/id :venues) + :base-type :type/BigInteger :lib/source-column-alias "ID" :lib/desired-column-alias "ID"} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :id (meta/id :venues :name) - :table_id (meta/id :venues) - :base_type :type/Text + :table-id (meta/id :venues) + :base-type :type/Text :lib/source-column-alias "NAME" :lib/desired-column-alias "NAME"} {:lib/type :metadata/field :name "CATEGORY_ID" - :display_name "Category ID" + :display-name "Category ID" :id (meta/id :venues :category-id) - :table_id (meta/id :venues) + :table-id (meta/id :venues) :lib/source-column-alias "CATEGORY_ID" :lib/desired-column-alias "CATEGORY_ID"} {:lib/type :metadata/field :name "LATITUDE" - :display_name "Latitude" + :display-name "Latitude" :id (meta/id :venues :latitude) - :table_id (meta/id :venues) - :base_type :type/Float + :table-id (meta/id :venues) + :base-type :type/Float :lib/source-column-alias "LATITUDE" :lib/desired-column-alias "LATITUDE"} {:lib/type :metadata/field :name "LONGITUDE" - :display_name "Longitude" + :display-name "Longitude" :id (meta/id :venues :longitude) - :table_id (meta/id :venues) - :base_type :type/Float + :table-id (meta/id :venues) + :base-type :type/Float :lib/source-column-alias "LONGITUDE" :lib/desired-column-alias "LONGITUDE"} {:lib/type :metadata/field :name "PRICE" - :display_name "Price" + :display-name "Price" :id (meta/id :venues :price) - :table_id (meta/id :venues) - :base_type :type/Integer + :table-id (meta/id :venues) + :base-type :type/Integer :lib/source-column-alias "PRICE" :lib/desired-column-alias "PRICE"} {:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :id (meta/id :categories :id) - :table_id (meta/id :categories) - :base_type :type/BigInteger + :table-id (meta/id :categories) + :base-type :type/BigInteger :lib/source-column-alias "ID" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__ID"} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :id (meta/id :categories :name) - :table_id (meta/id :categories) - :base_type :type/Text + :table-id (meta/id :categories) + :base-type :type/Text :lib/source-column-alias "NAME" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__NAME"}] (lib/orderable-columns query)))))) @@ -247,9 +244,9 @@ {:id (meta/id :venues :longitude) :name "LONGITUDE"} {:id (meta/id :venues :price) :name "PRICE"} {:lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "Category ID + 1" - :display_name "Category ID + 1" + :display-name "Category ID + 1" :lib/source :source/expressions} {:id (meta/id :categories :id) :name "ID"} {:id (meta/id :categories :name) :name "NAME"}] @@ -289,18 +286,18 @@ {:id (meta/id :venues :price) :name "PRICE"} {:lib/type :metadata/field :name "ID" - :display_name "ID" + :display-name "ID" :source_alias "Cat" :id (meta/id :categories :id) - :table_id (meta/id :categories) - :base_type :type/BigInteger} + :table-id (meta/id :categories) + :base-type :type/BigInteger} {:lib/type :metadata/field :name "NAME" - :display_name "Name" + :display-name "Name" :source_alias "Cat" :id (meta/id :categories :name) - :table_id (meta/id :categories) - :base_type :type/Text}] + :table-id (meta/id :categories) + :base-type :type/Text}] (lib/orderable-columns query))))))) (deftest ^:parallel orderable-columns-source-card-test @@ -309,57 +306,57 @@ :let [query (varr)]] (testing (str (pr-str varr) \newline (lib.util/format "Query =\n%s" (u/pprint-to-str query))) (is (=? [{:name "USER_ID" - :display_name "User ID" - :base_type :type/Integer + :display-name "User ID" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "USER_ID"} {:name "count" - :display_name "Count" - :base_type :type/Integer + :display-name "Count" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "count"} {:name "ID" - :display_name "ID" - :base_type :type/BigInteger + :display-name "ID" + :base-type :type/BigInteger :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__ID"} {:name "NAME" - :display_name "Name" - :base_type :type/Text + :display-name "Name" + :base-type :type/Text :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__NAME"} {:name "LAST_LOGIN" - :display_name "Last Login" - :base_type :type/DateTime + :display-name "Last Login" + :base-type :type/DateTime :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__LAST_LOGIN"}] (lib/orderable-columns query))) (testing `lib/display-info (is (=? [{:name "USER_ID" - :display_name "User ID" - :table {:name "My Card", :display_name "My Card"} - :is_from_previous_stage false - :is_implicitly_joinable false} + :display-name "User ID" + :table {:name "My Card", :display-name "My Card"} + :is-from-previous-stage false + :is-implicitly-joinable false} {:name "count" - :display_name "Count" - :table {:name "My Card", :display_name "My Card"} - :is_from_previous_stage false - :is_implicitly_joinable false} + :display-name "Count" + :table {:name "My Card", :display-name "My Card"} + :is-from-previous-stage false + :is-implicitly-joinable false} {:name "ID" - :display_name "ID" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true} + :display-name "ID" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true} {:name "NAME" - :display_name "Name" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true} + :display-name "Name" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true} {:name "LAST_LOGIN" - :display_name "Last Login" - :table {:name "USERS", :display_name "Users"} - :is_from_previous_stage false - :is_implicitly_joinable true}] + :display-name "Last Login" + :table {:name "USERS", :display-name "Users"} + :is-from-previous-stage false + :is-implicitly-joinable true}] (for [col (lib/orderable-columns query)] (lib/display-info query col)))))))) @@ -369,8 +366,7 @@ (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :source-table (meta/id :venues) - :lib/options {:lib/uuid string?}}]} + :source-table (meta/id :venues)}]} query)) (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query)) (let [orderable-columns (lib/orderable-columns query) @@ -380,7 +376,6 @@ :database (meta/id) :stages [{:lib/type :mbql.stage/mbql :source-table (meta/id :venues) - :lib/options {:lib/uuid string?} :order-by [[:asc {:lib/uuid string?} [:field {:lib/uuid string? :base-type :type/Text} (meta/id :venues :name)]]]}]} @@ -397,7 +392,7 @@ (let [name-col (m/find-first #(= (:name %) "USER_ID") (lib/orderable-columns query))] (is (=? {:name "USER_ID" - :base_type :type/Integer} + :base-type :type/Integer} name-col)) (let [query' (lib/order-by query name-col)] (is (=? {:stages @@ -463,20 +458,20 @@ :name "ID" :lib/source :source/previous-stage :lib/type :metadata/field - :base_type :type/BigInteger - :effective_type :type/BigInteger - :display_name "ID" - :table_id (meta/id :venues) + :base-type :type/BigInteger + :effective-type :type/BigInteger + :display-name "ID" + :table-id (meta/id :venues) :lib/source-column-alias "ID" :lib/desired-column-alias "ID"} {:id (meta/id :categories :id) :name "ID" :lib/source :source/previous-stage :lib/type :metadata/field - :base_type :type/BigInteger - :effective_type :type/BigInteger - :display_name "ID" - :table_id (meta/id :categories) + :base-type :type/BigInteger + :effective-type :type/BigInteger + :display-name "ID" + :table-id (meta/id :categories) :lib/source-column-alias "Cat__ID" :lib/desired-column-alias "Cat__ID"}] (-> (lib/query-for-table-name meta/metadata-provider "VENUES") @@ -495,38 +490,38 @@ (deftest ^:parallel orderable-columns-exclude-already-sorted-columns-test (testing "orderable-columns should not return normal Fields already included in :order-by (#29807)" (let [query (lib/query-for-table-name meta/metadata-provider "VENUES")] - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query))) (let [query' (lib/order-by query (second (lib/orderable-columns query)))] (is (=? {:stages [{:order-by [[:asc {} [:field {} (meta/id :venues :name)]]]}]} query')) (is (=? [[:asc {} [:field {} (meta/id :venues :name)]]] (lib/order-bys query'))) - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query'))) (testing "Introduce a new stage" (let [query'' (lib/append-stage query')] - (is (=? [{:display_name "ID", :lib/source :source/previous-stage} - {:display_name "Name", :lib/source :source/previous-stage} - {:display_name "Category ID", :lib/source :source/previous-stage} - {:display_name "Latitude", :lib/source :source/previous-stage} - {:display_name "Longitude", :lib/source :source/previous-stage} - {:display_name "Price", :lib/source :source/previous-stage} - {:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/previous-stage} + {:display-name "Name", :lib/source :source/previous-stage} + {:display-name "Category ID", :lib/source :source/previous-stage} + {:display-name "Latitude", :lib/source :source/previous-stage} + {:display-name "Longitude", :lib/source :source/previous-stage} + {:display-name "Price", :lib/source :source/previous-stage} + {:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query''))))))))) (deftest ^:parallel orderable-columns-exclude-already-sorted-aggregation-test @@ -534,11 +529,11 @@ (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (lib/aggregate (lib/sum (lib/field (meta/id :venues :price)))) (lib/aggregate (lib/sum (lib/field (meta/id :venues :id)))))] - (is (=? [{:display_name "Sum of Price", :lib/source :source/aggregations} - {:display_name "Sum of ID", :lib/source :source/aggregations}] + (is (=? [{:display-name "Sum of Price", :lib/source :source/aggregations} + {:display-name "Sum of ID", :lib/source :source/aggregations}] (lib/orderable-columns query))) (let [query' (lib/order-by query (first (lib/orderable-columns query)))] - (is (=? [{:display_name "Sum of ID", :lib/source :source/aggregations}] + (is (=? [{:display-name "Sum of ID", :lib/source :source/aggregations}] (lib/orderable-columns query'))))))) (deftest ^:parallel orderable-columns-exclude-already-sorted-joined-columns-test @@ -559,26 +554,26 @@ [:field {} (meta/id :venues :category-id)] [:field {:join-alias "Cat"} (meta/id :categories :id)]]]}]}]} query)) - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} ;; implicitly joinable versions shouldn't be returned either, since we have an explicit join. - {:display_name "ID", :lib/source :source/joins} - {:display_name "Name", :lib/source :source/joins}] + {:display-name "ID", :lib/source :source/joins} + {:display-name "Name", :lib/source :source/joins}] (lib/orderable-columns query))) (let [query' (lib/order-by query (m/find-first #(and (= (:source_alias %) "Cat") - (= (:display_name %) "Name")) + (= (:display-name %) "Name")) (lib/orderable-columns query)))] - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/joins}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/joins}] (lib/orderable-columns query'))))))) (deftest ^:parallel orderable-columns-exclude-already-sorted-implicitly-joinable-columns-test @@ -593,39 +588,39 @@ query)) (is (= "Venues, Sorted by Categories → Name ascending" (lib/describe-query query))) - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query)))))) (deftest ^:parallel orderable-columns-exclude-already-sorted-expression-test (testing "orderable-columns should not return expressions that are already in :order-by (#29807)" (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (lib/expression "My Expression" (lib/+ 2 3)))] - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "My Expression", :lib/source :source/expressions} - {:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "My Expression", :lib/source :source/expressions} + {:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query))) - (let [query' (lib/order-by query (m/find-first #(= (:display_name %) "My Expression") + (let [query' (lib/order-by query (m/find-first #(= (:display-name %) "My Expression") (lib/orderable-columns query)))] - (is (=? [{:display_name "ID", :lib/source :source/table-defaults} - {:display_name "Name", :lib/source :source/table-defaults} - {:display_name "Category ID", :lib/source :source/table-defaults} - {:display_name "Latitude", :lib/source :source/table-defaults} - {:display_name "Longitude", :lib/source :source/table-defaults} - {:display_name "Price", :lib/source :source/table-defaults} - {:display_name "ID", :lib/source :source/implicitly-joinable} - {:display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:display-name "ID", :lib/source :source/table-defaults} + {:display-name "Name", :lib/source :source/table-defaults} + {:display-name "Category ID", :lib/source :source/table-defaults} + {:display-name "Latitude", :lib/source :source/table-defaults} + {:display-name "Longitude", :lib/source :source/table-defaults} + {:display-name "Price", :lib/source :source/table-defaults} + {:display-name "ID", :lib/source :source/implicitly-joinable} + {:display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query'))))))) (deftest ^:parallel order-by-aggregation-test @@ -634,8 +629,8 @@ (lib/aggregate (lib/avg (lib/+ (lib/field "VENUES" "PRICE") 1)))) orderable-columns (lib/orderable-columns query)] (is (=? [{:lib/type :metadata/field - :base_type :type/Float - :display_name "Average of Price + 1" + :base-type :type/Float + :display-name "Average of Price + 1" :lib/source :source/aggregations :metabase.lib.aggregation/aggregation-index 0}] orderable-columns)) @@ -647,7 +642,7 @@ query')) (is (=? [[:asc {} [:aggregation {:effective-type :type/Float} 0]]] (lib/order-bys query'))) - (is (=? [{:display_name "Average of Price + 1" + (is (=? [{:display-name "Average of Price + 1" :direction :asc}] (map (partial lib/display-info query') (lib/order-bys query')))) (is (= "Venues, Average of Price + 1, Sorted by Average of Price + 1 ascending" @@ -668,15 +663,15 @@ (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES") (lib/expression "expr" (lib/absolute-datetime "2020" :month)) (lib/with-fields [(lib/field "VENUES" "ID")]))] - (is (=? [{:id (meta/id :venues :id), :name "ID", :display_name "ID", :lib/source :source/table-defaults} - {:id (meta/id :venues :name), :name "NAME", :display_name "Name", :lib/source :source/table-defaults} - {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display_name "Category ID", :lib/source :source/table-defaults} - {:id (meta/id :venues :latitude), :name "LATITUDE", :display_name "Latitude", :lib/source :source/table-defaults} - {:id (meta/id :venues :longitude), :name "LONGITUDE", :display_name "Longitude", :lib/source :source/table-defaults} - {:id (meta/id :venues :price), :name "PRICE", :display_name "Price", :lib/source :source/table-defaults} - {:name "expr", :display_name "expr", :lib/source :source/expressions} - {:id (meta/id :categories :id), :name "ID", :display_name "ID", :lib/source :source/implicitly-joinable} - {:id (meta/id :categories :name), :name "NAME", :display_name "Name", :lib/source :source/implicitly-joinable}] + (is (=? [{:id (meta/id :venues :id), :name "ID", :display-name "ID", :lib/source :source/table-defaults} + {:id (meta/id :venues :name), :name "NAME", :display-name "Name", :lib/source :source/table-defaults} + {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display-name "Category ID", :lib/source :source/table-defaults} + {:id (meta/id :venues :latitude), :name "LATITUDE", :display-name "Latitude", :lib/source :source/table-defaults} + {:id (meta/id :venues :longitude), :name "LONGITUDE", :display-name "Longitude", :lib/source :source/table-defaults} + {:id (meta/id :venues :price), :name "PRICE", :display-name "Price", :lib/source :source/table-defaults} + {:name "expr", :display-name "expr", :lib/source :source/expressions} + {:id (meta/id :categories :id), :name "ID", :display-name "ID", :lib/source :source/implicitly-joinable} + {:id (meta/id :categories :name), :name "NAME", :display-name "Name", :lib/source :source/implicitly-joinable}] (lib/orderable-columns query))) (let [expr (m/find-first #(= (:name %) "expr") (lib/orderable-columns query))] (is (=? {:lib/type :metadata/field @@ -692,29 +687,29 @@ (deftest ^:parallel orderable-columns-display-info-test (let [query (lib/query-for-table-name meta/metadata-provider "VENUES")] - (is (=? [{:semantic_type :type/PK - :is_calculated false - :table {:name "VENUES", :display_name "Venues" :is_source_table true} + (is (=? [{:semantic-type :type/PK + :is-calculated false + :table {:name "VENUES", :display-name "Venues" :is-source-table true} :name "ID" - :is_from_previous_stage false - :is_implicitly_joinable false - :effective_type :type/BigInteger - :is_from_join false - :display_name "ID"} - {:display_name "Name" - :table {:is_source_table true}} - {:display_name "Category ID" - :table {:is_source_table true}} - {:display_name "Latitude" - :table {:is_source_table true}} - {:display_name "Longitude" - :table {:is_source_table true}} - {:display_name "Price" - :table {:is_source_table true}} - {:display_name "ID" - :table {:name "CATEGORIES" :display_name "Categories" :is_source_table false}} - {:display_name "Name" - :table {:name "CATEGORIES" :display_name "Categories" :is_source_table false}}] + :is-from-previous-stage false + :is-implicitly-joinable false + :effective-type :type/BigInteger + :is-from-join false + :display-name "ID"} + {:display-name "Name" + :table {:is-source-table true}} + {:display-name "Category ID" + :table {:is-source-table true}} + {:display-name "Latitude" + :table {:is-source-table true}} + {:display-name "Longitude" + :table {:is-source-table true}} + {:display-name "Price" + :table {:is-source-table true}} + {:display-name "ID" + :table {:name "CATEGORIES" :display-name "Categories" :is-source-table false}} + {:display-name "Name" + :table {:name "CATEGORIES" :display-name "Categories" :is-source-table false}}] (for [col (lib/orderable-columns query)] (lib/display-info query col)))))) @@ -725,10 +720,10 @@ _ (is (some? col)) query' (lib/order-by query col)] (is (=? [{:name "NAME" - :display_name "Name" - :semantic_type :type/Name - :effective_type :type/Text - :table {:name "VENUES", :display_name "Venues"} + :display-name "Name" + :semantic-type :type/Name + :effective-type :type/Text + :table {:name "VENUES", :display-name "Venues"} :direction :asc}] (for [order-by (lib/order-bys query')] (lib/display-info query' order-by)))))) diff --git a/test/metabase/lib/query_test.cljc b/test/metabase/lib/query_test.cljc index 0c2f9e13307..c04be756985 100644 --- a/test/metabase/lib/query_test.cljc +++ b/test/metabase/lib/query_test.cljc @@ -35,10 +35,10 @@ :stages [{:lib/type :mbql.stage/native :lib/options {:lib/uuid string?} :native "SELECT * FROM VENUES;"}]} - (lib/native-query meta/metadata-provider meta/results-metadata "SELECT * FROM VENUES;")))) + (lib/native-query meta/metadata-provider meta/qp-results-metadata "SELECT * FROM VENUES;")))) (deftest ^:parallel native-query-suggested-name-test - (let [query (lib/native-query meta/metadata-provider meta/results-metadata "SELECT * FROM VENUES;")] + (let [query (lib/native-query meta/metadata-provider meta/qp-results-metadata "SELECT * FROM VENUES;")] (is (= "Native query" (lib.metadata.calculation/describe-query query))) (is (nil? (lib.metadata.calculation/suggested-name query))))) @@ -49,21 +49,18 @@ :stages [{:lib/type :mbql.stage/native :native "SELECT * FROM VENUES;"}]} (lib/saved-question-query meta/metadata-provider - {:dataset_query {:database (meta/id) + {:dataset-query {:database (meta/id) :type :native :native {:query "SELECT * FROM VENUES;"}} - :result_metadata meta/results-metadata})))) + :result-metadata meta/card-results-metadata})))) (deftest ^:parallel notebook-query-test (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?} :source-table (meta/id :venues)} - {:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?}} - {:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid string?}}]} + {:lib/type :mbql.stage/mbql} + {:lib/type :mbql.stage/mbql}]} (lib/query meta/metadata-provider {:database (meta/id) :type :query :query {:source-query {:source-query {:source-table (meta/id :venues)}}}})))) diff --git a/test/metabase/lib/stage_test.cljc b/test/metabase/lib/stage_test.cljc index 3fb527c3020..c60ad86bfdb 100644 --- a/test/metabase/lib/stage_test.cljc +++ b/test/metabase/lib/stage_test.cljc @@ -40,14 +40,14 @@ {} 0.8 [:avg {} (lib.tu/field-clause :venues :price)]]]})] - (is (=? [{:base_type :type/Float + (is (=? [{:base-type :type/Float :name "0_8_times_avg_PRICE" - :display_name "0.8 × Average of Price" + :display-name "0.8 × Average of Price" :lib/source-column-alias "0_8_times_avg_PRICE" :lib/desired-column-alias "0_8_times_avg_PRICE"} - {:base_type :type/Float + {:base-type :type/Float :name "0_8_times_avg_PRICE" - :display_name "0.8 × Average of Price" + :display-name "0.8 × Average of Price" :lib/source-column-alias "0_8_times_avg_PRICE" :lib/desired-column-alias "0_8_times_avg_PRICE_2"}] (lib.metadata.calculation/metadata query -1 query))))))) @@ -124,14 +124,14 @@ :name "ID" :lib/source :source/joins :source_alias "Cat" - :display_name "ID" + :display-name "ID" :lib/source-column-alias "ID" :lib/desired-column-alias "Cat__ID"} {:id (meta/id :categories :name) :name "NAME" :lib/source :source/joins :source_alias "Cat" - :display_name "Name" + :display-name "Name" :lib/source-column-alias "NAME" :lib/desired-column-alias "Cat__NAME"}] metadata)) @@ -153,9 +153,9 @@ (let [query (query-with-expressions) id-plus-1 (first (lib/expressions query))] (is (=? {:lib/type :metadata/field - :base_type :type/Integer + :base-type :type/Integer :name "ID + 1" - :display_name "ID + 1" + :display-name "ID + 1" :lib/source :source/expressions} id-plus-1)) (let [query' (-> query @@ -182,28 +182,28 @@ :let [query (varr)]] (testing (pr-str varr) (is (=? [{:name "USER_ID" - :display_name "User ID" - :base_type :type/Integer + :display-name "User ID" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "USER_ID"} {:name "count" - :display_name "Count" - :base_type :type/Integer + :display-name "Count" + :base-type :type/Integer :lib/source :source/card :lib/desired-column-alias "count"} {:name "ID" - :display_name "ID" - :base_type :type/BigInteger + :display-name "ID" + :base-type :type/BigInteger :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__ID"} {:name "NAME" - :display_name "Name" - :base_type :type/Text + :display-name "Name" + :base-type :type/Text :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__NAME"} {:name "LAST_LOGIN" - :display_name "Last Login" - :base_type :type/DateTime + :display-name "Last Login" + :base-type :type/DateTime :lib/source :source/implicitly-joinable :lib/desired-column-alias "USERS__via__USER_ID__LAST_LOGIN"}] (lib.metadata.calculation/visible-columns query))))))) diff --git a/test/metabase/lib/temporal_bucket_test.cljc b/test/metabase/lib/temporal_bucket_test.cljc index 2f311201c26..4219ec1396f 100644 --- a/test/metabase/lib/temporal_bucket_test.cljc +++ b/test/metabase/lib/temporal_bucket_test.cljc @@ -54,7 +54,8 @@ (lib.temporal-bucket/describe-temporal-unit nil))) (is (= "Day" (lib.temporal-bucket/describe-temporal-unit :day) - (lib.temporal-bucket/describe-temporal-unit 1 :day))) + (lib.temporal-bucket/describe-temporal-unit 1 :day) + (lib.temporal-bucket/describe-temporal-unit -1 :day))) (is (= "Days" (lib.temporal-bucket/describe-temporal-unit 2 :day))) (is (= "Unknown unit" diff --git a/test/metabase/lib/test_metadata.cljc b/test/metabase/lib/test_metadata.cljc index cc4cff48ae7..6a9b950d1fc 100644 --- a/test/metabase/lib/test_metadata.cljc +++ b/test/metabase/lib/test_metadata.cljc @@ -137,60 +137,60 @@ (defmethod field-metadata [:categories :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :categories) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :categories) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values :none + :fingerprint-version 0 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :categories :id) :position 0 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:categories :name] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Name - :table_id (id :categories) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Name + :table-id (id :categories) + :coercion-strategy nil :name "NAME" - :fingerprint_version 5 - :has_field_values :list + :fingerprint-version 5 + :has-field-values :list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :categories :name) :position 1 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Name" - :database_position 1 - :database_required true + :preview-display true + :display-name "Name" + :database-position 1 + :database-required true :fingerprint {:global {:distinct-count 75, :nil% 0.0} :type {:type/Text {:percent-json 0.0 @@ -198,16 +198,16 @@ :percent-email 0.0 :percent-state 0.0 :average-length 8.333333333333334}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :categories [_table-name] {:description nil - :entity_type :entity/GenericTable + :entity-type :entity/GenericTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "CATEGORIES" :fields [(field-metadata :categories :id) (field-metadata :categories :name)] @@ -215,146 +215,146 @@ :segments [] :active true :id (id :categories) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Categories" + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Categories" :metrics [] - :points_of_interest nil + :points-of-interest nil :lib/type :metadata/table}) (defmethod field-metadata [:checkins :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :checkins) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :checkins) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values :none + :fingerprint-version 0 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :checkins :id) :position 0 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:checkins :date] [_table-name _field-name] {:description nil - :database_type "DATE" - :semantic_type nil - :table_id (id :checkins) - :coercion_strategy nil + :database-type "DATE" + :semantic-type nil + :table-id (id :checkins) + :coercion-strategy nil :name "DATE" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Date + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Date :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :checkins :date) :position 1 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Date" - :database_position 1 - :database_required false + :preview-display true + :display-name "Date" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 618, :nil% 0.0} :type #:type{:DateTime {:earliest "2013-01-03", :latest "2015-12-29"}}} - :base_type :type/Date - :points_of_interest nil + :base-type :type/Date + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:checkins :user-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :checkins) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :checkins) + :coercion-strategy nil :name "USER_ID" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id (id :users :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :users :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :checkins :user-id) :position 2 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "User ID" - :database_position 2 - :database_required false + :preview-display true + :display-name "User ID" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 15, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:checkins :venue-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :checkins) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :checkins) + :coercion-strategy nil :name "VENUE_ID" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id (id :venues :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :venues :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :checkins :venue-id) :position 3 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Venue ID" - :database_position 3 - :database_required false + :preview-display true + :display-name "Venue ID" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 100, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :checkins [_table-name] {:description nil - :entity_type :entity/EventTable + :entity-type :entity/EventTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "CHECKINS" :fields [(field-metadata :checkins :id) (field-metadata :checkins :date) @@ -364,72 +364,72 @@ :segments [] :active true :id (id :checkins) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Checkins" + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Checkins" :metrics [] - :points_of_interest nil + :points-of-interest nil :lib/type :metadata/table}) (defmethod field-metadata [:users :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :users) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :users) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values :none + :fingerprint-version 0 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :users :id) :position 0 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:users :name] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Name - :table_id (id :users) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Name + :table-id (id :users) + :coercion-strategy nil :name "NAME" - :fingerprint_version 5 - :has_field_values :list + :fingerprint-version 5 + :has-field-values :list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :users :name) :position 1 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Name" - :database_position 1 - :database_required false + :preview-display true + :display-name "Name" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 15, :nil% 0.0} :type #:type{:Text {:percent-json 0.0 @@ -437,68 +437,68 @@ :percent-email 0.0 :percent-state 0.0 :average-length 13.266666666666667}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:users :last-login] [_table-name _field-name] {:description nil - :database_type "TIMESTAMP" - :semantic_type nil - :table_id (id :users) - :coercion_strategy nil + :database-type "TIMESTAMP" + :semantic-type nil + :table-id (id :users) + :coercion-strategy nil :name "LAST_LOGIN" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/DateTime + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/DateTime :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :users :last-login) :position 2 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Last Login" - :database_position 2 - :database_required false + :preview-display true + :display-name "Last Login" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 15, :nil% 0.0} :type #:type{:DateTime {:earliest "2014-01-01T08:30:00Z", :latest "2014-12-05T15:15:00Z"}}} - :base_type :type/DateTime - :points_of_interest nil + :base-type :type/DateTime + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:users :password] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Category - :table_id (id :users) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Category + :table-id (id :users) + :coercion-strategy nil :name "PASSWORD" - :fingerprint_version 5 - :has_field_values :list + :fingerprint-version 5 + :has-field-values :list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :users :password) :position 3 - :visibility_type :sensitive + :visibility-type :sensitive :target nil - :preview_display true - :display_name "Password" - :database_position 3 - :database_required false + :preview-display true + :display-name "Password" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 15, :nil% 0.0} :type #:type{:Text {:percent-json 0.0 @@ -506,16 +506,16 @@ :percent-email 0.0 :percent-state 0.0 :average-length 36.0}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :users [_table-name] {:description nil - :entity_type :entity/UserTable + :entity-type :entity/UserTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "USERS" :fields [(field-metadata :users :id) (field-metadata :users :name) @@ -525,72 +525,72 @@ :segments [] :active true :id (id :users) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Users" + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Users" :metrics [] - :points_of_interest nil + :points-of-interest nil :lib/type :metadata/table}) (defmethod field-metadata [:venues :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :venues) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :venues) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values :none + :fingerprint-version 0 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :id) :position 0 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:venues :name] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Name - :table_id (id :venues) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Name + :table-id (id :venues) + :coercion-strategy nil :name "NAME" - :fingerprint_version 5 - :has_field_values :list + :fingerprint-version 5 + :has-field-values :list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :name) :position 1 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Name" - :database_position 1 - :database_required false + :preview-display true + :display-name "Name" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 100, :nil% 0.0} :type #:type{:Text {:percent-json 0.0 @@ -598,67 +598,67 @@ :percent-email 0.0 :percent-state 0.0 :average-length 15.63}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:venues :category-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :venues) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :venues) + :coercion-strategy nil :name "CATEGORY_ID" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id (id :categories :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :categories :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :category-id) :position 2 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Category ID" - :database_position 2 - :database_required false + :preview-display true + :display-name "Category ID" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 28, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:venues :latitude] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Latitude - :table_id (id :venues) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Latitude + :table-id (id :venues) + :coercion-strategy nil :name "LATITUDE" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :latitude) :position 3 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Latitude" - :database_position 3 - :database_required false + :preview-display true + :display-name "Latitude" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 94, :nil% 0.0} :type #:type{:Number {:min 10.0646 @@ -667,36 +667,36 @@ :max 40.7794 :sd 3.4346725397190827 :avg 35.505891999999996}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:venues :longitude] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Longitude - :table_id (id :venues) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Longitude + :table-id (id :venues) + :coercion-strategy nil :name "LONGITUDE" - :fingerprint_version 5 - :has_field_values :none + :fingerprint-version 5 + :has-field-values :none :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :longitude) :position 4 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Longitude" - :database_position 4 - :database_required false + :preview-display true + :display-name "Longitude" + :database-position 4 + :database-required false :fingerprint {:global {:distinct-count 84, :nil% 0.0} :type @@ -707,36 +707,36 @@ :max -73.9533 :sd 14.162810671348238 :avg -115.99848699999998}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:venues :price] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/Category - :table_id (id :venues) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/Category + :table-id (id :venues) + :coercion-strategy nil :name "PRICE" - :fingerprint_version 5 - :has_field_values :list + :fingerprint-version 5 + :has-field-values :list :settings {:is_priceless true} :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :venues :price) :position 5 - :visibility_type :normal + :visibility-type :normal :target nil - :preview_display true - :display_name "Price" - :database_position 5 - :database_required false + :preview-display true + :display-name "Price" + :database-position 5 + :database-required false :fingerprint {:global {:distinct-count 4, :nil% 0.0} :type #:type{:Number {:min 1.0 @@ -745,16 +745,16 @@ :max 4.0 :sd 0.7713951678941896 :avg 2.03}}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :venues [_table-name] {:description nil - :entity_type :entity/GenericTable + :entity-type :entity/GenericTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "VENUES" :fields [(field-metadata :venues :id) (field-metadata :venues :name) @@ -766,72 +766,72 @@ :segments [] :active true :id (id :venues) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Venues" + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Venues" :metrics [] - :points_of_interest nil + :points-of-interest nil :lib/type :metadata/table}) (defmethod field-metadata [:products :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :products) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :products) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values nil + :fingerprint-version 0 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :id) - :database_is_auto_increment true + :database-is-auto-increment true :position 0 - :visibility_type :normal - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :rating] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Score - :table_id (id :products) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Score + :table-id (id :products) + :coercion-strategy nil :name "RATING" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :rating) - :database_is_auto_increment false + :database-is-auto-increment false :position 6 - :visibility_type :normal - :preview_display true - :display_name "Rating" - :database_position 6 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Rating" + :database-position 6 + :database-required false :fingerprint {:global {:distinct-count 23, :nil% 0.0} :type {:type/Number {:min 0.0 :q1 3.5120465053408525 @@ -839,72 +839,72 @@ :max 5.0 :sd 1.3605488657451452 :avg 3.4715}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :category] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Category - :table_id (id :products) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Category + :table-id (id :products) + :coercion-strategy nil :name "CATEGORY" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :category) - :database_is_auto_increment false + :database-is-auto-increment false :position 3 - :visibility_type :normal - :preview_display true - :display_name "Category" - :database_position 3 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Category" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 4, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 6.375}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :price] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type nil - :table_id (id :products) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type nil + :table-id (id :products) + :coercion-strategy nil :name "PRICE" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :price) - :database_is_auto_increment false + :database-is-auto-increment false :position 5 - :visibility_type :normal - :preview_display true - :display_name "Price" - :database_position 5 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Price" + :database-position 5 + :database-required false :fingerprint {:global {:distinct-count 168, :nil% 0.0} :type {:type/Number {:min 15.69 :q1 37.139492751669884 @@ -912,167 +912,167 @@ :max 98.82 :sd 21.711152906916283 :avg 55.746399999999994}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :title] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Title - :table_id (id :products) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Title + :table-id (id :products) + :coercion-strategy nil :name "TITLE" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :title) - :database_is_auto_increment false + :database-is-auto-increment false :position 2 - :visibility_type :normal - :preview_display true - :display_name "Title" - :database_position 2 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Title" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 199, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 21.495}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :created-at] [_table-name _field-name] {:description nil - :database_type "TIMESTAMP WITH TIME ZONE" - :semantic_type :type/CreationTimestamp - :table_id (id :products) - :coercion_strategy nil + :database-type "TIMESTAMP WITH TIME ZONE" + :semantic-type :type/CreationTimestamp + :table-id (id :products) + :coercion-strategy nil :name "CREATED_AT" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/DateTimeWithLocalTZ + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/DateTimeWithLocalTZ :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :created-at) - :database_is_auto_increment false + :database-is-auto-increment false :position 7 - :visibility_type :normal - :preview_display true - :display_name "Created At" - :database_position 7 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Created At" + :database-position 7 + :database-required false :fingerprint {:global {:distinct-count 200, :nil% 0.0} :type {:type/DateTime {:earliest "2016-04-26T19:29:55.147Z" :latest "2019-04-15T13:34:19.931Z"}}} - :base_type :type/DateTimeWithLocalTZ - :points_of_interest nil + :base-type :type/DateTimeWithLocalTZ + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :vendor] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Company - :table_id (id :products) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Company + :table-id (id :products) + :coercion-strategy nil :name "VENDOR" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :vendor) - :database_is_auto_increment false + :database-is-auto-increment false :position 4 - :visibility_type :normal - :preview_display true - :display_name "Vendor" - :database_position 4 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Vendor" + :database-position 4 + :database-required false :fingerprint {:global {:distinct-count 200, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 20.6}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:products :ean] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :products) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :products) + :coercion-strategy nil :name "EAN" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :products :ean) - :database_is_auto_increment false + :database-is-auto-increment false :position 1 - :visibility_type :normal - :preview_display true - :display_name "Ean" - :database_position 1 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Ean" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 200, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 13.0}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :products [_table-name] {:description nil - :entity_type :entity/ProductTable + :entity-type :entity/ProductTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "PRODUCTS" :caveats nil :active true :id (id :products) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Products" - :points_of_interest nil + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Products" + :points-of-interest nil :lib/type :metadata/table :fields [(field-metadata :products :id) (field-metadata :products :rating) @@ -1086,60 +1086,60 @@ (defmethod field-metadata [:orders :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :orders) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :orders) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values nil + :fingerprint-version 0 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :id) - :database_is_auto_increment true + :database-is-auto-increment true :position 0 - :visibility_type :normal - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :subtotal] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type nil - :table_id (id :orders) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type nil + :table-id (id :orders) + :coercion-strategy nil :name "SUBTOTAL" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :subtotal) - :database_is_auto_increment false + :database-is-auto-increment false :position 3 - :visibility_type :normal - :preview_display true - :display_name "Subtotal" - :database_position 3 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Subtotal" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 334, :nil% 0.0} :type {:type/Number {:min 15.69 @@ -1148,36 +1148,36 @@ :max 148.23 :sd 32.536819823931104 :avg 77.012717}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :total] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type nil - :table_id (id :orders) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type nil + :table-id (id :orders) + :coercion-strategy nil :name "TOTAL" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :total) - :database_is_auto_increment false + :database-is-auto-increment false :position 5 - :visibility_type :normal - :preview_display true - :display_name "Total" - :database_position 5 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Total" + :database-position 5 + :database-required false :fingerprint {:global {:distinct-count 3710, :nil% 0.0} :type {:type/Number {:min 8.94 @@ -1186,36 +1186,36 @@ :max 159.35 :sd 34.264752087910324 :avg 80.35850400000001}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :tax] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type nil - :table_id (id :orders) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type nil + :table-id (id :orders) + :coercion-strategy nil :name "TAX" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :tax) - :database_is_auto_increment false + :database-is-auto-increment false :position 4 - :visibility_type :normal - :preview_display true - :display_name "Tax" - :database_position 4 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Tax" + :database-position 4 + :database-required false :fingerprint {:global {:distinct-count 797, :nil% 0.0} :type {:type/Number {:min 0.0 @@ -1224,36 +1224,36 @@ :max 11.12 :sd 2.3206651358900316 :avg 3.8722100000000004}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :discount] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Discount - :table_id (id :orders) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Discount + :table-id (id :orders) + :coercion-strategy nil :name "DISCOUNT" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :discount) - :database_is_auto_increment false + :database-is-auto-increment false :position 6 - :visibility_type :normal - :preview_display true - :display_name "Discount" - :database_position 6 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Discount" + :database-position 6 + :database-required false :fingerprint {:global {:distinct-count 479, :nil% 0.898} :type {:type/Number {:min 0.17 :q1 2.978591571097236 @@ -1261,36 +1261,36 @@ :max 61.7 :sd 3.053736975739119 :avg 5.161009803921569}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :quantity] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/Quantity - :table_id (id :orders) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/Quantity + :table-id (id :orders) + :coercion-strategy nil :name "QUANTITY" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :quantity) - :database_is_auto_increment false + :database-is-auto-increment false :position 8 - :visibility_type :normal - :preview_display true - :display_name "Quantity" - :database_position 8 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Quantity" + :database-position 8 + :database-required false :fingerprint {:global {:distinct-count 62, :nil% 0.0} :type {:type/Number {:min 0.0 :q1 1.755882607764982 @@ -1298,121 +1298,121 @@ :max 100.0 :sd 4.214258386403798 :avg 3.7015}}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :created-at] [_table-name _field-name] {:description nil - :database_type "TIMESTAMP WITH TIME ZONE" - :semantic_type :type/CreationTimestamp - :table_id (id :orders) - :coercion_strategy nil + :database-type "TIMESTAMP WITH TIME ZONE" + :semantic-type :type/CreationTimestamp + :table-id (id :orders) + :coercion-strategy nil :name "CREATED_AT" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/DateTimeWithLocalTZ + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/DateTimeWithLocalTZ :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :created-at) - :database_is_auto_increment false + :database-is-auto-increment false :position 7 - :visibility_type :normal - :preview_display true - :display_name "Created At" - :database_position 7 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Created At" + :database-position 7 + :database-required false :fingerprint {:global {:distinct-count 10000, :nil% 0.0} :type {:type/DateTime {:earliest "2016-04-30T18:56:13.352Z" :latest "2020-04-19T14:07:15.657Z"}}} - :base_type :type/DateTimeWithLocalTZ - :points_of_interest nil + :base-type :type/DateTimeWithLocalTZ + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :product-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :orders) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :orders) + :coercion-strategy nil :name "PRODUCT_ID" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id (id :products :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :products :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :product-id) - :database_is_auto_increment false + :database-is-auto-increment false :position 2 - :visibility_type :normal - :preview_display true - :display_name "Product ID" - :database_position 2 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Product ID" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 200, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:orders :user-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :orders) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :orders) + :coercion-strategy nil :name "USER_ID" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id (id :people :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :people :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :orders :user-id) - :database_is_auto_increment false + :database-is-auto-increment false :position 1 - :visibility_type :normal - :preview_display true - :display_name "User ID" - :database_position 1 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "User ID" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 929, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :orders [_table-name] {:description nil - :entity_type :entity/TransactionTable + :entity-type :entity/TransactionTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "ORDERS" :caveats nil :active true :id (id :orders) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Orders" - :points_of_interest nil + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Orders" + :points-of-interest nil :lib/type :metadata/table :fields [(field-metadata :orders :id) (field-metadata :orders :subtotal) @@ -1427,276 +1427,276 @@ (defmethod field-metadata [:people :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :people) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :people) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values nil + :fingerprint-version 0 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :id) - :database_is_auto_increment true + :database-is-auto-increment true :position 0 - :visibility_type :normal - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :state] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/State - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/State + :table-id (id :people) + :coercion-strategy nil :name "STATE" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :state) - :database_is_auto_increment false + :database-is-auto-increment false :position 7 - :visibility_type :normal - :preview_display true - :display_name "State" - :database_position 7 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "State" + :database-position 7 + :database-required false :fingerprint {:global {:distinct-count 49, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 1.0 :average-length 2.0}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :city] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/City - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/City + :table-id (id :people) + :coercion-strategy nil :name "CITY" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :city) - :database_is_auto_increment false + :database-is-auto-increment false :position 5 - :visibility_type :normal - :preview_display true - :display_name "City" - :database_position 5 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "City" + :database-position 5 + :database-required false :fingerprint {:global {:distinct-count 1966, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.002 :average-length 8.284}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :address] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :people) + :coercion-strategy nil :name "ADDRESS" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :address) - :database_is_auto_increment false + :database-is-auto-increment false :position 1 - :visibility_type :normal - :preview_display true - :display_name "Address" - :database_position 1 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Address" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 2490, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 20.85}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :name] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Name - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Name + :table-id (id :people) + :coercion-strategy nil :name "NAME" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :name) - :database_is_auto_increment false + :database-is-auto-increment false :position 4 - :visibility_type :normal - :preview_display true - :display_name "Name" - :database_position 4 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Name" + :database-position 4 + :database-required false :fingerprint {:global {:distinct-count 2499, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 13.532}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :source] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Source - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Source + :table-id (id :people) + :coercion-strategy nil :name "SOURCE" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :source) - :database_is_auto_increment false + :database-is-auto-increment false :position 8 - :visibility_type :normal - :preview_display true - :display_name "Source" - :database_position 8 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Source" + :database-position 8 + :database-required false :fingerprint {:global {:distinct-count 5, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 7.4084}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :zip] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :people) + :coercion-strategy nil :name "ZIP" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :zip) - :database_is_auto_increment false + :database-is-auto-increment false :position 10 - :visibility_type :normal - :preview_display true - :display_name "Zip" - :database_position 10 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Zip" + :database-position 10 + :database-required false :fingerprint {:global {:distinct-count 2234, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 5.0}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :latitude] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Latitude - :table_id (id :people) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Latitude + :table-id (id :people) + :coercion-strategy nil :name "LATITUDE" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :latitude) - :database_is_auto_increment false + :database-is-auto-increment false :position 11 - :visibility_type :normal - :preview_display true - :display_name "Latitude" - :database_position 11 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Latitude" + :database-position 11 + :database-required false :fingerprint {:global {:distinct-count 2491, :nil% 0.0} :type {:type/Number {:min 25.775827 :q1 35.302705923023126 @@ -1704,105 +1704,105 @@ :max 70.6355001 :sd 6.390832341883712 :avg 39.87934670484002}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :password] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :people) + :coercion-strategy nil :name "PASSWORD" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :password) - :database_is_auto_increment false + :database-is-auto-increment false :position 3 - :visibility_type :normal - :preview_display true - :display_name "Password" - :database_position 3 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Password" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 2500, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 36.0}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :birth-date] [_table-name _field-name] {:description nil - :database_type "DATE" - :semantic_type nil - :table_id (id :people) - :coercion_strategy nil + :database-type "DATE" + :semantic-type nil + :table-id (id :people) + :coercion-strategy nil :name "BIRTH_DATE" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Date + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Date :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :birth-date) - :database_is_auto_increment false + :database-is-auto-increment false :position 9 - :visibility_type :normal - :preview_display true - :display_name "Birth Date" - :database_position 9 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Birth Date" + :database-position 9 + :database-required false :fingerprint {:global {:distinct-count 2308, :nil% 0.0} :type {:type/DateTime {:earliest "1958-04-26", :latest "2000-04-03"}}} - :base_type :type/Date - :points_of_interest nil + :base-type :type/Date + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :longitude] [_table-name _field-name] {:description nil - :database_type "DOUBLE PRECISION" - :semantic_type :type/Longitude - :table_id (id :people) - :coercion_strategy nil + :database-type "DOUBLE PRECISION" + :semantic-type :type/Longitude + :table-id (id :people) + :coercion-strategy nil :name "LONGITUDE" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Float + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Float :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :longitude) - :database_is_auto_increment false + :database-is-auto-increment false :position 6 - :visibility_type :normal - :preview_display true - :display_name "Longitude" - :database_position 6 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Longitude" + :database-position 6 + :database-required false :fingerprint {:global {:distinct-count 2491, :nil% 0.0} :type {:type/Number {:min -166.5425726 :q1 -101.58350792373135 @@ -1810,95 +1810,95 @@ :max -67.96735199999999 :sd 15.399698968175663 :avg -95.18741780363999}}} - :base_type :type/Float - :points_of_interest nil + :base-type :type/Float + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :email] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type :type/Email - :table_id (id :people) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type :type/Email + :table-id (id :people) + :coercion-strategy nil :name "EMAIL" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :email) - :database_is_auto_increment false + :database-is-auto-increment false :position 2 - :visibility_type :normal - :preview_display true - :display_name "Email" - :database_position 2 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Email" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 2500, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 1.0 :percent-state 0.0 :average-length 24.1824}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:people :created-at] [_table-name _field-name] {:description nil - :database_type "TIMESTAMP WITH TIME ZONE" - :semantic_type :type/CreationTimestamp - :table_id (id :people) - :coercion_strategy nil + :database-type "TIMESTAMP WITH TIME ZONE" + :semantic-type :type/CreationTimestamp + :table-id (id :people) + :coercion-strategy nil :name "CREATED_AT" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/DateTimeWithLocalTZ + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/DateTimeWithLocalTZ :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :people :created-at) - :database_is_auto_increment false + :database-is-auto-increment false :position 12 - :visibility_type :normal - :preview_display true - :display_name "Created At" - :database_position 12 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Created At" + :database-position 12 + :database-required false :fingerprint {:global {:distinct-count 2499, :nil% 0.0} :type {:type/DateTime {:earliest "2016-04-19T21:35:18.752Z" :latest "2019-04-19T14:06:27.3Z"}}} - :base_type :type/DateTimeWithLocalTZ - :points_of_interest nil + :base-type :type/DateTimeWithLocalTZ + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :people [_table-name] {:description nil - :entity_type :entity/UserTable + :entity-type :entity/UserTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "PEOPLE" :caveats nil :active true :id (id :people) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "People" - :points_of_interest nil + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "People" + :points-of-interest nil :lib/type :metadata/table :fields [(field-metadata :people :id) (field-metadata :people :state) @@ -1917,93 +1917,93 @@ (defmethod field-metadata [:reviews :id] [_table-name _field-name] {:description nil - :database_type "BIGINT" - :semantic_type :type/PK - :table_id (id :reviews) - :coercion_strategy nil + :database-type "BIGINT" + :semantic-type :type/PK + :table-id (id :reviews) + :coercion-strategy nil :name "ID" - :fingerprint_version 0 - :has_field_values nil + :fingerprint-version 0 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/BigInteger + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/BigInteger :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :id) - :database_is_auto_increment true + :database-is-auto-increment true :position 0 - :visibility_type :normal - :preview_display true - :display_name "ID" - :database_position 0 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "ID" + :database-position 0 + :database-required false :fingerprint nil - :base_type :type/BigInteger - :points_of_interest nil + :base-type :type/BigInteger + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:reviews :created-at] [_table-name _field-name] {:description nil - :database_type "TIMESTAMP WITH TIME ZONE" - :semantic_type :type/CreationTimestamp - :table_id (id :reviews) - :coercion_strategy nil + :database-type "TIMESTAMP WITH TIME ZONE" + :semantic-type :type/CreationTimestamp + :table-id (id :reviews) + :coercion-strategy nil :name "CREATED_AT" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/DateTimeWithLocalTZ + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/DateTimeWithLocalTZ :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :created-at) - :database_is_auto_increment false + :database-is-auto-increment false :position 5 - :visibility_type :normal - :preview_display true - :display_name "Created At" - :database_position 5 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Created At" + :database-position 5 + :database-required false :fingerprint {:global {:distinct-count 1112, :nil% 0.0} :type {:type/DateTime {:earliest "2016-06-03T00:37:05.818Z" :latest "2020-04-19T14:15:25.677Z"}}} - :base_type :type/DateTimeWithLocalTZ - :points_of_interest nil + :base-type :type/DateTimeWithLocalTZ + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:reviews :rating] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/Score - :table_id (id :reviews) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/Score + :table-id (id :reviews) + :coercion-strategy nil :name "RATING" - :fingerprint_version 5 - :has_field_values :auto-list + :fingerprint-version 5 + :has-field-values :auto-list :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :rating) - :database_is_auto_increment false + :database-is-auto-increment false :position 3 - :visibility_type :normal - :preview_display true - :display_name "Rating" - :database_position 3 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Rating" + :database-position 3 + :database-required false :fingerprint {:global {:distinct-count 5, :nil% 0.0} :type {:type/Number {:min 1.0 :q1 3.54744353181696 @@ -2011,129 +2011,129 @@ :max 5.0 :sd 1.0443899855660577 :avg 3.987410071942446}}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:reviews :reviewer] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :reviews) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :reviews) + :coercion-strategy nil :name "REVIEWER" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :reviewer) - :database_is_auto_increment false + :database-is-auto-increment false :position 2 - :visibility_type :normal - :preview_display true - :display_name "Reviewer" - :database_position 2 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Reviewer" + :database-position 2 + :database-required false :fingerprint {:global {:distinct-count 1076, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.001798561151079137 :average-length 9.972122302158274}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:reviews :body] [_table-name _field-name] {:description nil - :database_type "CHARACTER VARYING" - :semantic_type nil - :table_id (id :reviews) - :coercion_strategy nil + :database-type "CHARACTER VARYING" + :semantic-type nil + :table-id (id :reviews) + :coercion-strategy nil :name "BODY" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id nil - :custom_position 0 - :effective_type :type/Text + :fk-target-field-id nil + :custom-position 0 + :effective-type :type/Text :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :body) - :database_is_auto_increment false + :database-is-auto-increment false :position 4 - :visibility_type :normal - :preview_display false - :display_name "Body" - :database_position 4 - :database_required false + :visibility-type :normal + :preview-display false + :display-name "Body" + :database-position 4 + :database-required false :fingerprint {:global {:distinct-count 1112, :nil% 0.0} :type {:type/Text {:percent-json 0.0 :percent-url 0.0 :percent-email 0.0 :percent-state 0.0 :average-length 177.41996402877697}}} - :base_type :type/Text - :points_of_interest nil + :base-type :type/Text + :points-of-interest nil :lib/type :metadata/field}) (defmethod field-metadata [:reviews :product-id] [_table-name _field-name] {:description nil - :database_type "INTEGER" - :semantic_type :type/FK - :table_id (id :reviews) - :coercion_strategy nil + :database-type "INTEGER" + :semantic-type :type/FK + :table-id (id :reviews) + :coercion-strategy nil :name "PRODUCT_ID" - :fingerprint_version 5 - :has_field_values nil + :fingerprint-version 5 + :has-field-values nil :settings nil :caveats nil - :fk_target_field_id (id :products :id) - :custom_position 0 - :effective_type :type/Integer + :fk-target-field-id (id :products :id) + :custom-position 0 + :effective-type :type/Integer :active true - :nfc_path nil - :parent_id nil + :nfc-path nil + :parent-id nil :id (id :reviews :product-id) - :database_is_auto_increment false + :database-is-auto-increment false :position 1 - :visibility_type :normal - :preview_display true - :display_name "Product ID" - :database_position 1 - :database_required false + :visibility-type :normal + :preview-display true + :display-name "Product ID" + :database-position 1 + :database-required false :fingerprint {:global {:distinct-count 176, :nil% 0.0}} - :base_type :type/Integer - :points_of_interest nil + :base-type :type/Integer + :points-of-interest nil :lib/type :metadata/field}) (defmethod table-metadata :reviews [_table-name] {:description nil - :entity_type :entity/GenericTable + :entity-type :entity/GenericTable :schema "PUBLIC" - :show_in_getting_started false + :show-in-getting-started false :name "REVIEWS" :caveats nil :active true :id (id :reviews) - :db_id (id) - :visibility_type nil - :field_order :database - :initial_sync_status "complete" - :display_name "Reviews" - :points_of_interest nil + :db-id (id) + :visibility-type nil + :field-order :database + :initial-sync-status "complete" + :display-name "Reviews" + :points-of-interest nil :lib/type :metadata/table :fields [(field-metadata :reviews :id) (field-metadata :reviews :created-at) @@ -2170,10 +2170,10 @@ :right-join :standard-deviation-aggregations :temporal-extract} - :cache_field_values_schedule "0 50 0 * * ? *" + :cache-field-values-schedule "0 50 0 * * ? *" :timezone "UTC" - :auto_run_queries true - :metadata_sync_schedule "0 50 * * * ? *" + :auto-run-queries true + :metadata-sync-schedule "0 50 * * * ? *" :name "test-data" :settings nil :caveats nil @@ -2185,27 +2185,108 @@ (table-metadata :orders) (table-metadata :people) (table-metadata :reviews)] - :creator_id nil - :is_full_sync true - :cache_ttl nil - :is_sample false + :creator-id nil + :is-full-sync true + :cache-ttl nil + :is-sample false :id (id) - :is_on_demand false + :is-on-demand false :options nil :engine :h2 - :initial_sync_status "complete" - :dbms_version {:flavor "H2", :version "2.1.212 (2022-04-09)", :semantic-version [2 1]} + :initial-sync-status "complete" + :dbms-version {:flavor "H2", :version "2.1.212 (2022-04-09)", :semantic-version [2 1]} :refingerprint nil - :points_of_interest nil + :points-of-interest nil :lib/type :metadata/database}) (def metadata-provider "[[metabase.lib.metadata.protocols/MetadataProvider]] using the test [[metadata]]." (meta.graph-provider/->SimpleGraphMetadataProvider metadata)) -(def results-metadata +(def card-results-metadata + "Capture of the `result_metadata` saved with a Card with a `SELECT * FROM VENUES;` query. Actually this is a little + different because this is pre-massaged into the MLv2 shape (it has `:lib/type` and uses `kebab-case` keys), to make + it easier to use in tests. It should not make a difference because transformation to the MLv2 shape is idempotent... + and at some point we'll probably update the backend to store stuff in this shape anyway." + [{:lib/type :metadata/field + :display-name "ID" + :name "ID" + :base-type :type/BigInteger + :effective-type :type/BigInteger + :semantic-type :type/PK + :fingerprint nil} + {:lib/type :metadata/field + :display-name "NAME" ; TODO -- these display names are icky + :name "NAME" + :base-type :type/Text + :effective-type :type/Text + :semantic-type :type/Name + :fingerprint {:global {:distinct-count 100, :nil% 0.0} + :type {:type/Text {:percent-json 0.0 + :percent-url 0.0 + :percent-email 0.0 + :percent-state 0.0 + :average-length 15.63}}}} + {:lib/type :metadata/field + :display-name "CATEGORY_ID" + :name "CATEGORY_ID" + :base-type :type/Integer + :effective-type :type/Integer + :semantic-type nil + :fingerprint {:global {:distinct-count 28, :nil% 0.0} + :type {:type/Number + {:min 2.0 + :q1 6.89564392373896 + :q3 49.240253073352044 + :max 74.0 + :sd 23.058108414099443 + :avg 29.98}}}} + {:lib/type :metadata/field + :display-name "LATITUDE" + :name "LATITUDE" + :base-type :type/Float + :effective-type :type/Float + :semantic-type :type/Latitude + :fingerprint + {:global {:distinct-count 94, :nil% 0.0} + :type {:type/Number {:min 10.0646 + :q1 34.06098873016278 + :q3 37.77185 + :max 40.7794 + :sd 3.4346725397190827 + :avg 35.505891999999996}}}} + {:lib/type :metadata/field + :display-name "LONGITUDE" + :name "LONGITUDE" + :base-type :type/Float + :effective-type :type/Float + :semantic-type :type/Longitude + :fingerprint {:global {:distinct-count 84, :nil% 0.0} + :type {:type/Number + {:min -165.374 + :q1 -122.40857106781186 + :q3 -118.2635 + :max -73.9533 + :sd 14.162810671348238 + :avg -115.99848699999998}}}} + {:lib/type :metadata/field + :display-name "PRICE" + :name "PRICE" + :base-type :type/Integer + :effective-type :type/Integer + :semantic-type nil + :fingerprint {:global {:distinct-count 4, :nil% 0.0} + :type {:type/Number + {:min 1.0 + :q1 1.4591129021415095 + :q3 2.493086095768049 + :max 4.0 + :sd 0.7713951678941896 + :avg 2.03}}}}]) + +(def qp-results-metadata "Capture of the `data.results_metadata` that would come back when running `SELECT * FROM VENUES;` with the Query - Processor, or saved as `Card.result_metadata` for a Saved Question. + Processor. IRL queries actually come back with both `data.cols` and `data.results_metadata.columns`, which are slightly different from one another; the frontend merges these together into one unified metadata map. This is both icky and @@ -2214,86 +2295,13 @@ add it to `data.results_metadata.columns` in QP results, and add it here as well, so we can start moving toward a world where we don't have two versions of the metadata in query responses." {:lib/type :metadata/results - :columns [{:lib/type :metadata/field - :display_name "ID" - :name "ID" - :base_type :type/BigInteger - :effective_type :type/BigInteger - :semantic_type :type/PK - :fingerprint nil} - {:lib/type :metadata/field - :display_name "NAME" ; TODO -- these display names are icky - :name "NAME" - :base_type :type/Text - :effective_type :type/Text - :semantic_type :type/Name - :fingerprint {:global {:distinct-count 100, :nil% 0.0} - :type {:type/Text {:percent-json 0.0 - :percent-url 0.0 - :percent-email 0.0 - :percent-state 0.0 - :average-length 15.63}}}} - {:lib/type :metadata/field - :display_name "CATEGORY_ID" - :name "CATEGORY_ID" - :base_type :type/Integer - :effective_type :type/Integer - :semantic_type nil - :fingerprint {:global {:distinct-count 28, :nil% 0.0} - :type {:type/Number - {:min 2.0 - :q1 6.89564392373896 - :q3 49.240253073352044 - :max 74.0 - :sd 23.058108414099443 - :avg 29.98}}}} - {:lib/type :metadata/field - :display_name "LATITUDE" - :name "LATITUDE" - :base_type :type/Float - :effective_type :type/Float - :semantic_type :type/Latitude - :fingerprint - {:global {:distinct-count 94, :nil% 0.0} - :type {:type/Number {:min 10.0646 - :q1 34.06098873016278 - :q3 37.77185 - :max 40.7794 - :sd 3.4346725397190827 - :avg 35.505891999999996}}}} - {:lib/type :metadata/field - :display_name "LONGITUDE" - :name "LONGITUDE" - :base_type :type/Float - :effective_type :type/Float - :semantic_type :type/Longitude - :fingerprint {:global {:distinct-count 84, :nil% 0.0} - :type {:type/Number - {:min -165.374 - :q1 -122.40857106781186 - :q3 -118.2635 - :max -73.9533 - :sd 14.162810671348238 - :avg -115.99848699999998}}}} - {:lib/type :metadata/field - :display_name "PRICE" - :name "PRICE" - :base_type :type/Integer - :effective_type :type/Integer - :semantic_type nil - :fingerprint {:global {:distinct-count 4, :nil% 0.0} - :type {:type/Number - {:min 1.0 - :q1 1.4591129021415095 - :q3 2.493086095768049 - :max 4.0 - :sd 0.7713951678941896 - :avg 2.03}}}}]}) + :columns card-results-metadata}) (def saved-question "An representative Saved Question, with [[results-metadata]]. For testing queries that use a Saved Question as their - source." - {:dataset_query {:database (id) + source. If you added `:lib/type`, `:id`, and `:name`, you could also use this as + a [[metabase.lib.metadata/CardMetadata]]." + {:dataset-query {:database (id) :type :query :query {:source-table (id :venues)}} - :result_metadata results-metadata}) + :result-metadata card-results-metadata}) diff --git a/test/metabase/lib/test_util.cljc b/test/metabase/lib/test_util.cljc index e4950e87f21..6d284f5a570 100644 --- a/test/metabase/lib/test_util.cljc +++ b/test/metabase/lib/test_util.cljc @@ -33,7 +33,7 @@ (field-clause table field nil)) ([table field options] [:field - (merge {:base-type (:base_type (meta/field-metadata table field)) + (merge {:base-type (:base-type (meta/field-metadata table field)) :lib/uuid (str (random-uuid))} options) (meta/id table field)])) @@ -97,15 +97,15 @@ (deftest ^:parallel composed-metadata-provider-test (testing "Return things preferentially from earlier metadata providers" (let [time-field (assoc (meta/field-metadata :people :birth-date) - :base_type :type/Time - :effective_type :type/Time) + :base-type :type/Time + :effective-type :type/Time) metadata-provider (composed-metadata-provider (mock-metadata-provider {:fields [time-field]}) meta/metadata-provider)] (is (=? {:name "BIRTH_DATE" - :base_type :type/Time - :effective_type :type/Time} + :base-type :type/Time + :effective-type :type/Time} (lib.metadata/field metadata-provider (meta/id :people :birth-date))))))) @@ -117,7 +117,7 @@ (mock-metadata-provider {:cards [{:name "My Card" :id 1 - :dataset_query {:database (meta/id) + :dataset-query {:database (meta/id) :type :query :query {:source-table (meta/id :checkins) :aggregation [[:count]] @@ -140,13 +140,15 @@ (mock-metadata-provider {:cards [{:name "My Card" :id 1 - :dataset_query {:database (meta/id) + ;; THIS IS A LEGACY STYLE QUERY! + :dataset-query {:database (meta/id) :type :query :query {:source-table (meta/id :checkins) :aggregation [[:count]] :breakout [[:field (meta/id :checkins :user-id) nil]]}} - ;; this is copied directly from a QP response - :result_metadata [{:description nil + ;; this is copied directly from a QP response. NOT CONVERTED TO KEBAB-CASE YET, BECAUSE THIS IS HOW IT + ;; LOOKS IN LEGACY QUERIES! + :result-metadata [{:description nil :semantic_type :type/FK :table_id (meta/id :checkins) :coercion_strategy nil @@ -208,12 +210,12 @@ :lib/stage-metadata {:lib/type :metadata/results :columns [{:lib/type :metadata/field :name "abc" - :display_name "another Field" - :base_type :type/Integer - :semantic_type :type/FK} + :display-name "another Field" + :base-type :type/Integer + :semantic-type :type/FK} {:lib/type :metadata/field :name "sum" - :display_name "sum of User ID" - :base_type :type/Integer - :semantic_type :type/FK}]} + :display-name "sum of User ID" + :base-type :type/Integer + :semantic-type :type/FK}]} :native "SELECT whatever"}]}) diff --git a/test/metabase/lib/types/isa_test.cljc b/test/metabase/lib/types/isa_test.cljc index 8d464a52cff..46170e71487 100644 --- a/test/metabase/lib/types/isa_test.cljc +++ b/test/metabase/lib/types/isa_test.cljc @@ -12,19 +12,19 @@ :type/* :Relation/* :Semantic/* :type/Text :type/Address)) (testing "effective type passes" (is (lib.types.isa/isa? - {:effective_type :type/Text, :semantic_type :type/Address} + {:effective-type :type/Text, :semantic-type :type/Address} :type/Text))) (testing "semantic type passes" (is (lib.types.isa/isa? - {:effective_type :type/Number, :semantic_type :type/ZipCode} + {:effective-type :type/Number, :semantic-type :type/ZipCode} :type/Text))) (testing "both effective type and semantic type pass" (is (lib.types.isa/isa? - {:effective_type :type/Text, :semantic_type :type/City} + {:effective-type :type/Text, :semantic-type :type/City} :type/Text))) (testing "none of effective type and semantic type passes" (is (not (lib.types.isa/isa? - {:effective_type :type/Number, :semantic_type :type/IPAddress} + {:effective-type :type/Number, :semantic-type :type/IPAddress} :type/Text))))) (deftest ^:parallel column-isa-test @@ -35,71 +35,71 @@ (testing "effective type" (is (=? [{:name "NAME" :lib/desired-column-alias "NAME" - :semantic_type :type/Name - :effective_type :type/Text} + :semantic-type :type/Name + :effective-type :type/Text} {:name "NAME" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__NAME" - :semantic_type :type/Name - :effective_type :type/Text}] + :semantic-type :type/Name + :effective-type :type/Text}] (columns-of-type :type/Text)))) (testing "semantic type" (is (=? [{:name "ID" :lib/desired-column-alias "ID" - :semantic_type :type/PK - :effective_type :type/BigInteger} + :semantic-type :type/PK + :effective-type :type/BigInteger} {:name "CATEGORY_ID" :lib/desired-column-alias "CATEGORY_ID" - :semantic_type :type/FK - :effective_type :type/Integer} + :semantic-type :type/FK + :effective-type :type/Integer} {:name "ID" :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__ID" - :semantic_type :type/PK - :effective_type :type/BigInteger}] + :semantic-type :type/PK + :effective-type :type/BigInteger}] (columns-of-type :Relation/*)))))) (deftest ^:parallel field-type-test (testing "temporal" - (are [typ] (= ::lib.types.constants/temporal (lib.types.isa/field-type {:effective_type typ})) + (are [typ] (= ::lib.types.constants/temporal (lib.types.isa/field-type {:effective-type typ})) :type/Date :type/DateTime :type/Time)) (testing "numeric" - (are [typ] (= ::lib.types.constants/number (lib.types.isa/field-type {:effective_type typ})) + (are [typ] (= ::lib.types.constants/number (lib.types.isa/field-type {:effective-type typ})) :type/BigInteger :type/Integer :type/Float :type/Decimal)) (testing "string" - (is (= ::lib.types.constants/string (lib.types.isa/field-type {:effective_type :type/Text})))) + (is (= ::lib.types.constants/string (lib.types.isa/field-type {:effective-type :type/Text})))) (testing "types of string" - (are [typ] (= ::lib.types.constants/string (lib.types.isa/field-type {:effective_type :type/Text - :semantic_type typ})) + (are [typ] (= ::lib.types.constants/string (lib.types.isa/field-type {:effective-type :type/Text + :semantic-type typ})) :type/Name :type/Description :type/UUID :type/URL)) (testing "primary key" - (is (= ::lib.types.constants/primary_key (lib.types.isa/field-type {:effective_type :type/Integer - :semantic_type :type/PK})))) + (is (= ::lib.types.constants/primary_key (lib.types.isa/field-type {:effective-type :type/Integer + :semantic-type :type/PK})))) (testing "foreign key" - (is (= ::lib.types.constants/foreign_key (lib.types.isa/field-type {:effective_type :type/Integer - :semantic_type :type/FK})))) + (is (= ::lib.types.constants/foreign_key (lib.types.isa/field-type {:effective-type :type/Integer + :semantic-type :type/FK})))) (testing "boolean" - (is (= ::lib.types.constants/boolean (lib.types.isa/field-type {:effective_type :type/Boolean})))) + (is (= ::lib.types.constants/boolean (lib.types.isa/field-type {:effective-type :type/Boolean})))) (testing "location" - (are [typ] (= ::lib.types.constants/location (lib.types.isa/field-type {:semantic_type typ})) + (are [typ] (= ::lib.types.constants/location (lib.types.isa/field-type {:semantic-type typ})) :type/City :type/Country)) (testing "coordinate" - (are [typ] (= ::lib.types.constants/coordinate (lib.types.isa/field-type {:semantic_type typ})) + (are [typ] (= ::lib.types.constants/coordinate (lib.types.isa/field-type {:semantic-type typ})) :type/Latitude :type/Longitude)) (testing "string like" - (are [typ] (= ::lib.types.constants/string_like (lib.types.isa/field-type {:effective_type typ})) + (are [typ] (= ::lib.types.constants/string_like (lib.types.isa/field-type {:effective-type typ})) :type/TextLike :type/IPAddress)) (testing "strings, regardless of the effective type is" - (are [typ] (= ::lib.types.constants/string (lib.types.isa/field-type {:effective_type :type/Float - :semantic_type typ})) + (are [typ] (= ::lib.types.constants/string (lib.types.isa/field-type {:effective-type :type/Float + :semantic-type typ})) :type/Name :type/Category))) (testing "boolean, regardless of the semantic type" - (is (= ::lib.types.constants/boolean (lib.types.isa/field-type {:effective_type :type/Boolean - :semantic_type :type/Category})))) + (is (= ::lib.types.constants/boolean (lib.types.isa/field-type {:effective-type :type/Boolean + :semantic-type :type/Category})))) (testing "unexpected things" (are [column] (nil? (lib.types.isa/field-type column)) - {:effective_type "DERP DERP DERP"} - {:semantic_type "DERP DERP DERP"} - {:effective_type nil} - {:semantic_type nil} + {:effective-type "DERP DERP DERP"} + {:semantic-type "DERP DERP DERP"} + {:effective-type nil} + {:semantic-type nil} "DERP DERP DERP" :type/Category nil)) @@ -108,9 +108,9 @@ (letfn [(column [x] (cond (map? x) x - (isa? x :Semantic/*) {:semantic_type x} - (isa? x :Relation/*) {:semantic_type x} - :else {:effective_type x}))] + (isa? x :Semantic/*) {:semantic-type x} + (isa? x :Relation/*) {:semantic-type x} + :else {:effective-type x}))] (doseq [{:keys [nom pred positive negative]} [{:nom "date?", :pred lib.types.isa/date? :positive :type/DateTime, :negative :type/City} @@ -190,21 +190,21 @@ (deftest ^:parallel has-latitude-and-longitude?-test (is (true? (lib.types.isa/has-latitude-and-longitude? - [{:semantic_type :type/Latitude} {:semantic_type :type/Longitude}]))) + [{:semantic-type :type/Latitude} {:semantic-type :type/Longitude}]))) (are [columns] (false? (lib.types.isa/has-latitude-and-longitude? columns)) - [{:semantic_type :type/Latitude}] - [{:semantic_type :type/Longitude}] - [{:semantic_type :type/Longitude} {:semantic_type :type/Address}] + [{:semantic-type :type/Latitude}] + [{:semantic-type :type/Longitude}] + [{:semantic-type :type/Longitude} {:semantic-type :type/Address}] [] nil)) (deftest ^:parallel primary-key-pred-test (let [integer-table-id 1 - columns [{:semantic_type :type/PK, :table-id (inc integer-table-id), :name "column0"} - {:semantic_type :type/PK, :name "column1"} - {:semantic_type :type/PK, :table-id integer-table-id, :name "column2"} - {:semantic_type :type/Address, :name "column3"}]] + columns [{:semantic-type :type/PK, :table-id (inc integer-table-id), :name "column0"} + {:semantic-type :type/PK, :name "column1"} + {:semantic-type :type/PK, :table-id integer-table-id, :name "column2"} + {:semantic-type :type/Address, :name "column3"}]] (testing "with integer table-id :table-id has to match" (let [primary-key? (lib.types.isa/primary-key-pred integer-table-id)] (is (= ["column2"] diff --git a/test/metabase/util_test.cljc b/test/metabase/util_test.cljc index 074c97dacd2..a782ada2dc1 100644 --- a/test/metabase/util_test.cljc +++ b/test/metabase/util_test.cljc @@ -227,19 +227,44 @@ {} nil nil nil)) -;; TODO Can we achieve something like with-locale in CLJS? +(deftest ^:parallel lower-case-en-test + (is (= "id" + (u/lower-case-en "ID")))) + #?(:clj - (deftest lower-case-en-test + (deftest lower-case-en-turkish-test + ;; TODO Can we achieve something like with-locale in CLJS? (mt/with-locale "tr" (is (= "id" (u/lower-case-en "ID")))))) +(deftest ^:parallel upper-case-en-test + (is (= "ID" + (u/upper-case-en "id")))) + #?(:clj - (deftest upper-case-en-test + (deftest upper-case-en-turkish-test (mt/with-locale "tr" (is (= "ID" (u/upper-case-en "id")))))) +(deftest ^:parallel capitalize-en-test + (are [s expected] (= expected + (u/capitalize-en s)) + nil nil + "" "" + "ibis" "Ibis" + "IBIS" "Ibis" + "Ibis" "Ibis")) + +#?(:clj + (deftest capitalize-en-turkish-test + (mt/with-locale "tr" + (is (= "Ibis" + (u/capitalize-en "ibis") + (u/capitalize-en "IBIS") + (u/capitalize-en "Ibis")))))) + (deftest ^:parallel parse-currency-test (are [s expected] (= expected (u/parse-currency s)) @@ -290,7 +315,7 @@ (= x m) (= ys (concat non-pos rest)))))))) -(deftest normalize-map-test +(deftest ^:parallel normalize-map-test (testing "nil and empty maps return empty maps" (is (= {} (u/normalize-map nil))) (is (= {} (u/normalize-map {})))) @@ -306,6 +331,12 @@ (testing "JS objects get turned into Clojure maps" (is (= exp (u/normalize-map #js {"kebab-key" 1 "snake_key" 2 "camelKey" 3}))))))) +#?(:clj + (deftest normalize-map-turkish-test + (mt/with-locale "tr" + (is (= {:bird "Toucan"} + (u/normalize-map {:BIRD "Toucan"})))))) + (deftest ^:parallel or-with-test (testing "empty case" (is (= nil (u/or-with identity)))) -- GitLab