Compare commits

..

1 Commits

Author SHA1 Message Date
Christoph Deil
d22eeba6c3 Fix Python newline in code-templates.ts 2025-04-28 21:15:57 +02:00
538 changed files with 3220 additions and 6863 deletions

View File

@@ -417,11 +417,13 @@
metabase.model-persistence.core model-persistence
metabase.models.dependency dependency
metabase.models.interface mi
metabase.models.setting setting
metabase.notification.core notification
metabase.notification.payload.core notification.payload
metabase.notification.test-util notification.tu
metabase.permissions.core perms
metabase.plugins.initialize plugins.init
metabase.public-settings public-settings
metabase.public-sharing.core public-sharing
metabase.premium-features.core premium-features
metabase.pulse.core pulse
@@ -464,7 +466,6 @@
metabase.server.core server
metabase.server.protocols server.protocols
metabase.sso.core sso
metabase.settings.core setting
metabase.sync.analyze.fingerprint sync.fingerprint
metabase.sync.field-values sync.field-values
metabase.sync.interface i
@@ -549,10 +550,14 @@
metabase.driver.sql-jdbc.execute.diagnostic/capturing-diagnostic-info clojure.core/fn
metabase.models.collection-test/with-collection-in-location clojure.core/let
metabase.models.json-migration/def-json-migration clj-kondo.lint-as/def-catch-all
metabase.models.setting/defsetting clj-kondo.lint-as/def-catch-all
metabase.models.setting.multi-setting/define-multi-setting clojure.core/def
metabase.models.util.spec-update/define-spec clojure.core/def
metabase.notification.test-util/with-temp-notification clojure.core/let
metabase.notification.test-util/with-card-notification clojure.core/let
metabase.notification.test-util/with-system-event-notification! clojure.core/let
metabase.public-settings.premium-features/defenterprise-schema clj-kondo.lint-as/def-catch-all
metabase.public-settings.premium-features/define-premium-feature clojure.core/def
metabase.premium-features.core/defenterprise-schema clj-kondo.lint-as/def-catch-all
metabase.premium-features.core/define-premium-feature clojure.core/def
metabase.premium-features.defenterprise/defenterprise-schema clj-kondo.lint-as/def-catch-all
@@ -564,10 +569,6 @@
metabase.query-processor.middleware.process-userland-query-test/with-query-execution! clojure.core/let
metabase.query-processor.setup/with-qp-setup clojure.core/let
metabase.search.api-test/do-test-users clojure.core/let
metabase.settings.core/defsetting clj-kondo.lint-as/def-catch-all
metabase.settings.core/define-multi-setting clojure.core/def
metabase.settings.models.setting/defsetting clj-kondo.lint-as/def-catch-all
metabase.settings.models.setting.multi-setting/define-multi-setting clojure.core/def
metabase.sso.ldap/with-ldap-connection clojure.core/fn
metabase.sync.util/sum-for clojure.core/for
metabase.sync.util/with-emoji-progress-bar clojure.core/let
@@ -634,6 +635,10 @@
metabase.api.common.internal-test/with-jetty-server hooks.common/with-one-binding
metabase.api.dashboard-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
metabase.api.dashboard-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
metabase.api.embed-test/do-response-formats hooks.common/with-two-bindings
metabase.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
metabase.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
metabase.api.embed-test/with-temp-dashcard hooks.common/let-one-with-optional-value
metabase.api.macros/defendpoint hooks.metabase.api.macros/defendpoint
metabase.api.user-test/with-temp-user-email! hooks.common/with-one-binding
metabase.channel.template.handlebars-helper/defhelper hooks.metabase.channel.template.handlebars-helper/defhelper
@@ -643,10 +648,6 @@
metabase.driver.bigquery-cloud-sdk-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
metabase.driver.oracle-test/with-temp-user hooks.common/let-one-with-optional-value
metabase.driver/register! hooks.metabase.driver/register!
metabase.embedding.api.embed-test/do-response-formats hooks.common/with-two-bindings
metabase.embedding.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
metabase.embedding.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
metabase.embedding.api.embed-test/with-temp-dashcard hooks.common/let-one-with-optional-value
metabase.legacy-mbql.schema.macros/defclause hooks.metabase.legacy-mbql.schemas.macros/defclause
metabase.lib.schema.mbql-clause/define-catn-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
metabase.lib.schema.mbql-clause/define-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
@@ -663,6 +664,9 @@
metabase.models.dashboard-test/with-dash-in-collection! hooks.common/with-three-bindings
metabase.models.interface/define-batched-hydration-method hooks.metabase.models.interface/define-hydration-method
metabase.models.interface/define-simple-hydration-method hooks.metabase.models.interface/define-hydration-method
metabase.models.setting.multi-setting/define-multi-setting hooks.metabase.models.setting/define-multi-setting
metabase.models.setting.multi-setting/define-multi-setting-impl hooks.metabase.models.setting/define-multi-setting-impl
metabase.models.setting/defsetting hooks.metabase.models.setting/defsetting
metabase.permissions.core/allow-changing-all-users-group-members hooks.common/do*
metabase.permissions.core/disable-perms-cache hooks.common/do*
metabase.plugins.classloader/require hooks.clojure.core.require/lint-require
@@ -686,12 +690,6 @@
metabase.search.api-test/with-search-items-in-collection hooks.metabase.search.api-test/with-search-items-in-collection
metabase.search.api-test/with-search-items-in-root-collection hooks.common/do*
metabase.server.streaming-response-test/with-start-execution-chan hooks.common/with-one-binding
metabase.settings.core/define-multi-setting hooks.metabase.settings.models.setting/define-multi-setting
metabase.settings.core/define-multi-setting-impl hooks.metabase.settings.models.setting/define-multi-setting-impl
metabase.settings.core/defsetting hooks.metabase.settings.models.setting/defsetting
metabase.settings.models.setting.multi-setting/define-multi-setting hooks.metabase.settings.models.setting/define-multi-setting
metabase.settings.models.setting.multi-setting/define-multi-setting-impl hooks.metabase.settings.models.setting/define-multi-setting-impl
metabase.settings.models.setting/defsetting hooks.metabase.settings.models.setting/defsetting
metabase.stale-test/with-stale-items hooks.toucan2.tools.with-temp/with-temp
metabase.test.data.datasets/test-driver hooks.metabase.test.data.datasets/test-driver
metabase.test.data.datasets/test-drivers hooks.metabase.test.data.datasets/test-drivers
@@ -751,43 +749,43 @@
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!}
:macroexpand
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase.api.card-test/with-ordered-items macros.metabase.api.card-test/with-ordered-items
metabase.api.collection-test/with-collection-hierarchy! macros.metabase.api.collection-test/with-collection-hierarchy!
metabase.api.collection-test/with-some-children-of-collection! macros.metabase.api.collection-test/with-some-children-of-collection!
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
metabase.lib.common/defop macros.metabase.lib.common/defop
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
metabase.models.params.chain-filter-test/chain-filter macros.metabase.models.params.chain-filter-test/chain-filter
metabase.models.params.chain-filter-test/chain-filter-search macros.metabase.models.params.chain-filter-test/chain-filter
metabase.models.user-test/with-groups macros.metabase.models.user-test/with-groups
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing!
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
metabase.query-analysis.models.query-analysis-test/with-test-setup macros.metabase.query-analysis.models.query-analysis-test/with-test-setup
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
metabase.xrays.domain-entities.malli/define-getters-and-setters macros.metabase.xrays.domain-entities.malli/define-getters-and-setters
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase.api.card-test/with-ordered-items macros.metabase.api.card-test/with-ordered-items
metabase.api.collection-test/with-collection-hierarchy! macros.metabase.api.collection-test/with-collection-hierarchy!
metabase.api.collection-test/with-some-children-of-collection! macros.metabase.api.collection-test/with-some-children-of-collection!
metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
metabase.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
metabase.lib.common/defop macros.metabase.lib.common/defop
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
metabase.models.params.chain-filter-test/chain-filter macros.metabase.models.params.chain-filter-test/chain-filter
metabase.models.params.chain-filter-test/chain-filter-search macros.metabase.models.params.chain-filter-test/chain-filter
metabase.models.query-analysis-test/with-test-setup macros.metabase.models.query-analysis-test/with-test-setup
metabase.models.user-test/with-groups macros.metabase.models.user-test/with-groups
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing!
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
metabase.xrays.domain-entities.malli/define-getters-and-setters macros.metabase.xrays.domain-entities.malli/define-getters-and-setters
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
:config-in-comment
{:linters {:unresolved-symbol {:level :off}}}

View File

