Compare commits

...

8 Commits

Author SHA1 Message Date
Tamás Benkő
34c0ffc16d Fix comments 2026-06-30 23:12:53 +03:00
Tamás Benkő
86c9db9758 Fix references 2026-06-30 22:27:58 +03:00
Tamás Benkő
e47ec36236 Address review comments 2026-06-30 22:16:06 +03:00
Tamás Benkő
2a59e48e40 Fix runtime loading 2026-06-06 16:38:19 +03:00
Tamás Benkő
8fb10653e4 Bump driver affecting module count 2026-06-06 16:03:38 +03:00
Tamás Benkő
4b4737700b Fix module issues 2026-06-06 15:51:56 +03:00
Tamás Benkő
44283cc889 Merge remote-tracking branch 'origin/master' into cljs-compile-time-optimisation
# Conflicts:
#	modules/drivers/druid/test/metabase/test/data/druid.clj
2026-06-06 15:30:37 +03:00
Tamás Benkő
5799665131 Slim CLJS macro-load path by extracting BE-only namespaces
Shadow-cljs's cold build loads ~408 Clojure namespaces on the JVM as
macro sources; many of them transitively pull heavy BE-only deps
(clj-http, instaparse, cheshire, puget/fipp/rrb-vector, encore, the
whole metabase.lib.schema graph, etc.) that the CLJS analyzer never
actually needs. This branch extracts those dependencies behind sibling
namespaces so they stay out of the macro-load path while remaining
fully available to BE callers.

Cumulative numbers vs master baseline:
  shadow build wall    9.19 s -> 6.90 s   (-25 %)
  namespaces loaded    408    -> 265      (-35 %, -143)
  cold-build warnings  1      -> 0
2026-06-05 17:45:33 +03:00
283 changed files with 2032 additions and 1542 deletions

View File

@@ -680,8 +680,10 @@
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
metabase.tracing.test-util/with-span-exporter hooks.common/with-one-binding
metabase.users-rest.api-test/with-temp-user-email! hooks.common/with-one-binding
metabase.util.i18n/deferred-trs hooks.metabase.util.i18n/deferred-trs
metabase.util.i18n/deferred-tru hooks.metabase.util.i18n/deferred-tru
metabase.util.i18n-be.core/deferred-trs hooks.metabase.util.i18n/deferred-trs
metabase.util.i18n-be.core/deferred-tru hooks.metabase.util.i18n/deferred-tru
metabase.util.i18n-be.macros/deferred-trs hooks.metabase.util.i18n/deferred-trs
metabase.util.i18n-be.macros/deferred-tru hooks.metabase.util.i18n/deferred-tru
metabase.util.i18n/trs hooks.metabase.util.i18n/trs
metabase.util.i18n/tru hooks.metabase.util.i18n/tru
metabase.util.log.capture/with-log-messages-for-level hooks.metabase.util.log.capture/with-log-messages-for-level
@@ -723,6 +725,7 @@
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
metabase.lib.common/defop macros.metabase.lib.common/defop
metabase.lib.common.macros/defop macros.metabase.lib.common/defop
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
metabase.parameters.chain-filter-test/chain-filter macros.metabase.parameters.chain-filter-test/chain-filter
metabase.parameters.chain-filter-test/chain-filter-search macros.metabase.parameters.chain-filter-test/chain-filter

View File

@@ -109,6 +109,7 @@
search
settings
util
util-be
warehouses
workspaces}
:model-exports #{:model/Action}
@@ -174,7 +175,8 @@
request
server
settings
util}
util
util-be}
:model-imports #{:model/Card
:model/Dashboard
:model/DashboardCard
@@ -310,7 +312,8 @@
models
permissions
users
util}
util
util-be}
:model-exports #{:model/ApiKey}
:model-imports #{:model/User}}
@@ -337,7 +340,8 @@
config
connection-pool
task
util}
util
util-be}
:model-imports #{:model/Database
:model/Field
:model/QueryCache
@@ -506,7 +510,8 @@
task
timeline
types
util}
util
util-be}
:model-exports #{:model/Channel :model/ChannelTemplate}
:model-imports #{:model/Dashboard
:model/Database
@@ -533,7 +538,8 @@
settings
store-api
task
util}
util
util-be}
:model-imports #{:model/AuditLog
:model/Card
:model/LoginHistory
@@ -854,6 +860,7 @@
system
tracing
util
util-be
warehouse-schema
warehouses
;; `metabase.driver.sync/include-schema?` skips workspace iso schemas
@@ -949,7 +956,8 @@
query-processor
request
tiles
util}
util
util-be}
:model-imports #{:model/Card :model/Dashboard :model/EmbeddingTheme}}
events
@@ -1467,6 +1475,7 @@
models
query-processor
util
util-be
warehouse-schema
warehouses}
:model-imports #{:model/Card
@@ -1536,7 +1545,8 @@
:api #{metabase.plugins.core}
:uses #{classloader
driver
util}}
util
util-be}}
premium-features
{:team "UX West"
@@ -1811,6 +1821,7 @@
tracing
users
util
util-be
workspaces}
:model-imports #{:model/Card
:model/Dashboard
@@ -1846,7 +1857,8 @@
session
settings
users
util}
util
util-be}
:model-imports #{:model/User}}
revisions
@@ -1862,7 +1874,8 @@
models
queries
query-permissions
util}
util
util-be}
:model-exports #{:model/Revision}
:model-imports #{:model/Card
:model/Dashboard
@@ -1918,7 +1931,8 @@
task
tracing
transforms
util}
util
util-be}
:model-imports #{:model/Card
:model/Collection
:model/Dashboard
@@ -1928,7 +1942,7 @@
secrets
{:team "Graphy"
:api #{metabase.secrets.core}
:uses #{api driver models premium-features util}
:uses #{api driver models premium-features util util-be}
:model-exports #{:model/Secret}}
segments
@@ -2206,7 +2220,8 @@
request
task
tracing
util}
util
util-be}
:model-exports #{:model/TaskHistory}
:model-imports #{:model/Card :model/Dashboard :model/Database}}
@@ -2409,7 +2424,8 @@
:uses #{api
config
lib
util}}
util
util-be}}
users
{:team "UX West"
@@ -2477,6 +2493,11 @@
settings
system}}
util-be
{:team "DevEx"
:api :any
:uses #{util}}
version
{:team "UX West"
:api #{metabase.version.core
@@ -2590,7 +2611,8 @@
secrets
settings
sync
util}
util
util-be}
:model-exports #{:model/Database}
:model-imports #{:model/Card
:model/Field
@@ -2785,6 +2807,7 @@
sync
users
util
util-be
enterprise/serialization}
:model-imports #{:model/AuditLog
:model/Card
@@ -3276,6 +3299,7 @@
tenants
users
util
util-be
warehouse-schema
warehouses
enterprise/api}
@@ -3359,7 +3383,8 @@
settings
task
tracing
util}
util
util-be}
:model-imports #{:model/Card :model/Collection :model/Dashboard}}
enterprise/serialization
@@ -3417,6 +3442,7 @@
sso
system
util
util-be
enterprise/scim}
:model-imports #{:model/PermissionsGroup :model/Session}}
@@ -3513,7 +3539,8 @@
tracing
transforms
transforms-base
util}
util
util-be}
:model-exports #{:model/PythonLibrary}
:model-imports #{:model/Database
:model/Field

View File

@@ -50,12 +50,16 @@
"Vars that are looked for for translations strings"
#{'metabase.util.i18n/trs
'metabase.util.i18n/tru
'metabase.util.i18n/deferred-trs
'metabase.util.i18n/deferred-tru
'metabase.util.i18n/trsn
'metabase.util.i18n/trun
'metabase.util.i18n/deferred-trsn
'metabase.util.i18n/deferred-trun})
'metabase.util.i18n-be.core/deferred-trs
'metabase.util.i18n-be.core/deferred-tru
'metabase.util.i18n-be.core/deferred-trsn
'metabase.util.i18n-be.core/deferred-trun
'metabase.util.i18n-be.macros/deferred-trs
'metabase.util.i18n-be.macros/deferred-tru
'metabase.util.i18n-be.macros/deferred-trsn
'metabase.util.i18n-be.macros/deferred-trun})
(def ^:private plural-translation-macro-names
#{"trsn" "trun" "deferred-trsn" "deferred-trun"})

View File

@@ -8,7 +8,7 @@
fix the originals in Crowdin.
The pure predicates live in `metabase.util.i18n.validation` so they can be shared with the
macro-time call-site check (`metabase.util.i18n/validate-number-of-args`) and the regression-
macro-time call-site check (`metabase.util.i18n.validation/validate-number-of-args`) and the regression-
guard test (`metabase.util.i18n.validation-test`) without pulling in jgettext."
(:require
[clojure.string :as str]

View File

@@ -12,7 +12,7 @@
[metabase.api.common :as api]
[metabase.api.macros :as api.macros]
[metabase.app-db.core :as mdb]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))

View File

@@ -3,7 +3,7 @@
[clojure.string :as str]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting config-from-file-sync-databases
"Whether to (asynchronously) sync newly created Databases during config-from-file initialization. By default, true,

View File

