Compare commits
196 Commits
test-pytho
...
remote-syn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d585a8b779 | ||
|
|
f6582c78d1 | ||
|
|
10deae2a03 | ||
|
|
b910dc5566 | ||
|
|
65a4a7c9e8 | ||
|
|
8ab634d497 | ||
|
|
9431038a75 | ||
|
|
cccb11b0d2 | ||
|
|
53a2da3c62 | ||
|
|
8be450aa19 | ||
|
|
22929ac32b | ||
|
|
b2b5720843 | ||
|
|
8abb1420e0 | ||
|
|
23287f9f28 | ||
|
|
06e016fe4e | ||
|
|
be8686b577 | ||
|
|
71eb726eb5 | ||
|
|
60c65add21 | ||
|
|
e65f73716c | ||
|
|
be8790142c | ||
|
|
d04a2452f4 | ||
|
|
6ff47bfe22 | ||
|
|
2df9e2645f | ||
|
|
1e997d6d5c | ||
|
|
381d156ca6 | ||
|
|
43301c49ad | ||
|
|
a133b31fc4 | ||
|
|
d632109808 | ||
|
|
a316b1d9d3 | ||
|
|
d7a97b5704 | ||
|
|
53fdb817c4 | ||
|
|
ead22c46a3 | ||
|
|
1615c60cbf | ||
|
|
b3fa75c941 | ||
|
|
2821954e9b | ||
|
|
d4acb644ee | ||
|
|
1236a70059 | ||
|
|
ab8fcd54b0 | ||
|
|
6dbe6594a7 | ||
|
|
f83b585668 | ||
|
|
53d01c5274 | ||
|
|
8137d490b3 | ||
|
|
e39c2119d2 | ||
|
|
c471c5640d | ||
|
|
2f49744ee1 | ||
|
|
283eae65b6 | ||
|
|
1217fca9a4 | ||
|
|
5884bfe547 | ||
|
|
9d1d534e51 | ||
|
|
2fb4793c24 | ||
|
|
954857c036 | ||
|
|
6d1d618fff | ||
|
|
785c418578 | ||
|
|
9d1c3c0ab4 | ||
|
|
57daa4d5dc | ||
|
|
805badeb79 | ||
|
|
5ea9541981 | ||
|
|
72a7ebec91 | ||
|
|
cdb6640ea4 | ||
|
|
8b90ffd906 | ||
|
|
3758d88539 | ||
|
|
d6397e205e | ||
|
|
01f7eeb023 | ||
|
|
8f3730dd45 | ||
|
|
5e92088c7d | ||
|
|
2da5fb8aa8 | ||
|
|
a94a70fe46 | ||
|
|
5880342225 | ||
|
|
89adf23ef7 | ||
|
|
7c7241d350 | ||
|
|
9638db8b02 | ||
|
|
bace3da926 | ||
|
|
898c82ad4c | ||
|
|
c9792f6604 | ||
|
|
ec0bc5e081 | ||
|
|
9b4c474e11 | ||
|
|
8f10457e32 | ||
|
|
20b3834269 | ||
|
|
0150dcfe07 | ||
|
|
2b2a22b86d | ||
|
|
d3e976ed53 | ||
|
|
c950dad7eb | ||
|
|
2693173e7f | ||
|
|
3ca4c5a2ee | ||
|
|
9c0910f652 | ||
|
|
cbf7579c4c | ||
|
|
5ec5c9a9d3 | ||
|
|
9ee26d70fd | ||
|
|
73aab1d129 | ||
|
|
6375368c92 | ||
|
|
ee3cfb7e68 | ||
|
|
e62903b72a | ||
|
|
652f725d41 | ||
|
|
2c49eb977d | ||
|
|
b70e091fe8 | ||
|
|
dff2d77981 | ||
|
|
60ec838229 | ||
|
|
830a6d5c83 | ||
|
|
e0ebddd4f2 | ||
|
|
79b350eca6 | ||
|
|
af1be91a28 | ||
|
|
6322ffc3b3 | ||
|
|
e657e83c3e | ||
|
|
4b3c293f04 | ||
|
|
1447fa0e95 | ||
|
|
cb2510a55d | ||
|
|
90f2e42b19 | ||
|
|
cbcb70a438 | ||
|
|
e47e51c229 | ||
|
|
e6a3974c9a | ||
|
|
247db60bb7 | ||
|
|
619d9350cc | ||
|
|
9b53db936e | ||
|
|
024d197e3e | ||
|
|
7937c6888c | ||
|
|
3369bf7d15 | ||
|
|
c7fb07c387 | ||
|
|
525dec7eb0 | ||
|
|
f4dde2f744 | ||
|
|
c81e28e240 | ||
|
|
b859cbfd59 | ||
|
|
99a736360b | ||
|
|
93d099427e | ||
|
|
6fafafe082 | ||
|
|
1a2035d5a4 | ||
|
|
186df5d802 | ||
|
|
27d13486ac | ||
|
|
41ba6ce845 | ||
|
|
1eb96fc1ec | ||
|
|
bbc81e20b3 | ||
|
|
fafc53e645 | ||
|
|
d0f5d01eea | ||
|
|
a04b79857f | ||
|
|
9f0f6eb78c | ||
|
|
7ea1d83a67 | ||
|
|
9aa6611557 | ||
|
|
a7b420eeba | ||
|
|
7b8edfbad4 | ||
|
|
07172f56f7 | ||
|
|
9f93683518 | ||
|
|
23574a80d5 | ||
|
|
74113f603b | ||
|
|
622125d9d9 | ||
|
|
f6b1864d91 | ||
|
|
6b098da5de | ||
|
|
6b09c0476c | ||
|
|
a94c284625 | ||
|
|
fdac82dba2 | ||
|
|
46b8b2791d | ||
|
|
fd476e892b | ||
|
|
63f85c3706 | ||
|
|
9c6e1d1b7d | ||
|
|
7d879a9a86 | ||
|
|
80ac5931a0 | ||
|
|
fabb975482 | ||
|
|
d1c40b6c81 | ||
|
|
95fa314e44 | ||
|
|
5ded6d9623 | ||
|
|
4c160134ba | ||
|
|
46894a8008 | ||
|
|
215a3c6c49 | ||
|
|
4d14b02aa2 | ||
|
|
6bcfc60eff | ||
|
|
1bbe525200 | ||
|
|
83b0eb3174 | ||
|
|
eeb44b4b5a | ||
|
|
0e20f5a8b8 | ||
|
|
92dad88e6a | ||
|
|
94e1c1e387 | ||
|
|
6c6b702b29 | ||
|
|
38e2e81388 | ||
|
|
5b2fd35367 | ||
|
|
527b8e8e9a | ||
|
|
82ea0f4f7e | ||
|
|
39ceced98f | ||
|
|
a67ad3b258 | ||
|
|
bc6694c1a1 | ||
|
|
c257c954fb | ||
|
|
689d4f0e9b | ||
|
|
c831d2cabf | ||
|
|
bbb0074de4 | ||
|
|
d3b69c4c86 | ||
|
|
ae17be3ee1 | ||
|
|
4f9dc213ed | ||
|
|
280e416af4 | ||
|
|
5db25732ee | ||
|
|
c51d5b5f79 | ||
|
|
5b8a087754 | ||
|
|
7611b467f3 | ||
|
|
9ac8f80da8 | ||
|
|
6ef2472613 | ||
|
|
c24bea6939 | ||
|
|
8d09595bb8 | ||
|
|
4a23fc0ee0 | ||
|
|
e23658dd9a | ||
|
|
afaee50a4c |
@@ -56,20 +56,22 @@
|
||||
;;
|
||||
;; globally-enabled custom linters
|
||||
;;
|
||||
:metabase/check-defmulti-arglists {:level :warning}
|
||||
:metabase/check-me-humanize {:level :warning}
|
||||
:metabase/defsetting-must-specify-export {:level :warning}
|
||||
:metabase/defsetting-namespace {:level :warning}
|
||||
:metabase/mbql-query-first-arg {:level :warning}
|
||||
:metabase/missing-test-expr-requires-in-cljs {:level :warning}
|
||||
:metabase/modules {:level :warning} ; more config in `.clj-kondo/config/modules/config.edn`
|
||||
:metabase/namespace-name {:level :warning}
|
||||
:metabase/require-shape-checker {:level :warning}
|
||||
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning} ; more config in `.clj-kondo/config/test-helpers/config.edn`
|
||||
:metabase/validate-logging {:level :warning}
|
||||
:metabase/validate-mb-driver-tests {:level :warning}
|
||||
:metabase/validate-mb-once {:level :warning}
|
||||
:metabase/discourage-millis-duration {:level :warning}
|
||||
:metabase/check-defmulti-arglists {:level :warning}
|
||||
:metabase/check-me-humanize {:level :warning}
|
||||
:metabase/defsetting-must-specify-export {:level :warning}
|
||||
:metabase/defsetting-namespace {:level :warning}
|
||||
:metabase/discourage-millis-duration {:level :warning}
|
||||
:metabase/mbql-query-first-arg {:level :warning}
|
||||
:metabase/missing-test-expr-requires-in-cljs {:level :warning}
|
||||
:metabase/modules {:level :warning} ; more config in `.clj-kondo/config/modules/config.edn`
|
||||
:metabase/namespace-name {:level :warning}
|
||||
:metabase/require-shape-checker {:level :warning}
|
||||
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning} ; more config in `.clj-kondo/config/test-helpers/config.edn`
|
||||
:metabase/validate-defendpoint-route-uses-kebab-case {:level :warning}
|
||||
:metabase/validate-defendpoint-query-params-use-kebab-case {:level :warning}
|
||||
:metabase/validate-logging {:level :warning}
|
||||
:metabase/validate-mb-driver-tests {:level :warning}
|
||||
:metabase/validate-mb-once {:level :warning}
|
||||
|
||||
:metabase/validate-deftest
|
||||
{:level :warning
|
||||
@@ -551,6 +553,8 @@
|
||||
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.legacy-mbql.schema.macros/defclause* hooks.metabase.legacy-mbql.schemas.macros/defclause*
|
||||
metabase.legacy-mbql.schema.macros/one-of hooks.metabase.legacy-mbql.schemas.macros/one-of
|
||||
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
|
||||
metabase.lib.schema.mbql-clause/define-tuple-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
|
||||
@@ -898,7 +902,9 @@
|
||||
performance-sensitive
|
||||
{:linters
|
||||
{:discouraged-var
|
||||
{clojure.core/every? {:message "Use metabase.util.performance/every?"}
|
||||
{clojure.core/empty? {:message "Use metabase.util.performance/empty?"}
|
||||
clojure.core/not-empty {:message "Use metabase.util.performance/not-empty"}
|
||||
clojure.core/every? {:message "Use metabase.util.performance/every?"}
|
||||
clojure.core/mapv {:message "Use metabase.util.performance/mapv"}
|
||||
clojure.core/run! {:message "Use metabase.util.performance/run!"}
|
||||
clojure.core/select-keys {:message "Use metabase.util.performance/select-keys"}
|
||||
@@ -907,7 +913,9 @@
|
||||
clojure.walk/keywordize-keys {:message "Use metabase.util.performance/keywordize-keys"}
|
||||
clojure.walk/postwalk {:message "Use metabase.util.performance/postwalk"}
|
||||
clojure.walk/prewalk {:message "Use metabase.util.performance/prewalk"}
|
||||
clojure.walk/walk {:message "Use metabase.util.performance/walk"}}}}
|
||||
clojure.walk/walk {:message "Use metabase.util.performance/walk"}
|
||||
clojure.walk/for {:message "Use metabase.util.performance/for"}
|
||||
clojure.walk/doseq {:message "Use metabase.util.performance/doseq"}}}}
|
||||
|
||||
qp-and-driver-source-namespaces
|
||||
{:linters
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
;; you need to make
|
||||
{:metabase/modules
|
||||
{actions
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.actions.api
|
||||
metabase.actions.args
|
||||
metabase.actions.core
|
||||
@@ -249,7 +249,7 @@
|
||||
|
||||
;; TODO -- way too many API namespaces.
|
||||
channel
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.channel.api
|
||||
metabase.channel.core
|
||||
metabase.channel.email
|
||||
@@ -557,9 +557,10 @@
|
||||
util}}
|
||||
|
||||
glossary
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.glossary.api}
|
||||
:uses #{api
|
||||
events
|
||||
models
|
||||
util}}
|
||||
|
||||
@@ -604,6 +605,7 @@
|
||||
:api #{metabase.lib.binning
|
||||
metabase.lib.binning.util
|
||||
metabase.lib.card
|
||||
metabase.lib.computed ; temporarily so that `qp.compile` can use it as lib memoization evolves
|
||||
metabase.lib.core
|
||||
metabase.lib.equality
|
||||
metabase.lib.field
|
||||
@@ -740,7 +742,7 @@
|
||||
util}}
|
||||
|
||||
notification
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.notification.api
|
||||
metabase.notification.core
|
||||
metabase.notification.init
|
||||
@@ -875,7 +877,7 @@
|
||||
util}}
|
||||
|
||||
pulse
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.pulse.api
|
||||
metabase.pulse.core
|
||||
metabase.pulse.init}
|
||||
@@ -1503,12 +1505,13 @@
|
||||
warehouse-schema}}
|
||||
|
||||
enterprise/action-v2
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase-enterprise.action-v2.api
|
||||
metabase-enterprise.action-v2.init}
|
||||
:uses #{actions
|
||||
api
|
||||
driver
|
||||
events
|
||||
lib
|
||||
models
|
||||
query-processor
|
||||
@@ -1621,7 +1624,9 @@
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.billing.api.routes}
|
||||
:uses #{api
|
||||
premium-features util}}
|
||||
premium-features
|
||||
store-api
|
||||
util}}
|
||||
|
||||
enterprise/bookmarks
|
||||
{:team "UX West"
|
||||
@@ -1643,11 +1648,12 @@
|
||||
:uses #{api
|
||||
events
|
||||
premium-features
|
||||
store-api
|
||||
util
|
||||
enterprise/harbormaster}}
|
||||
|
||||
enterprise/comments
|
||||
{:team "Workflows"
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.comments.api
|
||||
metabase-enterprise.comments.init}
|
||||
:uses #{analytics
|
||||
@@ -1683,7 +1689,7 @@
|
||||
:uses :any}
|
||||
|
||||
enterprise/dashboard-subscription-filters
|
||||
{:team "Workflows"
|
||||
{:team "Querying"
|
||||
:api #{}
|
||||
:uses #{premium-features}}
|
||||
|
||||
@@ -1731,6 +1737,7 @@
|
||||
lib-be
|
||||
models
|
||||
native-query-snippets
|
||||
permissions
|
||||
premium-features
|
||||
queries
|
||||
query-processor
|
||||
@@ -1754,8 +1761,10 @@
|
||||
events
|
||||
models
|
||||
premium-features
|
||||
public-sharing
|
||||
queries
|
||||
query-permissions
|
||||
query-processor
|
||||
revisions
|
||||
search
|
||||
util
|
||||
@@ -1887,6 +1896,16 @@
|
||||
:uses #{premium-features
|
||||
util}}
|
||||
|
||||
enterprise/public-sharing
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.public-sharing.api}
|
||||
:uses #{api
|
||||
events
|
||||
premium-features
|
||||
public-sharing
|
||||
query-processor
|
||||
util}}
|
||||
|
||||
enterprise/remote-sync
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.remote-sync.api
|
||||
@@ -1900,8 +1919,8 @@
|
||||
models
|
||||
premium-features
|
||||
settings
|
||||
startup
|
||||
task
|
||||
task-history
|
||||
util
|
||||
enterprise/serialization}}
|
||||
|
||||
@@ -1972,7 +1991,7 @@
|
||||
enterprise/metabot-v3}}
|
||||
|
||||
enterprise/serialization
|
||||
{:team "Workflows"
|
||||
{:team "Querying"
|
||||
:api #{metabase-enterprise.serialization.api
|
||||
metabase-enterprise.serialization.cmd
|
||||
metabase-enterprise.serialization.core
|
||||
|
||||
@@ -1,7 +1,67 @@
|
||||
(ns hooks.metabase.api.macros
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as api]
|
||||
[hooks.common]))
|
||||
[clojure.string :as str]))
|
||||
|
||||
(defn- validate-route!
|
||||
"Make sure the route uses kebab-case rather than snake_case."
|
||||
[route]
|
||||
(letfn [(validate-route-string! [route-string]
|
||||
(when (and (api/string-node? route-string)
|
||||
(str/includes? (api/sexpr route-string) "_"))
|
||||
(api/reg-finding! (assoc (meta route-string)
|
||||
:message "REST API routes and query params should use kebab-case, not snake_case"
|
||||
:type :metabase/validate-defendpoint-route-uses-kebab-case))))]
|
||||
(cond
|
||||
(api/string-node? route)
|
||||
(validate-route-string! route)
|
||||
;; otherwise route is a vector like ["/abc/:d/" :d <regex>]
|
||||
(api/vector-node? route)
|
||||
(validate-route-string! (first (:children route))))))
|
||||
|
||||
(defn- validate-query-params!
|
||||
"Make sure query params use kebab-case rather than snake_case.
|
||||
|
||||
This only works on inline schemas for now. If you define the schema elsewhere then we'd have to implement this
|
||||
validation in Clojure-land since we can't resolve the schemas in Kondo. It should however still work on complicated
|
||||
schemas like
|
||||
|
||||
[:merge <map-1> <map-2>]"
|
||||
[query-param-schema-node]
|
||||
(letfn [(map-schema? [node]
|
||||
(and (api/vector-node? node)
|
||||
(when-let [first-child (first (:children node))]
|
||||
(and (api/keyword-node? first-child)
|
||||
(= (api/sexpr first-child) :map)))))
|
||||
(map-schema-keys [{:keys [children], :as _node}]
|
||||
(let [child-schemas (if (api/map-node? (second children))
|
||||
(drop 2 children)
|
||||
(drop 1 children))]
|
||||
(keep (fn [child-schema]
|
||||
(when (api/vector-node? child-schema)
|
||||
(when-let [k (first (:children child-schema))]
|
||||
(when (api/keyword-node? k)
|
||||
k))))
|
||||
child-schemas)))
|
||||
(validate-map-schema! [form]
|
||||
;; only warn once per endpoint -- just once on the first bad node
|
||||
(when-let [bad-node (some (fn [key-node]
|
||||
(when (str/includes? (str (api/sexpr key-node)) "_")
|
||||
key-node))
|
||||
(map-schema-keys form))]
|
||||
(api/reg-finding! (assoc (meta bad-node)
|
||||
:message "REST API routes and query params should use kebab-case, not snake_case"
|
||||
:type :metabase/validate-defendpoint-query-params-use-kebab-case))))
|
||||
(validate! [node]
|
||||
(cond
|
||||
(and (api/vector-node? node)
|
||||
(map-schema? node))
|
||||
(validate-map-schema! node)
|
||||
|
||||
(api/vector-node? node)
|
||||
(doseq [child (:children node)]
|
||||
(validate! child))))]
|
||||
(validate! query-param-schema-node)))
|
||||
|
||||
(defn defendpoint
|
||||
[arg]
|
||||
@@ -18,16 +78,21 @@
|
||||
(cons nil more))
|
||||
[params & body] more
|
||||
[bindings schemas] (when (api/vector-node? params)
|
||||
(loop [bindings [], schemas [], [x y & more] (:children params)]
|
||||
(loop [bindings [], schemas [], [x y z & more] (:children params)]
|
||||
(cond
|
||||
(not x)
|
||||
[bindings schemas]
|
||||
|
||||
(= (api/sexpr x) :-)
|
||||
(recur bindings (conj schemas y) more)
|
||||
(and (api/keyword-node? y)
|
||||
(= (api/sexpr y) :-))
|
||||
(recur (conj bindings x) (conj schemas z) more)
|
||||
|
||||
:else
|
||||
(recur (conj bindings x) schemas (cons y more)))))]
|
||||
(recur (conj bindings x) (conj schemas nil) (list* y z more)))))]
|
||||
(validate-route! route)
|
||||
(let [[_route-param-schema query-param-schema _body-param-schema] schemas]
|
||||
(when query-param-schema
|
||||
(validate-query-params! query-param-schema)))
|
||||
(-> (api/list-node
|
||||
(list
|
||||
(api/token-node 'do)
|
||||
@@ -54,3 +119,27 @@
|
||||
body))))
|
||||
(with-meta (meta node)))))]
|
||||
(update arg :node update-defendpoint)))
|
||||
|
||||
(comment
|
||||
(defn -defendpoint [form]
|
||||
(-> {:node (-> form pr-str api/parse-string)}
|
||||
defendpoint
|
||||
:node
|
||||
api/sexpr))
|
||||
|
||||
(defn x []
|
||||
(-defendpoint '(api.macros/defendpoint :get "/:id/syncable_schemas" ; snake_case route should trigger a warning
|
||||
"Returns a list of all syncable schemas found for the database `id`."
|
||||
[{:keys [id]} :- [:map
|
||||
[:id ms/PositiveInt]]
|
||||
;; snake_case query parameter, should trigger a warning
|
||||
{:keys [filter_by]} :- [:map
|
||||
[:filter_by {:optional true} :string]]]
|
||||
(let [db (get-database id)]
|
||||
(api/check-403 (or (:is_attached_dwh db)
|
||||
(and (mi/can-write? db)
|
||||
(mi/can-read? db))))
|
||||
(->> db
|
||||
(driver/syncable-schemas (:engine db))
|
||||
(vec)
|
||||
(sort)))))))
|
||||
|
||||
@@ -33,18 +33,36 @@
|
||||
(defclause [ag:var var] field-or-expression FieldOrExpressionDef)
|
||||
=>
|
||||
(def ag:var [FieldOrExpressionDef])"
|
||||
[{:keys [node]}]
|
||||
(let [[_defclause clause-name-form & arg-specs] (:children node)]
|
||||
{:node (-> (hooks/list-node
|
||||
(list
|
||||
(hooks/token-node 'def)
|
||||
(unwrap-defclause-clause-name clause-name-form)
|
||||
(hooks/string-node "Docstring.")
|
||||
(collect-defclause-body-schemas arg-specs)))
|
||||
(with-meta (meta node)))}))
|
||||
[{:keys [node], :as x}]
|
||||
(let [[_defclause clause-name & arg-specs] (:children node)]
|
||||
(assoc x :node (-> (hooks/list-node
|
||||
(list
|
||||
(hooks/token-node 'do)
|
||||
(-> (hooks/keyword-node (keyword "metabase.legacy-mbql.schema" (name (hooks/sexpr clause-name))))
|
||||
(with-meta (meta clause-name))
|
||||
(hooks/reg-keyword! 'metabase.legacy-mbql.schemas.macros/defclause))
|
||||
(collect-defclause-body-schemas arg-specs)))
|
||||
(with-meta (meta node))))))
|
||||
|
||||
(defn defclause*
|
||||
"e.g.
|
||||
|
||||
(defclause [ag:var var] field-or-expression FieldOrExpressionDef)
|
||||
=>
|
||||
(def ag:var [FieldOrExpressionDef])"
|
||||
[{:keys [node], :as x}]
|
||||
(let [[_defclause clause-name schema] (:children node)]
|
||||
(assoc x :node (-> (hooks/list-node
|
||||
(list
|
||||
(hooks/token-node 'do)
|
||||
(-> (hooks/keyword-node (keyword "metabase.legacy-mbql.schema" (name (hooks/sexpr clause-name))))
|
||||
(with-meta (meta clause-name))
|
||||
(hooks/reg-keyword! 'metabase.legacy-mbql.schemas.macros/defclause*))
|
||||
schema))
|
||||
(with-meta (meta node))))))
|
||||
|
||||
(comment
|
||||
(defn- defclause* [form]
|
||||
(defn- expand-defclause [form]
|
||||
(hooks/sexpr
|
||||
(:node
|
||||
(defclause
|
||||
@@ -55,15 +73,35 @@
|
||||
form)))}))))
|
||||
|
||||
(defn- x []
|
||||
(defclause* '(defclause [ag:var var]
|
||||
field-or-expression FieldOrExpressionDef)))
|
||||
(expand-defclause '(defclause var
|
||||
field-or-expression FieldOrExpressionDef)))
|
||||
;; =>
|
||||
(def ag:var "Docstring." [FieldOrExpressionDef])
|
||||
(do :metabase.legacy-mbql.schema/var [FieldOrExpressionDef])
|
||||
|
||||
(defn- y []
|
||||
(defclause* '(defclause var
|
||||
x (some-list-schema)
|
||||
y (optional FieldOrExpressionDef)
|
||||
z (rest (some-other-schema)))))
|
||||
(expand-defclause '(defclause var
|
||||
x (some-list-schema)
|
||||
y (optional FieldOrExpressionDef)
|
||||
z (rest (some-other-schema)))))
|
||||
;; =>
|
||||
(def var "Docstring." [(some-list-schema) FieldOrExpressionDef (some-other-schema)]))
|
||||
(do :metabase.legacy-mbql.schema/var [(some-list-schema) FieldOrExpressionDef (some-other-schema)]))
|
||||
|
||||
(defn one-of [{:keys [node], :as x}]
|
||||
(let [[_one-of & schema-nodes] (:children node)
|
||||
node' (-> (hooks/vector-node
|
||||
(mapv (fn [schema-node]
|
||||
(if (and (hooks/token-node? schema-node)
|
||||
(simple-symbol? (hooks/sexpr schema-node)))
|
||||
(-> (hooks/keyword-node (keyword "mteabase.legacy-mbql.schema" (name (hooks/sexpr schema-node))))
|
||||
(with-meta (meta schema-node)))
|
||||
schema-node))
|
||||
schema-nodes))
|
||||
(with-meta (meta node)))]
|
||||
(assoc x :node node')))
|
||||
|
||||
(comment
|
||||
(defn- expand-one-of [form]
|
||||
(hooks/sexpr (:node (one-of {:node (hooks/parse-string (with-out-str (clojure.pprint/pprint form)))}))))
|
||||
|
||||
(defn- z []
|
||||
(expand-one-of '(one-of absolute-datetime relative-datetime time))))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns hooks.metabase.api.macros-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as api]
|
||||
[clj-kondo.impl.utils]
|
||||
[clojure.test :refer :all]
|
||||
[hooks.metabase.api.macros]))
|
||||
|
||||
@@ -69,3 +70,41 @@
|
||||
(respond :x)
|
||||
(catch Throwable e
|
||||
(raise e))))))))
|
||||
|
||||
(defn- defendpoint [form]
|
||||
(hooks.metabase.api.macros/defendpoint
|
||||
{:node (-> form pr-str api/parse-string)}))
|
||||
|
||||
(defn- defendpoint-warnings
|
||||
[form]
|
||||
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/validate-defendpoint-route-uses-kebab-case {:level :warning}
|
||||
:metabase/validate-defendpoint-query-params-use-kebab-case {:level :warning}}}
|
||||
:ignores (atom nil)
|
||||
:findings (atom [])
|
||||
:namespaces (atom {})}]
|
||||
(defendpoint form)
|
||||
@(:findings clj-kondo.impl.utils/*ctx*)))
|
||||
|
||||
(deftest ^:parallel validate-route-kebab-case-test
|
||||
(is (=? [{:message "REST API routes and query params should use kebab-case, not snake_case"
|
||||
:type :metabase/validate-defendpoint-route-uses-kebab-case
|
||||
:row pos-int?
|
||||
:col pos-int?}]
|
||||
(defendpoint-warnings
|
||||
'(api.macros/defendpoint :get "/:id/syncable_schemas" ; snake_case route should trigger a warning
|
||||
"Returns a list of all syncable schemas found for the database `id`."
|
||||
[{:keys [id]}]
|
||||
id)))))
|
||||
|
||||
(deftest ^:parallel validate-query-params-kebab-case-test
|
||||
(is (=? [{:message "REST API routes and query params should use kebab-case, not snake_case",
|
||||
:type :metabase/validate-defendpoint-query-params-use-kebab-case
|
||||
:row pos-int?
|
||||
:col pos-int?}]
|
||||
(defendpoint-warnings
|
||||
'(api.macros/defendpoint :get "/:id/syncable-schemas"
|
||||
"Returns a list of all syncable schemas found for the database `id`."
|
||||
[{:keys [id]}
|
||||
{:keys [filter_by]} :- [:map
|
||||
[:filter_by {:optional true} :string]]]
|
||||
id)))))
|
||||
|
||||
@@ -2,3 +2,4 @@ frontend/src/cljs
|
||||
frontend/src/cljs_release
|
||||
e2e/support/cypress_sample_database.js
|
||||
e2e/support/cypress_sample_instance_data.js
|
||||
e2e/embedding-sdk-host-apps
|
||||
|
||||
5
.github/actions/get-cache-keys/action.yml
vendored
@@ -10,6 +10,9 @@ outputs:
|
||||
node-cache-key:
|
||||
description: "The cache key for the Node.js cache."
|
||||
value: ${{ steps.keys-factory.outputs.node-cache-key }}
|
||||
eslint-cache-key:
|
||||
description: "The cache key for Eslint cache"
|
||||
value: ${{ steps.keys-factory.outputs.eslint-cache-key }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -19,9 +22,11 @@ runs:
|
||||
run: |
|
||||
M2_CACHE_PREFIX='M2'
|
||||
NODE_CACHE_PREFIX='node-modules'
|
||||
ESLINT_CACHE_PREFIX='eslint'
|
||||
WEEK_OF_YEAR=$(/bin/date "+%G-%V")
|
||||
CACHE_SUFFIX="${{ runner.os }}-$WEEK_OF_YEAR"
|
||||
|
||||
echo "m2-cache-key=$M2_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "node-cache-key=$NODE_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "eslint-cache-key=$ESLINT_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
15
.github/actions/test-driver/action.yml
vendored
@@ -27,9 +27,18 @@ runs:
|
||||
- name: Test database driver
|
||||
run: clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test ${{ inputs.test-args }}
|
||||
shell: bash
|
||||
id: run-test
|
||||
# Trunk Quarantine controls the final state of the run in the CI!
|
||||
# Even if this step fails, we let it pass and then `trunk-io/analytics-uploader` determines whether to fail or to
|
||||
# pass the whole job, based on the quarantine list.
|
||||
# We only want to "continue on error" (ignore failures) when the Trunk upload is active - i.e. not on drafts!
|
||||
continue-on-error: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.draft == true) }}
|
||||
- name: Publish Test Report (JUnit)
|
||||
uses: dorny/test-reporter@v1
|
||||
if: failure()
|
||||
# As per the documentation: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#steps-context
|
||||
# test.outcome gives us access to the result before `continue-on-error` overrides it and sets it to 'success'.
|
||||
# We want test reports even if the test step gets cancelled (e.g. times out), hence we match anything that is not a 'success'.
|
||||
if: steps.run-test.outcome != 'success'
|
||||
with:
|
||||
path: "target/junit/**/*_test.xml"
|
||||
name: JUnit Test Report ${{ inputs.junit-name }}
|
||||
@@ -38,9 +47,11 @@ runs:
|
||||
list-tests: failed
|
||||
fail-on-error: false
|
||||
- name: Upload Logs on Failure
|
||||
if: always() && !success()
|
||||
if: always() && steps.run-test.outcome != 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-logs-${{ github.job }}
|
||||
path: logs/test-log.json
|
||||
retention-days: 1
|
||||
# Failing to upload the artifact should never affect the outcome of the whole test run.
|
||||
continue-on-error: true
|
||||
|
||||
2
.github/codeql/codeql-config.yml
vendored
@@ -1,3 +1,5 @@
|
||||
version: 1
|
||||
|
||||
name: "Metabase CodeQL config"
|
||||
|
||||
paths-ignore:
|
||||
|
||||
5
.github/file-paths.yaml
vendored
@@ -127,10 +127,6 @@ e2e_ci: &e2e_ci
|
||||
e2e_specs: &e2e_specs
|
||||
- e2e/!(test-host-app)**/*.cy.*.{js,ts}
|
||||
|
||||
e2e_cross_version: &e2e_cross_version
|
||||
- ".github/workflows/e2e-cross-version.yml"
|
||||
- "e2e/test/scenarios/cross-version/**"
|
||||
|
||||
e2e_embedding_sdk:
|
||||
- *frontend_embedding_sdk_ci
|
||||
- *sources
|
||||
@@ -170,6 +166,7 @@ yaml:
|
||||
- "**/*.yaml"
|
||||
|
||||
codeql:
|
||||
- ".github/workflows/codeql.yml"
|
||||
- "frontend/src/**"
|
||||
- "enterprise/frontend/src/**"
|
||||
|
||||
|
||||
13
.github/team.json
vendored
@@ -32,15 +32,6 @@
|
||||
"lorem--ipsum"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Workflows",
|
||||
"label": ".Team/Workflows",
|
||||
"members": [
|
||||
"piranha",
|
||||
"crisptrutski",
|
||||
"spalt08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Admin Webapp",
|
||||
"label": ".Team/AdminWebapp",
|
||||
@@ -69,7 +60,9 @@
|
||||
"sloansparger",
|
||||
"somtom",
|
||||
"tsplude",
|
||||
"lbrdnk"
|
||||
"lbrdnk",
|
||||
"piranha",
|
||||
"crisptrutski"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
189
.github/workflows/backend-cloverage.yml
vendored
@@ -10,19 +10,202 @@ jobs:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
modules:
|
||||
- actions
|
||||
- activity-feed
|
||||
- analytics
|
||||
- analyze
|
||||
- api
|
||||
- api-keys
|
||||
- api-routes
|
||||
- app-db
|
||||
- appearance
|
||||
- audit-app
|
||||
- auth-provider
|
||||
- batch-processing
|
||||
- bookmarks
|
||||
- bug-reporting
|
||||
- cache
|
||||
- channel
|
||||
- classloader
|
||||
- cloud-migration
|
||||
- cmd
|
||||
- collections
|
||||
- config
|
||||
- connection-pool
|
||||
- content-translation
|
||||
- content-verification
|
||||
- core
|
||||
- dashboards
|
||||
- database-routing
|
||||
- driver
|
||||
- driver-api
|
||||
- eid-translation
|
||||
- embedding
|
||||
- events
|
||||
- formatter
|
||||
- geojson
|
||||
- glossary
|
||||
- graph
|
||||
- indexed-entities
|
||||
- internal-stats
|
||||
- legacy-mbql
|
||||
- lib
|
||||
- lib-be
|
||||
- logger
|
||||
- login-history
|
||||
- model-persistence
|
||||
- models
|
||||
- native-query-snippets
|
||||
- notification
|
||||
- parameters
|
||||
- permissions
|
||||
- pivot
|
||||
- plugins
|
||||
- premium-features
|
||||
- product-feedback
|
||||
- public-sharing
|
||||
- pulse
|
||||
- queries
|
||||
- query-permissions
|
||||
- query-processor
|
||||
- remote-sync
|
||||
- request
|
||||
- revisions
|
||||
- sample-data
|
||||
- search
|
||||
- secrets
|
||||
- segments
|
||||
- server
|
||||
- session
|
||||
- settings
|
||||
- setup
|
||||
- sso
|
||||
- startup
|
||||
- store-api
|
||||
- sync
|
||||
- system
|
||||
- task
|
||||
- task-history
|
||||
- testing-api
|
||||
- tiles
|
||||
- timeline
|
||||
- types
|
||||
- upload
|
||||
- user-key-value
|
||||
- users
|
||||
- util
|
||||
- version
|
||||
- view-log
|
||||
- warehouse-schema
|
||||
- warehouses
|
||||
- xrays
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Collect the test coverage
|
||||
run: clojure -X:dev:ci:ee:ee-dev:test:cloverage
|
||||
run: |
|
||||
clojure -X:dev:ci:ee:ee-dev:test:cloverage \
|
||||
:ns-regex '["^metabase\\.${{ matrix.modules }}\\..*"]' \
|
||||
:test-ns-path '["test/metabase/${{ matrix.modules }}"]'
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload coverage to codecov.io
|
||||
uses: codecov/codecov-action@v4
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./target/coverage/codecov.json
|
||||
flags: back-end
|
||||
|
||||
be-linter-cloverage-enterprise:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
modules:
|
||||
- action-v2
|
||||
- advanced-config
|
||||
- advanced-permissions
|
||||
- ai-entity-analysis
|
||||
- ai-sql-fixer
|
||||
- ai-sql-generation
|
||||
- analytics
|
||||
- api
|
||||
- audit-app
|
||||
- auth-provider
|
||||
- billing
|
||||
- bookmarks
|
||||
- cache
|
||||
- cloud-add-ons
|
||||
- comments
|
||||
- content-translation
|
||||
- content-verification
|
||||
- core
|
||||
- dashboard-subscription-filters
|
||||
- database-replication
|
||||
- database-routing
|
||||
- dependencies
|
||||
- documents
|
||||
- email
|
||||
- embedding-hub
|
||||
- gsheets
|
||||
- harbormaster
|
||||
- impersonation
|
||||
- internal-stats
|
||||
- llm
|
||||
- metabot-v3
|
||||
- permission-debug
|
||||
- premium-features
|
||||
- public-sharing
|
||||
- remote-sync
|
||||
- sandbox
|
||||
- scim
|
||||
- search
|
||||
- semantic-search
|
||||
- serialization
|
||||
- snippet-collections
|
||||
- sso
|
||||
- stale
|
||||
- transforms
|
||||
- transforms-python
|
||||
- upload-management
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Collect the test coverage
|
||||
run: |
|
||||
clojure -X:dev:ci:ee:ee-dev:test:cloverage \
|
||||
:ns-regex '["^metabase-enterprise\\.${{ matrix.modules }}\\..*"]' \
|
||||
:test-ns-path '["enterprise/backend/test/metabase/${{ matrix.modules }}"]'
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload coverage to codecov.io
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./target/coverage/codecov.json
|
||||
|
||||
5
.github/workflows/backend.yml
vendored
@@ -129,6 +129,11 @@ jobs:
|
||||
clojure -X:dev:ci:test:${{ matrix.job.edition }}:${{ matrix.job.edition }}-dev
|
||||
:exclude-tags '[:mb/driver-tests ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
${{ matrix.job.test-args }}
|
||||
# Trunk Quarantine controls the final state of the run in the CI!
|
||||
# Even if this step fails, we let it pass and then `trunk-io/analytics-uploader` determines whether to fail or to
|
||||
# pass the whole job, based on the quarantine list.
|
||||
# We only want to "continue on error" (ignore failures) when the Trunk upload is active - i.e. not on drafts!
|
||||
continue-on-error: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.draft == true) }}
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
|
||||
10
.github/workflows/cache-generator.yml
vendored
@@ -48,6 +48,7 @@ jobs:
|
||||
outputs:
|
||||
m2-cache-key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
node-cache-key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
|
||||
eslint-cache-key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
|
||||
|
||||
generate-m2-cache:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -73,12 +74,13 @@ jobs:
|
||||
~/.gitlibs
|
||||
key: ${{ env.M2_CACHE_KEY }}
|
||||
|
||||
generate-node-modules-cache:
|
||||
generate-frontend-caches:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
needs: get-cache-keys
|
||||
env:
|
||||
NODE_CACHE_KEY: ${{ needs.get-cache-keys.outputs.node-cache-key }}
|
||||
ESLINT_CACHE_KEY: ${{ needs.get-cache-keys.outputs.eslint-cache-key }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# `prepare-frontend` action (1) prepares Node.js and (2) fetches the node_modules cache if it exists.
|
||||
@@ -97,3 +99,9 @@ jobs:
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ env.NODE_CACHE_KEY }}
|
||||
|
||||
- name: Update eslint cache
|
||||
uses: ./.github/actions/update-cache
|
||||
with:
|
||||
path: .eslintcache
|
||||
key: ${{ env.ESLINT_CACHE_KEY }}
|
||||
|
||||
11
.github/workflows/claude-code-review.yaml
vendored
@@ -2,6 +2,10 @@ name: "Claude Code Review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
- synchronize
|
||||
- labeled
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-claude-code-review
|
||||
@@ -9,7 +13,12 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
review:
|
||||
if: github.event.pull_request.draft == false
|
||||
if: |
|
||||
(
|
||||
github.event.action == 'closed' &&
|
||||
github.event.pull_request.merged == true
|
||||
) ||
|
||||
contains(github.event.pull_request.labels.*.name, 'Claude Code Review')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
5
.github/workflows/codeql.yml
vendored
@@ -30,9 +30,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: javascript
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
trap-caching: false
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
10
.github/workflows/drivers.yml
vendored
@@ -390,10 +390,6 @@ jobs:
|
||||
name: H2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test H2 driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
@@ -413,12 +409,6 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
|
||||
|
||||
be-tests-mysql-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Thi workflow does the backward compatibility tests
|
||||
# This workflow does the backward compatibility tests
|
||||
# between the Embedding SDK package of version N and Embedding SDK Bundle of version N - 1
|
||||
# N - 1 is a target of a PR
|
||||
name: Embedding SDK backward-compatibility E2E Component Tests
|
||||
@@ -93,13 +93,14 @@ jobs:
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
@@ -108,13 +108,14 @@ jobs:
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
|
||||
171
.github/workflows/e2e-cross-version.yml
vendored
@@ -1,171 +0,0 @@
|
||||
name: E2E Cross-version Tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1-5'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_cross_version: ${{ steps.changes.outputs.e2e_cross_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
cross-version-testing:
|
||||
needs: files-changed
|
||||
if: ${{ needs.files-changed.outputs.e2e_cross_version == 'true' || github.event_name == 'schedule'}}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
matrix:
|
||||
version: [
|
||||
# Major OSS upgrade
|
||||
{ source: v0.42.2, target: v0.48.7 },
|
||||
# OSS upgrade
|
||||
{ source: v0.41.3.1, target: v0.42.2 },
|
||||
# EE upgrade
|
||||
{ source: v1.43.4, target: v1.44.0 },
|
||||
# EE downgrade
|
||||
{ source: v1.42.0, target: v1.41.3.1 },
|
||||
# Cross-edition upgrade
|
||||
{ source: v0.41.3.1, target: v1.42.2 }
|
||||
]
|
||||
fail-fast: false
|
||||
env:
|
||||
CROSS_VERSION_SOURCE: ${{ matrix.version.source }}
|
||||
CROSS_VERSION_TARGET: ${{ matrix.version.target }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set source Docker image to OSS
|
||||
run: |
|
||||
echo "DOCKER_SOURCE_IMAGE=metabase/metabase:${{ matrix.version.source }}" >> $GITHUB_ENV
|
||||
if: startsWith(matrix.version.source, 'v0')
|
||||
- name: Set source Docker image to EE
|
||||
run: |
|
||||
echo "DOCKER_SOURCE_IMAGE=metabase/metabase-enterprise:${{ matrix.version.source }}" >> $GITHUB_ENV
|
||||
if: startsWith(matrix.version.source, 'v1')
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Start Metabase ${{ matrix.version.source }}
|
||||
run: |
|
||||
docker run -d \
|
||||
-v $PWD/my-metabase:/metabase.db \
|
||||
-p 3000:3000 \
|
||||
-e CYPRESS_MB_ALL_FEATURES_TOKEN=${{ secrets.MB_ALL_FEATURES_TOKEN }} \
|
||||
-e CYPRESS_MB_STARTER_CLOUD_TOKEN=${{ secrets.MB_STARTER_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_CLOUD_TOKEN=${{ secrets.MB_PRO_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_SELF_HOSTED_TOKEN=${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }} \
|
||||
--name metabase-${{ matrix.version.source }} \
|
||||
${{ env.DOCKER_SOURCE_IMAGE }}
|
||||
- name: Wait for Metabase to start on port 3000
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
- name: Run Cypress on the source
|
||||
run: |
|
||||
yarn cypress run \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }} \
|
||||
--config-file e2e/test/scenarios/cross-version/source/shared/cross-version-source.config.js \
|
||||
--spec e2e/test/scenarios/cross-version/source/**/*.cy.spec.js
|
||||
- name: Stop Metabase ${{ matrix.version.source }}
|
||||
run: docker stop metabase-${{ matrix.version.source }}
|
||||
|
||||
- name: Set target Docker image to OSS
|
||||
run: |
|
||||
echo "DOCKER_TARGET_IMAGE=metabase/metabase:${{ matrix.version.source }}" >> $GITHUB_ENV
|
||||
if: startsWith(matrix.version.source, 'v0')
|
||||
- name: Set target Docker image to EE
|
||||
run: |
|
||||
echo "DOCKER_TARGET_IMAGE=metabase/metabase-enterprise:${{ matrix.version.source }}" >> $GITHUB_ENV
|
||||
if: startsWith(matrix.version.source, 'v1')
|
||||
- name: Start Metabase ${{ matrix.version.target }}
|
||||
run: |
|
||||
docker run -d \
|
||||
-v $PWD/my-metabase:/metabase.db \
|
||||
-p 3001:3000 \
|
||||
-e CYPRESS_MB_ALL_FEATURES_TOKEN=${{ secrets.MB_ALL_FEATURES_TOKEN }} \
|
||||
-e CYPRESS_MB_STARTER_CLOUD_TOKEN=${{ secrets.MB_STARTER_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_CLOUD_TOKEN=${{ secrets.MB_PRO_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_SELF_HOSTED_TOKEN=${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }} \
|
||||
--name metabase-${{ matrix.version.target }} \
|
||||
${{ env.DOCKER_TARGET_IMAGE }}
|
||||
- name: Wait for Metabase to start on port 3001
|
||||
run: while ! curl -s 'http://localhost:3001/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
- name: Run Cypress on the target
|
||||
run: |
|
||||
yarn cypress run \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }} \
|
||||
--config-file e2e/test/scenarios/cross-version/target/shared/cross-version-target.config.js \
|
||||
--spec e2e/test/scenarios/cross-version/target/**/*.cy.spec.js
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-artifacts-${{ matrix.version.source}}-to-${{ matrix.version.target }}
|
||||
path: |
|
||||
./cypress
|
||||
if-no-files-found: ignore
|
||||
notify-on-failure:
|
||||
needs: cross-version-testing
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }}
|
||||
name: Cross-version test failure
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Jod # 22.x.x
|
||||
- run: npm install @slack/web-api
|
||||
- name: notify slack of failure
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | # js
|
||||
const FAILED_RUN_URL = "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}";
|
||||
const REF_NAME = "${{ github.ref_name }}";
|
||||
|
||||
// notify slack of failure
|
||||
const { WebClient } = require('@slack/web-api');
|
||||
const slack = new WebClient('${{ secrets.SLACK_BOT_TOKEN }}');
|
||||
|
||||
await slack.chat.postMessage({
|
||||
channel: 'engineering-ci',
|
||||
text: 'Cross-version Tests Have Failed',
|
||||
blocks: [
|
||||
{
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": `:broken_heart: Cross-version tests are failing on ${REF_NAME}`,
|
||||
"emoji": true,
|
||||
}
|
||||
},
|
||||
],
|
||||
attachments: [{
|
||||
color: "#BF40BF",
|
||||
blocks: [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": `See <${FAILED_RUN_URL}|failing run details>.`
|
||||
}
|
||||
},
|
||||
]
|
||||
}]
|
||||
});
|
||||
@@ -213,7 +213,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
@@ -343,7 +345,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
43
.github/workflows/e2e-stress-test-flake-fix.yml
vendored
@@ -37,8 +37,26 @@ on:
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
determine-suite:
|
||||
name: Determine test suite
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
suite: ${{ steps.detect.outputs.suite }}
|
||||
steps:
|
||||
- name: Detect suite from spec path
|
||||
id: detect
|
||||
run: |
|
||||
SPEC='${{ inputs.spec }}'
|
||||
if [[ "$SPEC" == e2e/test-component/scenarios/embedding-sdk/* ]]; then
|
||||
echo "suite=component" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "suite=e2e" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
workflow-summary:
|
||||
name: Stress test inputs
|
||||
needs: [determine-suite]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
@@ -48,6 +66,7 @@ jobs:
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `branch`: ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `spec`: ${{ inputs.spec }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `test_suite` (inferred): ${{ needs.determine-suite.outputs.suite }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `burn_in`: ${{ inputs.burn_in }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `grep`: "${{ inputs.grep }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `qa_db`: "${{ inputs.qa_db }}"' >> $GITHUB_STEP_SUMMARY
|
||||
@@ -56,6 +75,7 @@ jobs:
|
||||
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
stress-test-flake-fix:
|
||||
needs: [determine-suite]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
name: Stress test E2E flake fix
|
||||
@@ -63,10 +83,12 @@ jobs:
|
||||
DISPLAY: ""
|
||||
QA_DB_ENABLED: ${{ contains(fromJSON('["sql", "mongo"]'), inputs.qa_db) }}
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.qa_db == 'mongo' }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
MB_SNOWPLOW_AVAILABLE: true
|
||||
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
|
||||
MB_EDITION: ${{ inputs.mb_edition }}
|
||||
@@ -98,6 +120,16 @@ jobs:
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare back-end environment
|
||||
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Build Embedding SDK package
|
||||
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
run: yarn build-embedding-sdk-package
|
||||
|
||||
- name: Prepare JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
@@ -116,8 +148,9 @@ jobs:
|
||||
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
|
||||
env:
|
||||
GREP: ${{ github.event.inputs.grep }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
node e2e/runner/run_cypress_ci.js ${{ needs.determine-suite.outputs.suite }} \
|
||||
--spec '${{ github.event.inputs.spec }}' \
|
||||
--env burn=${{ github.event.inputs.burn_in }} \
|
||||
--config-file e2e/support/cypress-stress-test.config.js \
|
||||
|
||||
10
.github/workflows/e2e-test.yml
vendored
@@ -42,12 +42,14 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
JOB_NAME: ${{ inputs.name }}
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
MB_EXPERIMENTAL_SEARCH_BLOCK_ON_QUEUE: true
|
||||
|
||||
11
.github/workflows/frontend.yml
vendored
@@ -35,13 +35,16 @@ jobs:
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Get Eslint cache key
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
id: get-cache-keys
|
||||
- name: Restore ESLint cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: .eslintcache
|
||||
key: eslintcache-${{ hashFiles('yarn.lock', '.eslintrc.js') }}
|
||||
restore-keys: |
|
||||
eslintcache-
|
||||
key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
- name: Find CSS variables that are used but never defined
|
||||
|
||||
45
.github/workflows/pre-release.yml
vendored
@@ -24,6 +24,39 @@ on:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
test-mode-config:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
run_mode: ${{ fromJson(steps.config.outputs.result).runMode }}
|
||||
dev_server: ${{ fromJson(steps.config.outputs.result).metastoreDevServer }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
# note: this block can be removed after we stop needing to be able to test versions before v55
|
||||
- name: Get test mode config
|
||||
id: config
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | # js
|
||||
const { getMajorVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
// Versions before v55 used a single token for testing
|
||||
// with v55 and later we use enterprise tokens that mirror actual Metabase plans
|
||||
const MULTIPLE_TEST_TOKEN_THRESHOLD = 55;
|
||||
|
||||
const version = '${{ inputs.version }}';
|
||||
const majorVersion = Number(getMajorVersion(version));
|
||||
const oldTestToken = majorVersion < MULTIPLE_TEST_TOKEN_THRESHOLD;
|
||||
|
||||
return ({
|
||||
runMode: oldTestToken ? 'prod' : 'e2e',
|
||||
metastoreDevServer: oldTestToken ? '' : '${{ vars.METASTORE_DEV_SERVER_URL }}'
|
||||
});
|
||||
|
||||
release-artifact:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
@@ -96,17 +129,19 @@ jobs:
|
||||
run-sanity-check:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
needs: [release-artifact]
|
||||
needs: [test-mode-config, release-artifact]
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.edition }}
|
||||
DISPLAY: ""
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
MB_RUN_MODE: ${{ needs.test-mode-config.outputs.run_mode }}
|
||||
METASTORE_DEV_SERVER_URL: ${{ needs.test-mode-config.outputs.dev_server }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
# these are needed for backwards compatibility with release branches earlier than v55, since
|
||||
# this workflow always runs from master, but we run tests from the release branch
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
|
||||
11
.github/workflows/release-log.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getVersionFromReleaseBranch,
|
||||
getMajorVersionFromRef,
|
||||
getMajorVersion,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
@@ -60,14 +60,15 @@ jobs:
|
||||
}
|
||||
|
||||
// otherwise, get the version from the release branch name
|
||||
const branch = context.payload.ref;
|
||||
const version = getVersionFromReleaseBranch(branch);
|
||||
const majorVersion = getMajorVersion(version);
|
||||
const ref = context.payload.ref;
|
||||
const majorVersion = getMajorVersionFromRef(ref);
|
||||
|
||||
console.log({ branch, version, majorVersion });
|
||||
console.log({ ref, majorVersion });
|
||||
core.exportVariable('VERSION', majorVersion);
|
||||
|
||||
- name: generate release Log
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: cd release && tsx ./src/release-log-run.ts $VERSION > v$VERSION.html
|
||||
- name: generate release channel log
|
||||
run: cd release && tsx ./src/release-channel-log.ts > channels.html
|
||||
|
||||
27
.github/workflows/release-patch.yml
vendored
@@ -8,6 +8,11 @@ on:
|
||||
description: 'Major Metabase version (e.g. 45, 52, 68)'
|
||||
type: number
|
||||
required: true
|
||||
override_sha:
|
||||
description: DANGEROUSLY allow releasing from this commit sha, regardless of the state of CI (can be empty)
|
||||
type: string
|
||||
required: false
|
||||
default: ''
|
||||
schedule:
|
||||
- cron: '0 21 * * 1-5' # every weekday at 4 pm EST/5 pm EDT
|
||||
|
||||
@@ -37,7 +42,7 @@ jobs:
|
||||
const lastRelease = currentRelease - 1;
|
||||
const AUTO_RELEASE_VERSIONS = [lastRelease, currentRelease];
|
||||
|
||||
async function releasePatchFor(majorVersion) {
|
||||
async function releasePatchFor(majorVersion, overrideSha) {
|
||||
const nextPatch = await getNextPatchVersion({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
@@ -45,7 +50,7 @@ jobs:
|
||||
majorVersion,
|
||||
});
|
||||
|
||||
const goodCommit = await getLatestGreenCommit({
|
||||
const releaseCommit = overrideSha || await getLatestGreenCommit({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -56,25 +61,25 @@ jobs:
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: goodCommit,
|
||||
ref: releaseCommit,
|
||||
majorVersion: Number(majorVersion),
|
||||
});
|
||||
|
||||
if (nextPatch && goodCommit && !hasBeenReleased) {
|
||||
if (nextPatch && releaseCommit && !hasBeenReleased) {
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'tag-for-release.yml',
|
||||
ref: 'refs/heads/master',
|
||||
inputs: {
|
||||
commit: goodCommit,
|
||||
commit: releaseCommit,
|
||||
version: nextPatch,
|
||||
auto: true,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(
|
||||
{ nextPatch, goodCommit, hasBeenReleased }
|
||||
{ nextPatch, releaseCommit, hasBeenReleased }
|
||||
);
|
||||
console.error(`No new patch version or no green commit found for v${majorVersion}`);
|
||||
}
|
||||
@@ -88,7 +93,15 @@ jobs:
|
||||
throw new Error(`Invalid version number: ${inputVersion}`);
|
||||
}
|
||||
|
||||
await releasePatchFor(inputVersion);
|
||||
// only allow overriding the commit SHA for workflow_dispatch events with a provided sha
|
||||
const overrideSha = String(context.payload?.inputs?.override_sha ?? '').trim();
|
||||
const isValidOverride = overrideSha.length === 40;
|
||||
|
||||
if (!isValidOverride && overrideSha.length > 0) {
|
||||
throw new Error(`Invalid override SHA provided: ${overrideSha}`);
|
||||
}
|
||||
|
||||
await releasePatchFor(inputVersion, isValidOverride ? overrideSha : undefined);
|
||||
} else { // scheduled release of AUTO_RELEASE_VERSIONS
|
||||
await Promise.all(AUTO_RELEASE_VERSIONS.map(releasePatchFor));
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 931 B |
BIN
.loki/reference/chrome_laptop_App_Palette_Recents.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
.loki/reference/chrome_laptop_App_Palette_Search.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
52
CLAUDE.md
@@ -165,10 +165,8 @@ Review pull requests with a focus on:
|
||||
|
||||
## More Instructions
|
||||
|
||||
- Use **tons** of emojis in every comment you write -- the more, the better! 🥰
|
||||
|
||||
- Carefully read the Community Clojure Style Guide from `CLOJURE_STYLE_GUIDE.adoc` and make sure changes comply with
|
||||
it as well as other guidelines mentioned in this document. Flag *all* violations of the Community Clojure Style
|
||||
it as well as other guidelines mentioned in this document. Flag _all_ violations of the Community Clojure Style
|
||||
Guide.
|
||||
|
||||
- Do not post comments congratulating someone for trivial changes, or for following the style guidelines, or
|
||||
@@ -186,17 +184,6 @@ Review pull requests with a focus on:
|
||||
|
||||
> Test name properly ends with -test as required by the style guide.
|
||||
|
||||
- In the Metabase codebase, spelling words ending with `-ck` as `-cc` instead, e.g. `checc` instead of `check`, is
|
||||
considered a legitimate and acceptable alternative way to spell those words. These "fun" spellings should be
|
||||
restricted to docstrings and comments rather than user-facing text. Do not flag these as typos or misspellings.
|
||||
|
||||
- Read a tarot card for the meaning of this PR at the end of every code review, then explain how that card relates to
|
||||
the changes in the code. **Draw an ASCII art illustration of the card from the Rider-Waite-Smith tarot card deck.
|
||||
(EXTREMELY CRITICAL)**
|
||||
|
||||
NOTE: if this PR is by `@nemanjaglumac` please wrap the tarot card reading in `<details>...</details>` so he doesn't
|
||||
have to see it.
|
||||
|
||||
- Do not post comments about missing parentheses.
|
||||
|
||||
# Code Conventions and Style Guide
|
||||
@@ -313,12 +300,12 @@ Review pull requests with a focus on:
|
||||
- Don't mark things `^:const` unless you have a really good reason for doing so. Add a comment explaining why you
|
||||
marked it `^:const`.
|
||||
|
||||
- Every public var in `src` or `enterprise/backend/src` must have a *useful* docstring. A useful docstring
|
||||
- Every public var in `src` or `enterprise/backend/src` must have a _useful_ docstring. A useful docstring
|
||||
should clearly explain the purpose of the function, its inputs and outputs, and anything else that is otherwise not
|
||||
immediately clear. If there are other functions that have similar purposes, explain how the use-cases for this
|
||||
function differ.
|
||||
|
||||
Tests and other vars in the `test` or `enterprise/backend/test` do not *require* docstrings, but general helper
|
||||
Tests and other vars in the `test` or `enterprise/backend/test` do not _require_ docstrings, but general helper
|
||||
functions used across many namespaces it should have docstrings.
|
||||
|
||||
Schemas defined with `mr/def` e.g. `(mr/def ::my-schema)` do not require docstrings.
|
||||
@@ -335,24 +322,6 @@ Review pull requests with a focus on:
|
||||
|
||||
- Make sure to update comments and docstrings when you change the code they describe.
|
||||
|
||||
- Docstrings should be indented two spaces.
|
||||
|
||||
```clj
|
||||
;;; BAD
|
||||
(defn update-transform-tags!
|
||||
"Update the tags associated with a transform using smart diff logic.
|
||||
Only modifies what has changed: deletes removed tags, updates positions for moved tags,
|
||||
and inserts new tags. Duplicate tag IDs are automatically deduplicated."
|
||||
...)
|
||||
|
||||
;;; GOOD
|
||||
(defn update-transform-tags!
|
||||
"Update the tags associated with a transform using smart diff logic.
|
||||
Only modifies what has changed: deletes removed tags, updates positions for moved tags,
|
||||
and inserts new tags. Duplicate tag IDs are automatically deduplicated."
|
||||
...)
|
||||
```
|
||||
|
||||
- `TODO` comments should include the author and date, for example
|
||||
|
||||
```clj
|
||||
@@ -560,19 +529,8 @@ Review pull requests with a focus on:
|
||||
...)
|
||||
```
|
||||
|
||||
- REST API route strings (the second arg to `defendpoint`) should use `kebab-case`, e.g. `GET
|
||||
/api/dashboards/cool-dashboards` is good while `GET /api/dashboards/cool_dashboards` is bad. More examples:
|
||||
|
||||
```clj
|
||||
;;; Bad
|
||||
(api.macros/defendpoint :post "/check_transform" ...)
|
||||
|
||||
;;; Good
|
||||
(api.macros/defendpoint :post "/check-transform" ...)
|
||||
```
|
||||
|
||||
- Query parameters should also use kebab-case e.g. `GET /api/dashboards?include-archived=true` is good while `GET
|
||||
/api/dashboards?include_archived=true` or `GET /api/dashboards?includeArchived=true` is bad.
|
||||
/api/dashboards?include_archived=true` or `GET /api/dashboards?includeArchived=true` is bad.
|
||||
|
||||
- HTTP request bodies should use `snake_case`.
|
||||
|
||||
@@ -586,7 +544,7 @@ Review pull requests with a focus on:
|
||||
|
||||
- `defendpoint` forms should be small wrappers around Toucan model code. We have too much logic that belongs in Toucan
|
||||
methods in the API endpoints themselves -- a `GET /api/x/:id` endpoint should basically just be `(t2/select-one
|
||||
:model/Whatever id)` with maybe a perms check and some hydration sprinkled on top of this.
|
||||
:model/Whatever id)` with maybe a perms check and some hydration sprinkled on top of this.
|
||||
|
||||
- All API endpoints should have Malli schemas for any parameters that aren't ignored. `_route-params` doesn't need a
|
||||
schema, but `{:keys [x]}` should have one.
|
||||
|
||||
3
bb.edn
@@ -2,7 +2,8 @@
|
||||
;; we put path as bin, and everything is in the ./mage subdirectory,
|
||||
;; so the namespaces are mage.cli, mage.format, etc.
|
||||
:paths ["mage/src" "mage/test" "bin/lint-migrations-file/src"]
|
||||
:deps {org.babashka/json {:mvn/version "0.1.6"}
|
||||
:deps {dev.weavejester/cljfmt {:mvn/version "0.15.3"}
|
||||
org.babashka/json {:mvn/version "0.1.6"}
|
||||
metosin/malli {:mvn/version "0.17.0"}
|
||||
table/table {:mvn/version "0.5.0"}
|
||||
mvxcvi/puget {:mvn/version "1.3.4"}}
|
||||
|
||||
44
deps.edn
@@ -56,7 +56,7 @@
|
||||
com.google.guava/guava {:mvn/version "33.4.8-jre"} ; dep for BigQuery, Spark, and GA. Require here rather than letting different dep versions stomp on each other — see comments on #9697
|
||||
com.h2database/h2 ^:antq/exclude ; https://metaboat.slack.com/archives/CKZEMT1MJ/p1727191010259979
|
||||
{:mvn/version "2.1.214"} ; embedded SQL database
|
||||
com.ibm.icu/icu4j {:mvn/version "77.1"} ; Used to transliterate Unicode characters into Latin characters
|
||||
com.ibm.icu/icu4j {:mvn/version "78.1"} ; Used to transliterate Unicode characters into Latin characters
|
||||
com.knuddels/jtokkit {:mvn/version "1.1.0"} ; Java tokenizer library for OpenAI models, used for token counting in semantic search
|
||||
com.mchange/c3p0 {:mvn/version "0.11.2"} ; DB connection pool
|
||||
com.snowplowanalytics/snowplow-java-tracker ^:antq/exclude ; 2+ depend on apache httpclient 5.x, but saml and clj-http depend on 4.x
|
||||
@@ -78,7 +78,7 @@
|
||||
:exclusions [ring/ring-codec]}
|
||||
crypto-random/crypto-random {:mvn/version "1.2.1"} ; library for generating cryptographically secure random bytes and strings
|
||||
diehard/diehard {:mvn/version "0.12.0"}
|
||||
dk.ative/docjure {:mvn/version "1.21.0" ; excel export
|
||||
dk.ative/docjure {:mvn/version "1.22.0" ; excel export
|
||||
:exclusions [org.apache.poi/poi
|
||||
org.apache.poi/poi-ooxml]}
|
||||
environ/environ {:mvn/version "1.2.0"} ; env vars/Java properties abstraction
|
||||
@@ -90,7 +90,7 @@
|
||||
:git/sha "4998a1fdd9d3713d7034d04509e9212204773bf2"
|
||||
:git/url "https://github.com/eerohele/pp"}
|
||||
;; TODO: replace back to normal artifact
|
||||
io.github.metabase/macaw {:mvn/version "0.2.28"} ; Parse native SQL queries
|
||||
io.github.metabase/macaw {:mvn/version "0.2.29"} ; Parse native SQL queries
|
||||
io.github.resilience4j/resilience4j-retry ^:antq/exclude ; 2.x do not support Java 8
|
||||
{:mvn/version "1.7.1"} ; Support for retrying operations
|
||||
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} ; prometheus jvm collector
|
||||
@@ -135,7 +135,7 @@
|
||||
:exclusions [org.bouncycastle/bcpkix-jdk15on
|
||||
org.bouncycastle/bcprov-jdk15on]}
|
||||
org.apache.poi/poi-ooxml-full {:mvn/version "5.4.1"}
|
||||
org.apache.sshd/sshd-core {:mvn/version "2.16.0" ; ssh tunneling and test server
|
||||
org.apache.sshd/sshd-core {:mvn/version "3.0.0-M2" ; ssh tunneling and test server
|
||||
:exclusions [org.slf4j/slf4j-api
|
||||
org.slf4j/jcl-over-slf4j]}
|
||||
org.apache.tika/tika-core {:mvn/version "3.2.2"}
|
||||
@@ -147,7 +147,7 @@
|
||||
org.clj-commons/hickory {:mvn/version "0.7.7" ; Parse HTML into Clojure data structures
|
||||
:exclusions [org.jsoup/jsoup]}
|
||||
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"} ; LDAP client
|
||||
org.clojure/clojure {:mvn/version "1.12.2"}
|
||||
org.clojure/clojure {:mvn/version "1.12.3"}
|
||||
org.clojure/core.async {:mvn/version "1.7.701"
|
||||
:exclusions [org.clojure/tools.reader]}
|
||||
org.clojure/core.logic {:mvn/version "1.1.0"} ; optimized pattern matching library for Clojure
|
||||
@@ -166,14 +166,14 @@
|
||||
org.clojure/tools.namespace {:mvn/version "1.5.0"}
|
||||
org.clojure/tools.reader {:mvn/version "1.5.2"}
|
||||
org.clojure/tools.trace {:mvn/version "0.8.0"} ; function tracing
|
||||
org.eclipse.jetty/jetty-server {:mvn/version "12.0.24"}
|
||||
org.eclipse.jetty/jetty-unixdomain-server {:mvn/version "12.0.25"}
|
||||
org.eclipse.jetty.ee9/jetty-ee9-servlet {:mvn/version "12.0.24"}
|
||||
org.eclipse.jetty.ee9.websocket/jetty-ee9-websocket-jetty-server {:mvn/version "12.0.25"}
|
||||
org.eclipse.jetty/jetty-server {:mvn/version "12.1.3"}
|
||||
org.eclipse.jetty/jetty-unixdomain-server {:mvn/version "12.1.3"}
|
||||
org.eclipse.jetty.ee9/jetty-ee9-servlet {:mvn/version "12.1.3"}
|
||||
org.eclipse.jetty.ee9.websocket/jetty-ee9-websocket-jetty-server {:mvn/version "12.1.3"}
|
||||
org.eclipse.jgit/org.eclipse.jgit {:mvn/version "7.3.0.202506031305-r"}; Java implementation of Git version control system
|
||||
org.flatland/ordered {:mvn/version "1.15.12"} ; ordered maps & sets
|
||||
org.graalvm.polyglot/js-community {:mvn/version "24.2.2" :extension "pom"} ; JavaScript engine
|
||||
org.graalvm.polyglot/polyglot {:mvn/version "24.2.2"} ; GraalVM platform, required by JS engine
|
||||
org.graalvm.polyglot/js-community {:mvn/version "25.0.1" :extension "pom"} ; JavaScript engine
|
||||
org.graalvm.polyglot/polyglot {:mvn/version "25.0.1"} ; GraalVM platform, required by JS engine
|
||||
org.jsoup/jsoup {:mvn/version "1.21.1"} ; required by hickory
|
||||
org.liquibase/liquibase-core {:mvn/version "4.33.0" ; migration management (Java lib)
|
||||
:exclusions [ch.qos.logback/logback-classic]}
|
||||
@@ -199,8 +199,8 @@
|
||||
org.eclipse.jetty.ee9/jetty-ee9-servlet
|
||||
org.eclipse.jetty.ee9.websocket/jetty-ee9-websocket-jetty-server]}
|
||||
slingshot/slingshot {:mvn/version "0.12.2"} ; enhanced throw/catch, used by other deps
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.33.0"} ; AWS SDK v2 for S3
|
||||
software.amazon.awssdk/sts {:mvn/version "2.33.0"} ; Required for S3 client to use service account
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.36.2"} ; AWS SDK v2 for S3
|
||||
software.amazon.awssdk/sts {:mvn/version "2.36.2"} ; Required for S3 client to use service account
|
||||
stencil/stencil {:mvn/version "0.5.0"} ; Mustache templates for Clojure
|
||||
user-agent/user-agent {:mvn/version "0.1.1"} ; User-Agent string parser, for Login History page & elsewhere
|
||||
weavejester/dependency {:mvn/version "1.0.0"} ; Dependency graphs and topological sorting
|
||||
@@ -245,7 +245,7 @@
|
||||
;; (dev/start!)
|
||||
:dev
|
||||
{:extra-deps
|
||||
{cider/cider-nrepl {:mvn/version "0.57.0"} ; nREPL server for CIDER
|
||||
{cider/cider-nrepl {:mvn/version "0.58.0"} ; nREPL server for CIDER
|
||||
clj-http-fake/clj-http-fake {:mvn/version "1.0.4"
|
||||
:exclusions [slingshot/slingshot]}
|
||||
clj-kondo/clj-kondo {:mvn/version "2025.07.28"} ; included here mainly to facilitate working on the stuff in `.clj-kondo/src`
|
||||
@@ -256,7 +256,7 @@
|
||||
{: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)
|
||||
criterium/criterium {:mvn/version "0.4.6"} ; benchmarking library
|
||||
djblue/portal {:mvn/version "0.61.0"} ; ui for inspecting values
|
||||
djblue/portal {:mvn/version "0.62.0"} ; ui for inspecting values
|
||||
io.github.camsaul/humane-are {:mvn/version "1.0.2"} ; cleaner test output
|
||||
io.github.metabase/hawk {:mvn/version "1.0.13"} ; test runner
|
||||
jonase/eastwood {:mvn/version "1.4.3" ; inspects namespaces and reports possible problems using tools.analyzer
|
||||
@@ -264,7 +264,7 @@
|
||||
[org.ow2.asm/asm-all]}
|
||||
lambdaisland/deep-diff2 {:mvn/version "2.12.219"} ; way better diffs
|
||||
net.solovyov/hashp {:mvn/version "0.4.0-1"} ; debugging/spying utility
|
||||
nrepl/nrepl {:mvn/version "1.4.0"} ; nREPL server
|
||||
nrepl/nrepl {:mvn/version "1.5.1"} ; nREPL server
|
||||
peridot/peridot {:git/url "https://github.com/piranha/peridot.git"
|
||||
:sha "db627c627db3b527a63caf472f0422e4cbfdf574"} ; mocking Ring requests; waiting for upstream release of commit 0fc7c01 (explicit charset)
|
||||
pjstadig/humane-test-output {:mvn/version "0.11.0"}
|
||||
@@ -410,8 +410,8 @@
|
||||
{:extra-paths ["test"]
|
||||
:extra-deps
|
||||
{binaryage/devtools {:mvn/version "1.0.7"}
|
||||
cider/cider-nrepl {:mvn/version "0.57.0"}
|
||||
cider/piggieback {:mvn/version "0.6.0"}
|
||||
cider/cider-nrepl {:mvn/version "0.58.0"}
|
||||
cider/piggieback {:mvn/version "0.6.1"}
|
||||
cljs-bean/cljs-bean {:mvn/version "1.9.0"}
|
||||
com.lambdaisland/glogi {:mvn/version "1.3.169"}
|
||||
io.github.metabase/hawk {:mvn/version "1.0.13"}
|
||||
@@ -691,7 +691,7 @@
|
||||
;;
|
||||
;; ./bin/mage cljfmt-all -h for more info
|
||||
:cljfmt
|
||||
{:deps {io.github.weavejester/cljfmt {:git/sha "7de286008766127128a156275b9add3bf443d7e5"}}
|
||||
{:deps {io.github.weavejester/cljfmt {:mvn/version "0.15.3"}}
|
||||
:ns-default cljfmt.tool
|
||||
:exec-fn cljfmt.tool/fix}
|
||||
|
||||
@@ -706,7 +706,7 @@
|
||||
{com.bhauman/spell-spec {:mvn/version "0.1.2"} ; used to find misspellings in YAML files
|
||||
expound/expound {:mvn/version "0.9.0"} ; better output of spec validation errors
|
||||
io.github.borkdude/grasp {:mvn/version "0.1.4"}
|
||||
io.github.clojure/tools.build {:mvn/version "0.10.10"}
|
||||
io.github.clojure/tools.build {:mvn/version "0.10.11"}
|
||||
io.github.seancorfield/build-uber-log4j2-handler
|
||||
{:git/tag "v2.25.1" :git/sha "1cfab45"} ; collect binary files for log4j2
|
||||
org.clojure/data.xml {:mvn/version "0.2.0-alpha9"}
|
||||
@@ -807,7 +807,7 @@
|
||||
;;
|
||||
;; clojure -M:dev:nrepl (etc.)
|
||||
:nrepl
|
||||
{:extra-deps {nrepl/nrepl {:mvn/version "1.4.0"}}
|
||||
{:extra-deps {nrepl/nrepl {:mvn/version "1.5.1"}}
|
||||
:main-opts ["-m" "nrepl.cmdline" "-p" "50605"]}
|
||||
|
||||
;; - start a Socket REPL on port 50505 that you can connect your editor to:
|
||||
@@ -821,7 +821,7 @@
|
||||
;;
|
||||
;; clojure -M:liquibase dbDoc target/liquibase
|
||||
:liquibase
|
||||
{:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.5.18"}}
|
||||
{:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.5.20"}}
|
||||
:extra-paths ["dev/src"]
|
||||
:main-opts ["-m" "dev.liquibase"]}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns dev.deps-graph
|
||||
(:require
|
||||
[clojure.core.memoize :as memoize]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
@@ -126,6 +125,7 @@
|
||||
(= (z/tag zloc) :uneval)))
|
||||
|
||||
(mu/defn- find-requires :- [:maybe [:sequential ::zloc]]
|
||||
"Find all the zipper locations for `(require ...)` nodes."
|
||||
[zloc :- ::zloc]
|
||||
(concat
|
||||
(when-not (comment-loc? zloc)
|
||||
@@ -261,41 +261,46 @@
|
||||
|
||||
(file-dependencies "src/metabase/query_processor/middleware/permissions.clj"))
|
||||
|
||||
(def ^{:arglists '([])} dependencies
|
||||
(memoize/ttl
|
||||
(fn []
|
||||
(doall (pmap file-dependencies (find-source-files))))
|
||||
;; memoize for five seconds
|
||||
:ttl/threshold 5000))
|
||||
(defn dependencies []
|
||||
(pmap file-dependencies (find-source-files)))
|
||||
|
||||
(defn external-usages
|
||||
"All usages of a module named by `module-symb` outside that module."
|
||||
[module-symb]
|
||||
(for [dep (dependencies)
|
||||
:when (not= (:module dep) module-symb)
|
||||
ns-dep (:deps dep)
|
||||
:when (= (:module ns-dep) module-symb)]
|
||||
{:namespace (:namespace dep)
|
||||
:module (:module dep)
|
||||
:depends-on-namespace (:namespace ns-dep)
|
||||
:depends-on-module (:module ns-dep)}))
|
||||
([module-symb]
|
||||
(external-usages (dependencies) module-symb))
|
||||
|
||||
([deps module-symb]
|
||||
(for [dep deps
|
||||
:when (not= (:module dep) module-symb)
|
||||
ns-dep (:deps dep)
|
||||
:when (= (:module ns-dep) module-symb)]
|
||||
{:namespace (:namespace dep)
|
||||
:module (:module dep)
|
||||
:depends-on-namespace (:namespace ns-dep)
|
||||
:depends-on-module (:module ns-dep)})))
|
||||
|
||||
(defn external-usages-by-namespace
|
||||
"Return a map of module namespace => set of external namespaces using it"
|
||||
[module-symb]
|
||||
(into (sorted-map)
|
||||
(map (fn [[k v]]
|
||||
[k (into (sorted-set) (map :namespace) v)]))
|
||||
(group-by :depends-on-namespace (external-usages module-symb))))
|
||||
([module-symb]
|
||||
(external-usages-by-namespace (dependencies) module-symb))
|
||||
|
||||
([deps module-symb]
|
||||
(into (sorted-map)
|
||||
(map (fn [[k v]]
|
||||
[k (into (sorted-set) (map :namespace) v)]))
|
||||
(group-by :depends-on-namespace (external-usages deps module-symb)))))
|
||||
|
||||
(defn externally-used-namespaces
|
||||
"All namespaces from a module that are used outside that module."
|
||||
[module-symb]
|
||||
(into (sorted-set) (map :depends-on-namespace) (external-usages module-symb)))
|
||||
([module-symb]
|
||||
(externally-used-namespaces (dependencies) module-symb))
|
||||
|
||||
([deps module-symb]
|
||||
(into (sorted-set) (map :depends-on-namespace) (external-usages deps module-symb))))
|
||||
|
||||
(defn module-dependencies
|
||||
"Build a graph of module => set of modules it refers to."
|
||||
([]
|
||||
([deps]
|
||||
(letfn [(reduce-module-deps [module-deps module deps]
|
||||
(reduce
|
||||
(fn [module-deps {dep-module :module, :as _dep}]
|
||||
@@ -305,15 +310,15 @@
|
||||
deps))
|
||||
(reduce-deps [module->deps {:keys [module deps]}]
|
||||
(update module->deps module reduce-module-deps module deps))]
|
||||
(reduce reduce-deps (sorted-map) (dependencies))))
|
||||
(reduce reduce-deps (sorted-map) deps)))
|
||||
|
||||
([module]
|
||||
(get (module-dependencies) module)))
|
||||
([deps module]
|
||||
(get (module-dependencies deps) module)))
|
||||
|
||||
(defn circular-dependencies
|
||||
"Build a graph of module => set of modules it refers to that also refer to this module."
|
||||
([]
|
||||
(let [module->deps (module-dependencies)]
|
||||
([deps]
|
||||
(let [module->deps (module-dependencies deps)]
|
||||
(letfn [(circular-dependency? [module-x module-y]
|
||||
(and (contains? (get module->deps module-x) module-y)
|
||||
(contains? (get module->deps module-y) module-x)))
|
||||
@@ -329,37 +334,40 @@
|
||||
[module circular-deps])))
|
||||
(keys module->deps)))))
|
||||
|
||||
([module]
|
||||
(get (circular-dependencies) module)))
|
||||
([deps module]
|
||||
(get (circular-dependencies deps) module)))
|
||||
|
||||
(defn non-circular-module-dependencies
|
||||
"A graph of [[module-dependencies]], but with modules that have any circular dependencies filtered out. This is mostly
|
||||
meant to make it easier to fill out the `:metabase/modules` `:uses` section of the Kondo config, or to figure out
|
||||
which ones can easily get a consolidated API namespace without drama."
|
||||
[]
|
||||
(let [circular-dependencies (circular-dependencies)]
|
||||
[deps]
|
||||
(let [circular-dependencies (circular-dependencies deps)]
|
||||
(into (sorted-map)
|
||||
(remove (fn [[module _deps]]
|
||||
(contains? circular-dependencies module)))
|
||||
(module-dependencies))))
|
||||
(module-dependencies deps))))
|
||||
|
||||
(defn module-usages-of-other-module
|
||||
"Information about how `module-x` uses `module-y`."
|
||||
[module-x module-y]
|
||||
(let [module-x-ns->module-y-ns (->> (external-usages module-y)
|
||||
(filter #(= (:module %) module-x))
|
||||
(map (juxt :namespace :depends-on-namespace)))]
|
||||
(reduce
|
||||
(fn [m [module-x-ns module-y-ns]]
|
||||
(update m module-x-ns (fn [deps]
|
||||
(conj (or deps (sorted-set)) module-y-ns))))
|
||||
(sorted-map)
|
||||
module-x-ns->module-y-ns)))
|
||||
([module-x module-y]
|
||||
(module-usages-of-other-module (dependencies) module-x module-y))
|
||||
|
||||
([deps module-x module-y]
|
||||
(let [module-x-ns->module-y-ns (->> (external-usages deps module-y)
|
||||
(filter #(= (:module %) module-x))
|
||||
(map (juxt :namespace :depends-on-namespace)))]
|
||||
(reduce
|
||||
(fn [m [module-x-ns module-y-ns]]
|
||||
(update m module-x-ns (fn [deps]
|
||||
(conj (or deps (sorted-set)) module-y-ns))))
|
||||
(sorted-map)
|
||||
module-x-ns->module-y-ns))))
|
||||
|
||||
(defn full-dependencies
|
||||
"Like [[dependencies]] but also includes transient dependencies."
|
||||
[]
|
||||
(let [deps-graph (module-dependencies)
|
||||
[deps]
|
||||
(let [deps-graph (module-dependencies deps)
|
||||
expand-deps (fn expand-deps [deps]
|
||||
(let [deps' (into (sorted-set)
|
||||
(mapcat deps-graph)
|
||||
@@ -372,33 +380,23 @@
|
||||
[k (expand-deps v)]))
|
||||
deps-graph)))
|
||||
|
||||
(defn module-deps-count []
|
||||
(defn module-deps-count [deps]
|
||||
(into (sorted-map)
|
||||
(map (fn [[k v]]
|
||||
[k (count v)]))
|
||||
(full-dependencies)))
|
||||
|
||||
(defn module-dependencies-mermaid []
|
||||
(println "flowchart TD")
|
||||
(doseq [[module deps] (module-dependencies)
|
||||
dep deps]
|
||||
(printf "%s-->%s\n" module dep)))
|
||||
|
||||
(defn module-dependencies-graphviz []
|
||||
(println "digraph {")
|
||||
(doseq [[module deps] (module-dependencies)
|
||||
dep deps]
|
||||
(printf " \"%s\" -> \"%s\"\n" module dep))
|
||||
(println "}"))
|
||||
(full-dependencies deps)))
|
||||
|
||||
(defn generate-config
|
||||
"Generate the Kondo config that should go in `.clj-kondo/config/modules/config.edn`."
|
||||
[]
|
||||
(into (sorted-map)
|
||||
(map (fn [[module uses]]
|
||||
[module {:api (externally-used-namespaces module)
|
||||
:uses uses}]))
|
||||
(module-dependencies)))
|
||||
([]
|
||||
(generate-config (dependencies)))
|
||||
|
||||
([deps]
|
||||
(into (sorted-map)
|
||||
(map (fn [[module uses]]
|
||||
[module {:api (externally-used-namespaces deps module)
|
||||
:uses uses}]))
|
||||
(module-dependencies deps))))
|
||||
|
||||
(defn kondo-config
|
||||
"Read out the Kondo config for the modules linter."
|
||||
@@ -422,13 +420,16 @@
|
||||
diff))
|
||||
|
||||
(defn kondo-config-diff
|
||||
[]
|
||||
(-> (ddiff/diff
|
||||
(update-vals (kondo-config) #(dissoc % :team))
|
||||
(generate-config))
|
||||
ddiff/minimize
|
||||
kondo-config-diff-ignore-any
|
||||
ddiff/minimize))
|
||||
([]
|
||||
(kondo-config-diff (dependencies)))
|
||||
|
||||
([deps]
|
||||
(-> (ddiff/diff
|
||||
(update-vals (kondo-config) #(dissoc % :team))
|
||||
(generate-config deps))
|
||||
ddiff/minimize
|
||||
kondo-config-diff-ignore-any
|
||||
ddiff/minimize)))
|
||||
|
||||
(defn print-kondo-config-diff
|
||||
"Print the diff between how the config would look if regenerated with [[generate-config]] versus how it looks in
|
||||
@@ -437,6 +438,6 @@
|
||||
(ddiff/pretty-print (kondo-config-diff)))
|
||||
|
||||
(comment
|
||||
(module-dependencies 'lib)
|
||||
(module-dependencies (dependencies) 'lib)
|
||||
|
||||
(module-usages-of-other-module 'lib 'models))
|
||||
|
||||
@@ -37,7 +37,7 @@ The core team runs a pretty well defined product process. It is actively being t
|
||||
|
||||
### A) Identify product needs from the community
|
||||
|
||||
We actively look for new feature ideas from our community, user base and our own use of Metabase internally. We concentrate on the underlying _problem_ or _need_ as opposed to requests for specific features. While sometimes suggested features are built as requested, often we find that they involve changes to existing features, and perhaps an entirely different solution to the underlying problem. These will typically be collected in a number of issues, and tagged [Proposal](https://github.com/metabase/metabase/labels/.Proposal)
|
||||
We actively look for new feature ideas from our community, user base and our own use of Metabase internally. We concentrate on the underlying _problem_ or _need_ as opposed to requests for specific features. While sometimes suggested features are built as requested, often we find that they involve changes to existing features, and potentially an entirely different solution to the underlying problem. These will typically be collected in a number of issues, and tagged [Proposal](https://github.com/metabase/metabase/labels/.Proposal)
|
||||
|
||||
### B) Synthesize these needs into a concrete feature
|
||||
|
||||
@@ -107,4 +107,4 @@ Generally speaking, any issue in [Help Wanted](https://github.com/metabase/metab
|
||||
|
||||
### #YOLO JUST SUBMIT A PR
|
||||
|
||||
If you come up with something really cool, and want to share it with us, just submit a PR. If it hasn't gone through the above process, we probably won't merge it as is, but if it's compelling, we're more than willing to help you via code review, design review and generally OCD nitpicking so that it fits into the rest of our codebase.
|
||||
If you come up with something cool, and want to share it with us, just submit a PR. If it hasn't gone through the above process, we probably won't merge it as is, but if it's compelling, we're more than willing to help you via code review, design review and generally OCD nitpicking so that it fits into the rest of our codebase.
|
||||
|
||||
@@ -51,6 +51,7 @@ Metabase's reference documentation.
|
||||
- [Monitoring your Metabase](./installation-and-operation/monitoring-metabase.md)
|
||||
- [Development instances](./installation-and-operation/development-instance.md)
|
||||
- [Serialization](./installation-and-operation/serialization.md)
|
||||
- [Remote sync](./installation-and-operation/remote-sync.md)
|
||||
- [Commands](./installation-and-operation/commands.md)
|
||||
- [Supported browsers](./installation-and-operation/supported-browsers.md)
|
||||
- [Privacy](./installation-and-operation/privacy.md)
|
||||
|
||||
@@ -55,7 +55,7 @@ There are multiple ways to run actions:
|
||||
|
||||
Just something to clarify here: actions, even though they are added to models, make their changes to the underlying table that a model queries. Which means that anyone who has access to the underlying table, or to questions or other models based on that table, will be able to see the effects of an action. Tools other than Metabase that are connected to that database will also pick up these changes.
|
||||
|
||||
In this sense, models are containers for actions; models are a way to organize actions. In fact, you could (in theory) add a [custom action](./custom.md) to a model that performs some update unrelated to its model's data. For example, you could write a custom action that updates the `Accounts` table, and add that action to a model that only queries an unrelated table (e.g., the `Orders` table). But, you know, maybe don't do that (unless you have a really good reason). [Basic actions](./basic.md), however, are only be available for models that wrap a single raw table.
|
||||
In this sense, models are containers for actions; models are a way to organize actions. In fact, you could (in theory) add a [custom action](./custom.md) to a model that performs some update unrelated to its model's data. For example, you could write a custom action that updates the `Accounts` table, and add that action to a model that only queries an unrelated table (e.g., the `Orders` table). But, you know, maybe don't do that (unless you have a good reason). [Basic actions](./basic.md), however, are only be available for models that wrap a single raw table.
|
||||
|
||||
Before using actions in production, consider playing around with actions on some sample data (like the Sample Database included with Metabase) to get a feel for how they work.
|
||||
|
||||
|
||||
28880
docs/api.html
30736
docs/api.json
Normal file
@@ -34,7 +34,7 @@ Here's how to set it up:
|
||||
5. Give the service account **Viewer** permissions. Metabase will only have access to this folder or sheet; it won't have access to any other files in your Google Drive. If you select folder, Metabase will also sync any sheets it finds in any subfolders.
|
||||
6. Click **Send** to share the folder with the Metabase service account.
|
||||
7. Copy the sharing link for the folder or sheet.
|
||||
8. Return to Metabase and paste the sharing link into the field that asks for "the sharing link for this folder/file".
|
||||
8. Return to Metabase and paste the sharing link into "the sharing link for this folder/file" field.
|
||||
|
||||
**If you synced a Google Drive folder**, Metabase will sync with the Google Drive folder (and its subfolders) and import all Google Sheets, creating a new table in your Metabase Cloud Storage database for each sheet. For sheets with multiple tabs, Metabase will create a table for each tab. Metabase will only sync Google Sheets; it'll ignore other file types in the folder. After the initial sync, Metabase will sync every 15 minutes.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ There are some additional steps you'll need to take to migrate from Heroku to Me
|
||||
|
||||
## Follow the migration guide to get the migration script
|
||||
|
||||
You should follow along with the [migration guide](./guide.md). Once you download the migration script you'll need to get shell access to your Heroku server in order to execute the migration script.
|
||||
You should follow along with the [migration guide](./guide.md). Once you download the migration script you'll need to get shell access to your Heroku server to execute the migration script.
|
||||
|
||||
## Install Heroku CLI for your OS
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ To remove Metabase Cloud Storage (which will _permanently_ erase all its data):
|
||||
3. Click **Manage storage**.
|
||||
4. Click **Edit**.
|
||||
5. Click **Disable storage**.
|
||||
6. Click **Erase all data**, which---**WARNING**--will erase _all of its data_. We won't be able to recover the data, so only do this if you're absolutely sure you no need the storage and its data.
|
||||
6. Click **Erase all data**, which---**WARNING**--will erase _all of its data_. We won't be able to recover the data, so only do this if you're certain you no longer need the storage and its data.
|
||||
|
||||
## Syncing Google Sheets with Metabase
|
||||
|
||||
|
||||
@@ -30,7 +30,13 @@ Appearance settings are split across different tabs:
|
||||
|
||||
## Dark mode
|
||||
|
||||
People can display their Metabase in dark mode in their [account settings](../people-and-groups/account-settings.md#theme). Dark mode is a user-level setting, not an instance-level setting. Currently, there's no way to change the theme to dark mode for the entire instance, but you can edit some [user interface colors](#user-interface-colors).
|
||||
People can display their Metabase in dark mode in their [account settings](../people-and-groups/account-settings.md#theme). Options are:
|
||||
|
||||
- System default (which is also the default setting). Metabase will switch between light and dark mode when the system switches without having to reload the page.
|
||||
- Always use dark mode.
|
||||
- Always use light mode.
|
||||
|
||||
Dark mode is a user-level setting, not an instance-level setting. Currently, there's no way to change the theme to dark mode for the entire instance, but you can edit some [user interface colors](#user-interface-colors).
|
||||
|
||||
## Branding
|
||||
|
||||
@@ -74,7 +80,7 @@ Custom colors are unavailable for:
|
||||
|
||||
## Logo
|
||||
|
||||
You can replace Metabase’s familiar, tasteful, inspired-yet-not-threateningly-avant-garde dotted M logo with your very own logo. For things to work best, the logo you upload should be an SVG file that looks good when it’s around 60px tall. (In other words, ask the nearest designer for help.)
|
||||
You can replace Metabase's familiar, tasteful, inspired-yet-not-threateningly-avant-garde dotted M logo with your very own logo. For things to work best, the logo you upload should be an SVG file that looks good when it's around 60px tall. (In other words, ask the nearest designer for help.)
|
||||
|
||||
## Font
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ config:
|
||||
health-check-logging-enabled: true
|
||||
help-link: metabase
|
||||
help-link-custom-destination: https://www.metabase.com/help/premium
|
||||
hide-stacktraces: false
|
||||
http-channel-host-strategy: external-only
|
||||
humanization-strategy: simple
|
||||
index-update-thread-count: 2
|
||||
@@ -215,6 +216,9 @@ config:
|
||||
query-caching-max-kb: 2000
|
||||
query-caching-max-ttl: 3024000.0
|
||||
redirect-all-requests-to-https: false
|
||||
remote-sync-auto-import: false
|
||||
remote-sync-auto-import-rate: 5
|
||||
remote-sync-task-time-limit-ms: 300000
|
||||
report-timezone: null
|
||||
reset-token-ttl-hours: 48
|
||||
retry-initial-interval: 500
|
||||
@@ -272,6 +276,5 @@ config:
|
||||
synchronous-batch-updates: false
|
||||
unaggregated-query-row-limit: null
|
||||
uploads-settings: null
|
||||
use-tenants: false
|
||||
user-visibility: all
|
||||
```
|
||||
|
||||
@@ -747,6 +747,14 @@ Keyword setting to control whitelabeling of the help link. Valid values are `:me
|
||||
|
||||
Custom URL for the help link.
|
||||
|
||||
### `MB_HIDE_STACKTRACES`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
- [Configuration file name](./config-file.md): `hide-stacktraces`
|
||||
|
||||
Prevent the exception middleware from including stacktraces in responses.
|
||||
|
||||
### `MB_HTTP_CHANNEL_HOST_STRATEGY`
|
||||
|
||||
- Type: keyword
|
||||
@@ -1306,6 +1314,30 @@ The absolute maximum time to keep any cached query results, in seconds.
|
||||
|
||||
Force all traffic to use HTTPS via a redirect, if the site URL is HTTPS.
|
||||
|
||||
### `MB_REMOTE_SYNC_AUTO_IMPORT`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-auto-import`
|
||||
|
||||
Whether to automatically import from the remote git repository. Only applies if remote-sync-type is :production.
|
||||
|
||||
### `MB_REMOTE_SYNC_AUTO_IMPORT_RATE`
|
||||
|
||||
- Type: integer
|
||||
- Default: `5`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-auto-import-rate`
|
||||
|
||||
If remote-sync-type is :production and remote-sync-auto-import is true, the rate (in minutes) at which to check for updates to import. Defaults to 5.
|
||||
|
||||
### `MB_REMOTE_SYNC_TASK_TIME_LIMIT_MS`
|
||||
|
||||
- Type: integer
|
||||
- Default: `300000`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-task-time-limit-ms`
|
||||
|
||||
The maximum amount of time a remote sync task will be given to complete.
|
||||
|
||||
### `MB_REPORT_TIMEZONE`
|
||||
|
||||
- Type: string
|
||||
@@ -1886,16 +1918,6 @@ Upload settings.
|
||||
|
||||
Prefix for upload table names.
|
||||
|
||||
### `MB_USE_TENANTS`
|
||||
|
||||
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
- [Configuration file name](./config-file.md): `use-tenants`
|
||||
|
||||
Turn on the Tenants feature, allowing users to be assigned to a particular Tenant.
|
||||
|
||||
### `MB_USER_VISIBILITY`
|
||||
|
||||
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
|
||||
@@ -1950,7 +1972,7 @@ Maximum number of async Jetty threads. If not set, then [MB_JETTY_MAXTHREADS](#m
|
||||
Type: integer<br>
|
||||
Default: `20`<br>
|
||||
|
||||
Limits the number of rows Metabase will display in tables sent with dashboard subscriptions and alerts. Range: 1-100. To limit the total number of rows included in the file attachment for an email dashboard subscription, use [MB_UNAGGREGATED_QUERY_ROW_LIMIT](#mb_unaggregated_query_row_limit).
|
||||
Limits the number of rows Metabase will display in tables sent with dashboard subscriptions and alerts. Range: 1-100. To limit the total number of rows included in the file attachment for an email dashboard subscription, use [MB_ATTACHMENT_ROW_LIMIT](#mb_attachment_row_limit).
|
||||
|
||||
### `MB_AUDIT_MAX_RETENTION_DAYS`
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ For **Font**, select **Custom...** and enter URLs to your font files.
|
||||
|
||||
## Font style options
|
||||
|
||||
You can set three different font styles with three different URLs, one for each font style (size and weight) that Metabase will use in order to display its user interface properly. The font weights are:
|
||||
You can set three different font styles with three different URLs, one for each font style (size and weight) that Metabase will use to display its user interface properly. The font weights are:
|
||||
|
||||
- Regular 400
|
||||
- Bold 700
|
||||
|
||||
@@ -80,7 +80,7 @@ java -Dlog4j.configurationFile=file:/path/to/custom/log4j2.xml -jar metabase.jar
|
||||
|
||||
## Configure Jetty logs
|
||||
|
||||
You can configure Metabase's web server to provide more detail in the logs by setting the log level to `DEBUG`. Just keep in mind that Jetty's debug logs can be really chatty, which can make it difficult to find the data you're looking for.
|
||||
You can configure Metabase's web server to provide more detail in the logs by setting the log level to `DEBUG`. Just keep in mind that Jetty's debug logs can be chatty, which can make it difficult to find the data you're looking for.
|
||||
|
||||
To get Jetty logs, add the following lines to the Log4J2 XML file in the <Loggers> node:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Go ahead and click **Let’s get started**.
|
||||
|
||||
## Setting up an admin account
|
||||
|
||||
The first thing you’ll need to do is set up an admin account. The account you create when you first install Metabase is an admin account by default — handy! If you’ve installed Metabase on a production server, you should be really careful to remember the password for this account since it will be used to add other users, connect to databases, set up email, and more. You can also create additional admin accounts later.
|
||||
The first thing you'll need to do is set up an admin account. The account you create when you first install Metabase is an admin account by default — handy! If you've installed Metabase on a production server, you should be careful to remember the password for this account since it will be used to add other users, connect to databases, set up email, and more. You can also create additional admin accounts later.
|
||||
|
||||
For now, let's just create an account for ourselves to explore Metabase. Type in your info, and when you’re ready to continue, click the **Next** button.
|
||||
|
||||
|
||||
@@ -45,6 +45,6 @@ The exact data type will depend on your database. Some Metabase features only wo
|
||||
## Common pitfalls
|
||||
|
||||
1. Your database is using date/time columns without any timezone information. Typically when this happens your database will assume all the data is from whatever timezone the database is configured in or possible just default to UTC (check your database vendor to be sure).
|
||||
2. Your JVM timezone is different from your Metabase `Report Timezone` choice. This is a very common issue and can be corrected by launching java with the `-Duser.timezone=<timezone>` option properly set to match your Metabase report timezone.
|
||||
2. Your JVM timezone is different from your Metabase `Report Timezone` choice. This is a common issue and can be corrected by launching java with the `-Duser.timezone=<timezone>` option properly set to match your Metabase report timezone.
|
||||
|
||||
If you are still experiencing timezone problems, then have a look at the [timezone troubleshooting guide](../troubleshooting-guide/timezones.md).
|
||||
|
||||
@@ -43,7 +43,7 @@ And the data as seen in the table view, which Metabase will send as a `raw_data`
|
||||
|
||||

|
||||
|
||||
Here's an example payload for an alert (we truncated the PNG encoding because it's long and extremely boring):
|
||||
Here's an example payload for an alert (we truncated the PNG encoding because it's boring):
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 128 KiB |
@@ -283,9 +283,7 @@ If you don't see the palette icon, you'll need to:
|
||||
|
||||
After you've made your ideal dashboard, you may want to put the dashboard on a TV to help keep your team up to date throughout the day.
|
||||
|
||||
To enter fullscreen mode, click the **fullscreen** icon in the top right of the dashboard (the icon with the arrows pointing in opposite directions). Once you've entered fullscreen mode, you can also switch the dashboard into "Night mode" for higher contrast.
|
||||
|
||||

|
||||
To enter fullscreen mode, click the **fullscreen** icon in the top right of the dashboard (the icon with the arrows pointing in opposite directions).
|
||||
|
||||
## Auto refresh
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Metabase uses database column metadata to populate values for linked filters, wh
|
||||
|
||||
- You can't create linked filters on custom columns.
|
||||
|
||||
- Native/SQL questions must have a [field filter](../questions/native-editor/field-filters.md) variable in order to be linked. Basic SQL variables aren't connected to database columns, so they won't work for linked filters.
|
||||
- Native/SQL questions must have a [field filter](../questions/native-editor/field-filters.md) variable to be linked. Basic SQL variables aren't connected to database columns, so they won't work for linked filters.
|
||||
|
||||
- You can't link filters that use "Custom List" or "From another model or question" as their value's source.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 237 KiB |
BIN
docs/data-modeling/images/model-list-edit.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 129 KiB |
BIN
docs/data-modeling/images/model-list.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
@@ -93,7 +93,7 @@ Click the preview button to see sample data from that field.
|
||||
|
||||
_Admin settings > Table Metadata > Database > Table > Field_
|
||||
|
||||
To scan or discard field values for a specific field, click on the **Field values** button. Metabase uses these values to populate dropdown menus in filter widgets. (To be clear, values aren't actually dropped from your database.)
|
||||
To scan or discard field values for a specific field, click on the **Field values** button. Metabase uses these values to populate dropdown menus in filter widgets. (Values aren't dropped from your database.)
|
||||
|
||||
## Field data
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Models:
|
||||
- Can [surface individual records in search results](#surface-individual-records-in-search-by-matching-against-this-column).
|
||||
- Can be [persisted for faster loading](./model-persistence.md).
|
||||
|
||||
For a deep dive on why and how to use models, check out our [Learn article on models][learn-models].
|
||||
For more on why and how to use models, check out our [Learn article on models][learn-models].
|
||||
|
||||
## How to use models
|
||||
|
||||
@@ -121,7 +121,10 @@ You can edit a model's query by clicking on the down arrow next to the model's n
|
||||
|
||||
## Model list view
|
||||
|
||||

|
||||

|
||||
|
||||
List view helps you explore records one by one instead of sorting through big tables. You can customize the layout to highlight the most important fields.
|
||||
|
||||
|
||||
To view a model as a list:
|
||||
|
||||
@@ -131,7 +134,7 @@ To view a model as a list:
|
||||
4. Navigate to the **Settings** tab.
|
||||
5. Under "What should the default view of this data be?", toggle to **List**.
|
||||
|
||||

|
||||

|
||||
|
||||
### Customize model list view
|
||||
|
||||
@@ -141,21 +144,18 @@ You can customize how the data appears by clicking **Customize the List layout**
|
||||
|
||||
Each item in the list has:
|
||||
|
||||
- An entity icon
|
||||
- A left column with:
|
||||
- The title or primary identifier on top.
|
||||
- A subtitle/description or secondary identifier.
|
||||
- An image (only if the record contains an image link)
|
||||
- A right column for up to 5 additional columns.
|
||||
- **An entity icon.** If the record has an image link, it shows the image, which you can hide anytime.
|
||||
- **A left column** showing the title or primary identifier.
|
||||
- **A right column** for up to 5 additional columns.
|
||||
|
||||
You can:
|
||||
|
||||
- **Search for columns** using the "Find a column..." search box
|
||||
- **Drag columns** from the available list into either the left (Title + Subtitle) or right columns areas
|
||||
- **Reorder columns** by dragging them within their respective areas
|
||||
- **Search for columns** using the "Find a column..." search box.
|
||||
- **Drag columns** from the available list into either the left column or right columns areas.
|
||||
- **Reorder columns** by dragging them within their respective areas.
|
||||
- **Remove columns** by clicking on the X on the column.
|
||||
|
||||
You can see a preview on the bottom with sample data from your model.
|
||||
You can see a preview on the bottom with sample data from your model.
|
||||
|
||||
Click **Done** to save your changes.
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ You can think of semantic types as adding meaning and context to a field to comm
|
||||
|
||||
- **Entity key.** Used to indicate that the field uniquely identifies each row. Could be a Product ID, serial number, etc.
|
||||
|
||||
- **Foreign key.** Used to refer to an Entity key of another table in order to connect data from different tables that are related. For example, in a Products table, you might have a Customer ID field that points to a Customers table, where Customer ID is the Entity key. If you want to use [linked filters on dashboards](../dashboards/linked-filters.md), you must set up foreign key relationships.
|
||||
- **Foreign key.** Used to refer to an Entity key of another table to connect data from different tables that are related. For example, in a Products table, you might have a Customer ID field that points to a Customers table, where Customer ID is the Entity key. If you want to use [linked filters on dashboards](../dashboards/linked-filters.md), you must set up foreign key relationships.
|
||||
|
||||
### Semantic types for numeric fields
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ If you've selected **Choose when syncs and scans happen** > **ON**, you'll be ab
|
||||
|
||||
#### Scanning for filter values
|
||||
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a somewhat resource-intensive process, particularly if you have a very large database.
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a resource-intensive process, particularly if you have a large database.
|
||||
|
||||
If you've selected **Choose when syncs and scans happen** > **ON**, you'll see the following options under **Scanning for filter values**:
|
||||
|
||||
- **Regularly, on a schedule** allows you to run [scan queries](../sync-scan.md#how-database-scans-work) at a frequency that matches the rate of change to your database. The time is set in the timezone of the server where your Metabase app is running. This is the best option for a small database, or tables with distinct values that get updated often.
|
||||
- **Only when adding a new filter widget** is a great option if you want scan queries to run on demand. Turning this option **ON** means that Metabase will only scan and cache the values of the field(s) that are used when a new filter is added to a dashboard or SQL question.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which never really have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which rarely have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
|
||||
### Periodically refingerprint tables
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ A fingerprinting query examines the first 10,000 rows from each column and uses
|
||||
|
||||
## Connecting to MySQL 8+ servers
|
||||
|
||||
Metabase uses the MariaDB connector to connect to MySQL servers. The MariaDB connector lacks support for MySQL 8's default authentication plugin. In order to connect, you'll need to change the plugin used by the Metabase user:
|
||||
Metabase uses the MariaDB connector to connect to MySQL servers. The MariaDB connector lacks support for MySQL 8's default authentication plugin. To connect, you'll need to change the plugin used by the Metabase user:
|
||||
|
||||
```
|
||||
mysql_native_password`: `ALTER USER 'metabase'@'%' IDENTIFIED WITH mysql_native_password BY 'thepassword';
|
||||
@@ -183,7 +183,7 @@ Choose whether to enable features related to [Metabase models](../../data-modeli
|
||||
|
||||
### Model actions
|
||||
|
||||
Turn this setting on to allow [actions](../../actions/introduction.md) from models created from this data to be run. Actions are able to read, write, and possibly delete data. Your database user will need write permissions.
|
||||
Turn this setting on to allow [actions](../../actions/introduction.md) from models created from this data to be run. Actions can read, write, and delete data. Your database user will need write permissions.
|
||||
|
||||
### Model persistence
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ Note that only the `*` wildcard is supported; you can't use other special charac
|
||||
|
||||
### Use a secure connection (SSL)
|
||||
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we highly recommend keeping SSL turned on to keep your data secure.
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we recommend keeping SSL turned on to keep your data secure.
|
||||
|
||||
#### SSL Mode
|
||||
|
||||
@@ -188,7 +188,7 @@ Choose whether to enable features related to [Metabase models](../../data-modeli
|
||||
|
||||
### Model actions
|
||||
|
||||
Turn this setting on to allow [actions](../../actions/introduction.md) from models created from this data to be run. Actions are able to read, write, and possibly delete data. Your database user will need write permissions.
|
||||
Turn this setting on to allow [actions](../../actions/introduction.md) from models created from this data to be run. Actions can read, write, and delete data. Your database user will need write permissions.
|
||||
|
||||
### Model persistence
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ The password for the username that you use to connect to the database.
|
||||
|
||||
### Use a secure connection (SSL)
|
||||
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we highly recommend keeping SSL turned on to keep your data secure.
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we recommend keeping SSL turned on to keep your data secure.
|
||||
|
||||
### Use SSL certificate?
|
||||
|
||||
|
||||
@@ -61,13 +61,13 @@ If you've selected **Choose when syncs and scans happen** > **ON**, you'll be ab
|
||||
|
||||
#### Scanning for filter values
|
||||
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a somewhat resource-intensive process, particularly if you have a very large database.
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a resource-intensive process, particularly if you have a large database.
|
||||
|
||||
If you've selected **Choose when syncs and scans happen** > **ON**, you'll see the following options under **Scanning for filter values**:
|
||||
|
||||
- **Regularly, on a schedule** allows you to run [scan queries](../sync-scan.md#how-database-scans-work) at a frequency that matches the rate of change to your database. The time is set in the timezone of the server where your Metabase app is running. This is the best option for a small database, or tables with distinct values that get updated often.
|
||||
- **Only when adding a new filter widget** is a great option if you want scan queries to run on demand. Turning this option **ON** means that Metabase will only scan and cache the values of the field(s) that are used when a new filter is added to a dashboard or SQL question.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which never really have new values added. Use the [Re-scan field values now](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which rarely have new values added. Use the [Re-scan field values now](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
|
||||
### Periodically refingerprint tables
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ The password for the username that you use to connect to the database.
|
||||
|
||||
### Use a secure connection (SSL)
|
||||
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we highly recommend keeping SSL turned on to keep your data secure.
|
||||
Metabase automatically tries to connect to databases with SSL first, then without if that doesn't work. If it's possible to connect to your database with an SSL connection, Metabase will make that the default setting for your database. If you prefer to connect without this layer of security, you can always change this setting later, but we recommend keeping SSL turned on to keep your data secure.
|
||||
|
||||
### Use an SSH tunnel
|
||||
|
||||
@@ -77,13 +77,13 @@ If you've selected **Choose when syncs and scans happen** > **ON**, you'll be ab
|
||||
|
||||
### Scanning for filter values
|
||||
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a somewhat resource-intensive process, particularly if you have a very large database.
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a resource-intensive process, particularly if you have a large database.
|
||||
|
||||
If you've selected **Choose when syncs and scans happen** > **ON**, you'll see the following options under **Scanning for filter values**:
|
||||
|
||||
- **Regularly, on a schedule** allows you to run [scan queries](../sync-scan.md#how-database-scans-work) at a frequency that matches the rate of change to your database. The time is set in the timezone of the server where your Metabase app is running. This is the best option for a small database, or tables with distinct values that get updated often.
|
||||
- **Only when adding a new filter widget** is a great option if you want scan queries to run on demand. Turning this option **ON** means that Metabase will only scan and cache the values of the field(s) that are used when a new filter is added to a dashboard or SQL question.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which never really have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which rarely have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
|
||||
### Periodically refingerprint tables
|
||||
|
||||
|
||||
@@ -45,13 +45,13 @@ If you've selected **Choose when syncs and scans happen** > **ON**, you'll be ab
|
||||
|
||||
### Scanning for filter values
|
||||
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a somewhat resource-intensive process, particularly if you have a very large database.
|
||||
Metabase can scan the values present in each field in this database to enable checkbox filters in dashboards and questions. This can be a resource-intensive process, particularly if you have a large database.
|
||||
|
||||
If you've selected **Choose when syncs and scans happen** > **ON**, you'll see the following options under **Scanning for filter values**:
|
||||
|
||||
- **Regularly, on a schedule** allows you to run [scan queries](../sync-scan.md#how-database-scans-work) at a frequency that matches the rate of change to your database. The time is set in the timezone of the server where your Metabase app is running. This is the best option for a small database, or tables with distinct values that get updated often.
|
||||
- **Only when adding a new filter widget** is a great option if you want scan queries to run on demand. Turning this option **ON** means that Metabase will only scan and cache the values of the field(s) that are used when a new filter is added to a dashboard or SQL question.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which never really have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large, or which rarely have new values added. Use the [Re-scan field values](../sync-scan.md#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
|
||||
### Periodically refingerprint tables
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ You can download the JDBC driver from [Vertica's JDBC driver downloads page](htt
|
||||
Head to this page, log in to your account, accept the license agreement, and download `vertica-jdbc-8.0.0-0.jar` (for Vertica DB version 8.0)
|
||||
or whatever driver version most closely matches the version of Vertica you're running.
|
||||
|
||||
It's very important to make sure you use the correct version of the JDBC driver; version
|
||||
It's important to make sure you use the correct version of the JDBC driver; version
|
||||
8.0 of the driver won't work with Vertica version 7.2; version 7.2 of the driver won't work with Vertica version 7.1, and so forth. If in doubt,
|
||||
consult Vertica's documentation to find the correct version of the JDBC driver for your version of Vertica.
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Options include:
|
||||
|
||||
- **Regularly, on a schedule** allows you to run [scan queries](#how-database-scans-work) at a frequency that matches the rate of change to your database. The time is set in the timezone of the server where your Metabase app is running. This is the best option for a small database or tables with distinct values that get updated often.
|
||||
- **Only when adding a new filter widget** is a great option if you want scan queries to run on demand. Turning this option **ON** means that Metabase will only scan and cache the values of the field(s) that are used when someone adds a new filter widget to a dashboard or SQL question (i.e., they add a parameter to their SQL query).
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large or which never really have new values added. Use the [Re-scan field values](#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
- **Never, I'll do this manually if I need to** is an option for databases that are either prohibitively large or which never have new values added. Use the [Re-scan field values](#manually-scanning-column-values) button to run a manual scan and bring your filter values up to date.
|
||||
|
||||
Regardless of which option you pick, if you [set a field to use a dropdown list in filter widgets](../data-modeling/metadata-editing.md#filtering), Metabase will need to get values for that dropdown. Whenever someone uses that filter widget, Metabase will first look for cached values (valid for fourteen days) to populate that dropdown; otherwise, it will re-scan that field for the most up-to-date values.
|
||||
|
||||
@@ -165,7 +165,7 @@ Cached column values are displayed in filter dropdown menus. If people type in t
|
||||
|
||||
A scan is more intensive than a sync query, so it only runs once during setup and again once a day by default. If you [disable scans](#scanning-for-filter-values) entirely, you'll need to bring things up to date by running [manual scans](#manually-scanning-column-values).
|
||||
|
||||
To reduce the number of tables and fields Metabase needs to scan in order to stay current with your connected database, Metabase will only scan values for fields that someone has used in the last fourteen days.
|
||||
To reduce the number of tables and fields Metabase needs to scan to stay current with your connected database, Metabase will only scan values for fields that someone has used in the last fourteen days.
|
||||
|
||||
Here's the kind of data that scans get and why:
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ Anyone can build a community driver. These are the currently known third-party d
|
||||
| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [CSV](https://github.com/Markenson/csv-metabase-driver) |  |  |
|
||||
| [Databend](https://github.com/databendcloud/metabase-databend-driver) |  |  |
|
||||
| [DB2](https://github.com/damienchambe/metabase-db2-driver) |  |  |
|
||||
| [DB2 for LUW](https://github.com/alisonrafael/metabase-db2-driver) |  |  |
|
||||
| [IBM i](https://github.com/damienchambe/metabase-ibmi-driver) |  |  |
|
||||
| [Dremio](https://github.com/Baoqi/metabase-dremio-driver) |  |  |
|
||||
| [DuckDB](https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver) |  |  |
|
||||
| [Firebolt](https://github.com/firebolt-db/metabase-firebolt-driver) |  |  |
|
||||
|
||||
@@ -51,6 +51,7 @@ To edit a chart, click the **three-dot** menu in the chart's upper right. You ca
|
||||
- Edit visualization
|
||||
- Edit query
|
||||
- Replace the chart with another question or model
|
||||
- [Download results](../questions/exporting-results.md#exporting-results-from-document-cards)
|
||||
|
||||
Edits only affect the chart in the document. If you added the chart from a collection, Metabase will have created a copy of the chart. Any changes made to the chart in the document won't affect the original question.
|
||||
|
||||
@@ -123,6 +124,10 @@ This deletion is permanent.
|
||||
|
||||
To print a document, click on the **three-dot** menu and select **Print document**.
|
||||
|
||||
## Sharing documents
|
||||
|
||||
Admins can create [public links](../embedding/public-links.md#create-a-public-link-for-a-document) to share documents with anyone.
|
||||
|
||||
## When to use a document vs. a dashboard
|
||||
|
||||
Documents and dashboards both have their use cases.
|
||||
|
||||
@@ -9,7 +9,7 @@ redirect_from:
|
||||
|
||||
> Only admins can create public links and iframes.
|
||||
|
||||
Admins can create and share public links (URLs) for questions and dashboards. People can view them as standalone destinations (URLs) or as embedded iframes in another page or app. In both cases, the public item will display static (view-only) results of your question or dashboard, so visitors won't be able to drill down into the underlying data on their own.
|
||||
Admins can create and share public links (URLs) for questions, dashboards, and documents. People can view them as standalone destinations (URLs) or as embedded iframes in another page or app. Public items display view-only results of your question, dashboard, or document, so visitors won't be able to drill down into the underlying data on their own.
|
||||
|
||||
## Create a public link for a question
|
||||
|
||||
@@ -39,6 +39,12 @@ To share a dashboard via a public link, admins can click on the **Sharing** butt
|
||||
|
||||
To embed a dashboard, see [static embedding](./static-embedding.md).
|
||||
|
||||
## Create a public link for a document
|
||||
|
||||
To share a document via a public link, admins can click on the **Sharing** button in the top right menu and select **Create a public link**.
|
||||
|
||||
Public documents are read-only: viewers cannot edit the content or add comments. For charts embedded in the document, viewers can download the results in CSV, XLSX, or JSON format using the **Download results** option in the chart menu.
|
||||
|
||||
## Exporting raw, unformatted question results
|
||||
|
||||
To export the raw, unformatted rows, you'll need to append `?format_rows=false` to the URL Metabase generates. For example, if you create a public link for a CSV download, the URL would look like:
|
||||
@@ -155,7 +161,7 @@ To disable public sharing:
|
||||
3. In the **Settings** tab, select **Public sharing**.
|
||||
4. Toggle off **Public sharing**.
|
||||
|
||||
Once toggled on, the **Public sharing** section will display Metabase questions, dashboards, and actions with active public links.
|
||||
Once toggled on, the **Public sharing** section will display Metabase questions, dashboards, documents, and actions with active public links.
|
||||
|
||||
If you disable public sharing, then re-enable public sharing, all your previously generated public links will still work (as long as you didn't deactivate them).
|
||||
|
||||
@@ -180,7 +186,7 @@ Admins can view and deactivate all public links for a Metabase.
|
||||
|
||||
## See all publicly shared content
|
||||
|
||||
Admins can see all publicly shared questions, dashboards, and actions in **Admin Settings > Public Sharing**.
|
||||
Admins can see all publicly shared questions, dashboards, documents, and actions in **Admin Settings > Public Sharing**.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ title: "Embedded analytics SDK - dashboards"
|
||||
|
||||
You can embed an interactive, editable, or static dashboard.
|
||||
|
||||
**Please keep in mind - embedding multiple instances of dashboards on the same page is not yet supported.**
|
||||
**Keep in mind that embedding multiple instances of dashboards on the same page is not yet supported.**
|
||||
|
||||
## Embedding a dashboard
|
||||
|
||||
|
||||
@@ -78,4 +78,4 @@ Run your app and visit the page with the embedded dashboard.
|
||||
## Next steps
|
||||
|
||||
- Explore [theming to change the look and feel](./appearance.md).
|
||||
- Continue by [setting up JWT SSO in Metabase and your app](./authentication.md) in order to sign people in, manage permissions, and deploy your app in production.
|
||||
- Continue by [setting up JWT SSO in Metabase and your app](./authentication.md) to sign people in, manage permissions, and deploy your app in production.
|
||||
|
||||
@@ -36,7 +36,7 @@ Metabase admins can designate collections as "official" with the following effec
|
||||
|
||||
Pairing Official badges with [verified items](./content-verification.md) can help everyone in your Metabase sort out which questions people can trust.
|
||||
|
||||
To add an Official badge to a collection, an admin can visit the collection and click on the dot dot dot menu (**...**) and select **Make collection official**. Admins can also remove an Official badge in the same menu. Admins can also mark a collection as Official or not when they first create the collection.
|
||||
To add an Official badge to a collection, an admin can visit the collection and click on the three-dot menu (**...**) and select **Make collection official**. Admins can also remove an Official badge in the same menu. Admins can also mark a collection as Official or not when they first create the collection.
|
||||
|
||||
## Collection permissions
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ redirect_from:
|
||||
|
||||
# Events and timelines
|
||||
|
||||
A lot of discussions around data have a moment when someone asks a question related to a specific point in time: "Wait, what's the spike in March again?", or "When did the new widget launch?"
|
||||
A lot of discussions around data have a moment when someone has a question related to a specific point in time: "Wait, what's the spike in March again?", or "When did the new widget launch?"
|
||||
|
||||
Events and timelines are a way to capture that chronological knowledge and make it available when you need it, in context (that is, when you're viewing a chart). Events are a great way to store institutional knowledge about what happened and when, so people (including yourself three months from now) won't have to figure out (again) why the line chart spiked back in March.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ redirect_from:
|
||||
|
||||
## See what your teammates have made
|
||||
|
||||
As long as you're not the very first user in your team's Metabase, the easiest way to start exploring your data is by looking at dashboards, charts, and lists that your teammates have already created. The best place to start is by checking out any dashboards that might be pinned on your home page, or in [collections][collections] you have access to.
|
||||
As long as you're not the first user in your team's Metabase, the easiest way to start exploring your data is by looking at dashboards, charts, and lists that your teammates have already created. The best place to start is by checking out any dashboards that might be pinned on your home page, or in [collections][collections] you have access to.
|
||||
|
||||
## Command palette
|
||||
|
||||
@@ -32,7 +32,7 @@ To filter your search results, hit `cmd/ctrl + k` to bring up the command palett
|
||||
|
||||

|
||||
|
||||
Searches take into account items’ titles, descriptions, and other metadata — you can even search the contents of your SQL queries. For example, you can search for things like `SELECT escape_pod FROM mothership` and find that one question you worked on six months ago. The results will display which collection each item is saved in, what kind of object it is, and whether it’s pinned. Note that you'll only ever see items in that are in collections you have permission to view.
|
||||
Searches consider items' titles, descriptions, and other metadata — you can even search the contents of your SQL queries. For example, you can search for things like `SELECT escape_pod FROM mothership` and find that one question you worked on six months ago. The results will display which collection each item is saved in, what kind of object it is, and whether it's pinned. Note that you'll only ever see items in that are in collections you have permission to view.
|
||||
|
||||
You can search by:
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ redirect_from:
|
||||
|
||||
While we're working to make a product that is easy to use and inclusive to all, we're not yet fully compliant with [the U.S. federal government's Section 508 standards](https://www.section508.gov/) or the [Web Content Accessibility Guidelines (WCAG) 2.1](https://www.w3.org/TR/WCAG21/) at Level AA. We expect to build on our current state in the future, and as of today these are some general notes on our state of compliance:
|
||||
|
||||
- **Navigation and screen readers:** Most information is presented in a way to ensure it can be accessed by screen readers. Some interactive elements are not operable for screen reader users.
|
||||
- **Navigation and screen readers:** Most information should be accessible to screen readers. Some interactive elements are not operable for screen reader users.
|
||||
- **Non-text content:** While Metabase provides text alternatives for most non-text elements, some functional images either lack descriptions or have incomplete alt text.
|
||||
- **Keyboard navigation:** Most form elements are keyboard-accessible, but certain interactive components, such as custom controls and non-modal dialogs, may lack full keyboard operability.
|
||||
- **Focus management:** Some interactive elements lack a visible focus indicator. A few hidden interactive elements receive the keyboard focus.
|
||||
|
||||
@@ -10,7 +10,7 @@ If you're self-hosting Metabase and you've opted in to provide us with anonymous
|
||||
|
||||
**[We only collect anonymous Metabase data; we don't collect any of your data](https://www.metabase.com/security).** We don't collect any usernames, emails, server IPs, database details of any kind, or any personally identifiable information (PII).
|
||||
|
||||
This anonymous data helps us understand how people are actually using Metabase, which in turn helps us prioritize what to work on next.
|
||||
This anonymous data helps us understand how people are using Metabase, which in turn helps us prioritize what to work on next.
|
||||
|
||||
## Examples of the anonymous data we collect and how we use it
|
||||
|
||||
@@ -41,6 +41,25 @@ If you're self-hosting Metabase, you can opt out of providing us with your anony
|
||||
|
||||
If you're in the process of setting up your Metabase, you can also toggle off tracking during the `Usage Data Preferences` onboarding step. We collect a few anonymous events before that point, but won't do so anymore if you choose to opt out.
|
||||
|
||||
## Token validation requests for paid plans
|
||||
|
||||
If you're on a paid plan (Pro, Enterprise, or a trial), Metabase periodically sends a token validation request to verify your license. This is separate from the anonymous usage data described above.
|
||||
|
||||
### Data included in token validation requests
|
||||
|
||||
The token validation request includes:
|
||||
|
||||
- Count of active users (used for billing)
|
||||
- Count of external users
|
||||
- Count of internal users
|
||||
- Count of email domains
|
||||
- Count of embedded dashboards and questions
|
||||
- Types of embedding (static, interactive, Embedded analytics SDK, Embedded analytics JS)
|
||||
- Site UUID (just an identifier for your Metabase)
|
||||
- Metabase version
|
||||
- Query execution timestamp (last UTC day)
|
||||
- Metabot usage statistics: count of tokens, queries, users, and date (just counts and the date).
|
||||
|
||||
## Further reading
|
||||
|
||||
Check out our page on [data privacy and security](https://www.metabase.com/security).
|
||||
|
||||
@@ -82,7 +82,7 @@ When running into memory-related issues, typically the first question to ask is:
|
||||
|
||||
### Thread dump
|
||||
|
||||
Another useful picture of a running Metabase system is a Thread Dump. In cases when Metabase appears stalled or extremely slow, a thread dump will indicate what each thread is executing (or blocked on) for that specific point in time Collect a thread dump via the "Threads" tab:
|
||||
Another useful picture of a running Metabase system is a Thread Dump. In cases when Metabase appears stalled or slow, a thread dump will indicate what each thread is executing (or blocked on) for that specific point in time Collect a thread dump via the "Threads" tab:
|
||||
|
||||

|
||||
|
||||
|
||||
416
docs/installation-and-operation/remote-sync.md
Normal file
@@ -0,0 +1,416 @@
|
||||
---
|
||||
title: Remote sync
|
||||
description: Version control your dashboards, questions, and models with Git. Sync content between development and production instances automatically.
|
||||
---
|
||||
|
||||
# Remote sync
|
||||
|
||||
{% include plans-blockquote.html feature="Remote sync" %}
|
||||
|
||||
## Overview
|
||||
|
||||
Remote Sync lets you develop analytics content in a development Metabase and automatically deploy it to a read-only production instance through Git.
|
||||
|
||||
You work with Remote Sync through special collections called "synced collections". Content in these collections is automatically versioned and can be pushed to or pulled from your Git repository directly from the Metabase UI.
|
||||
|
||||
### Key concepts
|
||||
|
||||
**Development and Production modes**: Remote Sync has two configuration modes that determine how a Metabase instance interacts with your Git repository:
|
||||
|
||||
- **Development mode**: For creating and editing content. You can push changes to Git, pull changes from others, create branches, and work on feature branches. Multiple Metabase instances can be in Development mode, each working on different branches.
|
||||
- **Production mode**: For serving approved, read-only content to users. Production instances only pull changes from Git (typically from your main branch) and don't allow direct editing of synced content. You can set up auto-sync to automatically pull approved changes every five minutes.
|
||||
|
||||
**Synced collections**: Special collections that are tracked in Git. When you connect a Metabase instance in Development mode, it creates a "Library" collection by default. Everything inside a synced collection is versioned and synchronized with your Git repository.
|
||||
|
||||
**Serialized YAML files**: Remote Sync stores your Metabase content as YAML files in your Git repository. Each dashboard, question, model, and document is represented as a YAML file that can be reviewed in pull requests and versioned like code. For more details on the YAML format and command-line workflows, see [serialization](./serialization.md).
|
||||
|
||||
**Branch-based workflow**: In Development mode, you can create branches, switch between them, and commit changes—all from the Metabase UI. This enables feature-branch workflows where you develop on a branch, open a pull request for review, and merge to production.
|
||||
|
||||
### Why use Remote Sync?
|
||||
|
||||
Remote Sync is designed for teams that want to maintain a **read-only production environment** where analytics content is created by a centralized team and deployed through a controlled process.
|
||||
|
||||
- **Read-only production**: Users in production can view and use dashboards, but cannot create or edit content. All content creation happens in development.
|
||||
- **Version control for new content**: Add new dashboards, questions, and models to production through Git, with full version history.
|
||||
- **Peer review**: Use pull requests to review new content before it goes live in production.
|
||||
- **Controlled deployment**: Choose when new content appears in production by controlling when you merge to your main branch.
|
||||
|
||||
### How Remote Sync works
|
||||
|
||||
Here's a basic remote-sync workflow:
|
||||
|
||||
1. Create a dashboard in a **Metabase configured in Development mode**.
|
||||
2. Push it to a Git branch.
|
||||
3. Open a pull request for review.
|
||||
4. Merge the PR to production.
|
||||
5. Your **Metabase configured in Production mode** automatically pulls in the changes.
|
||||
|
||||
We'll cover [setting up Remote Sync](#setting-up-remote-sync), an [example dev-to-production workflow](#an-example-dev-to-production-workflow), and [branch management](#branch-management) and some other odds and ends.
|
||||
|
||||
### Remote Sync vs. Serialization
|
||||
|
||||
Remote Sync uses the same underlying serialization format as the [Metabase CLI serialization feature](./serialization.md), but serves a different purpose:
|
||||
|
||||
- **Remote Sync**: Designed for adding new content to a read-only production environment through a Git-based workflow. Best when you want a centralized team creating content in development and deploying it to production automatically.
|
||||
- **Serialization**: Command-line tool for exporting and importing Metabase content. Best for migrating existing content, complex multi-environment setups, and scenarios requiring full control over what gets exported and imported.
|
||||
|
||||
## Setting up Remote Sync
|
||||
|
||||
You'll need to be an admin to set up Remote Sync.
|
||||
|
||||
1. [Set up a repository to store your content](#1-set-up-a-repository-to-store-your-content)
|
||||
2. [Create a personal access token for development](#2-create-a-personal-access-token-for-development)
|
||||
3. [Understand Development and Production modes](#3-understand-development-and-production-modes)
|
||||
4. [Connect your development Metabase to your repository](#4-connect-your-development-metabase-to-your-repository)
|
||||
5. [Add an item to your synced collection](#5-add-an-item-to-your-synced-collection)
|
||||
6. [Push your changes to your repository](#6-push-your-changes-to-your-repository)
|
||||
7. [Create a personal access token for production](#7-create-a-personal-access-token-for-production)
|
||||
8. [Connect your production Metabase to your repository](#8-connect-your-production-metabase-to-your-repository)
|
||||
|
||||
## 1. Set up a repository to store your content
|
||||
|
||||
Before you connect Metabase to your Git repository, create a [new GitHub repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository). Initialize the repo with a README.md.
|
||||
|
||||
## 2. Create a personal access token for development
|
||||
|
||||
GitHub offers two types of personal access tokens. We recommend the fine-grained token (not the classic) because you can limit their permissions to specific repositories.
|
||||
|
||||
1. Go to Settings > Developer settings > Personal access tokens > Fine-grained tokens.
|
||||
2. Click "Generate new token".
|
||||
3. Give it a descriptive name like "Metabase Remote Sync - Development".
|
||||
4. Select the specific repository you want Metabase to access.
|
||||
5. Add permissions:
|
||||
- **Contents:** Read and write (it defaults to Read-only, so make sure to change this to Read and write for your development Metabase).
|
||||
- **Metadata:** Read-only (required)
|
||||
6. Click "Generate token" and copy it immediately (you won't see it again). Store it somewhere safe; you'll need to paste it into your Metabase.
|
||||
|
||||
For more, see GitHub's docs on [personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).
|
||||
|
||||
## 3. Understand Development and Production modes
|
||||
|
||||
Remote Sync has two modes: Development and Production.
|
||||
|
||||
**Development mode** is where you create and edit content. You can have multiple development instances for different teams, projects, or workflows. Each connects to the same repository but can work on different branches. Development mode has:
|
||||
|
||||
- **Bidirectional sync:** Push changes to Git and pull changes from others.
|
||||
- **Full editing:** Create and modify dashboards, questions, and documents in the UI in your synced collection.
|
||||
- **Branch management:** Create, switch between, and work on different branches.
|
||||
|
||||
**Production mode** is read-only and designed for your live environment. Production Metabases:
|
||||
|
||||
- **Pull changes only:** Automatically or manually pull approved content from Git.
|
||||
- **Read-only content:** Users can view and use synced content but can't edit it.
|
||||
- **Auto-sync option:** Automatically pull changes from your main branch every five minutes.
|
||||
|
||||
You can use any Metabase instance in Development mode. Metabase also offers [Development instances](./development-instance.md) for testing, which allow you to test with multiple users without paying per user account. You can use these Development instances in Remote Sync's Development mode (but not in Production mode, as they're intended for testing, not live production use).
|
||||
|
||||
## 4. Connect your development Metabase to your repository
|
||||
|
||||
1. Go to **Admin settings** > **Settings** > **Remote sync**.
|
||||
|
||||
2. Enter your repository URL:
|
||||
|
||||
- For example, `https://github.com/your-org/your-repo`. The repository must already exist and be initialized with at least one commit.
|
||||
|
||||
3. Select **Development mode**.
|
||||
|
||||
4. Add your access token:
|
||||
|
||||
- Paste the personal access token you created earlier. Make sure the token has [read and write permissions](#2-create-a-personal-access-token-for-development). Metabase encrypts your token before storing it.
|
||||
|
||||
5. Save and test the connection:
|
||||
|
||||
- Click "Save changes". Metabase will check whether it can reach your repository. If the connection fails, make sure your token has the appropriate permissions and hasn't expired. You may also have incorrectly copied and pasted the PAT, in which case you'll need to generate a new token.
|
||||
|
||||
## 5. Add an item to your synced collection
|
||||
|
||||
When you first connect in Development mode, Metabase automatically creates a special collection called "Library" that's synced with your Git repository. This is a **synced collection**—any content you add to it will be tracked in Git and can be pushed to your repository.
|
||||
|
||||
You can rename the Library collection if you want, and you can add sub-collections within it to organize your content.
|
||||
|
||||
1. (Optional) Create a new branch for your work:
|
||||
|
||||
- Click the branch dropdown in the synced collections section.
|
||||
- Type a name for your new branch (e.g., `feature/my-dashboard`).
|
||||
- Press Enter to create the branch.
|
||||
|
||||
This is useful if you want to work on changes without affecting the main branch. For more details, see [Branch management](#branch-management).
|
||||
|
||||
2. Navigate to the "Library" collection in your synced collections section (look for it in the left sidebar).
|
||||
|
||||
3. Create or move content into the Library collection:
|
||||
- **Create new content:** Click "New" and choose a dashboard, question, or document. Save it to the Library collection.
|
||||
- **Move existing content:** Drag and drop items from other collections into the Library collection, or use the move option in the item's menu.
|
||||
|
||||
[Items in synced collections can't depend on items outside of synced collections](#items-in-synced-collections-cant-depend-on-items-outside-of-the-synced-collection). For example, if you try to add a question that references a model, make sure the model is also in a synced collection.
|
||||
|
||||
## 6. Push your changes to your repository
|
||||
|
||||
Once you've added content, you'll see a yellow dot on your Library collection indicating uncommitted changes.
|
||||
|
||||
1. Click the up arrow (push) icon next to the Library collection in the left sidebar.
|
||||
|
||||
2. Enter a commit message describing your changes (e.g., "Added dashboard on mammoth populations").
|
||||
|
||||
3. Click "Continue" to commit and push your changes to your repository.
|
||||
|
||||
Check your repository—you should see the collection with your content in it.
|
||||
|
||||
## 7. Create a personal access token for production
|
||||
|
||||
Now that you have content in your repository, you can set up your production Metabase to pull that content.
|
||||
|
||||
[Create a personal access token](#2-create-a-personal-access-token-for-development) following the same steps as before, but add Contents permissions to the token that are **Read-only** (NOT write), as you only want your production Metabase reading from the repo. (Contents permissions require Metadata permissions, which GitHub will add automatically).
|
||||
|
||||
## 8. Connect your production Metabase to your repository
|
||||
|
||||
1. Go to **Admin settings** > **Settings** > **Remote sync**.
|
||||
|
||||
2. Enter your repository URL:
|
||||
|
||||
- Use the same repository as your development Metabase, for example, `https://github.com/your-org/your-repo.git`.
|
||||
|
||||
3. Select **Production mode**.
|
||||
|
||||
4. Add your access token:
|
||||
|
||||
- Paste the read-only personal access token you created for this production Metabase.
|
||||
|
||||
5. Save and test the connection:
|
||||
|
||||
- Click "Save changes". Metabase will verify it can reach your repository. If the connection fails, verify your token has the appropriate permissions and hasn't expired.
|
||||
|
||||
6. Pull changes and/or enable auto-sync (optional):
|
||||
- Pull changes to sync from your repo.
|
||||
- Toggle on "Auto-sync with Git" to automatically pull changes from your main branch every five minutes.
|
||||
|
||||
In Production mode, synced collections appear in the regular collections list with a special icon to indicate that they're versioned and read-only.
|
||||
|
||||
At this point, you should be all set up. Exit Admin settings, then reload your browser. You should see your synced Library collection in your production Metabase.
|
||||
|
||||
## An example dev-to-production workflow
|
||||
|
||||
Let's say your team wants to build a new analytics dashboard. Here's a workflow that ensures that all production content goes through a review process.
|
||||
|
||||
### Step 1: Create a new branch
|
||||
|
||||
In your development Metabase, click the branch dropdown in the synced collections section and [create a new branch](#branch-management) for your work, like `feature/megafauna-dashboard`.
|
||||
|
||||
### Step 2: Create content in your development Metabase
|
||||
|
||||
Create a dashboard called "Megafauna Analytics" and add some questions. The questions should be saved to the dashboard or to the synced collection. Save the dashboard to the synced collection.
|
||||
|
||||
### Step 3: Push to your development branch
|
||||
|
||||
1. You should see a yellow dot on your synced collection (indicating local changes).
|
||||
2. Click the up arrow (push) icon next to your synced collection.
|
||||
3. Enter a commit message: "Add Megafauna Analytics dashboard".
|
||||
4. Metabase commits your changes to the branch you're working on and pushes them to your repo.
|
||||
|
||||
### Step 4: Create a pull request
|
||||
|
||||
In your Git repository:
|
||||
|
||||
1. Create a pull request from your branch, `feature/megafauna-dashboard`, to the main branch `main`.
|
||||
2. Review the changes to the YAML files representing your dashboards and questions.
|
||||
3. Someone who knows what they're doing approves and merges the PR.
|
||||
|
||||
### Step 5: Production automatically updates
|
||||
|
||||
On your production Metabase instance:
|
||||
|
||||
1. Within five minutes, the auto-sync process detects the new commits on `main` (you can also manually import the changes).
|
||||
2. The "Megafauna Analytics" dashboard appears in production with all its questions.
|
||||
3. The content is read-only for users (they can view and use it, but can't edit it).
|
||||
|
||||
## How your synced collection works in Development mode
|
||||
|
||||
Synced collections are special collections that are tracked in Git. Content in synced collections is versioned and can be pushed to or pulled from your repository.
|
||||
|
||||
- [Synced collections in the UI](#synced-collections-in-the-ui)
|
||||
- [Moving and deleting content in synced collections](#moving-and-deleting-content-in-synced-collections)
|
||||
- [Items in synced collections can't depend on items outside of the synced collection](#items-in-synced-collections-cant-depend-on-items-outside-of-the-synced-collection)
|
||||
|
||||
### Synced collections in the UI
|
||||
|
||||
When you first connect a Metabase to an initialized repository in Development mode, Metabase automatically creates a default synced collection called "Library". You can add items to that synced collection, including sub-collections.
|
||||
|
||||
In Development mode, your synced collection shows its current state with visual indicators:
|
||||
|
||||
- **Yellow dot:** You have unsynced local changes that need to be committed.
|
||||
- **Up/down arrows:** Sync controls for pulling and pushing changes.
|
||||
|
||||
In Production mode, synced collections appear in the regular collections list (not in a separate "Synced Collections" section) with a special icon to indicate they're versioned and read-only.
|
||||
|
||||
### Moving and deleting content in synced collections
|
||||
|
||||
When you move content out of a synced collection, the UI may not immediately show the unpushed state. Refresh your browser to see the push indicator.
|
||||
|
||||
**Deletions sync to production:** When you remove content from a synced collection in Development mode and push that change, the content will also be removed from your Production instance when it syncs. This applies to moving content out of the synced collection or deleting it entirely.
|
||||
|
||||
Content in other Metabases that depended on this item may break since the dependency will no longer be in a synced collection.
|
||||
|
||||
### Items in synced collections can't depend on items outside of the synced collection
|
||||
|
||||
**Synced collections are self-contained.** Everything a dashboard or question needs must be inside the synced collection for Remote Sync to work properly. Metabase enforces these dependency constraints to ensure all content can be properly versioned and synced between instances.
|
||||
|
||||
**If it's not in the collection, it won't sync**—and that includes metadata. The one critical exception: table metadata (column types, descriptions, etc.) doesn't sync at all, even when questions that depend on it do sync. See [table metadata limitations](#important-table-metadata-limitations) for details.
|
||||
|
||||
For example:
|
||||
|
||||
- If a question references a model, both the question and the model must be in synced collections.
|
||||
- If a dashboard includes questions, those questions must be saved to either the dashboard itself or to the synced collection.
|
||||
- If a dashboard has click behaviors that link to other dashboards or questions, those linked items must be in synced collections.
|
||||
- If a document contains @ mentions of other items (questions, dashboards, models, etc.), those mentioned items must be in synced collections.
|
||||
|
||||
Questions that depend on snippets can't be added to synced collections, as snippets aren't stored in collections and therefore can't be synced.
|
||||
|
||||
Items can reference other items in sub-collections that are also synced.
|
||||
|
||||
When you try to save an item with external dependencies to a synced collection, Metabase will show an error.
|
||||
|
||||
To resolve this:
|
||||
|
||||
1. Identify which items your content depends on.
|
||||
2. Move those dependencies into synced collections first.
|
||||
3. Then save your item to the synced collection.
|
||||
|
||||
Alternatively, you can modify your content to use a different source data that's already in synced collections.
|
||||
|
||||
## What Metabase syncs
|
||||
|
||||
Remote Sync uses the same serialization format as the [Metabase CLI serialization feature](./serialization.md), storing your content as YAML files in your Git repository.
|
||||
|
||||
**What syncs:**
|
||||
|
||||
- Dashboards and their cards
|
||||
- Questions (saved queries and models)
|
||||
- Model metadata (column descriptions, display settings, etc.)
|
||||
- Documents
|
||||
- Timelines and events
|
||||
- Collection structure and metadata
|
||||
|
||||
**What doesn't sync:**
|
||||
|
||||
- Users, groups, and permissions
|
||||
- Alerts and subscriptions
|
||||
- Snippets
|
||||
- Database connections
|
||||
- Personal collections
|
||||
- **Table metadata** (column types, descriptions, visibility settings, etc.)
|
||||
|
||||
### Important: Table metadata limitations
|
||||
|
||||
**Table metadata is not synced by Remote Sync.** This means any customizations you make to table metadata in your development instance—such as changing a column's display type from "Unix timestamp" to "Date", adding column descriptions, or adjusting visibility—will not automatically sync to production.
|
||||
|
||||
If your questions or dashboards rely on customized table metadata, you must manually apply the same metadata changes in your production instance. Otherwise, content that works in development may not work correctly in production.
|
||||
|
||||
**Why this matters:** If you have a question that displays a unixtime column as a date (because you changed the column type in development's table metadata), that question will sync to production, but the column will still appear as a unixtime in production unless you manually update the table metadata there as well.
|
||||
|
||||
For more details on the serialization format and command-line workflows, see the [serialization documentation](./serialization.md).
|
||||
|
||||
## Branch management
|
||||
|
||||
Branching is only available in Development mode.
|
||||
|
||||
### Creating a branch
|
||||
|
||||
You can create branches in Metabase or directly in your Git repository. Branches created in Git will appear in the Metabase branch dropdown once Metabase syncs with your repository.
|
||||
|
||||
We recommend pushing an initial commit to your main branch before creating branches. Creating a branch from an empty main branch can cause unexpected behavior.
|
||||
|
||||
To create a new branch in Metabase:
|
||||
|
||||
1. Click the branch dropdown in the synced collections section.
|
||||
2. Type a name for the new branch in the search box.
|
||||
3. Press Enter to create the branch.
|
||||
|
||||
The new branch starts from the current commit (not the latest remote).
|
||||
|
||||
### Switching branches
|
||||
|
||||
In the left sidebar under "SYNCED COLLECTIONS", you'll see a branch dropdown next to each synced collection name:
|
||||
|
||||
1. Click the branch dropdown to see available branches.
|
||||
2. Select a different branch to switch to it.
|
||||
|
||||
If the branch doesn't appear, ensure it exists in your Git repository and that the name matches exactly (branch names are case-sensitive).
|
||||
|
||||
If you have unsynced changes, Metabase will show a dialog asking what you want to do:
|
||||
|
||||
- **Push changes to the current branch:** Commits your changes to the current branch before switching.
|
||||
- **Create a new branch and push changes there:** Saves your work to a new branch, keeping the original branch clean.
|
||||
- **Discard these changes:** Throws away your uncommitted changes (can't be undone).
|
||||
|
||||
The dialog shows you exactly which items have changed, so you can make an informed decision.
|
||||
|
||||
If you switch modes (from Development to Production or vice versa) with unpushed changes, you'll be prompted to save or discard them. You cannot switch to Production mode with uncommitted changes.
|
||||
|
||||
If changes don't appear after switching modes: Hard refresh your browser (Cmd/Ctrl + Shift + R).
|
||||
|
||||
## Deleting branches
|
||||
|
||||
Currently, you can only delete branches in GitHub or from the command line. Metabase will pick up the branch deletions on its next pull (though you may need to refresh your browser).
|
||||
|
||||
## Pushing changes to Git
|
||||
|
||||
You can only push changes in a Metabase with Remote sync set to Development mode.
|
||||
|
||||
### Committing your changes
|
||||
|
||||
When you make changes to items in a synced collection, a yellow dot appears on your synced collection (indicating uncommitted changes). To commit your changes:
|
||||
|
||||
1. Click the up arrow (push) icon next to the synced collection name in the left sidebar.
|
||||
2. Enter a descriptive commit message explaining your changes.
|
||||
3. Click "Continue" to push your changes to Git.
|
||||
|
||||
If you see "Remote is ahead of local", that means someone else pushed to the branch from another Metabase in Development mode. Pull the latest changes before pushing again.
|
||||
|
||||
## Pulling changes from Git
|
||||
|
||||
You can pull changes when in Development or Production mode.
|
||||
|
||||
In Development mode, you can get the latest changes from your Git repository:
|
||||
|
||||
1. Click the down arrow (pull) icon next to your synced collection in the left sidebar.
|
||||
2. Review any summary of incoming changes if shown.
|
||||
3. Confirm the import.
|
||||
4. Metabase updates your collections with the latest content from Git.
|
||||
|
||||
If changes don't appear after pulling:
|
||||
|
||||
- Verify you're on the correct branch.
|
||||
- Hard refresh your browser (Cmd/Ctrl + Shift + R).
|
||||
- If you encounter sync errors, review error messages in the sync dialog, manually resolve conflicts in your Git repository, then pull again.
|
||||
|
||||
In Production mode, go to **Admin settings** > **Settings** > **Remote sync** and click **Pull changes**.
|
||||
|
||||
### Handling unsynced changes
|
||||
|
||||
If you have local uncommitted changes when trying to pull or switch branches, Metabase will prompt you with options:
|
||||
|
||||
- **Push changes to the current branch:** Commit your changes first, then proceed.
|
||||
- **Create a new branch and push changes there:** Preserve your work on a new branch.
|
||||
- **Discard these changes:** Throw away your uncommitted changes to accept what's in Git.
|
||||
|
||||
When in doubt, create a new branch and push changes to that branch. That way you won't lose any work.
|
||||
|
||||
### Pulling changes automatically
|
||||
|
||||
In Production mode, you can set Metabase to auto-sync changes from your main branch.
|
||||
|
||||
1. Navigate to **Admin settings** > **Settings** > **Remote sync**.
|
||||
2. Enable Auto-sync with Git.
|
||||
|
||||
By default, Metabase will pull any changes (if any) from the branch you specify every five minutes. You can also manually sync as needed.
|
||||
|
||||
## If you already have a repo with serialized Metabase data
|
||||
|
||||
You can just keep doing what you're doing.
|
||||
|
||||
If you want to switch to this remote-sync setup, we still recommend you start with a new repo. In that case:
|
||||
|
||||
1. Check out a new branch in your Metabase instance in Development mode.
|
||||
2. Import your data to your Metabase instance with the serialization command as you normally would.
|
||||
3. Move the content you want to sync into the synced collection.
|
||||
4. Push up your changes to the new repo.
|
||||
@@ -49,7 +49,7 @@ From the Azure Management console homepage, create a new resource. This time sea
|
||||
|
||||

|
||||
|
||||
In the next page you will have to select the way you will use the service. Choose **Single server** for now, which is a good starting point, but take into account that this will create just one instance of PostgreSQL that will be your single point of failure.
|
||||
In the next page you will have to select the way you will use the service. Choose **Single server** for now, which is a good starting point, but consider that this will create just one instance of PostgreSQL that will be your single point of failure.
|
||||
|
||||
On the next screen, select or enter the following:
|
||||
|
||||
@@ -121,7 +121,7 @@ Return to the application configuration page and click on **Settings** -> **Conf
|
||||
|
||||
You'll need to add the [environment Variables](../configuring-metabase/environment-variables.md) for connecting Metabase to its [PostgreSQL Application Database](../installation-and-operation/configuring-application-database.md#postgresql). Make sure that you use the full **MB_DB_CONNECTION_URI**.
|
||||
|
||||
Also, take into account that the username in Azure PostgreSQL is `user@name_of_your_database_engine` so in this case the entire connection uri would be as follows:
|
||||
Also, consider that the username in Azure PostgreSQL is `user@name_of_your_database_engine` so in this case the entire connection uri would be as follows:
|
||||
|
||||
```
|
||||
postgresql://databasePrivateIPAddress:port/postgres?user=user@name_of_your_database_engine&password=configuredpassword&ssl=true&sslmode=required
|
||||
@@ -182,7 +182,7 @@ In your Azure web app, visit **Settings** -> **Custom domains** to use a custom
|
||||
|
||||
Azure provides an easy way to increase the server's capacity by doing a Scale-up. Simply choose a pricing tier with more RAM and CPU count and restart the instance. This approach is easier than scaling out and you should choose this option first before scaling out.
|
||||
|
||||
For a highly-available Metabase deployment, you can tell your web app to scale-out (more servers serving the application in parallel). Simply go to Scale-out and use the slider to ask for more instances, or use a custom autoscaling policy (like using more servers during your work hours and fewer during off hours).
|
||||
For a highly available Metabase deployment, you can tell your web app to scale-out (more servers serving the application in parallel). Simply go to Scale-out and use the slider to ask for more instances, or use a custom autoscaling policy (like using more servers during your work hours and fewer during off hours).
|
||||
|
||||
### CORS
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ If you've already been running Metabase with the default application database (H
|
||||
|
||||
### Running Metabase as a service
|
||||
|
||||
We can use the [systemd](https://systemd.io/) initialization service to register a Metabase service that can be started and stopped automatically. Before executing this process, ensure that the Metabase container is operational. Then, utilize Podman's built-in feature to generate the service file as follows:
|
||||
We can use the [systemd](https://systemd.io/) initialization service to register a Metabase service that can be started and stopped automatically. Before executing this process, ensure that the Metabase container is operational. Then, use Podman's built-in feature to generate the service file as follows:
|
||||
|
||||
```
|
||||
sudo podman generate systemd --new --name metabase > metabase.service
|
||||
|
||||
@@ -9,7 +9,7 @@ redirect_from:
|
||||
|
||||
{% include plans-blockquote.html feature="Serialization" %}
|
||||
|
||||
Once you really get rolling with Metabase, it's often the case that you'll have more than one Metabase instance spun up. You might have a couple of testing or development instances and a few production ones, or maybe you have a separate Metabase per office or region.
|
||||
Once you get rolling with Metabase, it's often the case that you'll have more than one Metabase instance spun up. You might have a couple of testing or development instances and a few production ones, or maybe you have a separate Metabase per office or region.
|
||||
|
||||
To help you out in situations like this, Metabase has a serialization feature which lets you create an _export_ of the contents of a Metabase that can then be _imported_ into one or more Metabases.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ This page covers how to upgrade to a new Metabase release.
|
||||
|
||||
## Upgrading Metabase Cloud
|
||||
|
||||
If you're on a [Metabase Cloud](https://www.metabase.com/pricing/) plan, we'll upgrade your Metabase automatically with each new release; no action needed on your end ([unless you're using the Embedded analytics SDK](#instances-using-the-embedded-analtyics-sdk-on-metabase-cloud-must-request-an-upgrade)).
|
||||
If you're on a [Metabase Cloud](https://www.metabase.com/pricing/) plan, we'll upgrade your Metabase automatically with each new release; no action needed on your end ([unless you're using the Embedded analytics SDK](#instances-using-the-embedded-analytics-sdk-on-metabase-cloud-must-request-an-upgrade)).
|
||||
|
||||
How soon we upgrade you depends on the type of release:
|
||||
|
||||
@@ -23,7 +23,7 @@ How soon we upgrade you depends on the type of release:
|
||||
|
||||
Cloud customers can request an early upgrade by emailing support at help@metabase.com. Include the URL of the Metabase you want us to upgrade.
|
||||
|
||||
### Instances using the Embedded analtyics SDK on Metabase Cloud must request an upgrade
|
||||
### Instances using the Embedded analytics SDK on Metabase Cloud must request an upgrade
|
||||
|
||||
If you're using the [Embedded analytics SDK](../embedding/sdk/introduction.md) on Metabase Cloud, we pin your version so that it doesn't upgrade automatically, as you should test the changes before upgrading.
|
||||
|
||||
@@ -144,7 +144,7 @@ Durning a **minor version upgrade** (e.g., 54.1 to 54.2), the new Metabase conta
|
||||
|
||||
In general, regular backups (especially backups before upgrading), are the best policy, so we recommend reverting to a backup of your application database to roll back an upgrade.
|
||||
|
||||
But if you've made any change (adding new questions/dashboards, etc) since upgrading that you want to keep, you may be able to use the `migrate down` command to roll back your Metabase application database to support the previous Metabase version you were running. When Metabase upgrades to a new version, it runs migrations that may change the application database schema. The `migrate down` command undoes those schema changes. In general, we recommend restoring from a backup (the backup that you definitely remembered to generate before upgrading), and only using the `migrate down` command if you really need to keep changes made after your upgrade.
|
||||
But if you've made any change (adding new questions/dashboards, etc) since upgrading that you want to keep, you may be able to use the `migrate down` command to roll back your Metabase application database to support the previous Metabase version you were running. When Metabase upgrades to a new version, it runs migrations that may change the application database schema. The `migrate down` command undoes those schema changes. In general, we recommend restoring from a backup (the backup that you remembered to generate before upgrading), and only using the `migrate down` command if you need to keep changes made after your upgrade.
|
||||
|
||||
### Using the migrate down command
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ redirect_from:
|
||||
Integrating your SSO with Metabase allows you to:
|
||||
|
||||
- Provision a Metabase account when someone logs in to Metabase.
|
||||
- Automatically pass user attributes from your SSO to Metabase in order to power [row and column security](../permissions/row-and-column-security.md).
|
||||
- Automatically pass user attributes from your SSO to Metabase to power [row and column security](../permissions/row-and-column-security.md).
|
||||
- Let people access Metabase without re-authenticating.
|
||||
|
||||
## Confirm the password for your Metabase admin account
|
||||
@@ -82,7 +82,7 @@ Different IdPs use different names for the redirect URL. Here are some common ex
|
||||
|
||||
### User attributes
|
||||
|
||||
Metabase will automatically log in people who've been authenticated by your SAML identity provider. In order to do so, the first assertion returned in the identity provider's SAML response _must_ contain attributes for each person's first name, last name, and email.
|
||||
Metabase will automatically log in people who've been authenticated by your SAML identity provider. To do so, the first assertion returned in the identity provider's SAML response _must_ contain attributes for each person's first name, last name, and email.
|
||||
|
||||
Most IdPs already include these assertions by default, but some (such as [Okta](./saml-okta.md)) must be configured to include them.
|
||||
|
||||
@@ -126,7 +126,7 @@ We recommend that you set this value to make your SAML configuration more secure
|
||||
|
||||
The SAML identity provider certificate is an encoded certificate that Metabase will use when connecting to the IdP URI. The certificate will look like a big blob of text that you'll want to copy and paste carefully — the spacing is important!
|
||||
|
||||
Your IdP might have you download this certificate as a file (usually `.cer` or `.pem`), which you'll then need to open up in a text editor in order to copy the contents to then paste into the box in Metabase.
|
||||
Your IdP might have you download this certificate as a file (usually `.cer` or `.pem`), which you'll then need to open up in a text editor to copy the contents to then paste into the box in Metabase.
|
||||
|
||||
Note that your certificate text may include header and footer comments that look like `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`. These comments should be included when pasting your certificate text into Metabase.
|
||||
|
||||
|
||||
@@ -38,9 +38,11 @@ Monitoring access sets permissions for the following:
|
||||
|
||||
## Subscriptions and alerts
|
||||
|
||||
This setting determines who can set up:
|
||||
This setting determines who can create:
|
||||
|
||||
- [Dashboard subscriptions](../dashboards/subscriptions.md)
|
||||
- [Alerts](../questions/alerts.md)
|
||||
|
||||
People will need to be in groups with either view or edit access to the collection that contains the dashboard or question in order to set up alerts. See [Collection permissions](../permissions/collections.md).
|
||||
|
||||
To prevent people from creating alerts and subscriptions, set the "Subscriptions and alerts" permission to "No".
|
||||
|
||||