@@ -30,12 +30,11 @@
;; ## Tips
;;
;; PRO TIP: Check out the [[dev.deps-graph]] namespace for helpful tools to see where a module is used externally, and
;; for tools for calculating this config -- you can use [[dev.deps-graph/print-kondo-config-diff]] to see what updates
;; you need to make
;; for tools for calculating this config.
{:metabase/modules
{actions
{:api #{metabase.actions.api metabase.actions.core}
:uses #{analytics api driver legacy-mbql lib model-persistence models query-processor search settings util}}
:uses #{analytics api driver legacy-mbql lib model-persistence models query-processor search util}}
activity-feed
{:api #{metabase.activity-feed.api metabase.activity-feed.init}
@@ -54,7 +53,8 @@
internal-stats
models
premium-features
settings
public-settings
server
util}}
analyze
{:api #{metabase.analyze.core metabase.analyze.query-results}
@@ -62,6 +62,7 @@
api
{:api #{metabase.api.auth
metabase.api.cache
metabase.api.card
metabase.api.collection
metabase.api.common
@@ -80,9 +81,8 @@
metabase.api.util.handlers}
:uses :any}
audit
{:api #{metabase.audit}
:uses #{db settings}}
audit {:api #{metabase.audit}
:uses #{db models}}
auth-provider
{:api #{metabase.auth-provider}
@@ -92,12 +92,6 @@
{:api #{metabase.bookmarks.api}
:uses #{api db models util}}
cache
{:api #{metabase.cache.api
metabase.cache.core
metabase.cache.init}
:uses #{api config db events models premium-features settings util}}
channel
{:api #{metabase.channel.api
metabase.channel.core
@@ -120,7 +114,7 @@
notification
permissions
premium-features
settings
public-settings
query-processor
timeline
types
@@ -130,13 +124,13 @@
{:api #{metabase.cloud-migration.api
metabase.cloud-migration.core
metabase.cloud-migration.init}
:uses #{api cmd config db models premium-features settings task util}}
:uses #{api cmd config db models premium-features task util}}
cmd
{:api #{metabase.cmd
metabase.cmd.copy
metabase.cmd.dump-to-h2}
:uses #{api config db legacy-mbql models plugins query-processor search settings util}}
:uses #{api config db legacy-mbql models plugins query-processor search util}}
;; technically this 'uses' `enterprise/core` and `test` since it tries to load them to see if they exists so we know
;; if EE/test code is available; however we can ignore them since they're not real usages.
@@ -186,7 +180,6 @@
metabase.driver.sync
metabase.driver.util}
:uses #{actions
api
auth-provider
config
connection-pool
@@ -199,44 +192,30 @@
notification
plugins
premium-features
settings
public-settings
pulse
query-processor
sync
task
upload
util}}
util
enterprise/database-routing}}
eid-translation
{:api #{metabase.eid-translation.core}
:uses #{settings util}}
:uses #{}}
embedding
{:api #{metabase.embedding.api
metabase.embedding.app-origins-sdk
metabase.embedding.init
metabase.embedding.settings}
:uses #{analytics
api
config
driver
eid-translation
events
models
notification
premium-features
public-sharing
query-processor
settings
tiles
util}}
embed
{:api #{metabase.embed.settings metabase.embed.app-origins-sdk}
:uses #{analytics models premium-features tiles util}}
events
{:api #{metabase.events metabase.events.init metabase.events.notification}
:uses #{api channel driver models notification premium-features task-history util}}
:uses #{api channel driver models notification premium-features util}}
formatter
{:api #{metabase.formatter}
:uses #{models settings query-processor types util}}
:uses #{models public-settings query-processor types util}}
indexed-entities
{:api #{metabase.indexed-entities.api metabase.indexed-entities.init}
@@ -244,10 +223,10 @@
integrations
{:api #{metabase.integrations.slack}
:uses #{events settings util}}
:uses #{events models util}}
internal-stats
{:api #{metabase.internal-stats.core}
{:api #{metabase.internal-stats}
:uses #{db models}}
legacy-mbql
@@ -295,11 +274,11 @@
metabase.lib.util
metabase.lib.util.match
metabase.lib.walk}
:uses #{config legacy-mbql models settings types util}}
:uses #{config driver legacy-mbql models types util}}
lib-be
{:api #{metabase.lib-be.init}
:uses #{models settings task util}}
:uses #{models task util}}
logger
{:api #{metabase.logger metabase.logger.init}
@@ -309,18 +288,19 @@
{:api #{metabase.login-history.api
metabase.login-history.core
metabase.login-history.init}
:uses #{analytics api channel request settings util}}
:uses #{analytics api channel models request util}}
model-persistence
{:api #{metabase.model-persistence.api
metabase.model-persistence.core
metabase.model-persistence.init}
:uses #{api db driver events lib models premium-features settings query-processor request task task-history util}}
:uses #{api db driver events lib models premium-features public-settings query-processor request task util}}
models
{:api #{metabase.models.api-key
metabase.models.application-permissions-revision
metabase.models.audit-log
metabase.models.cache-config
metabase.models.card
metabase.models.card.metadata
metabase.models.collection
@@ -342,17 +322,23 @@
metabase.models.moderation-review
metabase.models.native-query-snippet
metabase.models.native-query-snippet.permissions
metabase.notification.models
metabase.models.params
metabase.models.params.chain-filter
metabase.models.params.custom-values
metabase.models.params.field-values
metabase.models.params.shared
metabase.models.query
metabase.models.query-analysis
metabase.models.query.permissions
metabase.models.resolution
metabase.models.secret
metabase.models.serialization
metabase.models.setting
metabase.models.setting.cache
metabase.models.setting.multi-setting
metabase.models.table
metabase.models.task-history
metabase.models.user
metabase.models.user-parameter-value
metabase.models.util.spec-update
@@ -362,7 +348,6 @@
analyze
api
audit
cache
channel
config
db
@@ -375,17 +360,20 @@
permissions
plugins
premium-features
settings
public-settings
public-sharing
pulse
query-analysis
query-processor
request
search
server
setup
sync
task
types
util}}
util
enterprise/advanced-permissions}}
moderation
{:api #{metabase.moderation}
@@ -396,9 +384,7 @@
metabase.notification.core
metabase.notification.init
metabase.notification.models
metabase.notification.api.notification
metabase.notification.payload.core
metabase.notification.send}
metabase.notification.payload.core}
:uses #{analytics
api
channel
@@ -408,11 +394,10 @@
models
permissions
premium-features
settings
public-settings
query-processor
request
task
task-history
util}}
permissions
@@ -422,7 +407,7 @@
metabase.permissions.models.permissions
metabase.permissions.models.permissions-group
metabase.permissions.util}
:uses #{api audit config db models plugins premium-features request server settings util}}
:uses #{api audit config db models plugins premium-features request server util}}
pivot
{:api #{metabase.pivot.core}
@@ -433,17 +418,19 @@
:uses #{config driver util}}
premium-features
{:api #{metabase.premium-features.api
metabase.premium-features.core
metabase.premium-features.init
{:api #{metabase.premium-features.core
metabase.premium-features.token-check}
:uses #{api config db internal-stats plugins settings util}}
:uses #{config db internal-stats models plugins util}}
public-settings
{:api #{metabase.public-settings}
:uses #{api config models premium-features util}}
public-sharing
{:api #{metabase.public-sharing.api
metabase.public-sharing.core
metabase.public-sharing.init}
:uses #{actions analytics api db events lib models query-processor request settings tiles util}}
:uses #{actions analytics api db events lib models query-processor request tiles util}}
pulse
{:api #{metabase.pulse.api
@@ -451,12 +438,12 @@
metabase.pulse.init
metabase.pulse.task.send-pulses}
:uses #{api channel config db driver events integrations models notification permissions plugins premium-features
query-processor request task task-history util
query-processor request task util
enterprise/advanced-config enterprise/advanced-permissions enterprise/sandbox}}
query-analysis
{:api #{metabase.query-analysis.core metabase.query-analysis.init}
:uses #{config driver legacy-mbql lib query-processor settings task util}}
:uses #{config driver legacy-mbql lib public-settings query-processor task util}}
query-processor
{:api #{metabase.query-processor
@@ -497,8 +484,8 @@
analyze
api
audit
cache
config
db
driver
events
formatter
@@ -509,7 +496,7 @@
permissions
pivot
premium-features
settings
public-settings
request
server
types
@@ -517,7 +504,7 @@
request
{:api #{metabase.request.core}
:uses #{api config models permissions settings util}}
:uses #{api config models permissions public-settings util}}
revisions
{:api #{metabase.revisions.api metabase.revisions.core metabase.revisions.init}
@@ -536,7 +523,7 @@
metabase.search.ingestion
metabase.search.init
metabase.search.spec}
:uses #{analytics api audit config db driver models permissions premium-features settings request task util}}
:uses #{analytics api audit config db driver models permissions premium-features public-settings request task util}}
segments
{:api #{metabase.segments.api}
@@ -547,21 +534,14 @@
metabase.server.middleware.json
metabase.server.middleware.session
metabase.server.streaming-response}
:uses #{analytics api config core db driver embedding models premium-features settings session request util enterprise/sso}}
:uses #{analytics api config core db driver embed models premium-features public-settings session request util enterprise/sso}}
session
{:api #{metabase.session.core
metabase.session.api
metabase.session.init
metabase.session.models.session}
:uses #{api channel config db driver events login-history models settings request sso task util}}
settings
{:api #{metabase.settings.api
metabase.settings.core
metabase.settings.deprecated-grab-bag
metabase.settings.init}
:uses #{api config db events models plugins premium-features server util enterprise/advanced-permissions}}
:uses #{api channel config db driver events login-history models public-settings request sso task util}}
setup
{:api #{metabase.setup.api metabase.setup.core}
@@ -570,10 +550,13 @@
channel
config
db
embed
events
integrations
models
permissions
settings
premium-features
public-settings
request
session
util}}
@@ -582,7 +565,7 @@
{:api #{metabase.sso.api
metabase.sso.core
metabase.sso.init}
:uses #{api models permissions premium-features settings util}}
:uses #{api models permissions premium-features util}}
sync
{:api #{metabase.sync.api
@@ -591,41 +574,32 @@
metabase.sync.schedules
metabase.sync.task.sync-databases
metabase.sync.util}
:uses #{analyze api audit config db driver events lib models query-processor settings task task-history util}}
:uses #{analyze api audit config db driver events lib models plugins query-processor task util}}
task
{:api #{metabase.task
metabase.task.bootstrap
metabase.task.init}
metabase.task.init
metabase.task.notification}
:uses #{analytics
channel
config
db
driver
integrations
models
notification
plugins
premium-features
settings
task-history
public-settings
query-processor
util
enterprise/task}}
task-history
{:api #{metabase.task-history.api
metabase.task-history.core
metabase.task-history.init}
:uses #{api
models
permissions
premium-features
request
task
util}}
tiles
{:api #{metabase.tiles.api
metabase.tiles.init}
:uses #{api legacy-mbql query-processor settings util}}
:uses #{api legacy-mbql models query-processor util}}
timeline
{:api #{metabase.timeline.api metabase.timeline.core}
@@ -637,7 +611,7 @@
upload
{:api #{metabase.upload}
:uses #{analytics api driver events legacy-mbql lib model-persistence models permissions settings sync util}}
:uses #{analytics api driver events legacy-mbql lib model-persistence models permissions public-settings sync util}}
user-key-value
{:api #{metabase.user-key-value.api metabase.user-key-value.init}
@@ -645,25 +619,24 @@
util
{:api :any
:uses #{config db driver legacy-mbql lib models plugins settings}}
:uses #{config db driver legacy-mbql lib models plugins premium-features public-settings}}
xrays
{:api #{metabase.xrays.api
metabase.xrays.core
metabase.xrays.init}
:uses #{analyze api driver legacy-mbql lib models settings query-processor util}}
{:api #{metabase.xrays.api metabase.xrays.core}
:uses #{analyze api db driver legacy-mbql lib models public-settings query-processor util}}
enterprise/advanced-config
{:api #{metabase-enterprise.advanced-config.api.logs
metabase-enterprise.advanced-config.models.pulse-channel
metabase-enterprise.advanced-config.file}
:uses #{api db driver models permissions premium-features setup settings sync util}}
metabase-enterprise.advanced-config.file
metabase-enterprise.advanced-config.crufty}
:uses #{api db driver models permissions premium-features setup sync util}}
enterprise/advanced-permissions
{:api #{metabase-enterprise.advanced-permissions.api.routes
metabase-enterprise.advanced-permissions.common
metabase-enterprise.advanced-permissions.models.permissions.group-manager}
:uses #{api enterprise/impersonation lib models permissions premium-features query-processor util}}
:uses #{api audit driver enterprise/impersonation lib models permissions premium-features query-processor util enterprise/sandbox}}
enterprise/airgap
{:api #{}
@@ -686,7 +659,6 @@
plugins
premium-features
query-processor
settings
sync
util
enterprise/serialization}}
@@ -700,8 +672,8 @@
:uses #{api premium-features util}}
enterprise/cache
{:api #{metabase-enterprise.cache.init}
:uses #{cache premium-features query-processor task util}}
{:api #{metabase-enterprise.cache.strategies}
:uses #{api models premium-features query-processor util}}
enterprise/content-verification
{:api #{metabase-enterprise.content-verification.api.routes}
@@ -709,47 +681,44 @@
enterprise/core
{:api #{metabase-enterprise.core.init}
:uses :any}
:uses #{enterprise/enhancements}}
enterprise/dashboard-subscription-filters
{:api #{}
:uses #{premium-features}}
enterprise/data-editing
{:api #{metabase-enterprise.data-editing.api}
:uses #{api actions util}}
enterprise/database-routing
{:api #{metabase-enterprise.database-routing.api}
:uses #{api config database-routing events lib models premium-features query-processor settings util}}
{:api #{metabase-enterprise.database-routing.core metabase-enterprise.database-routing.api}
:uses #{api config database-routing events lib models premium-features query-processor util}}
enterprise/enhancements
{:api #{metabase-enterprise.enhancements.init}
:uses #{models premium-features settings sso util enterprise/sso}}
:uses #{models premium-features sso util enterprise/sso}}
enterprise/gsheets
{:api #{metabase-enterprise.gsheets.api}
:uses #{api analytics premium-features settings util enterprise/harbormaster}}
:uses #{api analytics enterprise/harbormaster models premium-features server util}}
enterprise/harbormaster
{:api #{metabase-enterprise.harbormaster.client}
:uses #{api cloud-migration util}}
:uses #{api config cloud-migration models util}}
enterprise/impersonation
{:api #{metabase-enterprise.impersonation.api}
:uses #{api audit driver lib models premium-features query-processor util enterprise/sandbox}}
{:api #{metabase-enterprise.impersonation.core
metabase-enterprise.impersonation.api}
:uses #{api audit driver enterprise/sandbox lib models permissions premium-features query-processor util}}
enterprise/internal-stats
{:api #{}
:uses #{premium-features settings}}
{:api #{metabase-enterprise.internal-stats}
:uses #{models premium-features}}
enterprise/llm
{:api #{metabase-enterprise.llm.api}
:uses #{analytics analyze api query-processor settings util}}
:uses #{analytics analyze api models query-processor util}}
enterprise/query-reference-validation
{:api #{metabase-enterprise.query-reference-validation.api}
:uses #{api models query-analysis request util}}
:uses #{api models public-settings request util}}
enterprise/sandbox
{:api #{metabase-enterprise.sandbox.api.routes
@@ -766,11 +735,12 @@
query-processor
request
util
enterprise/impersonation
enterprise/api}}
enterprise/scim
{:api #{metabase-enterprise.scim.api metabase-enterprise.scim.routes}
:uses #{analytics api models permissions settings server util enterprise/serialization}}
:uses #{analytics api models permissions public-settings server util enterprise/serialization}}
enterprise/search
{:api #{}
@@ -780,7 +750,7 @@
{:api #{metabase-enterprise.serialization.api
metabase-enterprise.serialization.cmd
metabase-enterprise.serialization.v2.backfill-ids}
:uses #{analytics api config db legacy-mbql lib logger models plugins premium-features settings setup util}}
:uses #{analytics api config db legacy-mbql lib logger models plugins premium-features public-settings setup util}}
enterprise/snippet-collections
{:api #{}
@@ -790,19 +760,23 @@
{:api #{metabase-enterprise.sso.api.routes
metabase-enterprise.sso.integrations.sso-settings
metabase-enterprise.sso.integrations.sso-utils}
:uses #{api channel embedding events premium-features request session settings sso util enterprise/scim}}
:uses #{api channel embed events models premium-features public-settings request session sso util enterprise/scim}}
enterprise/stale
{:api #{metabase-enterprise.stale.routes}
:uses #{analytics api embedding models premium-features request settings util}}
:uses #{analytics api embed models premium-features public-settings request util}}
enterprise/stats
{:api #{}
:uses #{driver premium-features enterprise/advanced-config enterprise/scim enterprise/sso}}
enterprise/data-editing
{:api #{}
:uses #{api actions util}}
enterprise/task
{:api #{metabase-enterprise.task.truncate-audit-tables}
:uses #{premium-features}}
:uses #{premium-features query-processor task util enterprise/cache}}
enterprise/upload-management
{:api #{metabase-enterprise.upload-management.api}

View File

@@ -74,6 +74,11 @@
metabase.models.field-values/create-or-update-full-field-values!
metabase.models.moderation-review/create-review!
metabase.models.on-demand-test/add-dashcard-with-parameter-mapping!
metabase.models.setting-test/test-user-local-allowed-setting!
metabase.models.setting-test/test-user-local-only-setting!
metabase.models.setting.cache/restore-cache!
metabase.models.setting/set!
metabase.models.setting/validate-settings-formatting!
metabase.permissions.models.permissions/grant-application-permissions!
metabase.permissions.models.permissions/grant-collection-read-permissions!
metabase.permissions.models.permissions/grant-collection-readwrite-permissions!
@@ -92,11 +97,6 @@
metabase.query-processor.streaming.interface/finish!
metabase.query-processor.streaming.interface/write-row!
metabase.sample-data/try-to-extract-sample-database!
metabase.settings.models.setting-test/test-user-local-allowed-setting!
metabase.settings.models.setting-test/test-user-local-only-setting!
metabase.settings.models.setting.cache/restore-cache!
metabase.settings.models.setting/set!
metabase.settings.models.setting/validate-settings-formatting!
metabase.setup.core/create-token!
metabase.sso.ldap-test-util/start-ldap-server!
metabase.sync.core/sync-database!

View File

@@ -1,4 +1,4 @@
(ns macros.metabase.embedding.api.embed-test)
(ns macros.metabase.api.embed-test)
(defmacro with-embedding-enabled-and-temp-card-referencing!
[table-kw field-kw [card-binding] & body]

View File

@@ -1,4 +1,4 @@
(ns macros.metabase.query-analysis.models.query-analysis-test)
(ns macros.metabase.models.query-analysis-test)
(defmacro with-test-setup [& body]
`(let [~'card-id 1

View File

@@ -1,4 +1,4 @@
(ns hooks.metabase.settings.models.setting
(ns hooks.metabase.models.setting
(:require
[clj-kondo.hooks-api :as hooks]
[hooks.common :as common]))
@@ -207,7 +207,7 @@
anon-binding (hooks/map-node options-list)])
(hooks/reg-keyword! (-> (hooks/keyword-node (keyword (hooks/sexpr setting-name)))
(with-meta (meta setting-name)))
'metabase.settings.models.setting/defsetting)
'metabase.models.setting/defsetting)
getter-node
setter-node))
(with-meta (meta node)))}))

View File

@@ -81,271 +81,269 @@
throttle.core/with-throttling [[:block 1]]
;; the rest of the stuff in this list is generated automatically based on `:style/indent` specs
;; by running [[dev.generate-cljfmt-config/specs]]... use that code to update this list
metabase-enterprise.cache.config-test/with-temp-persist-models [[:block 1]]
metabase-enterprise.impersonation.util-test/with-impersonations! [[:inner 0]]
metabase-enterprise.sandbox.test-util/with-gtaps! [[:inner 0]]
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! [[:inner 0]]
metabase-enterprise.sandbox.test-util/with-user-attributes [[:block 2]]
metabase-enterprise.serialization.test-util/with-dbs [[:inner 0]]
metabase-enterprise.serialization.test-util/with-random-dump-dir [[:block 1]]
metabase-enterprise.serialization.test-util/with-world [[:block 0]]
metabase-enterprise.sso.integrations.jwt-test/with-users-with-email-deleted [[:block 1]]
metabase-enterprise.test/with-gtaps! [[:inner 0]]
metabase-enterprise.test/with-gtaps-for-user! [[:inner 0]]
metabase-enterprise.test/with-user-attributes [[:block 2]]
metabase.actions.actions-test/with-actions-test-data-and-actions-permissively-enabled [[:block 0]]
metabase.actions.test-util/with-actions [[:block 1]]
metabase.actions.test-util/with-actions-disabled [[:block 0]]
metabase.actions.test-util/with-actions-enabled [[:block 0]]
metabase.actions.test-util/with-actions-test-data [[:inner 0]]
metabase.actions.test-util/with-actions-test-data-and-actions-enabled [[:block 0]]
metabase.actions.test-util/with-actions-test-data-tables [[:block 1]]
metabase.actions.test-util/with-empty-db [[:inner 0]]
metabase.actions.test-util/with-temp-test-data [[:inner 0]]
metabase.api.card-test/with-cards-in-readable-collection [[:block 1]]
metabase.api.card-test/with-cards-in-writeable-collection [[:block 1]]
metabase.api.card-test/with-ordered-items [[:inner 0]]
metabase.api.card-test/with-temp-native-card! [[:block 1]]
metabase.api.card-test/with-temp-native-card-with-params! [[:block 1]]
metabase.api.collection-test/with-collection-hierarchy [[:block 1]]
metabase.api.collection-test/with-french-user-and-personal-collection [[:block 2]]
metabase.api.collection-test/with-some-children-of-collection [[:block 1]]
metabase.api.common/check [[:default]]
metabase.api.common/define-routes [[:block 0]]
metabase.api.common/let-404 [[:block 1]]
metabase.cmd.copy/step [[:block 1]]
metabase.cmd.copy/with-connection-rollback-only [[:block 1]]
metabase.cmd.copy/with-disabled-db-constraints [[:block 2]]
metabase.config/build-type-case [[:block 0]]
metabase.dashboard-subscription-test/with-link-card-fixture-for-dashboard [[:block 2]]
metabase.db.liquibase/with-liquibase [[:block 1]]
metabase.db.liquibase/with-scope-locked [[:block 1]]
metabase.db.schema-migrations-test.impl/test-migrations [[:block 2]]
metabase.db/with-application-db [[:inner 0]]
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
metabase.driver.druid.client/DELETE [[:default]]
metabase.driver.druid.client/GET [[:default]]
metabase.driver.druid.client/POST [[:default]]
metabase.driver.druid.query-processor-test/druid-query [[:block 0]]
metabase.driver.druid.query-processor-test/druid-query-returning-rows [[:block 0]]
metabase.driver.mongo.query-processor/mongo-let [[:block 1]]
metabase.driver.sql-jdbc.actions/with-jdbc-transaction [[:block 1]]
metabase.driver.sql-jdbc.connection/with-connection-spec-for-testing-connection [[:block 1]]
metabase.driver.sql-jdbc.execute.diagnostic/capturing-diagnostic-info [[:block 1]]
metabase.driver.sql.query-processor/apply-top-level-clause [[:default]]
metabase.driver/with-driver [[:block 1]]
metabase.embedding.api.embed-test/do-response-formats [[:block 1]]
metabase.embedding.api.embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing [[:block 3]]
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing [[:block 3]]
metabase.embedding.api.embed-test/with-new-secret-key [[:block 0]]
metabase.embedding.api.embed-test/with-temp-card [[:block 1]]
metabase.embedding.api.embed-test/with-temp-dashcard [[:block 1]]
metabase.embedding.api.preview-embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
metabase.embedding.api.preview-embed-test/with-new-secret-key [[:block 0]]
metabase.lib.binning/with-binning [[:default]]
metabase.lib.core/with-binning [[:default]]
metabase.lib.core/with-join-alias [[:default]]
metabase.lib.core/with-join-conditions [[:default]]
metabase.lib.join/with-join-alias [[:default]]
metabase.lib.join/with-join-conditions [[:default]]
metabase.lib.test-util.macros/$ids [[:inner 0]]
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
metabase.lib.util.match/match [[:inner 0]]
metabase.lib.util.match/match-one [[:inner 0]]
metabase.lib.util.match/replace [[:inner 0]]
metabase.lib.util.match/replace-in [[:inner 0]]
metabase.models.collection-test/with-collection-hierarchy [[:block 1]]
metabase.models.collection-test/with-current-user-perms-for-collections [[:block 1]]
metabase.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
metabase.models.dashboard-test/with-dash-in-collection [[:inner 0]]
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
metabase.models.params.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
metabase.models.params.chain-filter-test/with-fk-field-to-field-remapping [[:block 0]]
metabase.models.params.chain-filter-test/with-human-readable-values-remapping [[:block 0]]
metabase.permissions.core/allow-changing-all-users-group-members [[:block 0]]
metabase.permissions.core/disable-perms-cache [[:block 0]]
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing [[:block 3]]
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing [[:block 3]]
metabase.public-sharing.api-test/with-temp-public-card [[:block 1]]
metabase.public-sharing.api-test/with-temp-public-dashboard [[:block 1]]
metabase.public-sharing.api-test/with-temp-public-dashboard-and-card [[:block 1]]
metabase.pulse.api.alert-test/with-alert-in-collection [[:block 1]]
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
metabase.pulse.models.pulse-test/with-pulse-in-collection [[:inner 0]]
metabase.query-processor-test.pipeline-queries-test/pmbql-query [[:block 1]]
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query [[:block 1]]
metabase.query-processor.debug/debug> [[:block 0]]
metabase.query-processor.error-type/deferror [[:block 1]]
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
metabase.query-processor.store/cached [[:block 1]]
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
metabase.query-processor.streaming/streaming-response [[:block 1]]
metabase.query-processor.test-util/format-rows-by [[:default]]
metabase.request.core/as-admin [[:block 0]]
metabase.request.core/with-current-request [[:inner 0]]
metabase.request.core/with-current-user [[:inner 0]]
metabase.request.core/with-limit-and-offset [[:inner 0]]
metabase.request.session/as-admin [[:block 0]]
metabase.request.session/with-current-user [[:inner 0]]
metabase.sample-data-test/with-temp-sample-database-db [[:block 1]]
metabase.search.api-test/do-test-users [[:block 1]]
metabase.server.middleware.session/as-admin [[:block 0]]
metabase.server.middleware.session/with-current-user [[:inner 0]]
metabase.server.streaming-response-test/with-streaming-response-thread-pool [[:block 0]]
metabase.server.streaming-response-test/with-test-driver-db! [[:block 0]]
metabase.server.streaming-response/streaming-response [[:block 2]]
metabase.session.api/http-401-on-error [[:block 0]]
metabase.settings.core/define-multi-setting [[:inner 0]]
metabase.settings.core/defsetting [[:block 1]]
metabase.settings.models.setting.multi-setting/define-multi-setting [[:inner 0]]
metabase.settings.models.setting/defsetting [[:block 1]]
metabase.sync.fetch-metadata/log-if-error [[:block 1]]
metabase.sync.sync-metadata.fields-test/with-test-db [[:block 0]]
metabase.sync.util/do-with-start-and-finish-debug-logging [[:default]]
metabase.sync.util/sum-for [[:block 1]]
metabase.sync.util/sync-operation [[:block 3]]
metabase.sync.util/with-db-logging-disabled [[:default]]
metabase.sync.util/with-duplicate-ops-prevented [[:default]]
metabase.sync.util/with-emoji-progress-bar [[:block 1]]
metabase.sync.util/with-error-handling [[:block 1]]
metabase.sync.util/with-start-and-finish-logging [[:default]]
metabase.sync.util/with-start-and-finish-logging* [[:default]]
metabase.sync.util/with-sync-events [[:default]]
metabase.task-history.models.task-history/with-task-history [[:block 1]]
metabase.task/rerun-on-error [[:block 1]]
metabase.test.data.datasets/test-driver [[:inner 0]]
metabase.test.data.datasets/test-drivers [[:inner 0]]
metabase.test.data.one-off-dbs/with-blank-db [[:block 0]]
metabase.test.data.one-off-dbs/with-blueberries-db [[:block 0]]
metabase.test.data.users/with-test-user [[:inner 0]]
metabase.test.data/$ids [[:inner 0]]
metabase.test.data/dataset [[:inner 0]]
metabase.test.data/mbql-query [[:inner 0]]
metabase.test.data/native-query [[:block 0]]
metabase.test.data/query [[:block 1]]
metabase.test.data/run-mbql-query [[:inner 0]]
metabase.test.data/with-empty-h2-app-db [[:block 0]]
metabase.test.data/with-temp-copy-of-db [[:block 0]]
metabase.test.util.public-settings/with-additional-premium-features [[:block 1]]
metabase.test.util.public-settings/with-premium-features [[:block 1]]
metabase.test.util.thread-local/test-helpers-set-global-values! [[:block 0]]
metabase.test.util/discard-setting-changes [[:block 1]]
metabase.test.util/let-url [[:block 1]]
metabase.test.util/throw-if-called! [[:block 1]]
metabase.test.util/with-temp-scheduler [[:block 0]]
metabase.test.util/with-temp-vals-in-db [[:block 3]]
metabase.test.util/with-user-in-groups [[:inner 0]]
metabase.test/$ids [[:inner 0]]
metabase.test/as-admin [[:inner 0]]
metabase.test/dataset [[:inner 0]]
metabase.test/discard-setting-changes [[:block 1]]
metabase.test/format-rows-by [[:default]]
metabase.test/let-url [[:block 1]]
metabase.test/mbql-query [[:inner 0]]
metabase.test/native-query [[:block 0]]
metabase.test/query [[:block 1]]
metabase.test/run-mbql-query [[:inner 0]]
metabase.test/test-driver [[:inner 0]]
metabase.test/test-drivers [[:inner 0]]
metabase.test/test-helpers-set-global-values! [[:block 0]]
metabase.test/throw-if-called! [[:block 1]]
metabase.test/with-actions [[:block 1]]
metabase.test/with-actions-disabled [[:block 0]]
metabase.test/with-actions-enabled [[:block 0]]
metabase.test/with-actions-test-data [[:inner 0]]
metabase.test/with-actions-test-data-and-actions-enabled [[:block 0]]
metabase.test/with-actions-test-data-tables [[:block 1]]
metabase.test/with-additional-premium-features [[:block 1]]
metabase.test/with-current-user [[:inner 0]]
metabase.test/with-driver [[:block 1]]
metabase.test/with-empty-db [[:inner 0]]
metabase.test/with-empty-h2-app-db [[:block 0]]
metabase.test/with-metadata-provider [[:inner 0]]
metabase.test/with-premium-features [[:block 1]]
metabase.test/with-temp [[:inner 0]]
metabase.test/with-temp-copy-of-db [[:block 0]]
metabase.test/with-temp-scheduler [[:block 0]]
metabase.test/with-temp-test-data [[:inner 0]]
metabase.test/with-temp-vals-in-db [[:block 3]]
metabase.test/with-test-user [[:inner 0]]
metabase.test/with-user-in-groups [[:inner 0]]
metabase.timeseries-query-processor-test.util/test-timeseries-drivers [[:block 0]]
metabase.timeseries-query-processor-test/run-mbql-query [[:inner 0]]
metabase.upload-test/with-upload-table! [[:inner 0]]
metabase.upload.types/with-currency [[:default]]
metabase.upload.types/with-parens [[:default]]
metabase.util.async/cancelable-thread [[:block 0]]
metabase.util.date-2/with-time-zone-same-instant [[:default]]
metabase.util.encryption-test/with-secret-key [[:block 1]]
metabase.util.honey-sql-2/with-database-type-info [[:default]]
metabase.util.i18n/deferred-trs [[:default]]
metabase.util.i18n/deferred-tru [[:default]]
metabase.util.i18n/trs [[:default]]
metabase.util.i18n/trsn [[:default]]
metabase.util.i18n/tru [[:default]]
metabase.util.i18n/trun [[:default]]
metabase.util.jvm/auto-retry [[:block 1]]
metabase.util.jvm/varargs [[:block 1]]
metabase.util.jvm/with-us-locale [[:block 0]]
metabase.util.malli/disable-enforcement [[:block 0]]
metabase.util.malli/with [[:default]]
metabase.util.malli/with-api-error-message [[:default]]
metabase.util.namespaces/import-fns [[:default]]
metabase.util.queue-test/await-test-while [[:block 1]]
metabase.util/auto-retry [[:block 1]]
metabase.util/case-enum [[:block 1]]
metabase.util/ignore-exceptions [[:block 0]]
metabase.util/or-with [[:block 1]]
metabase.util/profile [[:block 1]]
metabase.util/prog1 [[:inner 0]]
metabase.util/varargs [[:inner 0]]
metabase.util/with-us-locale [[:block 0]]
metabuild-common.core/exit-when-finished-nonzero-on-exception [[:block 0]]
metabuild-common.core/step [[:block 1]]
metabuild-common.entrypoint/exit-when-finished-nonzero-on-exception [[:block 0]]
metabuild-common.steps/step [[:block 1]]
methodical.core/defmethod [[:inner 0]]
methodical.core/defmulti [[:inner 0]]
methodical.core/with-dispatcher [[:default]]
methodical.core/with-method-table [[:default]]
methodical.core/with-prefers [[:default]]
methodical.interface/with-dispatcher [[:default]]
methodical.interface/with-method-table [[:default]]
methodical.interface/with-prefers [[:default]]
methodical.macros/defmethod [[:inner 0]]
methodical.macros/defmulti [[:inner 0]]
saml20-clj.sp.response/validate-confirmation-datas [[:block 1]]
toucan2.connection/with-transaction [[:block 1]]
toucan2.core/build [[:block 0]]
toucan2.core/compile [[:block 0]]
toucan2.core/debug [[:block 0]]
toucan2.core/define-after-insert [[:inner 0]]
toucan2.core/define-after-select [[:inner 0]]
toucan2.core/define-after-update [[:inner 0]]
toucan2.core/define-before-delete [[:inner 0]]
toucan2.core/define-before-insert [[:inner 0]]
toucan2.core/define-before-select [[:inner 0]]
toucan2.core/define-default-fields [[:inner 0]]
toucan2.core/define-named-query [[:block 1]]
toucan2.core/deftransforms [[:block 1]]
toucan2.core/with-transaction [[:block 1]]
toucan2.tools.after-insert/define-after-insert [[:inner 0]]
toucan2.tools.after-select/define-after-select [[:inner 0]]
toucan2.tools.after-update/define-after-update [[:inner 0]]
toucan2.tools.after/define-after-impl [[:default]]
toucan2.tools.before-delete/define-before-delete [[:inner 0]]
toucan2.tools.before-insert/define-before-insert [[:inner 0]]
toucan2.tools.before-select/define-before-select [[:inner 0]]
toucan2.tools.compile/build [[:block 0]]
toucan2.tools.compile/compile [[:block 0]]
toucan2.tools.compile/resolved [[:block 0]]
toucan2.tools.debug/debug [[:block 0]]
toucan2.tools.default-fields/define-default-fields [[:inner 0]]
toucan2.tools.named-query/define-named-query [[:block 1]]
toucan2.tools.simple-out-transform/define-out-transform [[:inner 0]]
toucan2.tools.transformed/deftransforms [[:block 1]]
toucan2.tools.with-temp/with-temp [[:inner 0]]
toucan2.util/try-with-error-context [[:inner 0]]}}
metabase-enterprise.impersonation.util-test/with-impersonations! [[:inner 0]]
metabase-enterprise.cache.config-test/with-temp-persist-models [[:block 1]]
metabase-enterprise.sandbox.test-util/with-gtaps! [[:inner 0]]
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! [[:inner 0]]
metabase-enterprise.sandbox.test-util/with-user-attributes [[:block 2]]
metabase-enterprise.serialization.test-util/with-dbs [[:inner 0]]
metabase-enterprise.serialization.test-util/with-random-dump-dir [[:block 1]]
metabase-enterprise.serialization.test-util/with-world [[:block 0]]
metabase-enterprise.sso.integrations.jwt-test/with-users-with-email-deleted [[:block 1]]
metabase-enterprise.test/with-gtaps! [[:inner 0]]
metabase-enterprise.test/with-gtaps-for-user! [[:inner 0]]
metabase-enterprise.test/with-user-attributes [[:block 2]]
metabase.actions.actions-test/with-actions-test-data-and-actions-permissively-enabled [[:block 0]]
metabase.actions.test-util/with-actions [[:block 1]]
metabase.actions.test-util/with-actions-disabled [[:block 0]]
metabase.actions.test-util/with-actions-enabled [[:block 0]]
metabase.actions.test-util/with-actions-test-data [[:inner 0]]
metabase.actions.test-util/with-actions-test-data-and-actions-enabled [[:block 0]]
metabase.actions.test-util/with-actions-test-data-tables [[:block 1]]
metabase.actions.test-util/with-empty-db [[:inner 0]]
metabase.actions.test-util/with-temp-test-data [[:inner 0]]
metabase.api.card-test/with-cards-in-readable-collection [[:block 1]]
metabase.api.card-test/with-cards-in-writeable-collection [[:block 1]]
metabase.api.card-test/with-ordered-items [[:inner 0]]
metabase.api.card-test/with-temp-native-card! [[:block 1]]
metabase.api.card-test/with-temp-native-card-with-params! [[:block 1]]
metabase.api.collection-test/with-collection-hierarchy [[:block 1]]
metabase.api.collection-test/with-french-user-and-personal-collection [[:block 2]]
metabase.api.collection-test/with-some-children-of-collection [[:block 1]]
metabase.api.common/check [[:default]]
metabase.api.common/define-routes [[:block 0]]
metabase.api.common/let-404 [[:block 1]]
metabase.api.embed-test/do-response-formats [[:block 1]]
metabase.api.embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing [[:block 3]]
metabase.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing [[:block 3]]
metabase.api.embed-test/with-new-secret-key [[:block 0]]
metabase.api.embed-test/with-temp-card [[:block 1]]
metabase.api.embed-test/with-temp-dashcard [[:block 1]]
metabase.api.preview-embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
metabase.api.preview-embed-test/with-new-secret-key [[:block 0]]
metabase.cmd.copy/step [[:block 1]]
metabase.cmd.copy/with-connection-rollback-only [[:block 1]]
metabase.cmd.copy/with-disabled-db-constraints [[:block 2]]
metabase.config/build-type-case [[:block 0]]
metabase.dashboard-subscription-test/with-link-card-fixture-for-dashboard [[:block 2]]
metabase.db.liquibase/with-liquibase [[:block 1]]
metabase.db.liquibase/with-scope-locked [[:block 1]]
metabase.db.schema-migrations-test.impl/test-migrations [[:block 2]]
metabase.db/with-application-db [[:inner 0]]
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
metabase.driver.druid.client/DELETE [[:default]]
metabase.driver.druid.client/GET [[:default]]
metabase.driver.druid.client/POST [[:default]]
metabase.driver.druid.query-processor-test/druid-query [[:block 0]]
metabase.driver.druid.query-processor-test/druid-query-returning-rows [[:block 0]]
metabase.driver.mongo.query-processor/mongo-let [[:block 1]]
metabase.driver.sql-jdbc.actions/with-jdbc-transaction [[:block 1]]
metabase.driver.sql-jdbc.connection/with-connection-spec-for-testing-connection [[:block 1]]
metabase.driver.sql-jdbc.execute.diagnostic/capturing-diagnostic-info [[:block 1]]
metabase.driver.sql.query-processor/apply-top-level-clause [[:default]]
metabase.driver/with-driver [[:block 1]]
metabase.lib.binning/with-binning [[:default]]
metabase.lib.core/with-binning [[:default]]
metabase.lib.core/with-join-alias [[:default]]
metabase.lib.core/with-join-conditions [[:default]]
metabase.lib.join/with-join-alias [[:default]]
metabase.lib.join/with-join-conditions [[:default]]
metabase.lib.test-util.macros/$ids [[:inner 0]]
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
metabase.lib.util.match/match [[:inner 0]]
metabase.lib.util.match/match-one [[:inner 0]]
metabase.lib.util.match/replace [[:inner 0]]
metabase.lib.util.match/replace-in [[:inner 0]]
metabase.models.collection-test/with-collection-hierarchy [[:block 1]]
metabase.models.collection-test/with-current-user-perms-for-collections [[:block 1]]
metabase.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
metabase.models.dashboard-test/with-dash-in-collection [[:inner 0]]
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
metabase.models.params.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
metabase.models.params.chain-filter-test/with-fk-field-to-field-remapping [[:block 0]]
metabase.models.params.chain-filter-test/with-human-readable-values-remapping [[:block 0]]
metabase.models.setting.multi-setting/define-multi-setting [[:inner 0]]
metabase.models.setting/defsetting [[:block 1]]
metabase.models.task-history/with-task-history [[:block 1]]
metabase.permissions.core/allow-changing-all-users-group-members [[:block 0]]
metabase.permissions.core/disable-perms-cache [[:block 0]]
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing [[:block 3]]
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing [[:block 3]]
metabase.public-sharing.api-test/with-temp-public-card [[:block 1]]
metabase.public-sharing.api-test/with-temp-public-dashboard [[:block 1]]
metabase.public-sharing.api-test/with-temp-public-dashboard-and-card [[:block 1]]
metabase.pulse.api.alert-test/with-alert-in-collection [[:block 1]]
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
metabase.pulse.models.pulse-test/with-pulse-in-collection [[:inner 0]]
metabase.query-processor-test.pipeline-queries-test/pmbql-query [[:block 1]]
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query [[:block 1]]
metabase.query-processor.debug/debug> [[:block 0]]
metabase.query-processor.error-type/deferror [[:block 1]]
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
metabase.query-processor.store/cached [[:block 1]]
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
metabase.query-processor.streaming/streaming-response [[:block 1]]
metabase.query-processor.test-util/format-rows-by [[:default]]
metabase.request.core/as-admin [[:block 0]]
metabase.request.core/with-current-request [[:inner 0]]
metabase.request.core/with-current-user [[:inner 0]]
metabase.request.core/with-limit-and-offset [[:inner 0]]
metabase.request.session/as-admin [[:block 0]]
metabase.request.session/with-current-user [[:inner 0]]
metabase.sample-data-test/with-temp-sample-database-db [[:block 1]]
metabase.search.api-test/do-test-users [[:block 1]]
metabase.server.middleware.session/as-admin [[:block 0]]
metabase.server.middleware.session/with-current-user [[:inner 0]]
metabase.server.streaming-response-test/with-streaming-response-thread-pool [[:block 0]]
metabase.server.streaming-response-test/with-test-driver-db! [[:block 0]]
metabase.server.streaming-response/streaming-response [[:block 2]]
metabase.session.api/http-401-on-error [[:block 0]]
metabase.sync.fetch-metadata/log-if-error [[:block 1]]
metabase.sync.sync-metadata.fields-test/with-test-db [[:block 0]]
metabase.sync.util/do-with-start-and-finish-debug-logging [[:default]]
metabase.sync.util/sum-for [[:block 1]]
metabase.sync.util/sync-operation [[:block 3]]
metabase.sync.util/with-db-logging-disabled [[:default]]
metabase.sync.util/with-duplicate-ops-prevented [[:default]]
metabase.sync.util/with-emoji-progress-bar [[:block 1]]
metabase.sync.util/with-error-handling [[:block 1]]
metabase.sync.util/with-start-and-finish-logging [[:default]]
metabase.sync.util/with-start-and-finish-logging* [[:default]]
metabase.sync.util/with-sync-events [[:default]]
metabase.task/rerun-on-error [[:block 1]]
metabase.test/$ids [[:inner 0]]
metabase.test/as-admin [[:inner 0]]
metabase.test/dataset [[:inner 0]]
metabase.test/discard-setting-changes [[:block 1]]
metabase.test/format-rows-by [[:default]]
metabase.test/let-url [[:block 1]]
metabase.test/mbql-query [[:inner 0]]
metabase.test/native-query [[:block 0]]
metabase.test/query [[:block 1]]
metabase.test/run-mbql-query [[:inner 0]]
metabase.test/test-driver [[:inner 0]]
metabase.test/test-drivers [[:inner 0]]
metabase.test/test-helpers-set-global-values! [[:block 0]]
metabase.test/throw-if-called! [[:block 1]]
metabase.test/with-actions [[:block 1]]
metabase.test/with-actions-disabled [[:block 0]]
metabase.test/with-actions-enabled [[:block 0]]
metabase.test/with-actions-test-data [[:inner 0]]
metabase.test/with-actions-test-data-and-actions-enabled [[:block 0]]
metabase.test/with-actions-test-data-tables [[:block 1]]
metabase.test/with-additional-premium-features [[:block 1]]
metabase.test/with-current-user [[:inner 0]]
metabase.test/with-driver [[:block 1]]
metabase.test/with-empty-db [[:inner 0]]
metabase.test/with-empty-h2-app-db [[:block 0]]
metabase.test/with-metadata-provider [[:inner 0]]
metabase.test/with-premium-features [[:block 1]]
metabase.test/with-temp [[:inner 0]]
metabase.test/with-temp-copy-of-db [[:block 0]]
metabase.test/with-temp-scheduler [[:block 0]]
metabase.test/with-temp-test-data [[:inner 0]]
metabase.test/with-temp-vals-in-db [[:block 3]]
metabase.test/with-test-user [[:inner 0]]
metabase.test/with-user-in-groups [[:inner 0]]
metabase.test.data/$ids [[:inner 0]]
metabase.test.data/dataset [[:inner 0]]
metabase.test.data/mbql-query [[:inner 0]]
metabase.test.data/native-query [[:block 0]]
metabase.test.data/query [[:block 1]]
metabase.test.data/run-mbql-query [[:inner 0]]
metabase.test.data/with-empty-h2-app-db [[:block 0]]
metabase.test.data/with-temp-copy-of-db [[:block 0]]
metabase.test.data.datasets/test-driver [[:inner 0]]
metabase.test.data.datasets/test-drivers [[:inner 0]]
metabase.test.data.one-off-dbs/with-blank-db [[:block 0]]
metabase.test.data.one-off-dbs/with-blueberries-db [[:block 0]]
metabase.test.data.users/with-test-user [[:inner 0]]
metabase.test.util/discard-setting-changes [[:block 1]]
metabase.test.util/let-url [[:block 1]]
metabase.test.util/throw-if-called! [[:block 1]]
metabase.test.util/with-temp-scheduler [[:block 0]]
metabase.test.util/with-temp-vals-in-db [[:block 3]]
metabase.test.util/with-user-in-groups [[:inner 0]]
metabase.test.util.public-settings/with-additional-premium-features [[:block 1]]
metabase.test.util.public-settings/with-premium-features [[:block 1]]
metabase.test.util.thread-local/test-helpers-set-global-values! [[:block 0]]
metabase.timeseries-query-processor-test.util/test-timeseries-drivers [[:block 0]]
metabase.timeseries-query-processor-test/run-mbql-query [[:inner 0]]
metabase.upload-test/with-upload-table! [[:inner 0]]
metabase.upload.types/with-currency [[:default]]
metabase.upload.types/with-parens [[:default]]
metabase.util/auto-retry [[:block 1]]
metabase.util/case-enum [[:block 1]]
metabase.util/ignore-exceptions [[:block 0]]
metabase.util/or-with [[:block 1]]
metabase.util/profile [[:block 1]]
metabase.util/prog1 [[:inner 0]]
metabase.util/varargs [[:inner 0]]
metabase.util/with-us-locale [[:block 0]]
metabase.util.async/cancelable-thread [[:block 0]]
metabase.util.date-2/with-time-zone-same-instant [[:default]]
metabase.util.encryption-test/with-secret-key [[:block 1]]
metabase.util.honey-sql-2/with-database-type-info [[:default]]
metabase.util.i18n/deferred-trs [[:default]]
metabase.util.i18n/deferred-tru [[:default]]
metabase.util.i18n/trs [[:default]]
metabase.util.i18n/trsn [[:default]]
metabase.util.i18n/tru [[:default]]
metabase.util.i18n/trun [[:default]]
metabase.util.jvm/auto-retry [[:block 1]]
metabase.util.jvm/varargs [[:block 1]]
metabase.util.jvm/with-us-locale [[:block 0]]
metabase.util.malli/disable-enforcement [[:block 0]]
metabase.util.malli/with [[:default]]
metabase.util.malli/with-api-error-message [[:default]]
metabase.util.namespaces/import-fns [[:default]]
metabase.util.queue-test/await-test-while [[:block 1]]
metabuild-common.core/exit-when-finished-nonzero-on-exception [[:block 0]]
metabuild-common.core/step [[:block 1]]
metabuild-common.entrypoint/exit-when-finished-nonzero-on-exception [[:block 0]]
metabuild-common.steps/step [[:block 1]]
methodical.core/defmethod [[:inner 0]]
methodical.core/defmulti [[:inner 0]]
methodical.core/with-dispatcher [[:default]]
methodical.core/with-method-table [[:default]]
methodical.core/with-prefers [[:default]]
methodical.interface/with-dispatcher [[:default]]
methodical.interface/with-method-table [[:default]]
methodical.interface/with-prefers [[:default]]
methodical.macros/defmethod [[:inner 0]]
methodical.macros/defmulti [[:inner 0]]
saml20-clj.sp.response/validate-confirmation-datas [[:block 1]]
toucan2.connection/with-transaction [[:block 1]]
toucan2.core/build [[:block 0]]
toucan2.core/compile [[:block 0]]
toucan2.core/debug [[:block 0]]
toucan2.core/define-after-insert [[:inner 0]]
toucan2.core/define-after-select [[:inner 0]]
toucan2.core/define-after-update [[:inner 0]]
toucan2.core/define-before-delete [[:inner 0]]
toucan2.core/define-before-insert [[:inner 0]]
toucan2.core/define-before-select [[:inner 0]]
toucan2.core/define-default-fields [[:inner 0]]
toucan2.core/define-named-query [[:block 1]]
toucan2.core/deftransforms [[:block 1]]
toucan2.core/with-transaction [[:block 1]]
toucan2.tools.after/define-after-impl [[:default]]
toucan2.tools.after-insert/define-after-insert [[:inner 0]]
toucan2.tools.after-select/define-after-select [[:inner 0]]
toucan2.tools.after-update/define-after-update [[:inner 0]]
toucan2.tools.before-delete/define-before-delete [[:inner 0]]
toucan2.tools.before-insert/define-before-insert [[:inner 0]]
toucan2.tools.before-select/define-before-select [[:inner 0]]
toucan2.tools.compile/build [[:block 0]]
toucan2.tools.compile/compile [[:block 0]]
toucan2.tools.compile/resolved [[:block 0]]
toucan2.tools.debug/debug [[:block 0]]
toucan2.tools.default-fields/define-default-fields [[:inner 0]]
toucan2.tools.named-query/define-named-query [[:block 1]]
toucan2.tools.simple-out-transform/define-out-transform [[:inner 0]]
toucan2.tools.transformed/deftransforms [[:block 1]]
toucan2.tools.with-temp/with-temp [[:inner 0]]
toucan2.util/try-with-error-context [[:inner 0]]}}

View File

@@ -13,9 +13,6 @@ on:
type: string
commit:
type: string
release:
type: string
default: 'true'
workflow_dispatch:
inputs:
artifact-name:
@@ -59,7 +56,7 @@ jobs:
const version = "${{ inputs.tag }}".split('-')[0];
if (!isValidVersionString(version)) {
return "linux/amd64,linux/arm64";
return "linux/amd64";
}
// versions before 53 need to be handled differently
const buildInfo = getBuildRequirements(version);
@@ -69,8 +66,8 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Set up QEMU
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/setup-qemu-action@v3
@@ -106,8 +103,8 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Pull the container image
run: docker pull ${{ inputs.repo }}:${{ inputs.tag }}
- name: Launch container
@@ -126,8 +123,8 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Pull the container image
run: docker pull ${{ inputs.repo }}:${{ inputs.tag }}
- name: Launch container

View File

@@ -20,15 +20,11 @@ jobs:
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: | #js
if (process.env.BRANCH_NAME === "master" ||
/^release-x\.\d+\.x$/.test(process.env.BRANCH_NAME)) {
github.rest.repos.createDispatchEvent({
owner: "${{ github.repository_owner }}",
repo: "docs.metabase.github.io",
event_type: "docs_update",
client_payload: { branch_name: process.env.BRANCH_NAME
}
});
} else {
console.log("Branch name is not to be published: ", process.env.BRANCH_NAME);
}
github.rest.repos.createDispatchEvent({
owner: "${{ github.repository_owner }}",
repo: "docs.metabase.github.io",
event_type: "docs_update",
client_payload: {
branch_name: process.env.BRANCH_NAME // This needs to match the workflow file
}
});

View File

@@ -12,6 +12,33 @@ concurrency:
cancel-in-progress: true
jobs:
be-tests-h2:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
name: H2
steps:
- uses: actions/checkout@v4
- name: Test H2 driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-h2'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-athena-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
@@ -81,53 +108,6 @@ jobs:
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-clickhouse-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: clickhouse
MB_CLICKHOUSE_TEST_HOST: localhost
MB_CLICKHOUSE_TEST_PORT: 8123
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
name: Clickhouse
steps:
- uses: actions/checkout@v4
- name: Add ClickHouse TLS instance to /etc/hosts
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
- name: Start ClickHouse in Docker
uses: hoverkraft-tech/compose-action@v2.0.0
with:
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
down-flags: "--volumes"
services: |
clickhouse
clickhouse_tls
clickhouse_older_version
clickhouse_cluster_node1
clickhouse_cluster_node2
nginx
- name: Test Clickhouse driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-clickhouse-ee'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-druid-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
@@ -163,41 +143,6 @@ jobs:
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-druid-jdbc-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: druid-jdbc
services:
druid:
image: metabase/druid:29.0.1-json-no-ingestion
ports:
- "8888:8888"
env:
CLUSTER_SIZE: nano-quickstart
name: Druid (JDBC)
steps:
- uses: actions/checkout@v4
- name: Test Druid JDBC driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-druid-jdbc-ee'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-databricks-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
@@ -232,54 +177,27 @@ jobs:
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-databricks-multi-catalog-ee:
be-tests-druid-jdbc-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: databricks
MB_DATABRICKS_TEST_MULTI_LEVEL_SCHEMA: true
MB_DATABRICKS_TEST_HOST: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HOST }}
MB_DATABRICKS_TEST_HTTP_PATH: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HTTP_PATH }}
MB_DATABRICKS_TEST_TOKEN: ${{ secrets.MB_DATABRICKS_JDBC_TEST_TOKEN }}
MB_DATABRICKS_TEST_CATALOG: 'metabase_ci'
MB_DATABRICKS_TEST_CLIENT_ID: ${{ secrets.MB_DATABRICKS_JDBC_TEST_CLIENT_ID }}
MB_DATABRICKS_TEST_OAUTH_SECRET: ${{ secrets.MB_DATABRICKS_JDBC_TEST_OAUTH_SECRET }}
name: Databricks (Multi-Catalog)
DRIVERS: druid-jdbc
services:
druid:
image: metabase/druid:29.0.1-json-no-ingestion
ports:
- "8888:8888"
env:
CLUSTER_SIZE: nano-quickstart
name: Druid (JDBC)
steps:
- uses: actions/checkout@v4
- name: Test Databricks driver with multi-catalog
- name: Test Druid JDBC driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-databricks-multi-catalog-ee'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-h2:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
name: H2
steps:
- uses: actions/checkout@v4
- name: Test H2 driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-h2'
junit-name: 'be-tests-druid-jdbc-ee'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
@@ -1086,6 +1004,53 @@ jobs:
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-clickhouse-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: clickhouse
MB_CLICKHOUSE_TEST_HOST: localhost
MB_CLICKHOUSE_TEST_PORT: 8123
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
name: Clickhouse
steps:
- uses: actions/checkout@v4
- name: Add ClickHouse TLS instance to /etc/hosts
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
- name: Start ClickHouse in Docker
uses: hoverkraft-tech/compose-action@v2.0.0
with:
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
down-flags: "--volumes"
services: |
clickhouse
clickhouse_tls
clickhouse_older_version
clickhouse_cluster_node1
clickhouse_cluster_node2
nginx
- name: Test Clickhouse driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-clickhouse-ee'
test-args: >-
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
drivers-tests-result:
needs:
# if you add a driver job, you must add it to this list for it to be a required check

View File

@@ -17,13 +17,10 @@ env:
TEST_SUITES: |
[
"metabase-nodejs-react-sdk-embedding-sample-e2e",
"metabase-nextjs-sdk-embedding-sample-e2e",
"shoppy-e2e"
"metabase-nextjs-sdk-embedding-sample-e2e"
]
TEST_SUITES_TO_RUN_IN_MASTER_ONLY: |
[
"shoppy-e2e"
]
[]
SAMPLE_APP_MAIN_BRANCH_NAME: "main"
SAMPLE_APP_STABLE_RELEASE_BRANCH_NAME_SUFFIX: "stable"
@@ -218,9 +215,8 @@ jobs:
TZ: US/Pacific # to make node match the instance tz
# Sample Apps testing variables
CYPRESS_CI: true
TEST_SUITE: ${{ matrix.test_suite }}
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
@@ -246,7 +242,6 @@ jobs:
- name: Check if sample app branch exists
id: check-sample-app-branch
run: |
TEST_SUITE="${{ matrix.test_suite }}"
REPO_NAME="${TEST_SUITE%-e2e}"
REPO_URL="https://github.com/metabase/${REPO_NAME}.git"
echo "Checking if branch '${SAMPLE_APP_BRANCH_NAME}' exists in ${REPO_URL}..."
@@ -256,29 +251,9 @@ jobs:
echo "sample_app_branch_exists=false" >> $GITHUB_OUTPUT
fi
- name: Configure AWS credentials
if: ${{ matrix.test_suite == 'shoppy-e2e' }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.SHOPPY_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Download Shoppy's App DB PostgreSQL dump from S3
if: ${{ matrix.test_suite == 'shoppy-e2e' }}
run: |
aws s3 cp s3://${{ secrets.SHOPPY_METABASE_APP_DB_S3_BUCKET }}/${{ secrets.SHOPPY_METABASE_APP_DB_S3_KEY }} ./e2e/tmp/db_dumps/shoppy_metabase_app_db_dump.sql
- name: Prepare and launch Sample Apps for shoppy-e2e test suite
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' && matrix.test_suite == 'shoppy-e2e' }}
env:
METABASE_APP_DB_USER: ${{ secrets.SHOPPY_METABASE_APP_DB_USER }}
METABASE_APP_DB: ${{ secrets.SHOPPY_METABASE_APP_DB_DATABASE }}
run: npx tsx ./e2e/runner/start-sample-app-containers/start-ci.ts shoppy-e2e
- name: Prepare and launch Sample Apps for other test suites
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' && matrix.test_suite != 'shoppy-e2e' }}
run: npx tsx ./e2e/runner/start-sample-app-containers/start-ci.ts ${{ matrix.test_suite }}
- name: Prepare and launch Sample Apps
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
run: npx tsx ./e2e/runner/start-sample-app-containers/start-ci.ts
- name: Run e2e tests for Sample App
id: run-e2e-tests

View File

@@ -1,14 +1,12 @@
name: Build + Docker Uberjar
# This workflow builds an uberjar and pushes it to dockerhub for any branch, either on-demand,
# or automatically for the listed branches
on:
push:
branches:
- master
- "master"
- metabot-v3-second-try
- feature-visualizer
- internal-tools
- "internal-tools"
paths-ignore:
# config files
- ".**"
@@ -25,63 +23,16 @@ on:
description: "Optional full-length commit SHA-1 hash"
jobs:
get-container-info:
runs-on: ubuntu-22.04
timeout-minutes: 5
name: Get container info
outputs:
editions: ${{ toJson(fromJson(steps.info.outputs.result).editions) }}
ee: ${{ toJson(fromJson(steps.info.outputs.result).ee) }}
oss: ${{ toJson(fromJson(steps.info.outputs.result).oss) }}
steps:
- name: Get docker repo and tag
id: info
uses: actions/github-script@v7
with:
script: | # js
const branch = '${{ github.head_ref || github.ref_name }}';
const owner = '${{ github.repository_owner }}';
console.log(`Branch: ${branch}`);
if (branch === "master") {
return {
editions: ["ee", "oss"],
ee: {
repo: `${owner}/metabase-enterprise-head`,
tag: "latest"
},
oss: {
repo: `${owner}/metabase-head`,
tag: "latest"
}
};
}
return {
editions: ["ee"],
ee: {
repo: `${owner}/metabase-dev`,
tag: branch.replaceAll("/", "-"), // slashes make docker sad
},
};
- name: Print output
run: |
echo "${{ steps.info.outputs.result }}"
build:
needs: get-container-info
name: Build MB ${{ matrix.edition }}
runs-on: ubuntu-22.04
timeout-minutes: 15
strategy:
matrix:
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
edition: [ee, oss]
env:
MB_EDITION: ${{ matrix.edition }}
INTERACTIVE: false
SKIP_LICENSES: true # faster builds for dev
steps:
- name: Check out the code
uses: actions/checkout@v4
@@ -99,16 +50,16 @@ jobs:
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
with:
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
check-jar-health:
check_jar_health:
runs-on: ubuntu-22.04
name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
needs: [build, get-container-info]
needs: build
timeout-minutes: 10
strategy:
matrix:
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
edition: [ee, oss]
java-version: [21]
steps:
- name: Prepare JRE (Java Run-time Environment)
@@ -121,51 +72,261 @@ jobs:
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
- name: Launch uberjar
run: >-
java --add-opens java.base/java.nio=ALL-UNNAMED -jar ./target/uberjar/metabase.jar &
- name: Wait for Metabase to start
run: while ! curl 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
containerize:
needs: [check-jar-health, get-container-info]
strategy:
matrix:
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
uses: ./.github/workflows/containerize-jar.yml
secrets: inherit
with:
artifact-name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
commit: ${{ github.event.inputs.commit || github.sha }}
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
release: "false"
run-trivy:
needs: [get-container-info, containerize]
strategy:
matrix:
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
containerize_test_and_push_container:
runs-on: ubuntu-22.04
name: Run Trivy vulnerability scanner
name: Containerize ${{ matrix.edition }}
needs: check_jar_health
strategy:
matrix:
edition: [ee, oss]
fail-fast: false
services:
registry:
image: registry:2
ports:
- 5000:5000
permissions:
id-token: write
contents: read
security-events: write
steps:
- name: Check out the code
- name: Extract and clean branch name
shell: bash
run: echo "branch=$(echo $GITHUB_REF_NAME | sed 's/[^-._a-zA-Z0-9]/-/g')" >> $GITHUB_OUTPUT
id: extract_branch
- name: Verify the intended tag of the container image
run: echo "Container image will be tagged as ${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}"
- name: Check out the code (Dockerfile needed)
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit || github.sha }}
- name: Run Trivy vulnerability scanner
ref: ${{ github.event.inputs.commit }}
- name: Download uploaded artifacts to insert into container
uses: actions/download-artifact@v4
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
path: bin/docker/
- name: Move the ${{ matrix.edition }} uberjar to the context dir
run: mv bin/docker/target/uberjar/metabase.jar bin/docker/.
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2.5.0
with:
driver-opts: network=host
- name: Build ${{ matrix.edition }} container
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
network: host
tags: localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: true
push: true
- name: Launch ${{ matrix.edition }} container
run: docker run --rm -dp 3000:3000 localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}
timeout-minutes: 5
- name: Is Docker running?
run: docker ps
- name: Wait for Metabase to start and reach 100% health
run: while ! curl 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Retag and push images if master (ee) - should be cached at this point
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
network: host
tags: ${{ github.repository_owner }}/metabase-enterprise-head:latest
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: false
push: true
# TODO: remove when we're done testing metabot
- name: Retag and push images if metabot-v3-second-try (ee) - should be cached at this point
if: ${{ (github.ref_name == 'metabot-v3-second-try') && matrix.edition == 'ee' }}
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
network: host
tags: ${{ github.repository_owner }}/metabase-enterprise-head:metabot
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: false
push: true
- name: Retag and push images if master (oss) - should be cached at this point
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
network: host
tags: ${{ github.repository_owner }}/metabase-head:latest
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: false
push: true
- name: Retag and push images if dev branch - should be cached at this point
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
network: host
tags: ${{ github.repository_owner }}/metabase-dev:${{ steps.extract_branch.outputs.branch }}
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: false
push: true
- name: Run Trivy vulnerability scanner if master (ee)
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
uses: aquasecurity/trivy-action@0.29.0
env:
TRIVY_OFFLINE_SCAN: true
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
with:
image-ref: docker.io/${{ env.repo }}:${{ env.tag }}
image-ref: docker.io/${{ github.repository_owner }}/metabase-enterprise-head:latest
format: sarif
output: trivy-results.sarif
version: "v0.57.1"
- name: Upload Trivy scan results to GitHub Security tab
- name: Run Trivy vulnerability scanner if master (oss)
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
uses: aquasecurity/trivy-action@0.29.0
env:
TRIVY_OFFLINE_SCAN: true
with:
image-ref: docker.io/${{ github.repository_owner }}/metabase-head:latest
format: sarif
output: trivy-results.sarif
version: "v0.57.1"
- name: Run Trivy vulnerability scanner if dev branch
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
uses: aquasecurity/trivy-action@0.29.0
env:
TRIVY_OFFLINE_SCAN: true
with:
version: "v0.57.1"
image-ref: docker.io/${{ github.repository_owner }}/metabase-dev:${{ steps.extract_branch.outputs.branch }}
format: sarif
output: trivy-results.sarif
- name: Upload Trivy scan results to GitHub Security tab if master (ee)
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab if master (oss)
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab if dev branch
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results.sarif"
containerize_multi_arch:
runs-on: ubuntu-22.04
name: Containerize multi-arch ${{ matrix.edition }}
needs: check_jar_health
strategy:
matrix:
edition: [ee, oss]
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
- name: Extract and clean branch name
shell: bash
run: echo "branch=$(echo $GITHUB_REF_NAME | sed 's/[^-._a-zA-Z0-9]/-/g')" >> $GITHUB_OUTPUT
id: extract_branch
- name: Verify the intended tag of the container image
run: echo "Container image will be tagged as ${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}"
- name: Check out the code (Dockerfile needed)
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit }}
- name: Download uploaded artifacts to insert into container
uses: actions/download-artifact@v4
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
path: bin/docker/
- name: Move the ${{ matrix.edition }} uberjar to the context dir
run: mv bin/docker/target/uberjar/metabase.jar bin/docker/.
# We need it for multi-arch build
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: "arm64"
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2.5.0
with:
driver-opts: network=host
# Build experimental ubuntu-based images only for master
- name: Build ${{ matrix.edition }} Ubuntu based multi-arch container
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64,linux/arm64
file: bin/docker/Dockerfile_ubuntu
network: host
tags: localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}-ubuntu
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
no-cache: true
push: true
- name: Launch ${{ matrix.edition }} Ubuntu based container
run: docker run --rm -dp 3001:3000 localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}-ubuntu
timeout-minutes: 5
- name: Is Docker with Ubuntu running?
run: docker ps
- name: Wait for Ubuntu-based Metabase container to start and reach 100% health
run: while ! curl 'http://localhost:3001/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3
- name: Login to Docker Hub
if: ${{ github.ref_name == 'master' }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Push experimental ubuntu image only for versions based on a master
- name: Install regctl
if: ${{ github.ref_name == 'master' }}
uses: regclient/actions/regctl-installer@main
with:
release: "v0.4.7"
- name: Switch regctl to point to localhost:5000 via http
if: ${{ github.ref_name == 'master' }}
run: regctl registry set --tls disabled localhost:5000
- name: Retag and push ubuntu-based images if master (ee)
if: ${{ (matrix.edition == 'ee') && (github.ref_name == 'master') }}
run: regctl image copy localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-ee-ubuntu ${{ github.repository_owner }}/metabase-enterprise-head:latest-ubuntu
- name: Retag and push ubuntu-based images if master (oss)
if: ${{ (matrix.edition == 'oss') && (github.ref_name == 'master') }}
run: regctl image copy localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-oss-ubuntu ${{ github.repository_owner }}/metabase-head:latest-ubuntu

3
.gitignore vendored
View File

@@ -43,9 +43,6 @@
/frontend/test/snapshots/*
/e2e/snapshots/*
/e2e/tmp/*
!/e2e/tmp/db_dumps
/e2e/tmp/db_dumps/*
!/e2e/tmp/db_dumps/.gitkeep
/lein-plugins/*/target
/local
/locales/metabase-*.pot

View File

@@ -1,41 +0,0 @@
// Copied from the instance settings and re-ordered, may go out of sync with the real ones
// Locales in the format `xx_AA` were also changed to `xx-AA`
export const availableLocales = [
{ value: undefined, title: "default (instance locale)" },
{ value: "en", right: "🇺🇸", title: "en" },
{ value: "de", right: "🇩🇪", title: "de" }, // one that we know should be well supported
{ value: "ar", right: "🇸🇦", title: "ar" },
{ value: "ar-SA", right: "🇸🇦", title: "ar-SA" },
{ value: "bg", right: "🇧🇬", title: "bg" },
{ value: "ca", right: "🇪🇺", title: "ca" },
{ value: "cs", right: "🇨🇿", title: "cs" },
{ value: "da", right: "🇩🇰", title: "da" },
{ value: "es", right: "🇪🇺", title: "es" },
{ value: "fa", right: "🇮🇷", title: "fa" },
{ value: "fi", right: "🇫🇮", title: "fi" },
{ value: "fr", right: "🇫🇷", title: "fr" },
{ value: "he", right: "🇮🇱", title: "he" },
{ value: "hu", right: "🇭🇺", title: "hu" },
{ value: "id", right: "🇮🇩", title: "id" },
{ value: "it", right: "🇮🇹", title: "it" },
{ value: "ja", right: "🇯🇵", title: "ja" },
{ value: "ko", right: "🇰🇷", title: "ko" },
{ value: "lv", right: "🇱🇻", title: "lv" },
{ value: "ms", right: "🇲🇾", title: "ms" },
{ value: "nb", right: "🇳🇴", title: "nb" },
{ value: "nl", right: "🇳🇱", title: "nl" },
{ value: "pl", right: "🇵🇱", title: "pl" },
{ value: "pt-BR", right: "🇧🇷", title: "pt-BR" },
{ value: "ru", right: "🇷🇺", title: "ru" },
{ value: "sk", right: "🇸🇰", title: "sk" },
{ value: "sl", right: "🇸🇱", title: "sl" },
{ value: "sq", right: "🇦🇱", title: "sq" },
{ value: "sr", right: "🇷🇸", title: "sr" },
{ value: "sv", right: "🇸🇪", title: "sv" },
{ value: "tr", right: "🇹🇷", title: "tr" },
{ value: "uk", right: "🇺🇦", title: "uk" },
{ value: "vi", right: "🇻🇳", title: "vi" },
{ value: "zh-CN", right: "🇨🇳", title: "zh-CN" },
{ value: "zh-HK", right: "🇭🇰", title: "zh-HK" },
{ value: "zh-TW", right: "🇹🇼", title: "zh-TW" },
];

View File

@@ -5,7 +5,6 @@ import { GlobalTypes } from "@storybook/types";
window.METABASE_REMOVE_DELAYS = true;
import { storybookThemeOptions } from "embedding-sdk/test/storybook-themes";
import { availableLocales } from "./constants";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
@@ -42,15 +41,4 @@ export const globalTypes: GlobalTypes = {
dynamicTitle: true,
},
},
locale: {
name: "Locale",
description: "Locale to be passed to the MetabaseProvider",
defaultValue: undefined,
toolbar: {
icon: "globe",
items: availableLocales,
showName: true,
dynamicTitle: true,
},
},
};

View File

@@ -247,7 +247,7 @@
{:mvn/version "1.6.1"} ; Enables local profiling and heat map generation
com.clojure-goes-fast/clj-memory-meter
{:mvn/version "0.4.0"} ; Enables easy memory measurement
com.gfredericks/test.chuck {:mvn/version "0.2.15"} ; generating strings from regexes (useful with malli)
com.gfredericks/test.chuck {:mvn/version "0.2.14"} ; generating strings from regexes (useful with malli)
criterium/criterium {:mvn/version "0.4.6"} ; benchmarking library
dev.weavejester/hashp {:mvn/version "0.4.0"} ; debugging/spying utility
djblue/portal {:mvn/version "0.59.0"} ; ui for inspecting values

View File

@@ -21,7 +21,7 @@
;; - [Drivers](#metabase.driver)
;; - [Permissions](#metabase.models.permissions)
;; - [The Query Processor](#metabase.query-processor)
;; - [Application Settings](#metabase.settings.models.setting)
;; - [Application Settings](#metabase.models.setting)
;;
;; ## Important Libraries
;;
@@ -72,11 +72,11 @@
[metabase.driver :as driver]
[metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
[metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
[metabase.models.setting :as setting]
[metabase.query-processor.compile :as qp.compile]
[metabase.query-processor.timezone :as qp.timezone]
[metabase.server.handler :as handler]
[metabase.server.instance :as server]
[metabase.settings.core :as setting]
[metabase.sync.core :as sync]
[metabase.test :as mt]
[metabase.test-runner]

View File

@@ -220,19 +220,15 @@
static-deps (ns.parse/deps-from-ns-decl decl)
dynamic-deps (for [symb (find-dynamically-loaded-namespaces file)]
(vary-meta symb assoc ::dynamic :require-and-friends))
;;
;; excluded from the diff for now, see https://metaboat.slack.com/archives/C0669P4AF9N/p1745875106092029 for
;; rationale.
;;
;; defenterprise-deps (for [symb (find-defenterprises file)]
;; (vary-meta symb assoc ::dynamic :defenterprise))
;; defenterprise-schema-deps (for [symb (find-defenterprise-schemas file)]
;; (vary-meta symb assoc ::dynamic :defenterprise-schema))
defenterprise-deps (for [symb (find-defenterprises file)]
(vary-meta symb assoc ::dynamic :defenterprise))
defenterprise-schema-deps (for [symb (find-defenterprise-schemas file)]
(vary-meta symb assoc ::dynamic :defenterprise-schema))
deps (into (sorted-set) cat
[static-deps
dynamic-deps
#_defenterprise-deps
#_defenterprise-schema-deps])]
defenterprise-deps
defenterprise-schema-deps])]
{:namespace ns-symb
:module (module ns-symb)
:deps (sort-by pr-str

View File

@@ -8,18 +8,12 @@ title: Driver interface changelog
- Added a feature `:expression-literals` for drivers that support expressions consisting of a single string, number, or boolean literal value.
- Added a feature `:multi-level-schema` for drivers that support hierarchical levels between database and schema. Such as databricks' catalog. Defaults to false.
- Added the multi-method `adjust-schema-qualification` that allows drivers to to qualify, or unqualify table schemas based on enabling or disabling multi-level-schema support. Drivers may need to implement `sql.qp/->honeysql [driver ::h2x/identifier]` to properly quote fully qualified schemas.
- Added a new abstract driver mix-in `:metabase.driver.sql.query-processor.boolean-is-comparison/boolean-is-comparison` that can be inherited by drivers that require boolean literal constants to be converted to comparison expressions in the top level of WHERE, AND, OR, NOT, and CASE clauses.
- Added the multi-method `float-dbtype` which returns the name of the float type we coerce to for coercion strategies and the `float()` custom expression function.
- Added a feature `:expressions/float` for drivers that support casting text to floats.
- Added the multi-method `integer-dbtype` that allows the driver to control which type integers are cast to.
## Metabase 0.54.0
- Added the multi-method `allowed-promotions` that allows driver control over which column type promotions are supported for uploads.

View File

@@ -189,8 +189,6 @@ In order to run the tests locally, see [sdk docs about e2e](https://github.com/m
In order to check compatibility between Sample Apps and Embedding SDK, we have a special test suite for each sample app that pulls this Sample App, starts it and runs its Cypress tests against the local `metabase.jar` and local `@metabase/embedding-sdk-react` package.
#### Local runs
To run these tests locally, run:
```
ENTERPRISE_TOKEN=<token> TEST_SUITE=<sample_app_repo_name>-e2e OPEN_UI=false EMBEDDING_SDK_VERSION=local START_METABASE=false GENERATE_SNAPSHOTS=false START_CONTAINERS=false yarn test-cypress
@@ -201,16 +199,6 @@ For example for the `metabase-nodejs-react-sdk-embedding-sample`, run:
ENTERPRISE_TOKEN=<token> TEST_SUITE=metabase-nodejs-react-sdk-embedding-sample-e2e OPEN_UI=false EMBEDDING_SDK_VERSION=local START_METABASE=false GENERATE_SNAPSHOTS=false START_CONTAINERS=false yarn test-cypress
```
##### :warning: Obtaining the Shoppy's Metabase App DB Dump locally
For the Shoppy's Sample App Tests (`TEST_SUITE=shoppy-e2e`) locally, a proper App DB dump of the Shoppy's Metabase Instance must be placed to the `./e2e/tmp/db_dumps/shoppy_metabase_app_db_dump.sql`
You can get it by:
- Enabling the `Tailscale` and logging in using your work email address.
- Running `pg_dump "postgres://{{ username }}:{{ password }}@{{ host }}:{{ port }}/{{ database }}" > ./e2e/tmp/db_dumps/shoppy_metabase_app_db_dump.sql` command.
- See the `Shoppy Coredev Appdb` record in `1password` for credentials.
#### CI runs
On our CI, test failures do not block the merging of a pull request (PR). However, if a test fails, it’s most likely due to one of the following reasons:
- **Build Failure**:

View File

@@ -109,7 +109,7 @@ You can add events to collections, and organize those events into timelines. See
## Uploading data
You can upload data to collections. See [Uploading data](./uploads.md).
You can upload data to collections. See [Uploading data](./uploads.md)]
## Further reading

View File

@@ -123,9 +123,12 @@ To view your conditional counts by plan, set the **Group by** column to "Plan".
## Related functions
Different ways to do the same thing, because it's fun to try new things.
**Metabase**
- [Conditional running counts](#conditional-running-counts)
- [case](#case)
- [CumulativeCount](#cumulativecount)
**Other tools**
@@ -133,10 +136,43 @@ To view your conditional counts by plan, set the **Group by** column to "Plan".
- [Spreadsheets](#spreadsheets)
- [Python](#python)
### case
### Conditional running counts
You can combine [`Count`](../expressions-list.md#count) with [`case`](./case.md):
`CountIf` doesn't do running counts, and [CumulativeCount](../expressions/cumulative) doesn't accept conditions (or any other arguments) . To do conditional running counts, you'll need to be creative: combine [CumulativeSum](../expressions/cumulative) (not `CumulativeCount`! ) with [`case`](./case.md). The idea is to use `case` to return `1` when a condition is satisfied and `0` when it isn't, then compute the running sum of all the 1's.
```
Count(case([Plan] = "Basic", [ID]))
```
to do the same thing as `CountIf`:
```
CountIf([Plan] = "Basic")
```
The `case` version lets you count a different column when the condition isn't met. For example, if you've got data from different sources:
| ID: Source A | Plan: Source A | ID: Source B | Plan: Source B |
| ------------ | -------------- | ------------ | -------------- |
| 1 | Basic | | |
| | | B | basic |
| | | C | basic |
| 4 | Business | D | business |
| 5 | Premium | E | premium |
To count the total number of Basic plans across both sources, you could create a `case` expression to:
- Count the rows in "ID: Source A" where "Plan: Source A = "Basic"
- Count the rows in "ID: Source B" where "Plan: Source B = "basic"
```
Count(case([Plan: Source A] = "Basic", [ID: Source A],
case([Plan: Source B] = "basic", [ID: Source B])))
```
### CumulativeCount
`CountIf` doesn't do running counts. You'll need to combine [CumulativeCount](../expressions-list.md#cumulativecount) with [`case`](./case.md).
If our sample data is a time series:
@@ -158,7 +194,7 @@ And we want to get the running count of active plans like this:
Create an aggregation from **Summarize** > **Custom expression**:
```
CumulativeSum(case([Active Subscription] = true, 1,0))
CumulativeCount(case([Active Subscription] = true, [ID]))
```
You'll also need to set the **Group by** column to "Created Date: Month".

View File

@@ -1,3 +0,0 @@
import path from "path";
export const E2E_TMP_FOLDER_PATH = path.resolve(__dirname, "../../../tmp");

View File

@@ -1,7 +1,10 @@
import fs from "fs";
import path from "path";
import { shell } from "../../cypress-runner-utils";
import { E2E_TMP_FOLDER_PATH } from "../constants/e2e-tmp-folder-path";
const E2E_TMP_FOLDER_PATH = path.resolve(__dirname, "../../../tmp");
export function fetchApp({
appName,

View File

@@ -3,7 +3,6 @@ import fs from "fs";
import * as path from "path";
const ROOT_FOLDER_PATH = path.resolve(__dirname, "../../../..");
import { E2E_TMP_FOLDER_PATH } from "../constants/e2e-tmp-folder-path";
const METABASE_JAR_DIST_PATH = path.join(ROOT_FOLDER_PATH, "target/uberjar");
@@ -30,31 +29,17 @@ export function copyExampleEnvFile({
}
export function copyLocalMetabaseJar(rootPath: string) {
const destinationPath = path.join(rootPath, LOCAL_DIST_PATH);
const metabaseJarToPath = path.join(rootPath, LOCAL_DIST_PATH);
fs.cpSync(METABASE_JAR_DIST_PATH, destinationPath, { recursive: true });
fs.cpSync(METABASE_JAR_DIST_PATH, metabaseJarToPath, { recursive: true });
}
export function copyLocalEmbeddingSdkPackage(rootPath: string) {
const destinationPath = path.join(rootPath, LOCAL_DIST_PATH, "embedding-sdk");
fs.cpSync(EMBEDDING_SDK_DIST_PATH, destinationPath, { recursive: true });
}
// See `docs/developers-guide/e2e-tests.md` for more info
export function copyShoppyMetabaseAppDBDump(rootPath: string) {
const sourcePath = path.join(
E2E_TMP_FOLDER_PATH,
"db_dumps/shoppy_metabase_app_db_dump.sql",
);
// The dump must be copied to the Shoppy's `local-dist` directory.
// After that it will be used by the Shoppy's Docker container when it is built and started.
const destinationPath = path.join(
const embeddingSdkToPath = path.join(
rootPath,
LOCAL_DIST_PATH,
"metabase_dump.sql",
"embedding-sdk",
);
fs.cpSync(sourcePath, destinationPath);
fs.cpSync(EMBEDDING_SDK_DIST_PATH, embeddingSdkToPath, { recursive: true });
}

View File

@@ -2,10 +2,4 @@ import type { SampleAppTestSuiteName } from "../sample-apps-shared/types";
import { startSampleAppContainers } from "./startSampleAppContainers";
const testSuite = process.argv?.[2]?.trim() as SampleAppTestSuiteName;
if (!testSuite) {
throw new Error("Test suite parameter is required");
}
startSampleAppContainers(testSuite);
startSampleAppContainers(process.env.TEST_SUITE as SampleAppTestSuiteName);

View File

@@ -6,7 +6,6 @@ import {
copyExampleEnvFile,
copyLocalEmbeddingSdkPackage,
copyLocalMetabaseJar,
copyShoppyMetabaseAppDBDump,
} from "../sample-apps-shared/helpers/prepare-app";
import { setupAppCleanup } from "../sample-apps-shared/helpers/setup-app-cleanup";
import { startContainers } from "../sample-apps-shared/helpers/start-containers";
@@ -60,10 +59,6 @@ export async function startSampleAppContainers(
copyLocalEmbeddingSdkPackage(rootPath);
}
if (testSuite === "shoppy-e2e") {
copyShoppyMetabaseAppDBDump(rootPath);
}
await startContainers({
cwd: rootPath,
env,

View File

@@ -1,29 +0,0 @@
import { CollectionBrowser } from "@metabase/embedding-sdk-react";
import { signInAsAdminAndEnableEmbeddingSdk } from "e2e/support/helpers/component-testing-sdk";
import {
mockAuthProviderAndJwtSignIn,
mountSdkContent,
} from "e2e/support/helpers/component-testing-sdk/component-embedding-sdk-helpers";
describe("scenarios > embedding-sdk > requests", () => {
describe("cache preflight requests", () => {
beforeEach(() => {
signInAsAdminAndEnableEmbeddingSdk();
cy.signOut();
mockAuthProviderAndJwtSignIn();
});
it("caches preflight requests by setting Access-Control-Max-Age header", () => {
cy.intercept("GET", "/api/user/current").as("getCurrentUser");
mountSdkContent(<CollectionBrowser />);
cy.wait("@getCurrentUser").then(({ response }) => {
const maxAgeHeader = response?.headers["access-control-max-age"];
expect(maxAgeHeader).to.equal("60");
});
});
});
});

View File

@@ -75,27 +75,6 @@ const INTEGER_TEST_CASES: CastTestCase[] = [
filterValue: "29",
expectedRowCount: 1,
},
{
name: "FloatLiteral",
expression: "integer(10.4)",
filterOperator: "Equal to",
filterValue: "10",
expectedRowCount: 200,
},
{
name: "FloatColumn",
expression: "integer([Price])",
filterOperator: "Equal to",
filterValue: "29",
expectedRowCount: 4,
},
{
name: "FloatExpression",
expression: "integer(42.333 + 0.56)",
filterOperator: "Equal to",
filterValue: "43",
expectedRowCount: 200,
},
];
const DATE_TEST_CASES: CastTestCase[] = [

View File

@@ -682,8 +682,10 @@ describe("issue 25990", () => {
cy.findByText("People").click();
cy.findByText("ID").click();
cy.findByPlaceholderText("Enter an ID").type("10").blur();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
cy.findByTestId("qb-filters-panel")
@@ -1206,8 +1208,9 @@ describe("issue 45252", { tags: "@external" }, () => {
H.popover().within(() => {
cy.findByText("Binary").click();
cy.findByLabelText("Is empty").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
H.assertQueryBuilderRowCount(0);
@@ -1223,8 +1226,9 @@ describe("issue 45252", { tags: "@external" }, () => {
H.popover().within(() => {
cy.findByText("Jsonb").click();
cy.findByLabelText("Not empty").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
H.assertQueryBuilderRowCount(2);
});

View File

@@ -94,9 +94,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("Quantity").click();
cy.findByText("20").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Quantity is equal to 20")
@@ -124,9 +124,9 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByText("Summaries").click();
cy.findByText("Count").click();
cy.findByPlaceholderText("Min").type("500");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Count is greater than or equal to 500")
.should("be.visible");
@@ -140,9 +140,9 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByText("Product").click();
cy.findByText("Category").click();
cy.findByText("Gadget").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Product → Category is Gadget")
.should("be.visible");
@@ -184,7 +184,6 @@ describe("scenarios > filters > bulk filtering", () => {
it("should be able to add and remove filters for all query stages", () => {
H.visitQuestionAdhoc(multiStageQuestionDetails);
cy.get("@dataset.all").should("have.length", 1);
cy.log("add filters for all stages in the filter modal");
cy.log("stage 0");
@@ -192,37 +191,33 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("Category").click();
cy.findByText("Gadget").click();
cy.button("Add another filter").click();
cy.button("Add filter").click();
});
cy.get("@dataset.all").should("have.length", 1);
cy.log("stage 1");
H.filter();
H.popover().within(() => {
cy.findByText("Summaries").click();
cy.findByText("Category").click();
cy.findByText("Widget").click();
cy.button("Add another filter").click();
cy.button("Add filter").click();
});
cy.get("@dataset.all").should("have.length", 1);
cy.log("stage 2");
H.filter();
H.popover().within(() => {
cy.findByText("Summaries (2)").click();
cy.findByText("Category").click();
cy.findByText("Gizmo").click();
cy.button("Add another filter").click();
cy.button("Add filter").click();
});
cy.get("@dataset.all").should("have.length", 1);
cy.log("stage 3");
H.filter();
H.popover().within(() => {
cy.findByText("Summaries (3)").click();
cy.findByText("Category").click();
cy.findByText("Doohickey").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
cy.get("@dataset.all").should("have.length", 2);
applyFilters();
cy.log("check filters from all stages to be present in the filter panel");
H.queryBuilderFiltersPanel().within(() => {
@@ -300,7 +295,7 @@ describe("scenarios > filters > bulk filtering", () => {
});
H.popover().findByText(SEGMENT_2_NAME).click();
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText(SEGMENT_2_NAME)
.should("be.visible");
@@ -348,9 +343,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("boolean").click();
cy.findByText("True").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(2);
});
@@ -358,9 +353,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("boolean").click();
cy.findByText("True").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(2);
H.queryBuilderFiltersPanel().findByText("boolean is true").click();
@@ -376,9 +371,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("boolean").click();
cy.findByText("True").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(2);
H.queryBuilderFiltersPanel()
@@ -401,7 +396,7 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByText("Created At").click();
cy.findByText("Today").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Created At is today")
@@ -413,7 +408,7 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByText("Created At").click();
cy.findByText("Previous 3 months").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Created At is in the previous 3 months")
.should("be.visible");
@@ -430,9 +425,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("Source").click();
cy.findByText("Affiliate").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Source is Affiliate")
.should("be.visible");
@@ -443,9 +438,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("State").click();
cy.findByText("AZ").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("State is AZ")
.should("be.visible");
@@ -463,9 +458,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("ID").click();
cy.findByLabelText("Filter value").type("17,18");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(2);
H.tableInteractive().within(() => {
cy.findByText("131.68").should("be.visible"); // total for order id 17
@@ -477,9 +472,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("Product ID").click();
cy.findByLabelText("Filter value").type("65");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(107);
});
});
@@ -495,9 +490,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.selectFilterOperator("Contains");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("Indian");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(5);
});
@@ -506,9 +501,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.selectFilterOperator("Ends with");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("Valley");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.assertQueryBuilderRowCount(8);
});
@@ -518,9 +513,9 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByLabelText("Filter value")
.type("Indiantown,Indian Valley")
.blur();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("City is 2 selections")
.should("be.visible");
@@ -539,9 +534,9 @@ describe("scenarios > filters > bulk filtering", () => {
cy.findByText("Price").click();
cy.findByPlaceholderText("Min").type("50");
cy.findByPlaceholderText("Max").type("80");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Price is between 50 and 80")
.should("be.visible");
@@ -553,9 +548,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.selectFilterOperator("Greater than");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("50");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Price is greater than 50")
.should("be.visible");
@@ -566,9 +561,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.popover().within(() => {
cy.findByText("Price").click();
cy.findByPlaceholderText("Max").type("50");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Price is less than or equal to 50")
.should("be.visible");
@@ -602,9 +597,9 @@ describe("scenarios > filters > bulk filtering", () => {
H.selectFilterOperator("Greater than");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("90");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
cy.wait("@dataset");
applyFilters();
H.queryBuilderFiltersPanel()
.findByText("Price is greater than 90")
.should("be.visible");
@@ -612,3 +607,8 @@ describe("scenarios > filters > bulk filtering", () => {
});
});
});
const applyFilters = () => {
H.runButtonOverlay().click();
cy.wait("@dataset");
};

View File

@@ -106,8 +106,9 @@ describe("scenarios > question > filter", () => {
H.popover().within(() => {
cy.findByText("Product ID").click();
cy.findByText("Aerodynamic Linen Coat").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.log("Reported failing on v0.36.4 and v0.36.5.1");
cy.findByTestId("loading-indicator").should("not.exist");
@@ -737,8 +738,9 @@ describe("scenarios > question > filter", () => {
H.popover().within(() => {
cy.findByText("Category").click();
cy.findByText("Gizmo").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
H.openNotebook();
H.verifyNotebookQuery("Products", [

View File

@@ -1330,8 +1330,9 @@ describe("issue 45300", () => {
cy.findAllByText("Product").should("have.length", 2).first().click();
cy.findByText("Category").click();
cy.findByText("Doohickey").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
cy.findByTestId("filter-pill").should(

View File

@@ -74,8 +74,9 @@ describe("scenarios > metrics > question", () => {
cy.findByText("Product").click();
cy.findByText("Category").click();
cy.findByText("Gadget").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.findByTestId("scalar-container")
.findByText("4,939")
.should("be.visible");

View File

@@ -393,8 +393,9 @@ describe("scenarios > models metadata", () => {
cy.findByText("User").click();
cy.findByText("Source").click();
cy.findByText("Twitter").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
cy.findByTestId("question-row-count")
.invoke("text")

View File

@@ -52,8 +52,9 @@ describe("scenarios > models", () => {
H.selectFilterOperator("Contains");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("Fisher");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
assertQuestionIsBasedOnModel({
@@ -102,8 +103,9 @@ describe("scenarios > models", () => {
H.selectFilterOperator("Contains");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("Fisher");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
assertQuestionIsBasedOnModel({
@@ -168,8 +170,9 @@ describe("scenarios > models", () => {
H.selectFilterOperator("Greater than");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("30");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset").then(({ response }) => {
expect(response.body.error).to.not.exist;
});
@@ -351,8 +354,8 @@ describe("scenarios > models", () => {
.and("contain.text", "Orders");
cy.findByText("Everywhere").click();
getResults().should("have.length", 5);
cy.findByText("5 results").should("be.visible");
getResults().should("have.length", 6);
cy.findByText("6 results").should("be.visible");
getResults()
.eq(0)
.should("have.attr", "data-model-type", "dataset")
@@ -371,6 +374,10 @@ describe("scenarios > models", () => {
.and("contain.text", "Orders, Count");
getResults()
.eq(4)
.should("have.attr", "data-model-type", "card")
.and("contain.text", "Orders");
getResults()
.eq(5)
.should("have.attr", "data-model-type", "table")
.and("contain.text", "Orders");
});
@@ -458,7 +465,8 @@ describe("scenarios > models", () => {
H.filter();
H.popover().findByText("Discount").click();
H.selectFilterOperator("Not empty");
H.popover().button("Apply filter").click();
H.popover().button("Add filter").click();
H.runButtonOverlay().click();
cy.wait("@dataset");
assertQuestionIsBasedOnModel({

View File

@@ -613,6 +613,7 @@ describe(
cy.findByText("Created At").click();
cy.findByText("Today").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
cy.get("[data-testid=cell-data]")
.should("have.length", 4)
@@ -955,8 +956,9 @@ describe("issue 28971", () => {
H.popover().within(() => {
cy.findByText("Quantity").click();
cy.findByText("20").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Quantity is equal to 20").should("exist");
@@ -995,8 +997,9 @@ describe("issue 28971", () => {
H.popover().within(() => {
cy.findByText("Quantity").click();
cy.findByText("20").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Quantity is equal to 20").should("exist");
@@ -2130,6 +2133,7 @@ describe("cumulative count - issue 33330", () => {
cy.findByText("Created At").click();
cy.findByText("Today").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
H.queryBuilderHeader().findByLabelText("Show filters").click();

View File

@@ -32,7 +32,12 @@ export function addWidgetStringFilter(
}
export function setWidgetStringFilter(value) {
popover().first().find("input").not("[type=hidden]").first().type(`${value}`);
popover()
.first()
.find("input")
.not("[type=hidden]")
.first()
.type(`${value}{enter}`);
}
/**
@@ -161,10 +166,10 @@ export function closeEntryForm() {
*/
function addBetweenFilter([low, high] = [], buttonLabel = "Add filter") {
popover().within(() => {
cy.get("input").first().type(`${low}`);
cy.get("input").first().type(`${low}{enter}`);
// eslint-disable-next-line no-unsafe-element-filtering
cy.get("input").last().type(`${high}`);
cy.get("input").last().type(`${high}{enter}`);
});
cy.button(buttonLabel).click();
@@ -175,7 +180,7 @@ function addBetweenFilter([low, high] = [], buttonLabel = "Add filter") {
* @param {string} value
*/
function addSimpleNumberFilter(value, buttonLabel = "Add filter") {
cy.findByPlaceholderText("Enter a number").type(`${value}`);
cy.findByPlaceholderText("Enter a number").type(`${value}{enter}`);
cy.button(buttonLabel).click();
}
@@ -185,7 +190,9 @@ function addSimpleNumberFilter(value, buttonLabel = "Add filter") {
*/
function enterDefaultValue(value, buttonLabel = "Add filter") {
cy.findByText("Enter a default value…").click();
cy.findByPlaceholderText("Enter a default value…").type(`${value}`).blur();
cy.findByPlaceholderText("Enter a default value…")
.type(`${value}{enter}`)
.blur();
cy.button(buttonLabel).click();
}

View File

@@ -197,8 +197,9 @@ describe("scenarios > question > native", () => {
H.selectFilterOperator(operator);
H.popover().within(() => {
cy.findByLabelText("Filter value").type("This has a value");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.log(
`**Mid-point assertion for "${operator}" filter| FAILING in v0.36.6**`,

View File

@@ -571,8 +571,9 @@ describe(
H.popover().within(() => {
cy.findAllByText("ID").should("have.length", 2).first().click();
cy.findByLabelText("Filter value").type(id).click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
H.assertQueryBuilderRowCount(1);
removeFilter();
@@ -2290,7 +2291,7 @@ describe("issue 48829", () => {
H.modal().should("not.exist");
});
it("should not show the unsaved changes warning when switching back to chill mode from the notebook editor after adding a filter via the filter picker (metabase#48829)", () => {
it("should not show the unsaved changes warning when switching back to chill mode from the notebook editor after adding a filter via the filter modal (metabase#48829)", () => {
H.createQuestion(questionDetails, { visitQuestion: true });
H.queryBuilderHeader()
@@ -2299,8 +2300,9 @@ describe("issue 48829", () => {
H.popover().within(() => {
cy.findByText("Category").click();
cy.findByText("Doohickey").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
H.queryBuilderHeader()
.button(/Editor/)

View File

@@ -1164,7 +1164,7 @@ describe("scenarios > question > multiple column breakouts", () => {
cy.findByText("Between").click();
cy.findByLabelText("Start date").clear().type(columnMinValue);
cy.findByLabelText("End date").clear().type(columnMaxValue);
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
}
@@ -1202,6 +1202,7 @@ describe("scenarios > question > multiple column breakouts", () => {
});
cy.log("assert query results");
H.runButtonOverlay().click();
cy.wait("@dataset");
}
@@ -1224,7 +1225,7 @@ describe("scenarios > question > multiple column breakouts", () => {
cy.findByPlaceholderText("Max")
.clear()
.type(String(columnMaxValue));
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
}
@@ -1262,6 +1263,7 @@ describe("scenarios > question > multiple column breakouts", () => {
});
cy.log("assert query results");
H.runButtonOverlay().click();
cy.wait("@dataset");
}

View File

@@ -482,8 +482,9 @@ describe("scenarios > question > nested", () => {
H.selectFilterOperator("Equal to");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("4");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.findAllByTestId("cell-data")
.should("contain", "Murray, Watsica and Wunsch")
@@ -523,8 +524,9 @@ describe("scenarios > question > nested", () => {
H.selectFilterOperator("Equal to");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("5");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.wait("@dataset");
H.queryBuilderFiltersPanel().findByText("Count is equal to 5");

View File

@@ -146,12 +146,8 @@ describe("scenarios > alert", () => {
const allowedDomain = "metabase.test";
const deniedDomain = "metabase.example";
const deniedEmail = `mailer@${deniedDomain}`;
const subscriptionError =
"You're only allowed to email subscriptions to allowed domains";
const alertError =
"You're only allowed to email alerts to allowed domains";
const adminSubscriptionError = `You're only allowed to email subscriptions to addresses ending in ${allowedDomain}`;
const adminAlertError = `You're only allowed to email alerts to addresses ending in ${allowedDomain}`;
const subscriptionError = `You're only allowed to email subscriptions to addresses ending in ${allowedDomain}`;
const alertError = `You're only allowed to email alerts to addresses ending in ${allowedDomain}`;
function addEmailRecipient(email) {
cy.findByRole("textbox").click().type(`${email}`).blur();
@@ -181,7 +177,7 @@ describe("scenarios > alert", () => {
addEmailRecipient(deniedEmail);
});
cy.findByText(adminAlertError);
cy.findByText(alertError);
cy.button("Done").should("be.disabled");
});
});
@@ -195,34 +191,6 @@ describe("scenarios > alert", () => {
addEmailRecipient(deniedEmail);
// Reproduces metabase#17977
cy.button("Send email now").should("be.disabled");
cy.button("Done").should("be.disabled");
cy.findByText(adminSubscriptionError);
});
});
it("should not display the list of approved domains for non-admins (metabase#57138)", () => {
cy.signInAsNormalUser();
H.visitQuestion(ORDERS_QUESTION_ID);
H.openSharingMenu("Create an alert");
H.modal().within(() => {
cy.findByText("New alert").should("be.visible");
cy.findByTestId("token-field").within(() => {
addEmailRecipient(deniedEmail);
});
cy.findByText(alertError);
cy.button("Done").should("be.disabled");
});
H.visitDashboard(ORDERS_DASHBOARD_ID);
H.openSharingMenu("Subscriptions");
H.sidebar().within(() => {
addEmailRecipient(deniedEmail);
cy.button("Send email now").should("be.disabled");
cy.button("Done").should("be.disabled");
cy.findByText(subscriptionError);

View File

@@ -172,105 +172,6 @@ describe("scenarios > question > download", () => {
);
});
describe("download format preference", { tags: "@flaky" }, () => {
it("should remember the selected format across page reloads", () => {
cy.intercept(
"PUT",
"/api/user-key-value/namespace/last_download_format/key/download_format_preference",
).as("saveFormat");
H.createQuestion(
{
name: "Format Preference Test",
query: {
"source-table": ORDERS_ID,
limit: 5,
},
display: "table",
},
{ visitQuestion: true },
);
cy.findByRole("button", { name: "Download results" }).click();
H.popover().within(() => {
cy.findByText(".xlsx")
.should("be.visible")
.click()
.then(() => {
cy.wait("@saveFormat");
});
});
cy.intercept(
"GET",
"/api/user-key-value/namespace/last_download_format/key/download_format_preference",
).as("fetchFormat");
cy.reload();
cy.wait("@fetchFormat");
cy.findByRole("button", { name: "Download results" }).click();
H.popover().within(() => {
cy.get("[data-checked='true']").should("contain", ".xlsx");
});
});
it("should remember the download format on dashboards", () => {
cy.intercept(
"PUT",
"/api/user-key-value/namespace/last_download_format/key/download_format_preference",
).as("saveFormat");
H.createQuestion({
name: "Dashboard Format Test",
query: {
"source-table": ORDERS_ID,
limit: 5,
},
display: "table",
}).then(({ body: { id: questionId } }) => {
H.createDashboard().then(({ body: { id: dashboardId } }) => {
H.addOrUpdateDashboardCard({
card_id: questionId,
dashboard_id: dashboardId,
});
H.visitDashboard(dashboardId);
H.getDashboardCard(0).realHover();
H.getDashboardCardMenu(0).click();
H.popover().within(() => {
cy.findByText("Download results").click();
});
H.popover().within(() => {
cy.findByText(".xlsx")
.should("be.visible")
.click()
.then(() => {
cy.wait("@saveFormat");
});
});
cy.intercept(
"GET",
"/api/user-key-value/namespace/last_download_format/key/download_format_preference",
).as("fetchFormat");
cy.reload();
cy.wait("@fetchFormat");
H.getDashboardCard(0).realHover();
H.getDashboardCardMenu(0).click();
H.popover().within(() => {
cy.findByText("Download results").click();
});
H.popover().within(() => {
cy.get("[data-checked='true']").should("contain", ".xlsx");
});
});
});
});
});
it("respects renamed columns in self-joins", () => {
const idLeftRef = [
"field",

View File

@@ -188,8 +188,9 @@ describe("scenarios > visualizations > bar chart", () => {
H.selectFilterOperator("Is not");
H.popover().within(() => {
cy.findByText("Gadget").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
H.getDraggableElements().should("have.length", 2);
H.getDraggableElements().eq(0).should("have.text", "Doohickey");
H.getDraggableElements().eq(1).should("have.text", "Widget");

View File

@@ -79,8 +79,9 @@ describe("scenarios > visualizations > funnel chart", () => {
H.selectFilterOperator("Is not");
H.popover().within(() => {
cy.findByText("Facebook").click();
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
H.getDraggableElements().should("have.length", 4);

View File

@@ -185,8 +185,9 @@ describe("issue 17524", () => {
H.selectFilterOperator("Greater than");
H.popover().within(() => {
cy.findByLabelText("Filter value").type("1");
cy.button("Apply filter").click();
cy.button("Add filter").click();
});
H.runButtonOverlay().click();
cy.get("polygon");
});
});

View File

@@ -334,74 +334,3 @@ where x < {{param}}`,
H.chartPathWithFillColor("#88BF4D").should("have.length", 4);
});
});
describe("issue 47757", () => {
beforeEach(() => {
H.restore();
cy.signInAsNormalUser();
});
it("should show correct tooltips for interpolated data points (metabase#47757)", () => {
H.visitQuestionAdhoc({
visualization_settings: {
"graph.dimensions": ["X"],
"graph.metrics": ["Y"],
series_settings: { Y: { "line.missing": "zero" } },
},
dataset_query: {
type: "native",
native: {
query: `select '2020-01-01' x, 10 y
union all select '2020-03-01' x, 30 y
union all select '2020-04-01' x, 40 y`,
},
database: SAMPLE_DB_ID,
},
display: "line",
});
H.cartesianChartCircleWithColor("#88BF4D").eq(0).trigger("mousemove");
H.assertEChartsTooltip({
header: "January 2020",
rows: [
{
color: "#88BF4D",
name: "Y",
value: 10,
},
],
footer: null,
blurAfter: true,
});
H.cartesianChartCircleWithColor("#88BF4D").eq(1).trigger("mousemove");
H.assertEChartsTooltip({
header: "February 2020",
rows: [
{
color: "#88BF4D",
name: "Y",
value: 0,
secondaryValue: "-100%",
},
],
footer: null,
blurAfter: true,
});
H.cartesianChartCircleWithColor("#88BF4D").eq(2).trigger("mousemove");
H.assertEChartsTooltip({
header: "March 2020",
rows: [
{
color: "#88BF4D",
name: "Y",
value: 30,
secondaryValue: "+∞%",
},
],
footer: null,
blurAfter: true,
});
});
});

View File

@@ -47,9 +47,7 @@ describe("issue 6010", () => {
.then(({ body: { id } }) => createQuestion(id))
.then(({ body: { id } }) => H.visitQuestion(id));
// Metric filters are transformed into aggregation case expression condition. The 21st point is first non filtered
// point.
H.cartesianChartCircle().eq(21).click();
H.cartesianChartCircle().eq(0).click();
H.popover().findByText("See these Orders").click();
cy.wait("@dataset");
@@ -1352,117 +1350,3 @@ describe("issue 57132", () => {
.should("be.visible");
});
});
describe("issue 52333", () => {
const baseQuery = `
SELECT *
FROM (
SELECT
category,
source,
state,
SUM(orders.discount) AS discount,
SUM(orders.total) AS total,
SUM(orders.quantity) AS quantity
FROM
orders
LEFT JOIN products ON orders.product_id = products.id
LEFT JOIN people ON orders.user_id = people.id
GROUP BY category, source, state
) AS filtered_orders
WHERE NOT (
category = 'Gizmo'
AND (
source IN ('Facebook', 'Google', 'Organic', 'Twitter')
OR state NOT IN ('AK')
)
);`;
const baseQuestionDetails = {
name: "52333",
display: "table",
native: {
query: baseQuery,
},
};
beforeEach(() => {
H.restore();
cy.signInAsAdmin();
});
it("pivot table should show subtotals for a group of a single value (metabase#52333)", () => {
H.createNativeQuestion(baseQuestionDetails, {
visitQuestion: true,
wrapId: true,
});
cy.get("@questionId").then((id) => {
const questionDetails = {
query: {
"source-table": `card__${id}`,
aggregation: [["count"]],
breakout: [
[
"field",
"CATEGORY",
{
"base-type": "type/Text",
},
],
[
"field",
"SOURCE",
{
"base-type": "type/Text",
},
],
[
"field",
"STATE",
{
"base-type": "type/Text",
},
],
],
},
display: "pivot",
visualization_settings: {
"pivot_table.column_split": {
rows: ["CATEGORY", "SOURCE", "STATE"],
columns: [],
values: ["count", "avg"],
},
"pivot_table.column_widths": {
leftHeaderWidths: [104, 92, 80],
totalLeftHeaderWidths: 276,
valueHeaderWidths: {},
},
"pivot_table.collapsed_rows": {
value: ['["Doohickey"]', '["Gadget"]', '["Widget"]'],
rows: ["CATEGORY", "SOURCE", "STATE"],
},
},
};
H.createQuestion(questionDetails, { visitQuestion: true });
});
H.queryBuilderMain().within(() => {
cy.findByText("Affiliate");
cy.findByText("AK");
// Ensure it does not show subtotals for the single value by default
cy.findByText("Totals for Affiliate").should("not.exist");
H.openVizSettingsSidebar();
});
H.sidebar().findByText("Condense duplicate totals").click();
// Ensure it shows subtotals for the single value
H.queryBuilderMain()
.findByText("Totals for Affiliate")
.should("be.visible");
});
});

View File

@@ -5,7 +5,7 @@
[medley.core :as m]
[metabase-enterprise.advanced-config.file.interface :as advanced-config.file.i]
[metabase.driver.util :as driver.u]
[metabase.settings.core :refer [defsetting]]
[metabase.models.setting :refer [defsetting]]
[metabase.util :as u]
[metabase.util.log :as log]
[metabase.util.quick-task :as quick-task]

View File

@@ -2,7 +2,7 @@
(:require
[clojure.spec.alpha :as s]
[metabase-enterprise.advanced-config.file.interface :as advanced-config.file.i]
[metabase.settings.core :as setting]
[metabase.models.setting :as setting]
[metabase.util :as u]
[metabase.util.log :as log]))

View File

@@ -1,8 +1,8 @@
(ns metabase-enterprise.advanced-config.models.pulse-channel
(:require
[clojure.string :as str]
[metabase.models.setting :refer [defsetting]]
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :refer [defsetting]]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru tru]]))

View File

@@ -6,9 +6,9 @@
[metabase-enterprise.serialization.cmd :as serialization.cmd]
[metabase.audit :as audit]
[metabase.db :as mdb]
[metabase.models.setting :refer [defsetting]]
[metabase.plugins :as plugins]
[metabase.premium-features.core :refer [defenterprise]]
[metabase.settings.core :refer [defsetting]]
[metabase.sync.util :as sync-util]
[metabase.util :as u]
[metabase.util.files :as u.files]

View File

@@ -1,3 +0,0 @@
(ns metabase-enterprise.cache.init
(:require
[metabase-enterprise.cache.task.refresh-cache-configs]))

View File

@@ -1,8 +1,8 @@
(ns metabase-enterprise.cache.strategies
(:require
[java-time.api :as t]
[metabase.cache.api]
[metabase.cache.core :as cache]
[metabase.api.cache]
[metabase.models.cache-config :as cache-config]
[metabase.premium-features.core :refer [defenterprise defenterprise-schema]]
[metabase.query-processor.middleware.cache-backend.db :as backend.db]
[metabase.util.log :as log]
@@ -11,26 +11,26 @@
(set! *warn-on-reflection* true)
(comment metabase.cache.api/keep-me)
(comment metabase.api.cache/keep-me)
;; Data shape
(mr/def ::cache-strategy
"Schema for a caching strategy used internally"
[:and
:metabase.cache.api/cache-strategy.base
:metabase.api.cache/cache-strategy.base
[:multi {:dispatch :type}
[:nocache :metabase.cache.api/cache-strategy.nocache]
[:nocache :metabase.api.cache/cache-strategy.nocache]
[:ttl [:merge
:metabase.cache.api/cache-strategy.ttl
:metabase.api.cache/cache-strategy.ttl
[:map
[:invalidated-at {:optional true} some?]]]]
[:duration [:merge
:metabase.cache.api/cache-strategy.ee.duration
:metabase.api.cache/cache-strategy.ee.duration
[:map
[:invalidated-at {:optional true} some?]]]]
[:schedule [:merge
:metabase.cache.api/cache-strategy.ee.schedule
:metabase.api.cache/cache-strategy.ee.schedule
[:map
[:invalidated-at {:optional true} some?]]]]]])
@@ -56,30 +56,30 @@
:order-by :ordering
:limit [:inline 1]}
item (t2/select-one :model/CacheConfig :id q)]
(cache/card-strategy item card)))
(cache-config/card-strategy item card)))
;;; Strategy execution
(defmulti ^:private fetch-cache-stmt-ee*
"Generate prepared statement for a db cache backend for a given strategy"
{:arglists '([strategy query-hash])}
(fn [strategy _hash] (:type strategy)))
{:arglists '([strategy query-hash conn])}
(fn [strategy _hash _conn] (:type strategy)))
(defmethod fetch-cache-stmt-ee* :ttl [strategy query-hash]
(backend.db/fetch-cache-stmt-ttl strategy query-hash))
(defmethod fetch-cache-stmt-ee* :ttl [strategy query-hash conn]
(backend.db/fetch-cache-stmt-ttl strategy query-hash conn))
(defmethod fetch-cache-stmt-ee* :duration [strategy query-hash]
(defmethod fetch-cache-stmt-ee* :duration [strategy query-hash conn]
(if-not (and (:duration strategy) (:unit strategy))
(log/debugf "Caching strategy %s should have :duration and :unit" (pr-str strategy))
(let [duration (t/duration (:duration strategy) (keyword (:unit strategy)))
duration-ago (t/minus (t/offset-date-time) duration)
invalidated-at (t/max duration-ago (:invalidated-at strategy))]
(backend.db/select-cache query-hash invalidated-at))))
(backend.db/prepare-statement conn query-hash invalidated-at))))
(defmethod fetch-cache-stmt-ee* :schedule [{:keys [invalidated-at] :as strategy} query-hash]
(defmethod fetch-cache-stmt-ee* :schedule [{:keys [invalidated-at] :as strategy} query-hash conn]
(if-not invalidated-at
(log/debugf "Caching strategy %s has not run yet" (pr-str strategy))
(backend.db/select-cache query-hash invalidated-at)))
(backend.db/prepare-statement conn query-hash invalidated-at)))
(defmethod fetch-cache-stmt-ee* :nocache [_ _ _]
nil)
@@ -87,5 +87,5 @@
(defenterprise fetch-cache-stmt
"Returns prepared statement to query for db cache backend."
:feature :cache-granular-controls
[strategy query-hash]
(fetch-cache-stmt-ee* strategy query-hash))
[strategy query-hash conn]
(fetch-cache-stmt-ee* strategy query-hash conn))

View File

@@ -6,5 +6,4 @@
See https://metaboat.slack.com/archives/CKZEMT1MJ/p1736556522733279 for rationale behind this pattern."
(:require
[metabase-enterprise.cache.init]
[metabase-enterprise.enhancements.init]))

View File

@@ -5,7 +5,7 @@
[metabase.api.macros :as api.macros]
[metabase.api.routes.common :refer [+auth]]
[metabase.events :as events]
[metabase.settings.core :as setting]
[metabase.models.setting :as setting]
[metabase.util :as u]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))
@@ -94,7 +94,7 @@
(api/check-404 db)
(api/check-400 (not (:router_database_id db)) "Cannot make a destination database a router database")
(api/check-400 (not (:uploads_enabled db)) "Cannot enable database routing for a database with uploads enabled")
(setting/with-database-local-values (:settings db)
(binding [setting/*database-local-values* (:settings db)]
(api/check-400 (not (setting/get :persist-models-enabled)) "Cannot enable database routing for a database with model persistence enabled")
(api/check-400 (not (setting/get :database-enable-actions)) "Cannot enable database routing for a database with actions enabled")))
(if (nil? user_attribute)

View File

@@ -1,6 +1,7 @@
(ns metabase-enterprise.enhancements.integrations.google
(:require
[metabase.settings.core :as setting :refer [define-multi-setting-impl]]))
[metabase.models.setting :as setting]
[metabase.models.setting.multi-setting :refer [define-multi-setting-impl]]))
(define-multi-setting-impl google-auth-auto-create-accounts-domain :ee
:getter (fn [] (setting/get-value-of-type :string :google-auth-auto-create-accounts-domain))

View File

@@ -2,9 +2,9 @@
"The Enterprise version of the LDAP integration is basically the same but also supports syncing user attributes."
(:require
[metabase-enterprise.sso.integrations.sso-utils :as sso-utils]
[metabase.models.setting :refer [defsetting]]
[metabase.models.user :as user]
[metabase.premium-features.core :refer [defenterprise-schema]]
[metabase.settings.core :refer [defsetting]]
[metabase.sso.core :as sso]
[metabase.util :as u]
[metabase.util.i18n :refer [deferred-tru]]

View File

@@ -167,14 +167,6 @@
(when-not (= delete-status :ok)
(log/debugf "Unable to delete gdrive connection %s." id))))))
(mu/defn hm-get-all-connections :- :hm-client/http-reply
"Get all connections from harbormaster."
[]
(hm.client/make-request :get "/api/v2/mb/connections"))
(comment
(hm-get-all-connections))
(mu/defn hm-get-gdrive-conn :- :hm-client/http-reply
"Get a specific gdrive connection by id."
[id]
@@ -277,7 +269,7 @@
cannot-check-message
{:gdrive/conn-id conn-id
:hm/exception e})))))
[hm-status {hm-status-code :status hm-body :body}] hm-response]
[hm-status {hm-body :body}] hm-response]
(if (= :ok hm-status)
(let [{:keys [status status-reason error last-sync-at last-sync-started-at]
:as _} (normalize-gdrive-conn hm-body)]
@@ -304,30 +296,15 @@
:last_sync_at (.getEpochSecond ^Instant (t/instant last-sync-at))
:hm/response (loggable-response hm-response))
(analytics/inc! :metabase-gsheets/connection-creation-error {:reason "status_error"})
(log/errorf "Error getting status of connection %s: status-reason=`%s` error-detail=`%s`" conn-id (:status-reason hm-body) (:error-detail hm-body)))))
(cond
(empty? saved-setting)
(log/errorf "Error getting status of connection %s: %s %s" conn-id (:status-reason hm-body) (:error-detail hm-body)))))
(if (empty? saved-setting)
{:status "not-connected"}
(= 403 hm-status-code)
(let [[list-status list-response] (hm-get-all-connections)]
(if (and (= :ok list-status) (not (some (partial = conn-id) (set (map :id (:body list-response))))))
(u/prog1 {:status "not-connected"}
(log/warnf "Removing google drive connection %s because harbormaster gives a 403 error for it." conn-id)
(reset-gsheets-status!))
;; It doesn't look like a spot where we can remove the connection, so we just return the error
(assoc (setting->response saved-setting)
:status "error"
:error_message cannot-check-message
:hm/response (loggable-response hm-response))))
:else
(u/prog1 (assoc (setting->response saved-setting)
:status "error"
:error_message cannot-check-message
:hm/response (loggable-response hm-response))
(analytics/inc! :metabase-gsheets/connection-creation-error {:reason "status_error"})
(log/errorf "Error getting status of connection %s: status-reason=`%s` error-detail=`%s`" conn-id (:status-reason hm-body) (:error-detail hm-body)))))))
(log/errorf "Error getting status of connection %s: %s %s" conn-id (:status-reason hm-body) (:error-detail hm-body)))))))
(api.macros/defendpoint :get "/connection" :- :gsheets/response
"Check the status of a connection. This endpoint gets polled by FE to determine when to

View File

@@ -2,8 +2,8 @@
(:require
[clojure.set :as set]
[metabase-enterprise.gsheets.constants :as gsheets.constants]
[metabase.models.setting :as setting :refer [defsetting]]
[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.json :as json]

View File

@@ -1,7 +1,7 @@
(ns metabase-enterprise.internal-stats.core
(ns metabase-enterprise.internal-stats
(:require
[metabase.premium-features.core :refer [defenterprise]]
[metabase.settings.core :as setting]))
[metabase.models.setting :as setting]
[metabase.premium-features.core :refer [defenterprise]]))
(defenterprise embedding-settings
"Boolean values that report on the state of different embedding configurations."

View File

@@ -1,6 +1,6 @@
(ns metabase-enterprise.llm.settings
(:require
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.models.setting :as setting :refer [defsetting]]
[metabase.util.i18n :refer [deferred-tru]]))
(defsetting ee-openai-model

View File

@@ -6,7 +6,8 @@
[metabase.api.open-api :as open-api]
[metabase.api.routes.common :refer [+auth]]
[metabase.models.collection :as collection]
[metabase.query-analysis.core :as query-analysis]
[metabase.models.query-analysis :as query-analysis]
[metabase.public-settings :as public-settings]
[metabase.request.core :as request]
[metabase.util.malli.schema :as ms]
[toucan2.core :as t2]))
@@ -120,7 +121,7 @@
[handler]
(open-api/handler-with-open-api-spec
(fn [request respond raise]
(if (query-analysis/query-analysis-enabled)
(if (public-settings/query-analysis-enabled)
(handler request respond raise)
(respond {:status 429 :body "Query Analysis must be enabled to use the Query Reference Validator"})))
(fn [prefix]

View File

@@ -5,8 +5,8 @@
[metabase.api.common :as api]
[metabase.api.macros :as api.macros]
[metabase.models.api-key :as api-key]
[metabase.settings.core :refer [defsetting]]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.models.setting :refer [defsetting]]
[metabase.public-settings :as public-settings]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.secret :as u.secret]
[toucan2.core :as t2]))

View File

@@ -13,7 +13,7 @@
[metabase.api.routes.common :refer [+auth]]
[metabase.logger :as logger]
[metabase.models.serialization :as serdes]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.public-settings :as public-settings]
[metabase.util :as u]
[metabase.util.compress :as u.compress]
[metabase.util.date-2 :as u.date]

View File

@@ -7,7 +7,7 @@
[metabase-enterprise.serialization.serialize :as serialize]
[metabase.config :as config]
[metabase.models.interface :as mi]
[metabase.settings.core :as setting]
[metabase.models.setting :as setting]
[metabase.util.log :as log]
[metabase.util.yaml :as yaml]
[toucan2.core :as t2]))

View File

@@ -14,8 +14,8 @@
[metabase.legacy-mbql.normalize :as mbql.normalize]
[metabase.legacy-mbql.util :as mbql.u]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.setting :as setting]
[metabase.models.visualization-settings :as mb.viz]
[metabase.settings.core :as setting]
[metabase.util.date-2 :as u.date]
[metabase.util.i18n :refer [trs]]
[metabase.util.log :as log]

View File

@@ -4,8 +4,8 @@
[clojure.string :as str]
[metabase.api.common :as api]
[metabase.api.macros :as api.macros]
[metabase.models.setting :as setting]
[metabase.premium-features.core :as premium-features]
[metabase.settings.core :as setting]
[metabase.util.i18n :refer [tru]]
[saml20-clj.core :as saml]))

View File

@@ -7,7 +7,7 @@
[metabase-enterprise.sso.api.interface :as sso.i]
[metabase-enterprise.sso.integrations.sso-settings :as sso-settings]
[metabase-enterprise.sso.integrations.sso-utils :as sso-utils]
[metabase.embedding.settings :as embed.settings]
[metabase.embed.settings :as embed.settings]
[metabase.premium-features.core :as premium-features]
[metabase.request.core :as request]
[metabase.session.models.session :as session]

View File

@@ -37,9 +37,9 @@
[metabase-enterprise.sso.integrations.sso-utils :as sso-utils]
[metabase.api.common :as api]
[metabase.premium-features.core :as premium-features]
[metabase.public-settings :as public-settings]
[metabase.request.core :as request]
[metabase.session.core :as session]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.sso.core :as sso]
[metabase.util :as u]
[metabase.util.i18n :refer [trs tru]]

View File

@@ -4,8 +4,9 @@
information. Separating out this information creates a better dependency graph and avoids circular dependencies."
(:require
[metabase-enterprise.scim.api :as scim]
[metabase.settings.core :as setting :refer [defsetting define-multi-setting-impl]]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.models.setting :as setting :refer [defsetting]]
[metabase.models.setting.multi-setting :refer [define-multi-setting-impl]]
[metabase.public-settings :as public-settings]
[metabase.util.i18n :refer [deferred-tru tru]]
[metabase.util.log :as log]
[metabase.util.malli :as mu]

View File

@@ -6,7 +6,7 @@
[metabase.channel.email.messages :as messages]
[metabase.events :as events]
[metabase.events.notification :as events.notification]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.public-settings :as public-settings]
[metabase.sso.core :as sso]
[metabase.util :as u]
[metabase.util.i18n :refer [trs tru]]

View File

@@ -1,8 +1,8 @@
(ns metabase-enterprise.stale
(:require
[malli.experimental.time]
[metabase.embedding.settings :as embed.settings]
[metabase.settings.core :as setting :refer [defsetting]]
[metabase.embed.settings :as embed.settings]
[metabase.models.setting :as setting :refer [defsetting]]
[metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [deferred-tru]]
[metabase.util.malli :as mu]

View File

@@ -1,4 +1,4 @@
(ns metabase-enterprise.cache.task.refresh-cache-configs
(ns metabase-enterprise.task.cache
(:require
[clojurewerkz.quartzite.jobs :as jobs]
[clojurewerkz.quartzite.schedule.cron :as cron]
@@ -11,7 +11,7 @@
[metabase.util.log :as log]
[toucan2.core :as t2])
(:import
(java.util.concurrent Callable ExecutorService SynchronousQueue ThreadPoolExecutor TimeUnit)
(java.util.concurrent Callable ExecutorService ThreadPoolExecutor TimeUnit SynchronousQueue)
(org.apache.commons.lang3.concurrent BasicThreadFactory$Builder)
(org.quartz.spi MutableTrigger)))
@@ -317,6 +317,8 @@
(triggers/with-schedule
(cron/cron-schedule "0 * * * * ? *"))))
(defmethod task/init! ::RefreshCacheConfigs [_]
(when (premium-features/has-feature? :cache-granular-controls)
(task/schedule-task! cache-job cache-trigger)))
(defenterprise init-cache-task!
"Inits periodical task checking for cache expiration"
:feature :cache-granular-controls
[]
(task/schedule-task! cache-job cache-trigger))

View File

@@ -1,45 +1,13 @@
(ns metabase.embedding.settings-test
(ns metabase.embed.settings-test
(:require
[clojure.string :as str]
[clojure.test :refer :all]
[metabase.analytics.snowplow-test :as snowplow-test]
[metabase.config :as config]
[metabase.embedding.app-origins-sdk :as aos]
[metabase.embedding.settings :as embed.settings]
[metabase.premium-features.token-check :as token-check]
[metabase.premium-features.token-check-test :as token-check-test]
[metabase.embed.app-origins-sdk :as aos]
[metabase.embed.settings :as embed.settings]
[metabase.test :as mt]
[toucan2.core :as t2]))
(deftest show-static-embed-terms-test
(mt/with-test-user :crowberto
(mt/with-temporary-setting-values [show-static-embed-terms nil]
(testing "Check if the user needs to accept the embedding licensing terms before static embedding"
(when-not config/ee-available?
(testing "should return true when user is OSS and has not accepted licensing terms"
(is (embed.settings/show-static-embed-terms)))
(testing "should return false when user is OSS and has already accepted licensing terms"
(embed.settings/show-static-embed-terms! false)
(is (not (embed.settings/show-static-embed-terms)))))
(when config/ee-available?
(testing "should return false when an EE user has a valid token"
(with-redefs [token-check/fetch-token-status (fn [_x]
{:valid true
:status "fake"
:features ["test" "fixture"]
:trial false})]
(mt/with-temporary-setting-values [premium-embedding-token (token-check-test/random-token)]
(is (not (embed.settings/show-static-embed-terms)))
(embed.settings/show-static-embed-terms! false)
(is (not (embed.settings/show-static-embed-terms))))))
(testing "when an EE user doesn't have a valid token"
(mt/with-temporary-setting-values [premium-embedding-token nil show-static-embed-terms nil]
(testing "should return true when the user has not accepted licensing terms"
(is (embed.settings/show-static-embed-terms)))
(testing "should return false when the user has already accepted licensing terms"
(embed.settings/show-static-embed-terms! false)
(is (not (embed.settings/show-static-embed-terms)))))))))))
(defn- embedding-event?
"Used to make sure we only test against embedding-events in `snowplow-test/pop-event-data-and-user-id!`."
[event]
@@ -198,8 +166,7 @@
:else (throw (ex-info "Invalid expected-behavior in test-origin-sync." {:expected-behavior expected-behavior}))))))
(deftest sync-origins-test
;; n.b. illegal combinations will be disallowed by [[embed.settings/check-and-sync-settings-on-startup!]], so we don't
;; test syncing for them.
;; n.b. illegal combinations will be disallowed by [[embed.settings/check-and-sync-settings-on-startup!]], so we don't test syncing for them.
(mt/with-premium-features #{:embedding :embedding-sdk}
(mt/with-temporary-setting-values [enable-embedding true
enable-embedding-sdk true

View File

@@ -2,7 +2,7 @@
(:require
[clojure.test :refer :all]
[metabase-enterprise.advanced-config.file :as advanced-config.file]
[metabase.settings.core :refer [defsetting]]
[metabase.models.setting :refer [defsetting]]
[metabase.test :as mt]
[metabase.util :as u]))

View File

@@ -122,11 +122,6 @@
"A 400 response from HM. nb: if you change this, change it in test_resources/gsheets/mock_hm_responses.edn"
"93662bf7-b1c7-442b-80ec-18dee23894fa")
(def ^:private
gdrive-403-error-link
"A 403 response from HM. nb: if you change this, change it in test_resources/gsheets/mock_hm_responses.edn"
"e5b50d83-c1d6-4382-8351-ff95a23af60e")
(def ^:private
gdrive-200-error-link
"A 200 'error' response from HM. nb: if you change this, change it in test_resources/gsheets/mock_hm_responses.edn"
@@ -267,41 +262,6 @@
(is (partial= {:status "error", :url "test-url" :created_by_id 2} response))
(is (pos-int? (:db_id response)))))))))))
(deftest get-folder-test-invalid-connections
(with-sample-db-as-dwh
(let [mock-gsheet {:created-by-id 2
:url "test-url",
:created-at 15
:db-id 1}]
(mt/with-premium-features #{:etl-connections :attached-dwh :hosting}
(testing "when the connection does not exist, it is deleted"
(mt/with-temporary-setting-values [gsheets (assoc mock-gsheet :gdrive/conn-id gdrive-403-error-link)]
(with-redefs [hm.client/make-request (partial mock-make-request happy-responses)]
(let [response (mt/user-http-request :crowberto :get 200 "ee/gsheets/connection")]
(is (= {:status "not-connected"} response))
(is (= {} (gsheets)))))))
(testing "when the HM gives a 403 response for the connection, but it shows in the connection list then it is not deleted"
(mt/with-temporary-setting-values [gsheets (assoc mock-gsheet :gdrive/conn-id gdrive-active-link)]
(with-redefs [hm.client/make-request (partial mock-make-request (assoc happy-responses
{:method :get, :url (str "/api/v2/mb/connections/" gdrive-active-link), :body nil}
[:error
{:status 403,
:body {:error "User not authorized to act over resource."}}]))]
(let [response (mt/user-http-request :crowberto :get 200 "ee/gsheets/connection")]
(is (= "error" (:status response)))
(is (= 15 (:created-at (gsheets))))))))
(testing "when the HM gives a 403 response for the connection, and the connection list fails, then it is not deleted"
(mt/with-temporary-setting-values [gsheets (assoc mock-gsheet :gdrive/conn-id gdrive-403-error-link)]
(with-redefs [hm.client/make-request (partial mock-make-request
(assoc happy-responses
{:method :get, :url "/api/v2/mb/connections", :body nil}
[:error
{:status 403,
:body {:error "User not authorized to act over resource."}}]))]
(let [response (mt/user-http-request :crowberto :get 200 "ee/gsheets/connection")]
(is (= "error" (:status response)))
(is (= 15 (:created-at (gsheets))))))))))))
(deftest delete-folder-test
(with-sample-db-as-dwh
(mt/with-premium-features #{:etl-connections :attached-dwh :hosting}

View File

@@ -3,7 +3,7 @@
[clojure.test :refer :all]
[java-time.api :as t]
[metabase-enterprise.cache.strategies :as strategies]
[metabase-enterprise.cache.task.refresh-cache-configs :as task.cache]
[metabase-enterprise.task.cache :as task.cache]
[metabase.models.query :as query]
[metabase.query-processor :as qp]
[metabase.query-processor.card :as qp.card]

View File

@@ -3,7 +3,7 @@
[clojure.test :refer :all]
[metabase-enterprise.enhancements.integrations.ldap :as ldap-ee]
[metabase-enterprise.sso.integrations.sso-settings :as sso-settings]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.public-settings :as public-settings]
[metabase.sso.ldap :as ldap]
[metabase.sso.ldap-test-util :as ldap.test]
[metabase.test :as mt]

View File

@@ -2,7 +2,7 @@
(:require
[clojure.test :refer [deftest is testing]]
[metabase-enterprise.gsheets.settings :as gsettings]
[metabase.settings.core :as setting]
[metabase.models.setting :as setting]
[toucan2.core :as t2]))
(defmacro with-sample-db-as-dwh [& body]

View File

@@ -2,8 +2,7 @@
(:require
[clojure.test :refer [deftest is testing]]
[metabase-enterprise.harbormaster.client :as hm.client]
[metabase.settings.core :as setting]
[metabase.settings.models.setting]
[metabase.models.setting :as setting]
[metabase.test.util :as mt]))
(deftest ->config-good-test
@@ -25,11 +24,11 @@
;; mt/with-temporary-setting-values is not used here because we want to test the behavior of the function when the
;; api-key is blank or nil, and mt/with-temporary-setting-values will not allow us to set the api-key to blank
;; or nil.
(with-redefs [metabase.settings.models.setting/get-raw-value (+value-for-setting grv :api-key "")]
(with-redefs [setting/get-raw-value (+value-for-setting grv :api-key "")]
(is (thrown-with-msg? Exception
#"Missing api-key."
(#'hm.client/->config))))
(with-redefs [metabase.settings.models.setting/get-raw-value (+value-for-setting grv :api-key nil)]
(with-redefs [setting/get-raw-value (+value-for-setting grv :api-key nil)]
(is (thrown-with-msg? Exception
#"Missing api-key."
(#'hm.client/->config))))))

View File

@@ -1,7 +1,7 @@
(ns metabase-enterprise.internal-stats.core-test
(ns metabase-enterprise.internal-stats-test
(:require
[clojure.test :refer [deftest testing is]]
[metabase-enterprise.internal-stats.core :as sut]
[metabase-enterprise.internal-stats :as sut]
[metabase.test :as mt]))
(deftest enabled-embedding-static-test

View File

@@ -1,8 +1,8 @@
(ns metabase-enterprise.public-settings-test
(:require
[clojure.test :refer :all]
[metabase.embedding.settings :as embed.settings]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.embed.settings :as embed.settings]
[metabase.public-settings :as public-settings]
[metabase.test :as mt]
[metabase.test.fixtures :as fixtures]
[metabase.test.util :as tu]))

View File

@@ -11,7 +11,7 @@
[metabase-enterprise.serialization.v2.load :as serdes.load]
[metabase-enterprise.serialization.v2.storage :as storage]
[metabase.models.serialization :as serdes]
[metabase.settings.core :as setting]
[metabase.models.setting :as setting]
[metabase.test :as mt]
[metabase.test.generate :as test-gen]
[metabase.util.yaml :as yaml]
@@ -358,7 +358,7 @@
(testing "for settings"
(let [settings (get @entities "Setting")]
(is (every? setting/export?
(is (every? @#'setting/export?
(set (map (comp symbol :key) settings))))
(is (= (into {} (for [{:keys [key value]} settings]
[key value]))

View File

@@ -11,7 +11,7 @@
[metabase.config :as config]
[metabase.http-client :as client]
[metabase.premium-features.token-check :as token-check]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.public-settings :as public-settings]
[metabase.test :as mt]
[metabase.test.fixtures :as fixtures]
[metabase.util :as u]

View File

@@ -7,9 +7,9 @@
[metabase-enterprise.sso.integrations.sso-settings :as sso-settings]
[metabase.http-client :as client]
[metabase.premium-features.token-check :as token-check]
[metabase.public-settings :as public-settings]
[metabase.request.core :as request]
[metabase.session.core :as session]
[metabase.settings.deprecated-grab-bag :as public-settings]
[metabase.sso.init]
[metabase.test :as mt]
[metabase.test.fixtures :as fixtures]

View File

@@ -1,9 +1,9 @@
(ns metabase-enterprise.cache.task.refresh-cache-configs-test
(ns metabase-enterprise.task.cache-test
(:require
[clojure.test :refer :all]
[java-time.api :as t]
[medley.core :as m]
[metabase-enterprise.cache.task.refresh-cache-configs :as task.cache]
[metabase-enterprise.task.cache :as task.cache]
[metabase.premium-features.core :as premium-features]
[metabase.query-processor :as qp]
[metabase.query-processor.card :as qp.card]

View File

@@ -12,7 +12,6 @@ import {
setupDashboardEndpoints,
setupDashboardQueryMetadataEndpoint,
setupDatabasesEndpoints,
setupLastDownloadFormatEndpoints,
} from "__support__/server-mocks";
import { setupDashcardQueryEndpoints } from "__support__/server-mocks/dashcard";
import { setupNotificationChannelsEndpoints } from "__support__/server-mocks/pulse";
@@ -151,8 +150,6 @@ const setup = async ({
setupDatabasesEndpoints([createMockDatabase()]);
setupLastDownloadFormatEndpoints();
const user = createMockUser();
const state = setupSdkState({

View File

@@ -53,22 +53,15 @@ export const storybookSdkAuthDefaultConfig =
export const CommonSdkStoryWrapper = (Story: StoryFn, context: any) => {
const sdkTheme = context.globals.sdkTheme;
const theme = sdkTheme ? storybookThemes[sdkTheme] : undefined;
const locale = context.globals.locale;
const user = context.globals.user;
const key = `${user}-${locale}`;
const user = context.globals.user;
const authConfig = useMemo(() => {
return getStorybookSdkAuthConfigForUser(user);
}, [user]);
return (
<MetabaseProvider
authConfig={authConfig}
theme={theme}
key={key}
locale={locale}
>
<MetabaseProvider authConfig={authConfig} theme={theme} key={user}>
<Story />
</MetabaseProvider>
);

View File

@@ -411,15 +411,14 @@ const HELPER_TEXT_STRINGS: HelpTextConfig[] = [
name: "integer",
structure: "integer",
category: "conversion",
description: () =>
t`Converts a string or float value to an integer number.`,
description: () => t`Converts a string to an integer.`,
args: [
{
get name() {
return t`value`;
},
get description() {
return t`The string or float column to convert to integers. Float values are rounded.`;
return t`The string column to convert to integers.`;
},
example: ["dimension", "User ID"],
},

View File

@@ -1,8 +1,4 @@
import type {
Parameter,
ParameterValues,
SearchParameterValuesRequest,
} from "metabase-types/api";
import type { Parameter, ParameterValues } from "metabase-types/api";
export const createMockParameter = (opts?: Partial<Parameter>): Parameter => ({
id: "1",
@@ -19,12 +15,3 @@ export const createMockParameterValues = (
has_more_values: false,
...opts,
});
export const createMockSearchParameterValuesRequest = (
opts?: Partial<SearchParameterValuesRequest>,
): SearchParameterValuesRequest => ({
parameter: createMockParameter(),
field_ids: [],
query: "",
...opts,
});

View File

@@ -4,10 +4,10 @@ import type {
Engine,
EngineField,
EngineSource,
EnterpriseSettingKey,
EnterpriseSettings,
FontFile,
SettingDefinition,
SettingKey,
Settings,
TokenFeatures,
TokenStatus,
@@ -129,7 +129,7 @@ export const createMockTokenFeatures = (
});
export const createMockSettingDefinition = <
Key extends EnterpriseSettingKey = EnterpriseSettingKey,
Key extends SettingKey = SettingKey,
>(
opts: SettingDefinition<Key>,
): SettingDefinition<Key> => ({

View File

@@ -255,25 +255,17 @@ export type PasswordComplexity = {
export type SessionCookieSameSite = "lax" | "strict" | "none";
export interface SettingDefinition<
Key extends EnterpriseSettingKey = EnterpriseSettingKey,
> {
export interface SettingDefinition<Key extends SettingKey = SettingKey> {
key: Key;
env_name?: string;
is_env_setting?: boolean;
value?: EnterpriseSettingValue<Key>;
default?: EnterpriseSettingValue<Key>;
value?: SettingValue<Key>;
default?: SettingValue<Key>;
display_name?: string;
description?: string | ReactNode | null;
type?: InputSettingType;
}
export type SettingDefinitionMap<
T extends EnterpriseSettingKey = EnterpriseSettingKey,
> = {
[K in T]: SettingDefinition<K>;
};
export type UpdateChannel = "latest" | "beta" | "nightly";
export interface OpenAiModel {

View File

@@ -121,14 +121,6 @@ export type UserKeyValue =
namespace: "user_acknowledgement";
key: string;
value: boolean;
}
| {
namespace: "last_download_format";
key: string;
value: {
last_download_format: "csv" | "xlsx" | "json" | "png";
last_table_download_format: "csv" | "xlsx" | "json";
};
};
export type UserKeyValueKey = Pick<UserKeyValue, "namespace" | "key">;

View File

@@ -245,15 +245,11 @@ describe("MetadataFieldSettings", () => {
).toBeInTheDocument();
});
it("should display type casting settings for float fields", async () => {
it("should not display type casting settings for non-supported fields", async () => {
await setup({ field: ORDERS_DISCOUNT_FIELD });
expect(
screen.getByText("Cast to a specific data type"),
).toBeInTheDocument();
await userEvent.click(screen.getByText("Don't cast"));
await userEvent.type(screen.getByPlaceholderText("Find..."), "Float");
expect(screen.getByText("Float → Integer")).toBeInTheDocument();
screen.queryByText("Cast to a specific data type"),
).not.toBeInTheDocument();
});
it("should show an access denied error if the foreign key field has an inaccessible target", async () => {

View File

@@ -1,4 +1,5 @@
import cx from "classnames";
import PropTypes from "prop-types";
import { useMemo, useRef, useState } from "react";
import { t } from "ttag";
@@ -8,37 +9,29 @@ import CS from "metabase/css/core/index.css";
import Typeahead from "metabase/hoc/Typeahead";
import { color } from "metabase/lib/colors";
import { Icon } from "metabase/ui";
import type { User } from "metabase-types/api";
import { AddMemberAutocompleteSuggestionRoot } from "./AddMemberRow.styled";
import { AddRow } from "./AddRow";
interface AddMemberRowProps {
users: User[];
excludeIds: Set<number>;
onCancel: () => void;
onDone: (userIds: number[]) => void;
}
AddMemberRow.propTypes = {
users: PropTypes.array.isRequired,
excludeIds: PropTypes.object,
onCancel: PropTypes.func.isRequired,
onDone: PropTypes.func.isRequired,
};
export function AddMemberRow({
users,
excludeIds,
onCancel,
onDone,
}: AddMemberRowProps) {
const rowRef = useRef<HTMLDivElement>(null);
export default function AddMemberRow({ users, excludeIds, onCancel, onDone }) {
const rowRef = useRef(null);
const [text, setText] = useState("");
const [selectedUsersById, setSelectedUsersById] = useState<Map<number, User>>(
new Map(),
);
const [selectedUsersById, setSelectedUsersById] = useState(new Map());
const handleRemoveUser = (user: User) => {
const handleRemoveUser = (user) => {
const newSelectedUsersById = new Map(selectedUsersById);
newSelectedUsersById.delete(user.id);
setSelectedUsersById(newSelectedUsersById);
};
const handleAddUser = (user: User) => {
const handleAddUser = (user) => {
const newSelectedUsersById = new Map(selectedUsersById);
newSelectedUsersById.set(user.id, user);
setSelectedUsersById(newSelectedUsersById);
@@ -59,7 +52,7 @@ export function AddMemberRow({
return (
<tr>
<td colSpan={4} style={{ padding: 0 }}>
<td colSpan="4" style={{ padding: 0 }}>
<AddRow
ref={rowRef}
value={text}
@@ -115,40 +108,42 @@ const getColorPalette = () => [
color("accent4"),
];
interface AddMemberTypeaheadPopoverProps {
suggestions: User[];
selectedSuggestion: User | null;
onSuggestionAccepted: (user: User) => void;
target: React.RefObject<HTMLDivElement>;
}
const AddMemberTypeaheadPopoverPropTypes = {
suggestions: PropTypes.array,
selectedSuggestion: PropTypes.object,
onSuggestionAccepted: PropTypes.func.isRequired,
target: PropTypes.shape({
current: PropTypes.instanceOf(Element),
}),
};
const AddMemberTypeaheadPopover = Typeahead({
optionFilter: (text: string, user: User) =>
optionFilter: (text, user) =>
(user.common_name || "").toLowerCase().includes(text.toLowerCase()),
optionIsEqual: (userA: User, userB: User) => userA.id === userB.id,
optionIsEqual: (userA, userB) => userA.id === userB.id,
})(function AddMemberTypeaheadPopover({
suggestions,
selectedSuggestion,
onSuggestionAccepted,
target,
}: AddMemberTypeaheadPopoverProps) {
}) {
const colors = useMemo(getColorPalette, []);
return (
<TippyPopover
className={CS.bordered}
offset={[0, 0]}
offset={0}
placement="bottom-start"
visible={suggestions.length > 0}
reference={target}
content={
suggestions &&
suggestions.map((user: User, index: number) => (
suggestions.map((user, index) => (
<AddMemberAutocompleteSuggestion
key={index}
user={user}
color={colors[index % colors.length]}
selected={!!selectedSuggestion && user.id === selectedSuggestion.id}
selected={selectedSuggestion && user.id === selectedSuggestion.id}
onClick={onSuggestionAccepted.bind(null, user)}
/>
))
@@ -157,19 +152,16 @@ const AddMemberTypeaheadPopover = Typeahead({
);
});
interface AddMemberAutocompleteSuggestionProps {
user: User;
color: string;
selected: boolean;
onClick: () => void;
}
AddMemberTypeaheadPopover.propTypes = AddMemberTypeaheadPopoverPropTypes;
function AddMemberAutocompleteSuggestion({
user,
color,
selected,
onClick,
}: AddMemberAutocompleteSuggestionProps) {
AddMemberAutocompleteSuggestion.propTypes = {
user: PropTypes.object.isRequired,
color: PropTypes.string.isRequired,
selected: PropTypes.bool,
onClick: PropTypes.func.isRequired,
};
function AddMemberAutocompleteSuggestion({ user, color, selected, onClick }) {
return (
<AddMemberAutocompleteSuggestionRoot
isSelected={selected}

View File

@@ -1,21 +1,11 @@
import cx from "classnames";
import { type ChangeEvent, type Ref, forwardRef } from "react";
import PropTypes from "prop-types";
import { forwardRef } from "react";
import { t } from "ttag";
import ButtonsS from "metabase/css/components/buttons.module.css";
import CS from "metabase/css/core/index.css";
interface AddRowProps {
value: string;
isValid: boolean;
placeholder?: string;
onKeyDown?: (event: React.KeyboardEvent) => void;
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
onDone: () => void;
onCancel: () => void;
children?: React.ReactNode;
}
export const AddRow = forwardRef(function AddRow(
{
value,
@@ -26,8 +16,8 @@ export const AddRow = forwardRef(function AddRow(
onDone,
onCancel,
children,
}: AddRowProps,
ref: Ref<HTMLDivElement>,
},
ref,
) {
return (
<div
@@ -69,3 +59,14 @@ export const AddRow = forwardRef(function AddRow(
</div>
);
});
AddRow.propTypes = {
value: PropTypes.string.isRequired,
isValid: PropTypes.bool.isRequired,
placeholder: PropTypes.string,
onKeyDown: PropTypes.func,
onChange: PropTypes.func.isRequired,
onDone: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
children: PropTypes.node,
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import cx from "classnames";
import { Fragment, useEffect, useState } from "react";
import { msgid, ngettext, t } from "ttag";
@@ -15,8 +16,6 @@ import {
import { connect } from "metabase/lib/redux";
import { PLUGIN_GROUP_MANAGERS } from "metabase/plugins";
import { getUser } from "metabase/selectors/user";
import type { Group, Member, User } from "metabase-types/api";
import type { State } from "metabase-types/store";
import {
createMembership,
@@ -28,11 +27,7 @@ import { getGroupMemberships, getMembershipsByUser } from "../selectors";
import GroupMembersTable from "./GroupMembersTable";
interface GroupDescriptionProps {
group: Group;
}
const GroupDescription = ({ group }: GroupDescriptionProps) =>
const GroupDescription = ({ group }) =>
isDefaultGroup(group) ? (
<div className={cx(CS.px2, CS.textMeasure)}>
<p>
@@ -54,43 +49,13 @@ const GroupDescription = ({ group }: GroupDescriptionProps) =>
</div>
) : null;
interface GroupDetailStateProps {
groupMemberships: Member[];
membershipsByUser: Record<User["id"], Member[]>;
currentUser: User | null;
}
interface GroupDetailOwnProps {
group: Group;
users: User[];
}
const mapStateToProps = (
state: State,
props: GroupDetailOwnProps,
): GroupDetailStateProps => ({
// @ts-expect-error -- .js file imports with wrong type here, not worth fixing as we should just move to RTKQuery
const mapStateToProps = (state, props) => ({
groupMemberships: getGroupMemberships(state, props),
membershipsByUser: getMembershipsByUser(state),
currentUser: getUser(state),
});
interface GroupDetailDispatchProps {
createMembership: (membership: { groupId: number; userId: number }) => void;
updateMembership: (membership: Member) => void;
deleteMembership: (membershipId: number) => void;
loadMemberships: () => void;
confirmDeleteMembershipAction: (
membershipId: number,
userMemberships: Member[],
) => void;
confirmUpdateMembershipAction: (
membership: Member,
userMemberships: Member[],
) => void;
}
const mapDispatchToProps: GroupDetailDispatchProps = {
const mapDispatchToProps = {
createMembership,
deleteMembership,
updateMembership,
@@ -107,11 +72,7 @@ const mapDispatchToProps: GroupDetailDispatchProps = {
),
};
type GroupDetailProps = GroupDetailStateProps &
GroupDetailOwnProps &
GroupDetailDispatchProps;
const GroupDetailInner = ({
const GroupDetail = ({
currentUser,
group,
users,
@@ -123,22 +84,22 @@ const GroupDetailInner = ({
loadMemberships,
confirmDeleteMembershipAction,
confirmUpdateMembershipAction,
}: GroupDetailProps) => {
}) => {
const { modalContent, show } = useConfirmation();
const [addUserVisible, setAddUserVisible] = useState(false);
const [alertMessage, setAlertMessage] = useState<string | null>(null);
const [alertMessage, setAlertMessage] = useState(null);
useEffect(() => {
loadMemberships();
}, [loadMemberships]);
const alert = (message: string | null) => setAlertMessage(message);
const alert = (alertMessage) => setAlertMessage(alertMessage);
const onAddUsersClicked = () => setAddUserVisible(true);
const onAddUserCanceled = () => setAddUserVisible(false);
const onAddUserDone = async (userIds: number[]) => {
const onAddUserDone = async (userIds) => {
setAddUserVisible(false);
try {
await Promise.all(
@@ -150,18 +111,11 @@ const GroupDetailInner = ({
}),
);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
alert(errorMessage);
alert(error && typeof error.data ? error.data : error);
}
};
const handleChange = async (membership: Member) => {
if (!currentUser) {
throw new Error("currentUser is not defined");
return;
}
const handleChange = async (membership) => {
const confirmation = PLUGIN_GROUP_MANAGERS.getChangeMembershipConfirmation(
currentUser,
membership,
@@ -173,7 +127,6 @@ const GroupDetailInner = ({
show({
...confirmation,
title: confirmation.title ?? "",
onConfirm: () =>
confirmUpdateMembershipAction(
membership,
@@ -182,12 +135,7 @@ const GroupDetailInner = ({
});
};
const handleRemove = async (membershipId: number) => {
if (!currentUser) {
throw new Error("currentUser is not defined");
return;
}
const handleRemove = async (membershipId) => {
const confirmation = PLUGIN_GROUP_MANAGERS.getRemoveMembershipConfirmation(
currentUser,
membershipsByUser[currentUser.id],
@@ -200,7 +148,6 @@ const GroupDetailInner = ({
show({
...confirmation,
title: confirmation.title ?? "",
onConfirm: () =>
confirmDeleteMembershipAction(
membershipId,
@@ -224,7 +171,7 @@ const GroupDetailInner = ({
</Fragment>
}
buttonText={t`Add members`}
buttonAction={canEditMembership(group) ? onAddUsersClicked : undefined}
buttonAction={canEditMembership(group) ? onAddUsersClicked : null}
buttonDisabled={addUserVisible}
>
<GroupDescription group={group} />
@@ -246,7 +193,4 @@ const GroupDetailInner = ({
);
};
export const GroupDetail = connect(
mapStateToProps,
mapDispatchToProps,
)(GroupDetailInner);
export default connect(mapStateToProps, mapDispatchToProps)(GroupDetail);

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