@@ -3,7 +3,7 @@
(:require
[metabase.api.open-api :as open-api]
[metabase.premium-features.core :as premium-features]
[metabase.util.i18n :as i18n]))
[metabase.util.i18n-be.core :as i18n-be]))
(defn +require-premium-feature
"Wraps Ring `handler`. Check that we have a premium token with `feature` (a keyword; see
@@ -16,7 +16,7 @@
Use [[when-premium-feature]] instead if you want the handler to apply if we have the premium feature but pass-thru
if we do not."
[feature feature-name handler]
(assert (i18n/localized-string? feature-name), "`feature-name` must be i18ned")
(assert (i18n-be/localized-string? feature-name), "`feature-name` must be i18ned")
(open-api/handler-with-open-api-spec
(fn [request respond raise]
(premium-features/assert-has-feature feature feature-name)

View File

@@ -47,7 +47,7 @@
[metabase-enterprise.workspaces.api]
[metabase.api.macros :as api.macros]
[metabase.api.util.handlers :as handlers]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(comment metabase-enterprise.advanced-config.api.logs/keep-me)

View File

@@ -13,6 +13,7 @@
[metabase.sync.core :as sync]
[metabase.sync.util :as sync-util]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.files :as u.files]
[metabase.util.json :as json]
[metabase.util.log :as log]
@@ -214,10 +215,10 @@
files (single hash over sorted pairs)."
([root] (directory-content-checksum root ""))
([^Path root suffix]
(with-open [stream (Files/walk root (u/varargs FileVisitOption))]
(with-open [stream (Files/walk root (util-be/varargs FileVisitOption))]
(->> (iterator-seq (.iterator stream))
(filter (fn [^Path p]
(and (Files/isRegularFile p (u/varargs LinkOption))
(and (Files/isRegularFile p (util-be/varargs LinkOption))
(str/ends-with? (str (.getFileName p)) suffix))))
(mapv (fn [^Path path]
[(str (.relativize root path))

View File

@@ -11,7 +11,7 @@
[metabase.store-api.core :as store-api]
[metabase.util :as u]
[metabase.util.date-2.parse :as u.date.parse]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.json :as json]
[toucan2.core :as t2])
(:import
@@ -67,7 +67,7 @@
[]
(let [token (premium-features/premium-embedding-token)
email (t2/select-one-fn :email :model/User :id api/*current-user-id*)
language (i18n/user-locale-string)]
language (i18n-be/user-locale-string)]
(if (and token (str/starts-with? token "airgap_"))
(billing-status)
(fetch-billing-status* token email language))))

View File

@@ -9,7 +9,8 @@
[metabase.events.core :as events]
[metabase.premium-features.core :as premium-features]
[metabase.store-api.core :as store-api]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]))
(def ^:private requires-terms-of-service?

View File

@@ -7,7 +7,8 @@
[clojure.string :as str]
[metabase.premium-features.core :as premium-features]
[metabase.util :as u]
[metabase.util.i18n :as i18n :refer [tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :as i18n-be]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)
@@ -30,7 +31,7 @@
(defn- collect-locale-error
"Returns an error message if a row does not have a valid locale."
[_state index {:keys [locale]}]
(when (not (i18n/included-locale? locale))
(when (not (i18n-be/included-locale? locale))
(tru "Row {0}: Invalid locale: {1}" (adjust-index index) locale)))
(defn- collect-duplication-error
@@ -58,7 +59,7 @@
(get row-with-headers "locale code"))
msgid (get row-with-headers "string")
msgstr (get row-with-headers "translation")
normalized-locale (i18n/normalized-locale-string (str/trim locale))
normalized-locale (i18n-be/normalized-locale-string (str/trim locale))
formatted-locale (if (nil? normalized-locale)
(str/trim locale)
normalized-locale)

View File

@@ -9,7 +9,8 @@
[metabase.api.macros :as api.macros]
[metabase.content-translation.models :as ct]
[metabase.embedding.jwt :as embedding.jwt]
[metabase.util.i18n :as i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :as i18n-be :refer [deferred-tru]]
[metabase.util.malli.schema :as ms]))
(set! *warn-on-reflection* true)
@@ -98,7 +99,7 @@
;; this will error if bad
(embedding.jwt/unsign token)
(if locale
{:data (ct/get-translations (i18n/normalized-locale-string (str/trim locale)))}
{:data (ct/get-translations (i18n-be/normalized-locale-string (str/trim locale)))}
(throw (ex-info (str (tru "Locale is required.")) {:status-code 400}))))
(api.macros/defendpoint :get "/dictionary" :- DictionaryResponse
@@ -106,7 +107,7 @@
[_route-params
{:keys [locale]} :- [:map [:locale :string]]]
(api/check api/*current-user-id* 401 "Unauthenticated")
{:data (ct/get-translations (i18n/normalized-locale-string (str/trim locale)))})
{:data (ct/get-translations (i18n-be/normalized-locale-string (str/trim locale)))})
(defn- +require-content-translation [handler]
(ee.api/+require-premium-feature :content-translation (deferred-tru "Content translation") handler))

View File

@@ -4,7 +4,7 @@
[metabase-enterprise.content-verification.api.moderation-review]
[metabase.api.macros :as api.macros]
[metabase.api.routes.common :refer [+auth]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(comment metabase-enterprise.content-verification.api.moderation-review/keep-me)

View File

@@ -3,7 +3,8 @@
[metabase.premium-features.core :refer [defenterprise]]
[metabase.server.settings :as server.settings]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru tru]]))
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting custom-viz-plugin-dev-mode-enabled
(deferred-tru "Whether custom visualization plugin dev mode is enabled. When false, the dev endpoints are disabled.")

View File

@@ -3,7 +3,7 @@
(:require
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting ^:deprecated data-complexity-scoring-enabled
(deferred-tru

View File

@@ -2,7 +2,7 @@
(:require
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting database-replication-enabled
(deferred-tru "When enabled, we show users a button to replicate their database to a data warehouse.")

View File

@@ -5,7 +5,7 @@
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.json :as json]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]

View File

@@ -2,7 +2,7 @@
"Enterprise-only metabot settings for usage limits."
(:require
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(def ^:private valid-limit-units #{:tokens :messages})

View File

@@ -6,7 +6,7 @@
[metabase-enterprise.remote-sync.source.git :as git]
[metabase.collections.models.collection :as collection]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)

View File

@@ -8,7 +8,7 @@
[metabase.api.macros :as api.macros]
[metabase.api.routes.common :refer [+auth]]
[metabase.api.util.handlers :as handlers]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(comment metabase-enterprise.sandbox.api.table/keep-me)

View File

@@ -4,7 +4,7 @@
[metabase.api.common :as api]
[metabase.api.macros :as api.macros]
[metabase.tenants.core :as tenants]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))

View File

@@ -27,6 +27,7 @@
[metabase.query-processor.util.persisted-cache :as qp.persisted]
[metabase.request.core :as request]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -165,7 +166,7 @@
persisted? (qp.persisted/can-substitute? card persisted-info)
query (lib/card->underlying-query metadata-providerable card)]
;; log the query at this point, it's useful for some purposes
(log/debugf "Fetched query from Card %s:\n%s" card-id (u/cprint-to-str (select-keys query [:stages :parameters])))
(log/debugf "Fetched query from Card %s:\n%s" card-id (util-be/cprint-to-str (select-keys query [:stages :parameters])))
(cond-> query
;; This will be applied, if still appropriate, by the persistence middleware
persisted?
@@ -372,8 +373,8 @@
replacement-stages (cond-> new-source-stages
wrapper-stage (conj wrapper-stage))]
(log/tracef "Applied Sandbox: replaced stage\n\n%s\n\nwith stages\n\n%s"
(u/cprint-to-str stage)
(u/cprint-to-str replacement-stages))
(util-be/cprint-to-str stage)
(util-be/cprint-to-str replacement-stages))
replacement-stages))
(mu/defn- apply-sandboxes :- ::lib.schema/query

View File

@@ -2,7 +2,7 @@
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.system.core :as system]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting scim-enabled
(deferred-tru "Is SCIM currently enabled?")

View File

@@ -11,7 +11,7 @@
[metabase.permissions.core :as perms]
[metabase.users.schema :as users.schema]
[metabase.util :as u]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[ring.util.codec :as codec]
@@ -183,7 +183,7 @@
:is_active is-active?
:type :personal
:sso_source "scim"}
(when (and locale (i18n/available-locale? locale))
(when (and locale (i18n-be/available-locale? locale))
{:locale locale}))))
(mu/defn ^:private get-user-by-entity-id

View File

@@ -3,7 +3,7 @@
(:require
[metabase.permissions.core :as perms]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting security-center-email-recipients
(deferred-tru "List of email recipients for Security Center notifications.")

View File

@@ -10,6 +10,7 @@
[metabase.premium-features.core :as premium-features]
[metabase.tracing.core :as tracing]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli :as mu])
@@ -75,7 +76,7 @@
[data]
(vec
(for [{:keys [embedding]} data]
(let [bytes (u/decode-base64-to-bytes ^String embedding)
(let [bytes (util-be/decode-base64-to-bytes ^String embedding)
buffer (doto (java.nio.ByteBuffer/wrap bytes)
(.order java.nio.ByteOrder/LITTLE_ENDIAN))
length (/ (alength bytes) 4)
@@ -390,24 +391,24 @@
{:search.semantic/provider provider
:search.semantic/model-name model-name
:search.semantic/text-count (count texts)}
(u/profile (str "Generating embeddings " {:model model-name
:dimensions vector-dimensions
:texts (calc-token-metrics texts)})
(if (= "openai" provider)
(let [max-tokens-per-batch (semantic-settings/openai-max-tokens-per-batch)
batches (create-batches max-tokens-per-batch count-tokens texts)
(util-be/profile (str "Generating embeddings " {:model model-name
:dimensions vector-dimensions
:texts (calc-token-metrics texts)})
(if (= "openai" provider)
(let [max-tokens-per-batch (semantic-settings/openai-max-tokens-per-batch)
batches (create-batches max-tokens-per-batch count-tokens texts)
process-batch
(fn [batch-idx batch-texts]
(let [embeddings (u/profile (format "Embedding batch %d/%d %s"
(inc batch-idx) (count batches) (str (calc-token-metrics batch-texts)))
(get-embeddings-batch embedding-model batch-texts opts))
text-embedding-map (zipmap batch-texts embeddings)]
(process-fn text-embedding-map)))]
(transduce (map-indexed process-batch) (partial merge-with +) batches))
(let [embeddings (get-embeddings-batch embedding-model texts opts)
text-embedding-map (zipmap texts embeddings)]
(process-fn text-embedding-map))))))))
process-batch
(fn [batch-idx batch-texts]
(let [embeddings (util-be/profile (format "Embedding batch %d/%d %s"
(inc batch-idx) (count batches) (str (calc-token-metrics batch-texts)))
(get-embeddings-batch embedding-model batch-texts opts))
text-embedding-map (zipmap batch-texts embeddings)]
(process-fn text-embedding-map)))]
(transduce (map-indexed process-batch) (partial merge-with +) batches))
(let [embeddings (get-embeddings-batch embedding-model texts opts)
text-embedding-map (zipmap texts embeddings)]
(process-fn text-embedding-map))))))))
(comment
;; Configuration:

View File

@@ -24,6 +24,7 @@
[metabase-enterprise.semantic-search.settings :as semantic.settings]
[metabase.analytics-interface.core :as analytics]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[next.jdbc :as jdbc]
@@ -62,7 +63,7 @@
:updated_at default-updated-at})
(defn- document-hash [search-doc]
(u/encode-base64-bytes (buddy-hash/sha1 (json/encode (into (sorted-map) search-doc)))))
(util-be/encode-base64-bytes (buddy-hash/sha1 (json/encode (into (sorted-map) search-doc)))))
(defn- ->instant
^Instant [inst]

View File

@@ -18,6 +18,7 @@
[metabase.search.core :as search]
[metabase.tracing.core :as tracing]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[next.jdbc :as jdbc]
@@ -220,12 +221,12 @@
[connectable table-name records->sql documents]
(when (seq documents)
(u/prog1 (->> documents (map :model) frequencies)
(u/profile (str "Semantic index database update of " (count documents) " documents " <>)
(let [owner-ids (batch-resolve-personal-owner-ids (map :collection_id documents))]
(doseq [batch (->> (map #(doc->db-record owner-ids %) documents)
(partition-all *batch-size*))]
(jdbc/execute! connectable (records->sql batch))))
(analytics-set-index-size! connectable table-name)))))
(util-be/profile (str "Semantic index database update of " (count documents) " documents " <>)
(let [owner-ids (batch-resolve-personal-owner-ids (map :collection_id documents))]
(doseq [batch (->> (map #(doc->db-record owner-ids %) documents)
(partition-all *batch-size*))]
(jdbc/execute! connectable (records->sql batch))))
(analytics-set-index-size! connectable table-name)))))
(defn- execute-with-counts [connectable model ids sql]
(jdbc/execute! connectable sql)
@@ -341,20 +342,20 @@
embeddable-texts (keys text->docs)
upsert-embedding! (upsert-embedding!-fn connectable index text->docs)
[new-texts stats]
(u/profile (str "Semantic search embedding caching attempt for " {:docs (count documents) :texts (count embeddable-texts)})
(let [[new-texts existing-embeddings] (partition-existing-embeddings connectable index embeddable-texts)]
(if-not (seq existing-embeddings)
[embeddable-texts nil]
(u/profile (str "Semantic search cached embedding db update for " {:texts (count existing-embeddings)})
[new-texts (upsert-embedding! existing-embeddings)]))))]
(util-be/profile (str "Semantic search embedding caching attempt for " {:docs (count documents) :texts (count embeddable-texts)})
(let [[new-texts existing-embeddings] (partition-existing-embeddings connectable index embeddable-texts)]
(if-not (seq existing-embeddings)
[embeddable-texts nil]
(util-be/profile (str "Semantic search cached embedding db update for " {:texts (count existing-embeddings)})
[new-texts (upsert-embedding! existing-embeddings)]))))]
(->>
(when (seq new-texts)
(u/profile (str "Semantic search embedding generation and db update for " {:docs (count documents) :texts (count new-texts)})
(embedding/process-embeddings-streaming
(:embedding-model index)
new-texts
upsert-embedding!
opts)))
(util-be/profile (str "Semantic search embedding generation and db update for " {:docs (count documents) :texts (count new-texts)})
(embedding/process-embeddings-streaming
(:embedding-model index)
new-texts
upsert-embedding!
opts)))
(merge-with + stats)))))
(def ^:private ^:dynamic *retrying* false)

View File

@@ -4,7 +4,7 @@
[metabase.premium-features.core :as premium-features]
[metabase.search.config :as search.config]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(def ^:private valid-embedding-providers
"The set of valid embedding provider names."

View File

@@ -14,6 +14,7 @@
[metabase.session.core :as session]
[metabase.system.core :as system]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
@@ -105,7 +106,7 @@
:idp-url (sso-settings/saml-identity-provider-slo-uri)
:issuer (sso-settings/saml-application-name)
:user-email email
:relay-state (u/encode-base64
:relay-state (util-be/encode-base64
(str (system/site-url) metabase-slo-redirect-url))))}))
;; POST /auth/sso/to_session

View File

@@ -44,6 +44,7 @@
[metabase.session.core :as session]
[metabase.system.core :as system]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [trs tru]]
[metabase.util.log :as log]
[ring.util.response :as response]
@@ -135,7 +136,7 @@
"Process the RelayState to extract continue URL and related parameters"
[relay-state]
(let [continue-url (u/ignore-exceptions
(when-let [s (some-> relay-state u/decode-base64)]
(when-let [s (some-> relay-state util-be/decode-base64)]
(when-not (str/blank? s)
s)))
;; Extract token value from URL parameter

View File

@@ -9,6 +9,7 @@
[metabase.sso.core :as sso]
[metabase.system.core :as system]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.log :as log]
[methodical.core :as methodical]
@@ -77,7 +78,7 @@
(try
(let [idp-url (sso-settings/saml-identity-provider-uri)
relay-state (when redirect-url
(u/encode-base64 redirect-url))
(util-be/encode-base64 redirect-url))
response (saml/idp-redirect-response {:request-id (str "id-" (random-uuid))
:sp-name (sso-settings/saml-application-name)
:issuer (sso-settings/saml-application-name)

View File

@@ -8,7 +8,8 @@
[metabase.appearance.core :as appearance]
[metabase.settings.core :as setting :refer [define-multi-setting-impl defsetting]]
[metabase.system.core :as system]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]

View File

@@ -1,7 +1,7 @@
(ns metabase-enterprise.stale.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting dismissed-collection-cleanup-banner
(deferred-tru "Was the collection cleanup banner dismissed?")

View File

@@ -1,7 +1,7 @@
(ns metabase-enterprise.support-access-grants.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting support-access-grant-email
(deferred-tru "Email to grant temporary access to for support")

View File

@@ -8,7 +8,7 @@
[metabase.collections.models.collection :as collection]
[metabase.events.core :as events]
[metabase.request.core :as request]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]
[metabase.util.malli.schema :as ms]

View File

@@ -6,7 +6,8 @@
[metabase.models.interface :as mi]
[metabase.premium-features.core :refer [defenterprise]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[methodical.core :as methodical]

View File

@@ -10,7 +10,7 @@
[metabase.api.util.handlers :as handlers]
[metabase.permissions.core :as perms]
[metabase.transforms-base.util :as transforms-base.u]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))
@@ -48,7 +48,7 @@
(api.macros/defendpoint :post "/test-run"
:- [:map
[:logs :string]
[:error {:optional true} [:map [:message i18n/LocalizedString]]]
[:error {:optional true} [:map [:message i18n-be/LocalizedString]]]
[:output {:optional true} [:map
[:cols [:sequential [:map [:name :string]]]]
[:rows [:sequential :any]]]]]
@@ -68,7 +68,7 @@
[:output_row_limit {:optional true} [:and :int [:> 1] [:<= 100]]]
[:per_input_row_limit {:optional true} [:and :int [:> 1] [:<= 100]]]]]
(let [db-ids (t2/select-fn-set :db_id [:model/Table :db_id] :id [:in (map :table_id source_tables)])]
(api/check-400 (= (count db-ids) 1) (i18n/deferred-tru "All source tables must belong to the same database."))
(api/check-400 (= (count db-ids) 1) (i18n-be/deferred-tru "All source tables must belong to the same database."))
(api/check-403 (perms/has-db-transforms-permission? api/*current-user-id* (first db-ids))))
;; NOTE: we do not test database support, as there is no write target.
(let [result (python-runner/execute-and-read-output!

View File

@@ -18,6 +18,7 @@
[metabase.transforms-base.util :as transforms-base.u]
[metabase.transforms.instrumentation :as transforms.instrumentation]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.format :as u.format]
[metabase.util.i18n :as i18n]
[metabase.util.log :as log]
@@ -117,9 +118,9 @@
"Transfer data using the rename-tables*! multimethod with atomicity guarantees."
[driver db-id table-name metadata data-source]
(let [source-table-name (transforms-base.u/temp-table-name driver (namespace table-name))
temp-table-name (u/poll {:thunk #(transforms-base.u/temp-table-name driver (namespace table-name))
:done? #(not= source-table-name %)
:interval-ms 1})]
temp-table-name (util-be/poll {:thunk #(transforms-base.u/temp-table-name driver (namespace table-name))
:done? #(not= source-table-name %)
:interval-ms 1})]
(log/info "Using rename-tables strategy with atomicity guarantees")
(try
(create-table-and-insert-data! driver db-id (table-schema source-table-name metadata) data-source)
@@ -261,7 +262,7 @@
:events events
:timeout (:timeout body)}))
(try
(let [temp-path (Files/createTempFile "transform-output-" ".jsonl" (u/varargs FileAttribute))
(let [temp-path (Files/createTempFile "transform-output-" ".jsonl" (util-be/varargs FileAttribute))
temp-file ^File (.toFile temp-path)]
(when-not (seq (:fields output-manifest))
(throw (ex-info "No fields in metadata"

View File

@@ -18,6 +18,7 @@
[metabase.transforms.instrumentation :as transforms.instrumentation]
[metabase.util :as u]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -371,12 +372,12 @@
(:timeout body)
{:status :failed
:logs events
:message (i18n/deferred-tru "Python execution timed out")}
:message (i18n-be/deferred-tru "Python execution timed out")}
(not= 200 status)
{:status :failed
:logs events
:message (i18n/deferred-tru "Python execution failure (exit code {0})" (:exit_code body "?"))}
:message (i18n-be/deferred-tru "Python execution failure (exit code {0})" (:exit_code body "?"))}
:else
(let [output-manifest (read-output-manifest @shared-storage-ref)
@@ -386,7 +387,7 @@
#_(if-not (seq fields)
{:status :failed
:logs events
:message (i18n/deferred-tru "No fields in output metadata")})
:message (i18n-be/deferred-tru "No fields in output metadata")})
(with-open [in (open-output @shared-storage-ref)
rdr (io/reader in)]
(let [cols (mapv (fn [c]

View File

@@ -2,7 +2,7 @@
(:require
[metabase.config.core :as config]
[metabase.settings.core :as setting]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(setting/defsetting python-runner-url
(deferred-tru "URL for the Python execution server that runs transform functions.")

View File

@@ -1,7 +1,7 @@
(ns metabase-enterprise.workspaces.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(def keep-me
"Marker so callers can `(comment ...keep-me)` to retain the require that registers the settings in this ns."

View File

@@ -13,7 +13,7 @@
[metabase.test :as mt]
[metabase.transforms.core :as transforms]
[metabase.transforms.test-util :refer [with-transform-cleanup!]]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)
@@ -319,7 +319,7 @@
(replacement.protocols/advance! progress)))]
;; Poll until done
(replacement.execute/execute-async! work-fn progress)
(u/deref-with-timeout done? 2000)
(util-be/deref-with-timeout done? 2000)
(is (=? {:total 3 :so-far 3 :status :succeeded}
@state))))))

View File

@@ -6,7 +6,7 @@
[metabase-enterprise.replacement.models.replacement-run :as replacement-run]
[metabase-enterprise.replacement.protocols :as replacement.protocols]
[metabase.test :as mt]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)
@@ -30,7 +30,7 @@
(replacement.protocols/advance! progress)))
progress)
;; wait for virtual thread
(is (= :run/success (u/deref-with-timeout done? 500)))
(is (= :run/success (util-be/deref-with-timeout done? 500)))
;; Writes at items 50, 100, 120 (final) → progress 50/120, 100/120, 120/120
(is (= [(double (/ 50 120)) (double (/ 100 120)) 1.0]
@writes))
@@ -57,7 +57,7 @@
(replacement.protocols/advance! progress 30)
(replacement.protocols/advance! progress 10))
progress)
(is (= :run/success (u/deref-with-timeout done? 500)))
(is (= :run/success (util-be/deref-with-timeout done? 500)))
;; advance(30): 0→30, crosses 0/50→0/50 boundary? quot(0,50)=0, quot(30,50)=0 → no
;; advance(30): 30→60, crosses? quot(30,50)=0, quot(60,50)=1 → yes, writes 0.6
;; advance(30): 60→90, crosses? quot(60,50)=1, quot(90,50)=1 → no
@@ -91,7 +91,7 @@
(replacement-run/cancel-run! (:id record))
(deliver pause :continue)
;; Poll until run is no longer active (cancel-run! flips is_active)
(is (= :run/cancelled (u/deref-with-timeout done? 500)))
(is (= :run/cancelled (util-be/deref-with-timeout done? 500)))
;; First boundary write at 50/100 succeeds, canceled? is false.
;; cancel-run! sets is_active=nil. Next boundary at 100 writes progress,
;; then checks canceled? → true → throws. Both writes happen.
@@ -112,7 +112,7 @@
(throw (ex-info "1 of 5 entities failed\n [:card 42]: boom"
{:failures [{:entity [:card 42] :error "boom"}]})))
progress)
(is (= :run/fail (u/deref-with-timeout done? 500)))
(is (= :run/fail (util-be/deref-with-timeout done? 500)))
(let [run (t2/select-one :model/ReplacementRun :id (:id record))]
(is (= :failed (:status run)))
(is (str/starts-with? (:message run) "1 of 5 entities failed"))))))))

View File

@@ -10,7 +10,7 @@
[metabase-enterprise.semantic-search.index-metadata :as semantic.index-metadata]
[metabase-enterprise.semantic-search.test-util :as semantic.tu]
[metabase.test :as mt]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[next.jdbc :as jdbc]
[next.jdbc.result-set :as jdbc.rs])
@@ -46,7 +46,7 @@
:document (doto (PGobject.)
(.setType "jsonb")
(.setValue (json/encode search-doc)))
:document_hash (u/encode-base64-bytes (buddy-hash/sha1 (json/encode (into (sorted-map) search-doc))))
:document_hash (util-be/encode-base64-bytes (buddy-hash/sha1 (json/encode (into (sorted-map) search-doc))))
:updated_at (:updated_at search-doc)}
(sut search-doc t2)))
(testing "uses default updated_at when search doc has none"
@@ -80,7 +80,7 @@
(walk/postwalk
(fn [x]
(cond
(bytes? x) (u/encode-base64-bytes x)
(bytes? x) (util-be/encode-base64-bytes x)
(inst? x) (inst-ms x)
(instance? PGobject x) (json/decode (.getValue ^PGobject x))
:else x))

View File

@@ -18,6 +18,7 @@
[metabase.test.fixtures :as fixtures]
[metabase.test.http-client :as client]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[ring.util.codec :as codec]
[saml20-clj.core :as saml]
[toucan2.core :as t2])
@@ -188,7 +189,7 @@
base-64 (-> location uri->params-map :SAMLRequest)
xml (-> base-64
codec/url-decode
u/decode-base64-to-bytes
util-be/decode-base64-to-bytes
byte-inflate
bytes->str
(str/replace #"\n+" "")
@@ -247,10 +248,10 @@
(testing (format "result = %s" (pr-str result))
(is (string? redirect-url))
(is (= default-redirect-uri
(u/decode-base64 (:RelayState (uri->params-map redirect-url))))))))))))
(util-be/decode-base64 (:RelayState (uri->params-map redirect-url))))))))))))
(defn- saml-response-from-file [filename]
(u/encode-base64 (slurp filename)))
(util-be/encode-base64 (slurp filename)))
(defn- saml-test-response []
(saml-response-from-file "test_resources/saml-test-response.xml"))
@@ -280,7 +281,7 @@
{:request-options {:content-type :x-www-form-urlencoded
:redirect-strategy :none
:form-params {:SAMLResponse saml-response
:RelayState (u/encode-base64 relay-state)}}})
:RelayState (util-be/encode-base64 relay-state)}}})
(defn- extract-csp-script-nonce
"Pull the `'nonce-<value>'` out of the Content-Security-Policy `script-src` directive."
@@ -706,12 +707,12 @@
(testing (format "\nresult =\n%s" (u/pprint-to-str params-map))
(testing "\nRelay state URL should be base-64 encoded"
(is (= redirect-url
(u/decode-base64 (:RelayState params-map)))))
(util-be/decode-base64 (:RelayState params-map)))))
(testing "\nPOST request should redirect to the original redirect URL"
(do-with-some-validators-disabled!
(fn []
(let [req-options (saml-post-request-options (saml-test-response)
(u/decode-base64 (:RelayState params-map)))
(util-be/decode-base64 (:RelayState params-map)))
response (client/client-real-response :post 302 "/auth/sso" req-options)]
(is (successful-login? response))
(is (= redirect-url
@@ -737,7 +738,7 @@
(do-with-some-validators-disabled!
(fn []
(let [req-options (saml-post-request-options (saml-test-response)
(u/decode-base64 (:RelayState params-map)))
(util-be/decode-base64 (:RelayState params-map)))
response (client/client-real-response :post 302 "/auth/sso" req-options)]
(is (successful-login? response))
(is (= (str "http://localhost:3001/path" redirect-url)
@@ -837,7 +838,7 @@
origin (-> (get-in result [:body :url])
uri->params-map
:RelayState
u/decode-base64
util-be/decode-base64
uri->params-map
:origin)]
(is (= "https://app.example.com" origin)))))))
@@ -854,7 +855,7 @@
token (-> (get-in result [:body :url])
uri->params-map
:RelayState
u/decode-base64
util-be/decode-base64
uri->params-map
:token)]
(is (not (nil? token)))))))))

View File

@@ -13,7 +13,7 @@
[metabase.session.api :as api.session]
[metabase.session.core :as session]
[metabase.test :as mt]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[toucan2.core :as t2]))
(use-fixtures :each (fn [f] (mt/with-premium-features #{:support-access-grants}
@@ -314,7 +314,7 @@
(testing "POST /api/session/forgot_password - Support user with no active grant gets no email"
(with-redefs [api.session/forgot-password-impl
(let [orig @#'api.session/forgot-password-impl]
(fn [& args] (u/deref-with-timeout (apply orig args) 1000)))]
(fn [& args] (util-be/deref-with-timeout (apply orig args) 1000)))]
(mt/with-temp [:model/User user {:first_name "support"
:last_name "user"
:email "support@example.com"}
@@ -332,7 +332,7 @@
(testing "POST /api/session/forgot_password - Support user with active grant gets a refreshed token"
(with-redefs [api.session/forgot-password-impl
(let [orig @#'api.session/forgot-password-impl]
(fn [& args] (u/deref-with-timeout (apply orig args) 1000)))]
(fn [& args] (util-be/deref-with-timeout (apply orig args) 1000)))]
(mt/with-temp [:model/User {creator-id :id} {}]
(mt/with-model-cleanup [:model/SupportAccessGrantLog :model/AuthIdentity :model/User]
(with-redefs [sag.settings/support-access-grant-email (constantly "support-forgot@example.com")
@@ -370,7 +370,7 @@
(testing "Consuming a refreshed support-access token via reset_password keeps the support-access-grant provider"
(with-redefs [api.session/forgot-password-impl
(let [orig @#'api.session/forgot-password-impl]
(fn [& args] (u/deref-with-timeout (apply orig args) 1000)))]
(fn [& args] (util-be/deref-with-timeout (apply orig args) 1000)))]
(mt/with-temp [:model/User {creator-id :id} {}]
(mt/with-model-cleanup [:model/SupportAccessGrantLog :model/AuthIdentity :model/User]
(with-redefs [sag.settings/support-access-grant-email (constantly "support-reset@example.com")

View File

@@ -7,7 +7,7 @@
[metabase-enterprise.transforms-python.s3 :as s3]
[metabase.transforms-base.util :as transforms-base.u]
[metabase.transforms.instrumentation :as transforms.instrumentation]
[metabase.util :as u])
[metabase.util-be.core :as util-be])
(:import
(java.io ByteArrayInputStream File)
(java.nio.file Files)
@@ -18,7 +18,7 @@
(defn- jsonl-temp-file
^File [^String contents]
(let [path (Files/createTempFile "transforms-python-base-test-" ".jsonl"
(u/varargs FileAttribute))
(util-be/varargs FileAttribute))
f (.toFile path)]
(.deleteOnExit f)
(spit f contents)

View File

@@ -11,7 +11,7 @@
[metabase.transforms.schedule :as transforms.schedule]
[metabase.transforms.test-dataset :as transforms-dataset]
[metabase.transforms.test-util :as transforms.tu]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[toucan2.core :as t2]))
(deftest python-transform-scheduled-job-test
@@ -40,14 +40,14 @@
:model/TransformJobTransformTag _ {:job_id job-id :tag_id tag-id :position 0}]
(transforms.schedule/initialize-job! job)
(transforms.schedule/update-job! job-id "* * * * * ? *")
(is (true? (u/poll {:thunk (fn [] (driver/table-exists? driver/*driver* (mt/db) target))
:done? true?
:timeout-ms 20000
:interval-ms 1000})))
(is (true? (u/poll {:thunk (fn [] (t2/exists? :model/Table :name (:name target)))
:done? true?
:timeout-ms 10000
:interval-ms 1000})))))))))))
(is (true? (util-be/poll {:thunk (fn [] (driver/table-exists? driver/*driver* (mt/db) target))
:done? true?
:timeout-ms 20000
:interval-ms 1000})))
(is (true? (util-be/poll {:thunk (fn [] (t2/exists? :model/Table :name (:name target)))
:done? true?
:timeout-ms 10000
:interval-ms 1000})))))))))))
(def ^:private python-source {:type "python"})

View File

@@ -11,7 +11,7 @@
[metabase.test :as mt]
[metabase.transforms.test-dataset :as transforms-dataset]
[metabase.transforms.test-util :as transforms.tu :refer [with-transform-cleanup! get-test-schema]]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[toucan2.core :as t2])
(:import
(clojure.lang IDeref)
@@ -405,10 +405,10 @@
;; Cancellation currently overwrites the message, so we should wait for some log output if we expect it
;; Ideally we would not use the message field for log output, or otherwise avoid having logs being lost on cancellation.
_ (when expect-script
(u/poll {:thunk #(deref message-observer)
:done? #(some #{"script started"} %)
:interval-ms 1
:timeout-ms 5000}))
(util-be/poll {:thunk #(deref message-observer)
:done? #(some #{"script started"} %)
:interval-ms 1
:timeout-ms 5000}))
_ (when redefs (await-signal ready-signal))
_ (get-last-run transform-id)
_ (mt/user-http-request :crowberto :post 204 (format "transform/%d/cancel" transform-id))

View File

@@ -344,13 +344,8 @@
;; 2. Your module is infrastructure/gating, not driver logic
;; -- add it to driver-affecting-overrides in mage.modules
;;
;; History:
;; 2026-02-06 Initial count: 37
;; 2026-02-10 Bumped to 38 for sql-tools + sql-parsing
;; 2026-03-10 Bumped to 40 for lib-metric + metrics (Metrics Explorer #68961)
;; Added premium-features to driver-affecting-overrides (#69561)
;; 2026-04-07 Bumped to 41 due to agent-lib addition (Metabot MBQL improvements #71524)
max-allowed-count 41]
;; For the history see the git logs.
max-allowed-count 42]
(is (<= (count modules-triggering-drivers) max-allowed-count)
(format "Too many modules trigger driver tests! Expected <= %d, got %d.
Modules triggering driver tests: %s

View File

@@ -25,6 +25,7 @@
[metabase.driver.sql.util :as sql.u]
[metabase.driver.sync :as driver.s]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.i18n :refer [tru]]
[metabase.util.log :as log]
@@ -147,7 +148,7 @@
[{:keys [dataset-filters-type dataset-filters-patterns] :as details} & {:keys [logging-schema-exclusions?]}]
(let [client (database-details->client details)
project-id (bigquery.common/get-project-id details)
datasets (.listDatasets client project-id (u/varargs BigQuery$DatasetListOption))
datasets (.listDatasets client project-id (util-be/varargs BigQuery$DatasetListOption))
inclusion-patterns (when (= "inclusion" dataset-filters-type) dataset-filters-patterns)
exclusion-patterns (when (= "exclusion" dataset-filters-type) dataset-filters-patterns)]
(for [^Dataset dataset (.iterateAll datasets)
@@ -178,7 +179,7 @@
true)))
(def ^:private empty-table-options
(u/varargs BigQuery$TableOption))
(util-be/varargs BigQuery$TableOption))
(defn- get-table*
[^BigQuery client project-id dataset-id table-id]
@@ -548,7 +549,7 @@
(let [field-idxs (mapv :database_position fields)
all-parsers (get-field-parsers (.. bq-table getDefinition getSchema))
parsers (mapv all-parsers field-idxs)
page (.list bq-table (u/varargs BigQuery$TableDataListOption))]
page (.list bq-table (util-be/varargs BigQuery$TableDataListOption))]
(transduce
(comp (take table-rows-sample/max-sample-rows)
(map (partial extract-fingerprint field-idxs parsers)))
@@ -743,7 +744,7 @@
;; Wrap exception to avoid responding with HTTP 500 and reporting "We're experiencing server issues"
;; in the UI. (#71558)
^Job job (try
(.create client (JobInfo/of request) (u/varargs BigQuery$JobOption))
(.create client (JobInfo/of request) (util-be/varargs BigQuery$JobOption))
(catch Throwable t
(handle-bigquery-exception t sql parameters)))
job-id (.getJobId job)
@@ -753,7 +754,7 @@
(try
(*page-callback*)
(let [result-options (if *page-size* [(BigQuery$QueryResultsOption/pageSize *page-size*)] [])
result (.getQueryResults job (u/varargs BigQuery$QueryResultsOption result-options))]
result (.getQueryResults job (util-be/varargs BigQuery$QueryResultsOption result-options))]
(if result
(deliver result-promise [:ready result])
(throw (ex-info "Null response from query" {}))))
@@ -799,7 +800,7 @@
(try
(thunk)
(catch Throwable e
(let [ex-data (u/all-ex-data e)]
(let [ex-data (util-be/all-ex-data e)]
(if (:retryable? ex-data)
(thunk)
(throw e)))))))
@@ -1100,7 +1101,7 @@
(let [client (database-details->client conn-spec) ;; for bigquery, connection spec *is* the details
project-id (bigquery.common/get-project-id conn-spec)
dataset-id (DatasetId/of project-id schema)]
(when-not (.getDataset client dataset-id (u/varargs BigQuery$DatasetOption))
(when-not (.getDataset client dataset-id (util-be/varargs BigQuery$DatasetOption))
;; Dataset doesn't exist, try to create it
(let [sql [[(format "CREATE SCHEMA IF NOT EXISTS `%s`;" schema)]]]
(driver/execute-raw-queries! driver conn-spec sql)))))

View File

@@ -14,7 +14,7 @@
[metabase.driver.bigquery-cloud-sdk :as bigquery]
[metabase.driver.bigquery-cloud-sdk.common :as bigquery.common]
[metabase.driver.bigquery-cloud-sdk.workspaces :as bigquery.ws]
[metabase.util :as u])
[metabase.util-be.core :as util-be])
(:import
(com.google.api.gax.core CredentialsProvider)
(com.google.auth.oauth2 ImpersonatedCredentials ServiceAccountCredentials)
@@ -100,7 +100,7 @@
[^BigQuery client project-id dataset-name]
(.create client
(.build (DatasetInfo/newBuilder (DatasetId/of project-id dataset-name)))
(u/varargs BigQuery$DatasetOption [])))
(util-be/varargs BigQuery$DatasetOption [])))
(defn drop-dataset!
"Drop a BQ dataset and its contents. Idempotent: no-op if not present.
@@ -109,18 +109,18 @@
that side-effect."
[^BigQuery client project-id dataset-name]
(let [ds-id (DatasetId/of project-id dataset-name)]
(when (.getDataset client ds-id (u/varargs BigQuery$DatasetOption []))
(when (.getDataset client ds-id (util-be/varargs BigQuery$DatasetOption []))
(.delete client ds-id
(u/varargs BigQuery$DatasetDeleteOption
[(BigQuery$DatasetDeleteOption/deleteContents)])))))
(util-be/varargs BigQuery$DatasetDeleteOption
[(BigQuery$DatasetDeleteOption/deleteContents)])))))
(defn list-tables
"Return a vector of `{:schema dataset :table table-id}` maps for every table
in `dataset-name`. Empty vector when the dataset doesn't exist."
[^BigQuery client project-id dataset-name]
(let [ds-id (DatasetId/of project-id dataset-name)]
(if-let [_ds (.getDataset client ds-id (u/varargs BigQuery$DatasetOption []))]
(vec (for [^Table t (.iterateAll (.listTables client ds-id (u/varargs BigQuery$TableListOption [])))]
(if-let [_ds (.getDataset client ds-id (util-be/varargs BigQuery$DatasetOption []))]
(vec (for [^Table t (.iterateAll (.listTables client ds-id (util-be/varargs BigQuery$TableListOption [])))]
{:schema dataset-name :table (.. t getTableId getTable)}))
[])))
@@ -210,7 +210,7 @@
[project-id ^BigQuery admin-client out-dataset]
(testing "workspace output dataset is dropped"
(let [ds-id (DatasetId/of project-id out-dataset)
ds (.getDataset admin-client ds-id (u/varargs BigQuery$DatasetOption []))]
ds (.getDataset admin-client ds-id (util-be/varargs BigQuery$DatasetOption []))]
(is (nil? ds)
(format "output dataset %s should be removed after destroy" out-dataset)))))

View File

@@ -27,6 +27,7 @@
[metabase.test.data.interface :as tx]
[metabase.test.data.sql :as sql.tx]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli.schema :as ms]
@@ -664,11 +665,11 @@
the middleware. The distinct values must still come back correctly."
(mt/test-driver :bigquery-cloud-sdk
(mt/dataset native-dataset
(let [category-field-id (u/auto-retry 1
(try (mt/id :fv_partitioned_table :category)
(catch Exception e
(sync/sync-database! (mt/db) {:scan :schema})
(throw e))))
(let [category-field-id (util-be/auto-retry 1
(try (mt/id :fv_partitioned_table :category)
(catch Exception e
(sync/sync-database! (mt/db) {:scan :schema})
(throw e))))
category-field (t2/select-one :model/Field :id category-field-id)
table (t2/select-one :model/Table :id (:table_id category-field))]
(testing "Dispatcher identifies this as a non-batch-able table"
@@ -688,13 +689,13 @@
:bigquery-cloud-sdk
(mt/dataset
native-dataset
(let [category-field-id (u/auto-retry
(let [category-field-id (util-be/auto-retry
1
(try
(mt/id :fv_partitioned_table :category)
(catch Exception e
(sync/sync-database! (mt/db) {:scan :schema})
(throw e))))]
(try
(mt/id :fv_partitioned_table :category)
(catch Exception e
(sync/sync-database! (mt/db) {:scan :schema})
(throw e))))]
(t2/update! :model/Field category-field-id {:has_field_values :search})
(t2/delete! :model/FieldValues :field_id category-field-id)
(= [["coffee"]]

View File

@@ -13,6 +13,7 @@
[metabase.test.data.sql :as sql.tx]
[metabase.transforms.test-util :as transforms.test-util]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -116,7 +117,7 @@
(str/replace table-or-field-name #"-" "_"))
(mu/defn- create-dataset! [^String dataset-id :- ::dataset-id]
(.create (bigquery) (DatasetInfo/of (DatasetId/of (project-id) dataset-id)) (u/varargs BigQuery$DatasetOption))
(.create (bigquery) (DatasetInfo/of (DatasetId/of (project-id) dataset-id)) (util-be/varargs BigQuery$DatasetOption))
(log/info (u/format-color 'blue "Created BigQuery dataset `%s.%s`." (project-id) dataset-id)))
(defn execute!
@@ -147,9 +148,9 @@
(println "Deleting dataset: " dataset-id)
(when (= dataset-id (test-dataset-id (tx/get-dataset-definition (data.impl/resolve-dataset-definition *ns* 'test-data))))
(.printStackTrace (Exception. "This should not happen")))
(.delete (bigquery) dataset-id (u/varargs
BigQuery$DatasetDeleteOption
[(BigQuery$DatasetDeleteOption/deleteContents)]))
(.delete (bigquery) dataset-id (util-be/varargs
BigQuery$DatasetDeleteOption
[(BigQuery$DatasetDeleteOption/deleteContents)]))
(execute-params!
(format "DELETE FROM `%s.metabase_test_tracking.datasets` WHERE `name` = ?"
(project-id))
@@ -201,11 +202,11 @@
(defn- create-table*!
[dataset-id table-id field-definitions]
(let [tbl-id (TableId/of dataset-id table-id)
schema (Schema/of (u/varargs Field (field-definitions->Fields (cons {:field-name "id"
:base-type :type/Integer}
field-definitions))))
schema (Schema/of (util-be/varargs Field (field-definitions->Fields (cons {:field-name "id"
:base-type :type/Integer}
field-definitions))))
tbl (TableInfo/of tbl-id (StandardTableDefinition/of schema))]
(.create (bigquery) tbl (u/varargs BigQuery$TableOption))))
(.create (bigquery) tbl (util-be/varargs BigQuery$TableOption))))
(mu/defn- create-table!
[^String dataset-id :- ::lib.schema.common/non-blank-string
@@ -213,7 +214,7 @@
field-definitions]
(create-table*! dataset-id table-id field-definitions)
;; now verify that the Table was created
(.listTables (bigquery) dataset-id (u/varargs BigQuery$TableListOption))
(.listTables (bigquery) dataset-id (util-be/varargs BigQuery$TableListOption))
(log/info (u/format-color 'blue "Created BigQuery table `%s.%s.%s`." (project-id) dataset-id table-id)))
(defn- table-row-count ^Integer [^String dataset-id, ^String table-id]
@@ -266,7 +267,7 @@
(defn- rows->request ^InsertAllRequest [^String dataset-id ^String table-id row-maps]
(let [insert-rows (map (fn [r]
(InsertAllRequest$RowToInsert/of (str (get r :id)) (->json r))) row-maps)]
(InsertAllRequest/of (TableId/of dataset-id table-id) (u/varargs InsertAllRequest$RowToInsert insert-rows))))
(InsertAllRequest/of (TableId/of dataset-id table-id) (util-be/varargs InsertAllRequest$RowToInsert insert-rows))))
(def ^:private max-rows-per-request
"Max number of rows BigQuery lets us insert at once."

View File

@@ -37,6 +37,7 @@
[metabase.test.data.sql :as sql.tx]
[metabase.test.data.sql.ddl :as ddl]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.log :as log]
@@ -108,7 +109,7 @@
(try (sql-jdbc.conn/connection-details->spec :oracle {:host "localhost"
:port 1521})
(catch Throwable e
(driver/humanize-connection-error-message :oracle (u/all-ex-messages e))))))))
(driver/humanize-connection-error-message :oracle (util-be/all-ex-messages e))))))))
(deftest connection-properties-test
(testing "Connection properties should be returned properly (including transformation of secret types)"

View File

@@ -21,7 +21,7 @@
[metabase.driver.sql.parameters.substitution :as sql.params.substitution]
[metabase.driver.sql.query-processor :as sql.qp]
[metabase.driver.sql.util :as sql.u]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [trs]]
@@ -161,7 +161,7 @@
(defmethod sql.qp/->honeysql [:presto-jdbc (Class/forName "[B")]
[_driver bs]
[:from_base64 (u/encode-base64-bytes bs)])
[:from_base64 (util-be/encode-base64-bytes bs)])
(defmethod sql.qp/->honeysql [:presto-jdbc :time]
[_ [_ t]]

View File

@@ -21,6 +21,7 @@
[metabase.driver.sync :as driver.s]
[metabase.driver.util :as driver.u]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.json :as json]
@@ -170,15 +171,15 @@
;; Redshift sync is super duper flaky and un-robust! This auto-retry is a temporary workaround until we can actually
;; fix #45874
(try
(u/auto-retry (if driver-api/is-prod? 2 5)
(try
{:tables (into #{} (describe-database-tables database))}
(catch Throwable e
;; during test/REPL runs, wait a second before throwing the exception, that way when we do our retry there is
;; a better chance of it succeeding.
(when-not driver-api/is-prod?
(Thread/sleep 1000))
(throw e))))
(util-be/auto-retry (if driver-api/is-prod? 2 5)
(try
{:tables (into #{} (describe-database-tables database))}
(catch Throwable e
;; during test/REPL runs, wait a second before throwing the exception, that way when we do our retry there is
;; a better chance of it succeeding.
(when-not driver-api/is-prod?
(Thread/sleep 1000))
(throw e))))
(catch Throwable e
(throw (ex-info (format "Error in %s describe-database: %s" driver (ex-message e))
{}

View File

@@ -27,6 +27,7 @@
[metabase.test.data.sql :as sql.tx]
[metabase.test.fixtures :as fixtures]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.log :as log]
[toucan2.core :as t2])
@@ -356,13 +357,13 @@
qual-tbl-nm
qual-mview-nm)
(binding [redshift.tx/*override-describe-database-to-filter-by-db-name?* false]
(u/auto-retry 3
(let [table-names (set (map :name (:tables (driver/describe-database :redshift database))))]
(when-not (contains? table-names mview-nm)
(Thread/sleep 1000)
(throw (ex-info "Materialized view not yet visible in describe-database results"
{:expected mview-nm :actual table-names})))
(is (contains? table-names mview-nm)))))))))))
(util-be/auto-retry 3
(let [table-names (set (map :name (:tables (driver/describe-database :redshift database))))]
(when-not (contains? table-names mview-nm)
(Thread/sleep 1000)
(throw (ex-info "Materialized view not yet visible in describe-database results"
{:expected mview-nm :actual table-names})))
(is (contains? table-names mview-nm)))))))))))
(mt/defdataset unix-timestamps
[["timestamps"

View File

@@ -45,6 +45,7 @@
[metabase.test.data.sql :as sql.tx]
[metabase.test.data.sql.ddl :as ddl]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.log.capture :as log.capture]
@@ -65,7 +66,7 @@
(qp/process-query query)
(is false "no statement created")
(catch Exception e
(-> e u/all-ex-data ::native-query))))))
(-> e util-be/all-ex-data ::native-query))))))
(use-fixtures :each (fn [thunk]
;; 1. If sync fails when loading a test dataset, don't swallow the error; throw an Exception so we
@@ -752,11 +753,11 @@
:model/Secret {upload-secret-id :id} {:name "Private key upload for Snowflake"
:kind :pem-cert
:source "uploaded"
:value (u/string-to-bytes pk-key)}
:value (util-be/string-to-bytes pk-key)}
:model/Secret {base64-upload-secret-id :id} {:name "Private key base64 upload for Snowflake"
:kind :pem-cert
:source "uploaded"
:value (mt/bytes->base64-data-uri (u/string-to-bytes pk-key))}]
:value (mt/bytes->base64-data-uri (util-be/string-to-bytes pk-key))}]
(testing "private key authentication via uploaded keys or local key with path stored in a secret"
(spit pk-path pk-key)
(is (can-connect? (-> (:details (mt/db))
@@ -770,10 +771,10 @@
{:private-key-value pk-key
:private-key-options "uploaded"}
;; uploaded byte array
{:private-key-value (mt/bytes->base64-data-uri (u/string-to-bytes pk-key))
{:private-key-value (mt/bytes->base64-data-uri (util-be/string-to-bytes pk-key))
:private-key-options "uploaded"}
;; uploaded byte array without private-key-options
{:private-key-value (mt/bytes->base64-data-uri (u/string-to-bytes pk-key))}
{:private-key-value (mt/bytes->base64-data-uri (util-be/string-to-bytes pk-key))}
;; saved local path
{:private-key-id path-secret-id}
;; saved uploaded bytes
@@ -855,9 +856,9 @@
(let [source (case (:private-key-options details-to-succeed "local")
"local" :file-path
"uploaded" :uploaded)]
(is (=? {:value (u/string-to-bytes (if (= :file-path source)
(:private-key-path details-to-succeed pk-path)
pk-key))
(is (=? {:value (util-be/string-to-bytes (if (= :file-path source)
(:private-key-path details-to-succeed pk-path)
pk-key))
:source source}
(secret/latest-for-id secret-id))))))))))))))))))
@@ -881,7 +882,7 @@
:advanced-options false
:schema-filters-type "all"
:account account
:private-key-value (mt/bytes->base64-data-uri (u/string-to-bytes private-key-value))
:private-key-value (mt/bytes->base64-data-uri (util-be/string-to-bytes private-key-value))
:tunnel-enabled false
:user user}}]
;; TODO: We should make those message returned when role is incorrect more descriptive!
@@ -940,7 +941,7 @@
:advanced-options false
:schema-filters-type "all"
:account account
:private-key-value (mt/bytes->base64-data-uri (u/string-to-bytes private-key-value))
:private-key-value (mt/bytes->base64-data-uri (util-be/string-to-bytes private-key-value))
:tunnel-enabled false
:user user}}]
(testing "Database can be created using _default_ `nil` role"

View File

@@ -21,6 +21,7 @@
[metabase.query-processor.error-type :as qp.error-type]
[metabase.query-processor.middleware.permissions :as qp.perms]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -47,7 +48,7 @@
(binding [qp.perms/*card-id* model-id]
(qp/execute-write-query! query)))
(catch Throwable e
(if (= (:type (u/all-ex-data e)) qp.error-type/missing-required-permissions)
(if (= (:type (util-be/all-ex-data e)) qp.error-type/missing-required-permissions)
(api/throw-403 e)
(throw (ex-info (format "Error executing Action: %s" (ex-message e))
{:action action

View File

@@ -3,7 +3,7 @@
[metabase.lib.schema.id :as lib.schema.id]
[metabase.parameters.schema :as parameters.schema]
[metabase.queries.schema :as queries.schema]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]
[metabase.util.malli.schema :as ms]))

View File

@@ -1,12 +1,12 @@
(ns metabase.actions.settings
(:require
[metabase.settings.core :as setting]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.warehouses.models.database :as database]
[toucan2.core :as t2]))
(setting/defsetting database-enable-actions
(i18n/deferred-tru "Whether to enable Actions for a specific Database.")
(i18n-be/deferred-tru "Whether to enable Actions for a specific Database.")
:default false
:driver-feature :actions
:type :boolean
@@ -18,28 +18,28 @@
(def ^:private db-routing-reason
{:key :setting/database-routing
:type :error
:message (i18n/deferred-tru "Table editing is not supported with database routing.")})
:message (i18n-be/deferred-tru "Table editing is not supported with database routing.")})
(def ^:private no-writable-tables-reason
{:key :permissions/no-writable-table
:type :error
:message (i18n/deferred-tru "Table editing requires at least one table with INSERT, UPDATE, and DELETE support.")})
:message (i18n-be/deferred-tru "Table editing requires at least one table with INSERT, UPDATE, and DELETE support.")})
(def ^:private busy-sync-reason
{:key :database-metadata/sync-in-progress
:type :warning
:message (i18n/deferred-tru "Unable to determine whether the database connection is readonly, as it is still syncing.")})
:message (i18n-be/deferred-tru "Unable to determine whether the database connection is readonly, as it is still syncing.")})
(def ^:private missing-permissions-reason
{:key :database-metadata/not-populated
:type :warning
:message (i18n/deferred-tru "Unable to determine whether the database connection is readonly, as we are missing metadata from sync.")})
:message (i18n-be/deferred-tru "Unable to determine whether the database connection is readonly, as we are missing metadata from sync.")})
;; Marking reasons as warnings allow the user to proactively set the configuration, but it will only come into
;; effect once the warning is resolved.
(setting/defsetting database-enable-table-editing
(i18n/deferred-tru "Whether the Database has table data editing enabled.")
(i18n-be/deferred-tru "Whether the Database has table data editing enabled.")
:default false
:feature :table-data-editing
:driver-feature :actions/data-editing

View File

@@ -33,6 +33,7 @@
[metabase.request.core :as request]
[metabase.server.streaming-response :as streaming-response]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli.registry :as mr]
@@ -271,7 +272,7 @@
_query-params
{:keys [prompt] :as body} :- ::construct-query-request]
(let [query (evaluate-external-query-for-execution body)]
(cond-> {:query (-> query json/encode u/encode-base64)}
(cond-> {:query (-> query json/encode util-be/encode-base64)}
prompt (assoc :prompt prompt))))
;;; ------------------------------------------------- Combined Query -------------------------------------------------
@@ -283,7 +284,7 @@
(-> {:query query-map
:pagination {:limit total-limit :page (inc page)}}
json/encode
u/encode-base64))
util-be/encode-base64))
(defn- decode-continuation-token
"Decode a base64-encoded continuation token into {:query ... :pagination ...}.
@@ -292,7 +293,7 @@
the embedded query happens in [[check-token-query-permissions!]] — a token
doesn't grant access the bearer wouldn't otherwise have."
[token]
(let [decoded (-> token u/decode-base64 json/decode+kw)
(let [decoded (-> token util-be/decode-base64 json/decode+kw)
{:keys [limit page]} (:pagination decoded)]
(api/check (and (int? limit) (pos? limit))
[400 "Invalid continuation token: limit must be a positive integer"])
@@ -490,7 +491,7 @@
_query-params
{encoded-query :query} :- ::execute-query-request]
(let [query (-> encoded-query
u/decode-base64
util-be/decode-base64
json/decode+kw)]
;; `/v1/execute` is gated by the `agent:query:execute` scope and is intended for MBQL.
;; The opaque base64 payload could carry `:type :native`, but `execute_sql` is gated by
@@ -636,7 +637,7 @@
{:keys [query display description collection_id visualization_settings]
question-name :name}
:- ::create-question-request]
(let [dataset-query (-> query u/decode-base64 json/decode+kw)]
(let [dataset-query (-> query util-be/decode-base64 json/decode+kw)]
;; Mirror REST `POST /api/card/` pre-checks before calling `queries/create-card!`.
;; `create-card!` itself does NOT run permissions checks; without these mirroring the
;; REST endpoint, an LLM caller could (a) save a card whose query references data the
@@ -733,7 +734,7 @@
;; canonical MBQL shape regardless of whether the LLM
;; sent legacy or MBQL 5.
(-> (:query body)
u/decode-base64
util-be/decode-base64
json/decode+kw
lib-be/normalize-query)))
;; Set :archived_directly to mirror :archived (mark as Trash if explicitly archived).

View File

@@ -2,7 +2,7 @@
(:require
[metabase.llm.settings :as llm.settings]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting agent-api-enabled?
(deferred-tru "Whether the Agent API is enabled.")

View File

@@ -4,7 +4,7 @@
[metabase.config.core :as config]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.date-2 :as u.date]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[toucan2.core :as t2]))
(defsetting analytics-uuid

View File

@@ -8,7 +8,7 @@
[metabase.analyze.fingerprint.fingerprinters :as fingerprinters]
[metabase.analyze.fingerprint.insights :as insights]
[metabase.query-processor.schema :as query-processor.schema]
[metabase.util.i18n :as i18n]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]
@@ -21,7 +21,7 @@
(mr/def ::ResultsMetadata
(mu/with-api-error-message
[:maybe [:sequential ResultColumnMetadata]]
(i18n/deferred-tru "value must be an array of valid results column metadata maps.")))
(i18n-be/deferred-tru "value must be an array of valid results column metadata maps.")))
(def ResultsMetadata
"Schema for valid values of the `result_metadata` column."

View File

@@ -85,7 +85,8 @@
[metabase.events.core :as events]
[metabase.models.interface :as mi]
[metabase.util :as u]
[metabase.util.i18n :as i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :as i18n-be :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
@@ -142,7 +143,7 @@
(defn- check-one [condition code message]
(when-not condition
(let [[message info] (if (and (map? message)
(not (i18n/localized-string? message)))
(not (i18n-be/localized-string? message)))
[(:message message) message]
[message])]
(throw (ex-info (str message) (assoc info :status-code code)))))

View File

@@ -6,7 +6,7 @@
[metabase.api.open-api :as open-api]
[metabase.api.response :as api.response]
[metabase.api.settings :as api.settings]
[metabase.util.i18n :refer [deferred-trs deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-trs deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]))

View File

@@ -14,6 +14,7 @@
[metabase.permissions.core :as perms]
[metabase.users.models.user :as user]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]
@@ -192,14 +193,14 @@
(mu/defn key-with-unique-prefix :- ::api-keys.schema/key.secret
"Generate an unique API key with a unique prefix."
[]
(u/auto-retry 5
(let [api-key (generate-key)
prefix (prefix (u.secret/expose api-key))]
;; we could make this more efficient by generating 5 API keys up front and doing one select to remove any
;; duplicates. But a duplicate should be rare enough to just do multiple queries for now.
(if-not (t2/exists? :model/ApiKey :key_prefix prefix)
api-key
(throw (ex-info (tru "could not generate key with unique prefix") {}))))))
(util-be/auto-retry 5
(let [api-key (generate-key)
prefix (prefix (u.secret/expose api-key))]
;; we could make this more efficient by generating 5 API keys up front and doing one select to remove any
;; duplicates. But a duplicate should be rare enough to just do multiple queries for now.
(if-not (t2/exists? :model/ApiKey :key_prefix prefix)
api-key
(throw (ex-info (tru "could not generate key with unique prefix") {}))))))
(mu/defn create-api-key-with-new-user!
"Create a new API key and a new user for that key at the same time."

View File

@@ -67,7 +67,7 @@
[metabase.upload.api]
[metabase.user-key-value.api]
[metabase.users-rest.api]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.warehouse-schema-rest.api]
[metabase.warehouses-rest.api]
[metabase.xrays.api]))

View File

@@ -12,6 +12,7 @@
[metabase.classloader.core :as classloader]
[metabase.config.core :as config]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [trs]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -74,7 +75,7 @@
"Check if a table exists."
[table-name ^Connection conn]
(-> (.getMetaData conn)
(.getTables nil nil table-name (u/varargs String ["TABLE"]))
(.getTables nil nil table-name (util-be/varargs String ["TABLE"]))
jdbc/metadata-query
seq
boolean))
@@ -234,16 +235,16 @@
chance the lock will end up clearing up so we can run migrations normally."
[^LockService lock-service]
(let [retry-counter (volatile! 0)]
(u/auto-retry 5
(when-not (.acquireLock lock-service)
(Thread/sleep 2000)
(vswap! retry-counter inc)
(throw
(LockException.
(str
(trs "Database has migration lock; cannot run migrations.")
" "
(trs "You can force-release these locks by running `java --add-opens java.base/java.nio=ALL-UNNAMED -jar metabase.jar migrate release-locks`."))))))
(util-be/auto-retry 5
(when-not (.acquireLock lock-service)
(Thread/sleep 2000)
(vswap! retry-counter inc)
(throw
(LockException.
(str
(trs "Database has migration lock; cannot run migrations.")
" "
(trs "You can force-release these locks by running `java --add-opens java.base/java.nio=ALL-UNNAMED -jar metabase.jar migrate release-locks`."))))))
(if (pos? @retry-counter)
(log/warnf "Migration lock was acquired after %d retries." @retry-counter)
(do (log/info "No migration lock found.")

View File

@@ -19,6 +19,7 @@
[metabase.app-db.liquibase :as liquibase]
[metabase.config.core :as config]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.encryption :as encryption]
[metabase.util.honey-sql-2]
[metabase.util.i18n :refer [trs]]
@@ -258,15 +259,15 @@
data-source :- (ms/InstanceOfClass javax.sql.DataSource)
auto-migrate? :- :boolean
create-sample-content? :- :boolean]
(u/profile (trs "Database setup")
(u/with-us-locale
(binding [mdb.connection/*application-db* (mdb.connection/application-db db-type data-source :create-pool? false) ; should already be a pool
config/*disable-setting-cache* true
custom-migrations/*create-sample-content* create-sample-content?]
(verify-db-connection db-type data-source)
(error-if-downgrade-required! data-source)
(run-schema-migrations! data-source auto-migrate?)
(check-encryption))))
(util-be/profile (trs "Database setup")
(util-be/with-us-locale
(binding [mdb.connection/*application-db* (mdb.connection/application-db db-type data-source :create-pool? false) ; should already be a pool
config/*disable-setting-cache* true
custom-migrations/*create-sample-content* create-sample-content?]
(verify-db-connection db-type data-source)
(error-if-downgrade-required! data-source)
(run-schema-migrations! data-source auto-migrate?)
(check-encryption))))
:done)
(defn release-migration-locks!

View File

@@ -5,7 +5,8 @@
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util :as u]
[metabase.util.fonts :as u.fonts]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)

View File

@@ -1,7 +1,7 @@
(ns metabase.audit-app.settings
(:require
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]))
(defsetting last-analytics-checksum

View File

@@ -57,7 +57,7 @@
[metabase.notification.core :as notification]
[metabase.premium-features.core :refer [defenterprise]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[methodical.core :as methodical]

View File

@@ -1,7 +1,7 @@
(ns metabase.batch-processing.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting synchronous-batch-updates
(deferred-tru "Process batches updates synchronously. If true, all `submit!` calls will be processed immediately. Default is false.")

View File

@@ -1,7 +1,7 @@
(ns metabase.bug-reporting.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting bug-reporting-enabled
(deferred-tru "Enable bug report submissions.")

View File

@@ -2,7 +2,8 @@
(:require
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[toucan2.core :as t2]))
(defsetting enable-query-caching

View File

@@ -10,7 +10,7 @@
[metabase.models.interface :as mi]
[metabase.permissions.core :as perms]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))

View File

@@ -9,6 +9,7 @@
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :as setting]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -307,7 +308,7 @@
(smtp->mb-setting :port mb-to-smtp-map) "Wrong host or port"}}
creds-error {:errors {(smtp->mb-setting :user mb-to-smtp-map) "Wrong username or password"
(smtp->mb-setting :pass mb-to-smtp-map) "Wrong username or password"}}
exceptions (u/full-exception-chain error)
exceptions (util-be/full-exception-chain error)
message (str/join ": " (map ex-message exceptions))
match-error (fn match-error [regex-or-exception-class [message exceptions]]
(cond (instance? java.util.regex.Pattern regex-or-exception-class)

View File

@@ -25,6 +25,7 @@
[metabase.util.date-2 :as u.date]
[metabase.util.encryption :as encryption]
[metabase.util.i18n :as i18n :refer [trs tru]]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
@@ -175,7 +176,7 @@
(let [user-info (or (t2/select-one [:model/User :last_name :first_name :email :locale] :id user-id)
(throw (ex-info (tru "User {0} does not exist" user-id)
{:user-id user-id, :status-code 404})))
user-locale (or (:locale user-info) (i18n/site-locale))
user-locale (or (:locale user-info) (i18n-be/site-locale))
timestamp (u.date/format-human-readable timestamp user-locale)
username (or (:first_name user-info) (:last_name user-info) (:email user-info))
context (merge (common-context)

View File

@@ -7,7 +7,7 @@
[metabase.channel.settings :as channel.settings]
[metabase.channel.shared :as channel.shared]
[metabase.channel.urls :as urls]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.json :as json]
[metabase.util.malli :as mu]
@@ -39,7 +39,7 @@
(mu/defmethod channel/send! :channel/http
[{{:keys [url method auth-method auth-info]} :details} :- HTTPChannel
request]
(when-not (u/valid-host? (channel.settings/http-channel-host-strategy) url)
(when-not (util-be/valid-host? (channel.settings/http-channel-host-strategy) url)
(throw (ex-info (tru "URLs referring to hosts that supply internal hosting metadata are prohibited.")
{:status-code 400})))
(let [req (merge

View File

@@ -19,7 +19,8 @@
[metabase.timeline.core :as timeline]
[metabase.types.core :as types]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-trs trs tru]]
[metabase.util.i18n :refer [trs tru]]
[metabase.util.i18n-be.core :refer [deferred-trs]]
[metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr]
[metabase.util.malli.schema :as ms])

View File

@@ -3,7 +3,8 @@
[clojure.string :as str]
[java-time.api :as t]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli.registry :as mr]
[metabase.util.malli.schema :as ms]
[metabase.util.string :as u.str]))

View File

@@ -8,6 +8,7 @@
[metabase.channel.settings :as channel.settings]
[metabase.events.core :as events]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.date-2 :as u.date]
[metabase.util.i18n :refer [tru]]
[metabase.util.json :as json]
@@ -273,12 +274,12 @@
(throw (ex-info (ex-message e) (assoc (ex-data e) :filename filename)))))
uploaded? (fn [complete-response] (seq (get-in complete-response [:files 0 :filetype])))
_ (when-not (or (uploaded? complete-response)
(u/poll {:thunk complete!
:done? uploaded?
;; Cal 2024-04-30: this typically takes 1-2 seconds to succeed.
;; If it takes more than 20 seconds, something else is wrong and we should abort.
:timeout-ms 20000
:interval-ms 500}))
(util-be/poll {:thunk complete!
:done? uploaded?
;; Cal 2024-04-30: this typically takes 1-2 seconds to succeed.
;; If it takes more than 20 seconds, something else is wrong and we should abort.
:timeout-ms 20000
:interval-ms 500}))
(throw (ex-info "Timed out waiting to confirm the file was uploaded to Slack."
{:filename filename})))]
(get-in complete-response [:files 0 :url_private])))

View File

@@ -15,6 +15,7 @@
[metabase.task.bootstrap :as task.bootstrap]
[metabase.task.core :as task]
[metabase.util :as u]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.json :as json]
[metabase.util.log :as log]
@@ -145,12 +146,12 @@
(defn- put-file
"Put file, whole or from start to end, on url, reporting on-progress. Retries up to 3 times."
[url ^File file on-progress & {:keys [headers start end]}]
(u/auto-retry 3
(with-open [file-stream (progress-file-input-stream file on-progress)]
(let [[stream length] (if (and start end)
[(sub-stream file-stream start end) (- end start)]
[file-stream (.length file)])]
(http/put url {:headers headers :length length :body stream})))))
(util-be/auto-retry 3
(with-open [file-stream (progress-file-input-stream file on-progress)]
(let [[stream length] (if (and start end)
[(sub-stream file-stream start end) (- end start)]
[file-stream (.length file)])]
(http/put url {:headers headers :length length :body stream})))))
;; ~100mb
(def ^:private part-size 100e6)

View File

@@ -3,7 +3,7 @@
[clojure.string :as str]
[metabase.config.core :as config]
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting store-use-staging
(deferred-tru "If staging store should be used instead of prod. True on dev.")

View File

@@ -3,7 +3,8 @@
[metabase.app-db.core :as mdb]
[metabase.auth-identity.core :as auth-identity]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-trs trs]]
[metabase.util.i18n :refer [trs]]
[metabase.util.i18n-be.core :refer [deferred-trs]]
[toucan2.core :as t2]))
(set! *warn-on-reflection* true)

View File

@@ -22,7 +22,8 @@
[metabase.search.spec :as search.spec]
[metabase.util :as u]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [trs tru deferred-tru]]
[metabase.util.i18n :refer [trs tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]

View File

@@ -1,13 +1,13 @@
(ns metabase.collections-rest.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :as i18n]))
[metabase.util.i18n-be.core :as i18n-be]))
(defsetting can-run-adhoc-query-check-threshold
(i18n/deferred-tru (str "Maximum number of cards to compute can_run_adhoc_query for. When the number of cards exceeds "
"this threshold, can_run_adhoc_query will return true for all cards without computing actual "
"permissions. Set to 0 to always compute permissions. This only affects how cards are displayed"
" in the query builder and does not affect actual permission enforcement."))
(i18n-be/deferred-tru (str "Maximum number of cards to compute can_run_adhoc_query for. When the number of cards exceeds "
"this threshold, can_run_adhoc_query will return true for all cards without computing actual "
"permissions. Set to 0 to always compute permissions. This only affects how cards are displayed"
" in the query builder and does not affect actual permission enforcement."))
:type :integer
:export? false
:default 250

View File

@@ -15,7 +15,7 @@
[metabase.users.core :as users]
[metabase.users.models.user :as user]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))

View File

@@ -26,7 +26,8 @@
[metabase.util :as u]
[metabase.util.embed :refer [maybe-populate-initially-published-at]]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :as i18n :refer [tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :as i18n-be]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms]
@@ -336,7 +337,7 @@
(t2/with-transaction [_conn]
(let [{dashcards :dashcards
tabs :tabs
:keys [description] :as dashboard} (i18n/localized-strings->strings dashboard)
:keys [description] :as dashboard} (i18n-be/localized-strings->strings dashboard)
dashboard (first (t2/insert-returning-instances!
:model/Dashboard
(-> dashboard

View File

@@ -1,7 +1,7 @@
(ns metabase.dashboards.settings
(:require
[metabase.settings.core :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
[metabase.util.i18n-be.core :refer [deferred-tru]]))
(defsetting dashboards-save-last-used-parameters
(deferred-tru "Whether dashboards should default to a user''s last used parameters on load.")

View File

@@ -46,7 +46,8 @@
[metabase.revisions.core :as revisions]
[metabase.util :as u]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.json :as json]
[metabase.util.log :as log]
[metabase.util.malli :as mu]

View File

@@ -10,7 +10,7 @@
[metabase.public-sharing.core :as public-sharing]
[metabase.search.spec :as search.spec]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[methodical.core :as methodical]

View File

@@ -7,7 +7,7 @@
[metabase.driver.connection :as driver.conn]
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :as setting]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.warehouses.core :as warehouses]))

View File

@@ -27,7 +27,8 @@
[metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.util :as u]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
[metabase.util.performance :as perf :refer [every? some]])

View File

@@ -7,7 +7,9 @@
[metabase.driver.sql-jdbc.actions :as sql-jdbc.actions]
[metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-trun tru]]
[metabase.util-be.core :as util-be]
[metabase.util.i18n :refer [tru]]
[metabase.util.i18n-be.core :refer [deferred-trun]]
[metabase.util.log :as log]))
(defmethod sql-jdbc.actions/base-type->sql-type-map :h2
@@ -77,7 +79,7 @@
(re-find #"Unique index or primary key violation: \"[^.]+.(.+?) ON [^.]+.\"\"(.+?)\"\"" error-message)]
(let [columns (constraint->column-names database table constraint-name)]
{:type error-type
:message (tru "{0} already {1}." (u/build-sentence (map str/capitalize columns) :stop? false) (deferred-trun "exists" "exist" (count columns)))
:message (tru "{0} already {1}." (util-be/build-sentence (map str/capitalize columns) :stop? false) (deferred-trun "exists" "exist" (count columns)))
:errors (reduce (fn [acc col]
(assoc acc col (tru "This {0} value already exists." (str/capitalize col))))
{}

Some files were not shown because too many files have changed in this diff Show More