This namespace allows the frontend to store and retrieve arbitrary key-value pairs for individual users in the database. Each KVP is stored in a single 'namespace', which has a schema. You can write a schema in
``` [:map [:value [:maybe :string]]] ``` would define a new namespace, If you want, you can get more creative - for example, if you have a defined set of allowed keys, you could say: ``` [:map [:key [:enum "allowed-key-1" "allowed-key-2"]] [:value [:maybe :string]]] ``` Or you could go even further, and define a ``` [:multi {:dispatch :key} ["string-key" [:map [:value [:maybe :string]]]] ["number-key" [:map [:value [:maybe :int]]]]] ``` Note that: | (ns metabase.user-key-value.models.user-key-value (:require [malli.core :as mc] [malli.transform :as mtx] [metabase.user-key-value.models.user-key-value.types :as types] [metabase.util.malli :as mu] [methodical.core :as methodical] [toucan2.core :as t2])) |
(set! *warn-on-reflection* true) | |
(methodical/defmethod t2/table-name :model/UserKeyValue [_model] :user_key_value) | |
(doto :model/UserKeyValue (derive :metabase/model) (derive :hook/timestamped?)) | |
Upserts a KV-pair | (mu/defn put!
[user-id :- :int
kvp :- ::types/user-key-value]
(let [{:keys [namespace key value expires-at]}
(mc/encode ::types/user-key-value
kvp
(mtx/transformer
(mtx/default-value-transformer)
{:name :database}))]
(t2/with-transaction [_]
(if (t2/select-one :model/UserKeyValue :user_id user-id :namespace namespace :key key)
(t2/update! :model/UserKeyValue :user_id user-id :namespace namespace :key key {:value value
:expires_at expires-at})
(try
(t2/insert! :model/UserKeyValue {:user_id user-id
:namespace namespace
:key key
:value value
:expires_at expires-at})
;; in case we caught a duplicate key exception (a row was inserted between our read and write), try updating
(catch Exception _
(t2/update! :model/UserKeyValue :user_id user-id :namespace namespace :key key {:value value
:expires_at expires-at})))))
value)) |
Deletes a KV-pair | (mu/defn delete! [user-id :- :int namespace :- :string k :- :string] (t2/delete! :model/UserKeyValue :namespace namespace :user_id user-id :key k)) |
Retrieves a KV-pair | (mu/defn retrieve
[user-id :- :int
namespace :- :string
k :- :string]
(when-let [ukv
(t2/select-one :model/UserKeyValue
{:where
[:and
[:= :user_id user-id]
[:= :namespace namespace]
[:= :key k]
[:or
[:>= :expires_at :%now]
[:= :expires_at nil]]]})]
(:value (mc/decode ::types/user-key-value
ukv
(mtx/transformer
(mtx/default-value-transformer)
{:name :database}))))) |
Retrieves all KV-pairs in a namespace | (mu/defn retrieve-all
[user-id :- :int
namespace :- :string]
(when-let [kvs (seq (t2/select :model/UserKeyValue
{:where
[:and
[:= :user_id user-id]
[:= :namespace namespace]
[:or
[:>= :expires_at :%now]
[:= :expires_at nil]]]}))]
(let [parsed-kvs (mc/decode [:sequential ::types/user-key-value]
kvs
(mtx/transformer
(mtx/default-value-transformer)
{:name :database}))]
(into {} (map (juxt :key :value) parsed-kvs))))) |