Skip to content
Snippets Groups Projects
Commit 4aeaf055 authored by Cam Saül's avatar Cam Saül
Browse files

convert models to record types

parent bc63f160
No related branches found
No related tags found
No related merge requests found
Showing
with 254 additions and 180 deletions
...@@ -9,11 +9,9 @@ ...@@ -9,11 +9,9 @@
[medley.core :as m] [medley.core :as m]
[metabase.config :as config] [metabase.config :as config]
[metabase.db.internal :refer :all :as i] [metabase.db.internal :refer :all :as i]
[metabase.models.interface :as models]
[metabase.util :as u])) [metabase.util :as u]))
(declare post-select)
;; ## DB FILE, JDBC/KORMA DEFINITONS ;; ## DB FILE, JDBC/KORMA DEFINITONS
(defn db-file (defn db-file
...@@ -255,15 +253,6 @@ ...@@ -255,15 +253,6 @@
;; ## SEL ;; ## SEL
(defmulti post-select
"Called on the results from a call to `sel`. Default implementation doesn't do anything, but
you can provide custom implementations to do things like add hydrateable keys or remove sensitive fields."
(fn [entity _] entity))
;; Default implementation of post-select
(defmethod post-select :default [_ result]
result)
(defmulti default-fields (defmulti default-fields
"The default fields that should be used for ENTITY by calls to `sel` if none are specified." "The default fields that should be used for ENTITY by calls to `sel` if none are specified."
identity) identity)
...@@ -371,6 +360,12 @@ ...@@ -371,6 +360,12 @@
(sel :many entity# ~@forms))) (sel :many entity# ~@forms)))
nil `(-sel-select ~entity ~@forms))))) nil `(-sel-select ~entity ~@forms)))))
(defn -sel-transform [entity result]
(->> result
(models/internal-post-select entity)
#_(apply-type-fns :out (seq (::types entity)))
(models/post-select entity)))
(defmacro -sel-select (defmacro -sel-select
"Internal macro used by `sel` (don't call this directly). "Internal macro used by `sel` (don't call this directly).
Generates the korma `select` form." Generates the korma `select` form."
...@@ -388,8 +383,7 @@ ...@@ -388,8 +383,7 @@
`[~(name form) ~(apply str (interpose " " args))]) `[~(name form) ~(apply str (interpose " " args))])
forms))) forms)))
(->> (select entity-select-form# ~@forms) (->> (select entity-select-form# ~@forms)
(map (partial apply-type-fns :out (seq (::types entity#)))) (map (partial -sel-transform entity#))))))
(map (partial post-select entity#)))))) ; map `post-select` over the results
;; ## INS ;; ## INS
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Symbols like `'metabase.models.user/User` are handled the same way as strings." * Symbols like `'metabase.models.user/User` are handled the same way as strings."
(memoize (memoize
(fn -entity->korma [entity] (fn -entity->korma [entity]
{:post [(= (type %) :korma.core/Entity)]} ;; {:post [(= (type %) :korma.core/Entity)]}
(cond (vector? entity) (-entity->korma (first entity)) (cond (vector? entity) (-entity->korma (first entity))
(string? entity) (-entity->korma (symbol entity)) (string? entity) (-entity->korma (symbol entity))
(symbol? entity) (try (eval entity) (symbol? entity) (try (eval entity)
......
(ns metabase.models.card (ns metabase.models.card
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.api.common :refer [*current-user-id*]] [metabase.api.common :refer [*current-user-id*]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :refer :all] (metabase.models [common :refer :all]
[interface :refer :all]
[user :refer [User]]))) [user :refer [User]])))
(def ^:const display-types (def ^:const display-types
...@@ -19,17 +20,18 @@ ...@@ -19,17 +20,18 @@
:timeseries}) :timeseries})
(defentity Card (defentity Card
(table :report_card) [(table :report_card)
(types {:dataset_query :json (types {:dataset_query :json
:display :keyword :display :keyword
:visualization_settings :json}) :visualization_settings :json})
timestamped timestamped
(assoc :hydration-keys #{:card})) (assoc :hydration-keys #{:card})]
(defmethod post-select Card [_ {:keys [creator_id] :as card}] IEntityPostSelect
(-> (assoc card (post-select [_ {:keys [creator_id] :as card}]
:creator (delay (sel :one User :id creator_id))) (-> (assoc card
assoc-permissions-sets)) :creator (delay (sel :one User :id creator_id)))
assoc-permissions-sets)))
(defmethod pre-cascade-delete Card [_ {:keys [id]}] (defmethod pre-cascade-delete Card [_ {:keys [id]}]
(cascade-delete 'metabase.models.dashboard-card/DashboardCard :card_id id) (cascade-delete 'metabase.models.dashboard-card/DashboardCard :card_id id)
......
(ns metabase.models.card-favorite (ns metabase.models.card-favorite
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [card :refer [Card]] (metabase.models [card :refer [Card]]
[interface :refer :all]
[user :refer [User]]))) [user :refer [User]])))
(defentity CardFavorite (defentity CardFavorite
(table :report_cardfavorite) [(table :report_cardfavorite)
timestamped) timestamped]
(defmethod post-select CardFavorite [_ {:keys [card_id owner_id] :as card-favorite}] IEntityPostSelect
(assoc card-favorite (post-select [_ {:keys [card_id owner_id] :as card-favorite}]
:owner (delay (sel :one User :id owner_id)) (assoc card-favorite
:card (delay (sel :one Card :id card_id)))) :owner (delay (sel :one User :id owner_id))
:card (delay (Card card_id)))))
(ns metabase.models.dashboard (ns metabase.models.dashboard
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :refer :all] (metabase.models [common :refer :all]
[dashboard-card :refer [DashboardCard]] [dashboard-card :refer [DashboardCard]]
[interface :refer :all]
[user :refer [User]]) [user :refer [User]])
[metabase.util :as u])) [metabase.util :as u]))
(defentity Dashboard (defentity Dashboard
(table :report_dashboard) [(table :report_dashboard)
timestamped) timestamped]
(defmethod post-select Dashboard [_ {:keys [id creator_id description] :as dash}] IEntityPostSelect
(-> dash (post-select [_ {:keys [id creator_id description] :as dash}]
(assoc :creator (delay (sel :one User :id creator_id)) (-> dash
:description (u/jdbc-clob->str description) (assoc :creator (delay (sel :one User :id creator_id))
:ordered_cards (delay (sel :many DashboardCard :dashboard_id id (order :created_at :asc)))) :description (u/jdbc-clob->str description)
assoc-permissions-sets)) :ordered_cards (delay (sel :many DashboardCard :dashboard_id id (order :created_at :asc))))
assoc-permissions-sets)))
(defmethod pre-cascade-delete Dashboard [_ {:keys [id]}] (defmethod pre-cascade-delete Dashboard [_ {:keys [id]}]
(cascade-delete DashboardCard :dashboard_id id)) (cascade-delete DashboardCard :dashboard_id id))
(ns metabase.models.dashboard-card (ns metabase.models.dashboard-card
(:require [korma.core :refer :all] (:require [clojure.set :as set]
[korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [card :refer [Card]]))) (metabase.models [card :refer [Card]]
[interface :refer :all])))
(defentity DashboardCard (defentity DashboardCard
(table :report_dashboardcard) [(table :report_dashboardcard)
timestamped) timestamped]
;; #### fields: IEntityPostSelect
;; * `id` (post-select [_ {:keys [card_id dashboard_id] :as dashcard}]
;; * `created_at` (-> dashcard
;; * `updated_at` (set/rename-keys {:sizex :sizeX ; mildly retarded: H2 columns are all uppercase, we're converting them
;; * `sizeX` :sizey :sizeY}) ; to all downcase, and the Angular app expected mixed-case names here
;; * `sizeY` (assoc :card (delay (Card card_id))
;; * `row` :dashboard (delay (sel :one 'metabase.models.dashboard/Dashboard :id dashboard_id))))))
;; * `col`
;; * `card_id`
;; * `dashboard_id`
(defmethod post-select DashboardCard [_ {:keys [card_id dashboard_id] :as dashcard}]
(-> dashcard
(clojure.set/rename-keys {:sizex :sizeX ; mildly retarded: H2 columns are all uppercase, we're converting them
:sizey :sizeY}) ; to all downcase, and the Angular app expected mixed-case names here
(assoc :card (delay (sel :one Card :id card_id))
:dashboard (delay (sel :one 'metabase.models.dashboard/Dashboard :id dashboard_id)))))
(defmethod pre-insert DashboardCard [_ dashcard] (defmethod pre-insert DashboardCard [_ dashcard]
(let [defaults {:sizeX 2 (let [defaults {:sizeX 2
......
(ns metabase.models.database (ns metabase.models.database
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.api.common :refer [*current-user*]] [metabase.api.common :refer [*current-user*]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :refer [assoc-permissions-sets]] (metabase.models [common :refer [assoc-permissions-sets]]
[interface :refer :all]))) [interface :refer :all])))
(defentity Database
(table :metabase_database)
(types {:details :json
:engine :keyword})
timestamped
(assoc :hydration-keys #{:database
:db}))
(defrecord DatabaseInstance [] (defrecord DatabaseInstance []
;; preserve normal IFn behavior so things like ((sel :one Database) :id) work correctly ;; preserve normal IFn behavior so things like ((sel :one Database) :id) work correctly
clojure.lang.IFn clojure.lang.IFn
...@@ -26,11 +17,32 @@ ...@@ -26,11 +17,32 @@
(cond-> this (cond-> this
(not (:is_superuser @*current-user*)) (dissoc :details)))) (not (:is_superuser @*current-user*)) (dissoc :details))))
(defmethod post-select Database [_ db] (defentity Database
(map->DatabaseInstance [(table :metabase_database)
(assoc db (types {:details :json
:can_read (delay true) :engine :keyword})
:can_write (delay (:is_superuser @*current-user*))))) timestamped
(assoc :hydration-keys #{:database
:db})]
IEntityPostSelect
(post-select [_ db]
(map->DatabaseInstance
(assoc db
:can_read (delay true)
:can_write (delay (:is_superuser @*current-user*))))))
(defmethod pre-cascade-delete Database [_ {:keys [id] :as database}] (defmethod pre-cascade-delete Database [_ {:keys [id] :as database}]
(cascade-delete 'metabase.models.table/Table :db_id id)) (cascade-delete 'metabase.models.table/Table :db_id id))
(defn x []
(sel :one Database :id 1))
(defn y []
(Database 1))
(defn z []
(println "X")
(time (dorun (repeatedly 1000 x))) ; ~ 1500 ms
(println "Y")
(time (dorun (repeatedly 1000 y)))) ; ~ 250 ms
(ns metabase.models.field (ns metabase.models.field
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.api.common :refer [check]] [metabase.api.common :refer [check]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :as common] (metabase.models [common :as common]
[database :refer [Database]] [database :refer [Database]]
[field-values :refer [field-should-have-field-values? create-field-values create-field-values-if-needed]] [field-values :refer [field-should-have-field-values? create-field-values create-field-values-if-needed]]
[foreign-key :refer [ForeignKey]]
[hydrate :refer [hydrate]] [hydrate :refer [hydrate]]
[foreign-key :refer [ForeignKey]]) [interface :refer :all])
[metabase.util :as u])) [metabase.util :as u]))
(declare field->fk-field)
(def ^:const special-types (def ^:const special-types
"Possible values for `Field` `:special_type`." "Possible values for `Field` `:special_type`."
#{:avatar #{:avatar
...@@ -72,14 +75,24 @@ ...@@ -72,14 +75,24 @@
:sensitive}) ; A Fields that should *never* be shown *anywhere* :sensitive}) ; A Fields that should *never* be shown *anywhere*
(defentity Field (defentity Field
(table :metabase_field) [(table :metabase_field)
timestamped timestamped
(types {:base_type :keyword (types {:base_type :keyword
:field_type :keyword :field_type :keyword
:special_type :keyword}) :special_type :keyword})
(assoc :hydration-keys #{:destination (assoc :hydration-keys #{:destination
:field :field
:origin})) :origin})]
IEntityPostSelect
(post-select [_ {:keys [table_id] :as field}]
(u/assoc* field
:table (delay (sel :one 'metabase.models.table/Table :id table_id))
:db (delay @(:db @(:table <>)))
:target (delay (field->fk-field field))
:can_read (delay @(:can_read @(:table <>)))
:can_write (delay @(:can_write @(:table <>)))
:human_readable_name (when (name :field)
(delay (common/name->human-readable-name (:name field)))))))
(defn field->fk-field (defn field->fk-field
"Attempts to follow a `ForeignKey` from the the given `Field` to a destination `Field`. "Attempts to follow a `ForeignKey` from the the given `Field` to a destination `Field`.
...@@ -90,16 +103,6 @@ ...@@ -90,16 +103,6 @@
(let [dest-id (sel :one :field [ForeignKey :destination_id] :origin_id id)] (let [dest-id (sel :one :field [ForeignKey :destination_id] :origin_id id)]
(sel :one Field :id dest-id)))) (sel :one Field :id dest-id))))
(defmethod post-select Field [_ {:keys [table_id] :as field}]
(u/assoc* field
:table (delay (sel :one 'metabase.models.table/Table :id table_id))
:db (delay @(:db @(:table <>)))
:target (delay (field->fk-field field))
:can_read (delay @(:can_read @(:table <>)))
:can_write (delay @(:can_write @(:table <>)))
:human_readable_name (when (name :field)
(delay (common/name->human-readable-name (:name field))))))
(defmethod pre-insert Field [_ field] (defmethod pre-insert Field [_ field]
(let [defaults {:active true (let [defaults {:active true
:preview_display true :preview_display true
......
(ns metabase.models.field-values (ns metabase.models.field-values
(:require [clojure.tools.logging :as log] (:require [clojure.tools.logging :as log]
[korma.core :refer :all] [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] (metabase [db :refer :all]
[metabase.util :as u])) [util :as u])
[metabase.models.interface :refer :all]))
;; ## Entity + DB Multimethods ;; ## Entity + DB Multimethods
(defentity FieldValues (defentity FieldValues
(table :metabase_fieldvalues) [(table :metabase_fieldvalues)
timestamped timestamped
(types {:human_readable_values :json (types {:human_readable_values :json
:values :json})) :values :json})]
IEntityPostSelect
(post-select [_ field-values]
(update-in field-values [:human_readable_values] #(or % {}))))
;; columns: ;; columns:
;; * :id ;; * :id
...@@ -20,10 +25,6 @@ ...@@ -20,10 +25,6 @@
;; * :values (JSON-encoded array like ["table" "scalar" "pie"]) ;; * :values (JSON-encoded array like ["table" "scalar" "pie"])
;; * :human_readable_values (JSON-encoded map like {:table "Table" :scalar "Scalar"} ;; * :human_readable_values (JSON-encoded map like {:table "Table" :scalar "Scalar"}
(defmethod post-select FieldValues [_ field-values]
(update-in field-values [:human_readable_values] #(or % {}))) ; return an empty map for :human_readable_values in cases where it is nil
;; ## `FieldValues` Helper Functions ;; ## `FieldValues` Helper Functions
(defn field-should-have-field-values? (defn field-should-have-field-values?
......
(ns metabase.models.foreign-key (ns metabase.models.foreign-key
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all])) [metabase.db :refer :all]
[metabase.models.interface :refer :all]))
(def relationships (def ^:const relationships
"Valid values for `ForeginKey.relationship`." "Valid values for `ForeginKey.relationship`."
#{:1t1 #{:1t1
:Mt1 :Mt1
:MtM}) :MtM})
(def relationship->name (def ^:const relationship->name
{:1t1 "One to One" {:1t1 "One to One"
:Mt1 "Many to One" :Mt1 "Many to One"
:MtM "Many to Many"}) :MtM "Many to Many"})
(defentity ForeignKey (defentity ForeignKey
(table :metabase_foreignkey) [(table :metabase_foreignkey)
timestamped timestamped
(types {:relationship :keyword})) (types {:relationship :keyword})]
IEntityPostSelect
(defmethod post-select ForeignKey [_ {:keys [origin_id destination_id] :as fk}] (post-select [_ {:keys [origin_id destination_id] :as fk}]
(assoc fk (assoc fk
:origin (delay (sel :one 'metabase.models.field/Field :id origin_id)) :origin (delay (sel :one 'metabase.models.field/Field :id origin_id))
:destination (delay (sel :one 'metabase.models.field/Field :id destination_id)))) :destination (delay (sel :one 'metabase.models.field/Field :id destination_id)))))
...@@ -209,12 +209,11 @@ ...@@ -209,12 +209,11 @@
"Delay that returns map of `hydration-key` -> korma entity. "Delay that returns map of `hydration-key` -> korma entity.
e.g. `:user -> User`. e.g. `:user -> User`.
This is built pulling the `:hydration-keys` set from all korma entities." This is built pulling the `:hydration-keys` set from all of our entities."
(delay (->> (all-ns) (delay (->> (all-ns)
(mapcat ns-publics) (mapcat ns-publics)
vals vals
(map var-get) (map var-get)
(filter #(= (type %) :korma.core/Entity))
(filter :hydration-keys) (filter :hydration-keys)
(mapcat (fn [{:keys [hydration-keys] :as entity}] (mapcat (fn [{:keys [hydration-keys] :as entity}]
(assert (and (set? hydration-keys) (every? keyword? hydration-keys)) (assert (and (set? hydration-keys) (every? keyword? hydration-keys))
......
(ns metabase.models.interface) (ns metabase.models.interface
(:require [clojure.tools.logging :as log]
[clojure.walk :refer [macroexpand-all]]
[korma.core :as k]
[medley.core :as m]
[metabase.config :as config]))
(defprotocol IEntityPostSelect
(post-select [this instance]
"Called on the results from a call to `sel`. Default implementation doesn't do anything, but
you can provide custom implementations to do things like add hydrateable keys or remove sensitive fields."))
(defprotocol IModelInstanceApiSerialize (defprotocol IModelInstanceApiSerialize
(api-serialize [this] (api-serialize [this]
"Called on all objects being written out by the API. Default implementations return THIS as-is, but models can provide "Called on all objects being written out by the API. Default implementations return THIS as-is, but models can provide
custom methods to strip sensitive data, from non-admins, etc.")) custom methods to strip sensitive data, from non-admins, etc."))
(defprotocol IEntityInternal
"Internal methods automatically defined by entities created with `defentity`."
(internal-post-select [this instance]))
(defn- identity-second [_ obj]
obj)
(extend Object (extend Object
IModelInstanceApiSerialize {:api-serialize identity}) IEntityPostSelect {:post-select identity-second}
IEntityInternal {:internal-post-select identity-second}
IModelInstanceApiSerialize {:api-serialize identity})
(extend nil (extend nil
IModelInstanceApiSerialize {:api-serialize identity}) IModelInstanceApiSerialize {:api-serialize identity})
(def ^:const ^:private type-fns
{:json {:in 'metabase.db.internal/write-json
:out 'metabase.db.internal/read-json}
:keyword {:in `name
:out `keyword}})
(defn- resolve-type-fns [types-map]
(m/map-vals #(:out (type-fns %)) types-map))
(defn -invoke-entity [entity id]
(future
(when (metabase.config/config-bool :mb-db-logging)
(clojure.tools.logging/debug
"DB CALL: " (:name entity) id)))
(let [[obj] (k/select entity (k/where {:id id}) (k/limit 1))]
(when obj
(->> obj
(internal-post-select entity)
(post-select entity)))))
(defmacro make-internal-post-select [obj kvs]
`(cond-> ~obj
~@(mapcat (fn [[k f]]
[`(~k ~obj) `(update-in [~k] ~f)])
(seq kvs))))
(defmacro defentity
"Similar to korma `defentity`, but creates a new record type where you can specify protocol implementations."
[entity entity-forms & specs]
{:pre [vector? entity-forms]}
(let [entity-symb (symbol (format "%sEntity" (name entity)))
internal-post-select-symb (symbol (format "internal-post-select-%s" (name entity)))
entity-map (eval `(-> (k/create-entity ~(name entity))
~@entity-forms))
type-fns (resolve-type-fns (:metabase.db/types entity-map))]
`(do
(defrecord ~entity-symb []
IEntityInternal
(internal-post-select [~'_ ~'obj]
~(macroexpand-1 `(make-internal-post-select ~'obj ~type-fns)))
clojure.lang.IFn
(~'invoke [~'this ~'id]
(-invoke-entity ~'this ~'id))
~@specs)
(def ~entity ; ~(vary-meta entity assoc :const true)
(~(symbol (format "map->%sEntity" (name entity))) ~entity-map)))))
(ns metabase.models.query-execution (ns metabase.models.query-execution
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.api.common :refer [check]] [metabase.api.common :refer [check]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :refer :all] (metabase.models [common :refer :all]
[database :refer [Database]]))) [database :refer [Database]]
[interface :refer :all])))
(defentity QueryExecution (defentity QueryExecution
(table :query_queryexecution) [(table :query_queryexecution)
(types {:json_query :json (types {:json_query :json
:result_data :json :result_data :json
:status :keyword})) :status :keyword})]
IEntityPostSelect
(post-select [_ {:keys [result_rows] :as query-execution}]
;; sadly we have 2 ways to reference the row count :(
(assoc query-execution
:row_count (or result_rows 0))))
;; default fields to return for `sel QueryExecution ;; default fields to return for `sel QueryExecution
;; specifically excludes stored data columns ;; specifically excludes stored data columns
...@@ -26,7 +33,3 @@ ...@@ -26,7 +33,3 @@
:running_time :running_time
:error :error
:result_rows]) :result_rows])
(defmethod post-select QueryExecution [_ {:keys [result_rows] :as query-execution}]
(assoc query-execution
:row_count (or result_rows 0))) ; sadly we have 2 ways to reference the row count :(
(ns metabase.models.session (ns metabase.models.session
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :refer :all] (metabase.models [common :refer :all]
[interface :refer :all]
[user :refer [User]]) [user :refer [User]])
[metabase.util :as u])) [metabase.util :as u]))
(defentity Session (defentity Session
(table :core_session) [(table :core_session)
(belongs-to User {:fk :user_id})) (belongs-to User {:fk :user_id})])
(defmethod pre-insert Session [_ session] (defmethod pre-insert Session [_ session]
(let [defaults {:created_at (u/new-sql-timestamp)}] (let [defaults {:created_at (u/new-sql-timestamp)}]
......
(ns metabase.models.table (ns metabase.models.table
(:require [korma.core :refer :all] (:require [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
(metabase.models [common :as common] (metabase.models [common :as common]
[database :as db] [database :as db]
[field :refer [Field]] [field :refer [Field]]
[field-values :refer [FieldValues]]) [field-values :refer [FieldValues]]
[interface :refer :all])
[metabase.util :as u])) [metabase.util :as u]))
(def entity-types (def ^:const entity-types
"Valid values for `Table.entity_type` (field may also be `nil`)." "Valid values for `Table.entity_type` (field may also be `nil`)."
#{:person :event :photo :place}) #{:person :event :photo :place})
(defentity Table (defentity Table
(table :metabase_table) [(table :metabase_table)
timestamped timestamped
(types {:entity_type :keyword}) (types {:entity_type :keyword})
(assoc :hydration-keys #{:table})) (assoc :hydration-keys #{:table})]
; also missing :active and :pk_field
(defmethod post-select Table [_ {:keys [id db db_id description] :as table}]
(u/assoc* table
:db (or db (delay (sel :one db/Database :id db_id)))
:fields (delay (sel :many Field :table_id id :active true (order :position :ASC) (order :name :ASC)))
:field_values (delay
(let [field-ids (sel :many :field [Field :id]
:table_id id
:active true
:field_type [not= "sensitive"]
(order :position :asc)
(order :name :asc))]
(sel :many :field->field [FieldValues :field_id :values] :field_id [in field-ids])))
:description (u/jdbc-clob->str description)
:pk_field (delay (:id (sel :one :fields [Field :id] :table_id id (where {:special_type "id"}))))
:can_read (delay @(:can_read @(:db <>)))
:can_write (delay @(:can_write @(:db <>)))
:human_readable_name (when (:name table)
(delay (common/name->human-readable-name (:name table))))))
IEntityPostSelect
(post-select [_ {:keys [id db db_id description] :as table}]
(u/assoc* table
:db (or db (delay (sel :one db/Database :id db_id)))
:fields (delay (sel :many Field :table_id id :active true (order :position :ASC) (order :name :ASC)))
:field_values (delay
(let [field-ids (sel :many :field [Field :id]
:table_id id
:active true
:field_type [not= "sensitive"]
(order :position :asc)
(order :name :asc))]
(sel :many :field->field [FieldValues :field_id :values] :field_id [in field-ids])))
:description (u/jdbc-clob->str description)
:pk_field (delay (:id (sel :one :fields [Field :id] :table_id id (where {:special_type "id"}))))
:can_read (delay @(:can_read @(:db <>)))
:can_write (delay @(:can_write @(:db <>)))
:human_readable_name (when (:name table)
(delay (common/name->human-readable-name (:name table)))))))
(defmethod pre-cascade-delete Table [_ {:keys [id] :as table}] (defmethod pre-cascade-delete Table [_ {:keys [id] :as table}]
(cascade-delete Field :table_id id)) (cascade-delete Field :table_id id))
(ns metabase.models.table-segment
(:require [korma.core :refer :all]))
(defentity TableSegment
(table :metabase_tablesegment))
(ns metabase.models.user (ns metabase.models.user
(:require [cemerick.friend.credentials :as creds] (:require [cemerick.friend.credentials :as creds]
[korma.core :refer :all] [korma.core :refer :all, :exclude [defentity]]
[metabase.db :refer :all] [metabase.db :refer :all]
[metabase.email.messages :as email] [metabase.email.messages :as email]
[metabase.models.interface :refer :all]
[metabase.util :as u])) [metabase.util :as u]))
;; ## Enity + DB Multimethods ;; ## Enity + DB Multimethods
(defentity User (defentity User
(table :core_user) [(table :core_user)
(assoc :hydration-keys #{:author :creator :user})) (assoc :hydration-keys #{:author :creator :user})]
IEntityPostSelect
(post-select [_ user]
(assoc user :common_name (str (:first_name user) " " (:last_name user)))))
;; fields to return for Users other `*than current-user*` ;; fields to return for Users other `*than current-user*`
(defmethod default-fields User [_] (defmethod default-fields User [_]
...@@ -21,16 +26,12 @@ ...@@ -21,16 +26,12 @@
:last_login :last_login
:is_superuser]) :is_superuser])
(def current-user-fields (def ^:const current-user-fields
"The fields we should return for `*current-user*` (used by `metabase.middleware.current-user`)" "The fields we should return for `*current-user*` (used by `metabase.middleware.current-user`)"
(concat (default-fields User) (concat (default-fields User)
[:is_active [:is_active
:is_staff])) ; but not `password` ! :is_staff])) ; but not `password` !
(defmethod post-select User [_ user]
(-> user
(assoc :common_name (str (:first_name user) " " (:last_name user)))))
(defmethod pre-insert User [_ {:keys [email password] :as user}] (defmethod pre-insert User [_ {:keys [email password] :as user}]
(assert (u/is-email? email)) (assert (u/is-email? email))
(assert (and (string? password) (assert (and (string? password)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment