Compare commits
286 Commits
test-pytho
...
docs-sdk-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caa9484148 | ||
|
|
bef0a89f9f | ||
|
|
63f2359659 | ||
|
|
62fe686ffc | ||
|
|
8c5cac2369 | ||
|
|
d514efc3c3 | ||
|
|
f4c15ce5d7 | ||
|
|
1c649fb56f | ||
|
|
6d349fd1b2 | ||
|
|
5660df7a85 | ||
|
|
1951cfe3f7 | ||
|
|
ea03f4819b | ||
|
|
af548d9daa | ||
|
|
f568cc793b | ||
|
|
0cfcfd561e | ||
|
|
046df0694e | ||
|
|
257f23f104 | ||
|
|
f78cf1648f | ||
|
|
559d3357a2 | ||
|
|
9584d61b16 | ||
|
|
13e92995df | ||
|
|
a939c427ad | ||
|
|
2b7980868d | ||
|
|
3c92dcd071 | ||
|
|
e02de4a3bb | ||
|
|
b5495dc6aa | ||
|
|
e976b1af84 | ||
|
|
1a827eb9a0 | ||
|
|
ecd803f067 | ||
|
|
00ee60d8ed | ||
|
|
a2a31bd9e3 | ||
|
|
c1e548ca9e | ||
|
|
d32fb5f798 | ||
|
|
a8345c94f8 | ||
|
|
734949f267 | ||
|
|
14d2f55fa2 | ||
|
|
e8d760345b | ||
|
|
d52732d113 | ||
|
|
fcacb480e0 | ||
|
|
321d9266f2 | ||
|
|
4ff0d16f78 | ||
|
|
7a5a24a347 | ||
|
|
f506b2ce36 | ||
|
|
d7d4141e19 | ||
|
|
d4bbf59cc0 | ||
|
|
22ebb53487 | ||
|
|
4e1d6c0796 | ||
|
|
cec321e06c | ||
|
|
fcce676094 | ||
|
|
450dddb456 | ||
|
|
acadfded3e | ||
|
|
4578d66e2e | ||
|
|
ff2b3d80ff | ||
|
|
d3efd21c50 | ||
|
|
4d54ae84d5 | ||
|
|
15f92af504 | ||
|
|
9408112eeb | ||
|
|
b614b64281 | ||
|
|
7e19ce967f | ||
|
|
671e406c28 | ||
|
|
3dd2b5a7ac | ||
|
|
761e01ff71 | ||
|
|
16c875265c | ||
|
|
f2610651a1 | ||
|
|
5dc4b8809a | ||
|
|
f70a7b3a7f | ||
|
|
5a7f27262d | ||
|
|
f893c48fde | ||
|
|
f91fa374af | ||
|
|
aec03f0e37 | ||
|
|
ee9dd41cd2 | ||
|
|
97206bcf80 | ||
|
|
02eec9c5b3 | ||
|
|
d3f45bcced | ||
|
|
0d3bae5103 | ||
|
|
410c4aeb12 | ||
|
|
92a25096ae | ||
|
|
5937d3bb38 | ||
|
|
85ff7199c0 | ||
|
|
9731625cc4 | ||
|
|
ff099455d0 | ||
|
|
5037eec437 | ||
|
|
a5db416a8a | ||
|
|
b9df4f2f7e | ||
|
|
b9f50ff543 | ||
|
|
71f0ce1469 | ||
|
|
1969873cb0 | ||
|
|
8bde92165a | ||
|
|
4591f96f57 | ||
|
|
08a43180d7 | ||
|
|
8a9a134989 | ||
|
|
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
|
||||
@@ -381,7 +381,7 @@
|
||||
|
||||
core
|
||||
{:team "UX West"
|
||||
:api #{metabase.core.initialization-status}
|
||||
:api #{}
|
||||
:uses :any}
|
||||
|
||||
dashboards
|
||||
@@ -557,9 +557,10 @@
|
||||
util}}
|
||||
|
||||
glossary
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.glossary.api}
|
||||
:uses #{api
|
||||
events
|
||||
models
|
||||
util}}
|
||||
|
||||
@@ -584,6 +585,11 @@
|
||||
task
|
||||
util}}
|
||||
|
||||
initialization-status
|
||||
{:team "UX West"
|
||||
:api #{metabase.initialization-status.core}
|
||||
:uses #{}}
|
||||
|
||||
internal-stats
|
||||
{:team "UX West"
|
||||
:api #{metabase.internal-stats.core}
|
||||
@@ -604,6 +610,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 +747,7 @@
|
||||
util}}
|
||||
|
||||
notification
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.notification.api
|
||||
metabase.notification.core
|
||||
metabase.notification.init
|
||||
@@ -794,9 +801,9 @@
|
||||
|
||||
permissions
|
||||
{:team "UX West"
|
||||
:api #{metabase.permissions.api
|
||||
metabase.permissions.core
|
||||
metabase.permissions.init}
|
||||
:api #{metabase.permissions.core
|
||||
metabase.permissions.init
|
||||
metabase.permissions.schema}
|
||||
:uses #{api
|
||||
app-db
|
||||
audit-app
|
||||
@@ -807,11 +814,21 @@
|
||||
models
|
||||
premium-features
|
||||
remote-sync
|
||||
request
|
||||
settings
|
||||
util
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
permissions-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.permissions-rest.api}
|
||||
:uses #{api
|
||||
audit-app
|
||||
events
|
||||
permissions
|
||||
premium-features
|
||||
request
|
||||
util}}
|
||||
|
||||
pivot
|
||||
{:team "UX West"
|
||||
:api #{metabase.pivot.core}
|
||||
@@ -875,7 +892,7 @@
|
||||
util}}
|
||||
|
||||
pulse
|
||||
{:team "Workflows"
|
||||
{:team "Gadget"
|
||||
:api #{metabase.pulse.api
|
||||
metabase.pulse.core
|
||||
metabase.pulse.init}
|
||||
@@ -906,7 +923,7 @@
|
||||
metabase.queries.core
|
||||
metabase.queries.init
|
||||
metabase.queries.models.query ; TODO FIXME
|
||||
metabase.queries.schema} ; TODO FIXME
|
||||
metabase.queries.schema} ; TODO FIXME
|
||||
:uses #{analytics
|
||||
analyze
|
||||
api
|
||||
@@ -1035,7 +1052,7 @@
|
||||
:uses #{api
|
||||
config
|
||||
embedding
|
||||
permissions ; FIXME
|
||||
permissions ; FIXME
|
||||
session
|
||||
settings
|
||||
users
|
||||
@@ -1131,9 +1148,9 @@
|
||||
app-db
|
||||
appearance
|
||||
config
|
||||
core
|
||||
driver
|
||||
embedding
|
||||
initialization-status
|
||||
lib-be
|
||||
premium-features
|
||||
query-processor
|
||||
@@ -1150,7 +1167,7 @@
|
||||
metabase.session.core
|
||||
metabase.session.init
|
||||
metabase.session.models.session ; TODO -- shouldn't be exposed as an API namespace
|
||||
metabase.session.settings} ; TODO -- shouldn't be exposed as an API namespace
|
||||
metabase.session.settings} ; TODO -- shouldn't be exposed as an API namespace
|
||||
:uses #{api
|
||||
app-db
|
||||
channel
|
||||
@@ -1167,19 +1184,25 @@
|
||||
|
||||
settings
|
||||
{:team "DevEx"
|
||||
:api #{metabase.settings.api
|
||||
metabase.settings.core
|
||||
:api #{metabase.settings.core
|
||||
metabase.settings.init}
|
||||
:uses #{api
|
||||
app-db
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
util
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
settings-rest
|
||||
{:team "DevEx"
|
||||
:api #{metabase.settings-rest.api}
|
||||
:uses #{api
|
||||
permissions
|
||||
settings
|
||||
util}}
|
||||
|
||||
setup
|
||||
{:team "UX West"
|
||||
:api #{metabase.setup.api
|
||||
@@ -1503,12 +1526,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
|
||||
@@ -1584,8 +1608,14 @@
|
||||
|
||||
enterprise/api
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.api.routes
|
||||
metabase-enterprise.api.routes.common}
|
||||
:api #{metabase-enterprise.api.core}
|
||||
:uses #{api
|
||||
premium-features
|
||||
util}}
|
||||
|
||||
enterprise/api-routes
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.api-routes.routes}
|
||||
:uses :any}
|
||||
|
||||
enterprise/audit-app
|
||||
@@ -1621,7 +1651,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 +1675,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 +1716,7 @@
|
||||
:uses :any}
|
||||
|
||||
enterprise/dashboard-subscription-filters
|
||||
{:team "Workflows"
|
||||
{:team "Querying"
|
||||
:api #{}
|
||||
:uses #{premium-features}}
|
||||
|
||||
@@ -1731,6 +1764,7 @@
|
||||
lib-be
|
||||
models
|
||||
native-query-snippets
|
||||
permissions
|
||||
premium-features
|
||||
queries
|
||||
query-processor
|
||||
@@ -1754,8 +1788,10 @@
|
||||
events
|
||||
models
|
||||
premium-features
|
||||
public-sharing
|
||||
queries
|
||||
query-permissions
|
||||
query-processor
|
||||
revisions
|
||||
search
|
||||
util
|
||||
@@ -1887,6 +1923,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 +1946,8 @@
|
||||
models
|
||||
premium-features
|
||||
settings
|
||||
startup
|
||||
task
|
||||
task-history
|
||||
util
|
||||
enterprise/serialization}}
|
||||
|
||||
@@ -1972,7 +2018,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
|
||||
|
||||
@@ -94,10 +94,7 @@ module.exports = {
|
||||
"react/jsx-no-target-blank": 2,
|
||||
"react/jsx-key": 2,
|
||||
"react/forbid-component-props": [2, { forbid: ["sx"] }],
|
||||
"react-hooks/exhaustive-deps": [
|
||||
"warn",
|
||||
{ additionalHooks: "(useSafeAsyncFunction)" },
|
||||
],
|
||||
"react-hooks/exhaustive-deps": ["warn"],
|
||||
"prefer-const": [1, { destructuring: "all" }],
|
||||
"no-restricted-globals": ["error", "close"],
|
||||
"no-useless-escape": 0,
|
||||
|
||||
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
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Run Embedding SDK Storybook
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare frontend environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
shell: bash
|
||||
|
||||
- name: Build Storybook
|
||||
run: yarn build-storybook-embedding-sdk
|
||||
shell: bash
|
||||
|
||||
- name: Serve Storybook
|
||||
run: yarn http-server storybook-static -p 6006 &
|
||||
shell: bash
|
||||
|
||||
- name: Wait for Storybook to become available
|
||||
run: |
|
||||
echo "Waiting for Storybook to be served..."
|
||||
while ! curl --silent --fail http://localhost:6006; do
|
||||
sleep 5
|
||||
echo "Retrying..."
|
||||
done
|
||||
echo "Storybook is being served!"
|
||||
shell: bash
|
||||
14
.github/actions/test-driver/action.yml
vendored
@@ -27,9 +27,17 @@ 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.
|
||||
continue-on-error: 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 +46,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
|
||||
|
||||
@@ -48,10 +48,11 @@ runs:
|
||||
aws s3 cp ${OUTPUT_FILE}.zip s3://$BUCKET/$DATE/$GITHUB_RUN_ID/$GITHUB_RUN_ATTEMPT/
|
||||
|
||||
- name: Upload results to Trunk
|
||||
if: ${{ always() && !(github.event_name == 'pull_request' && github.event.pull_request.draft == true) }}
|
||||
if: always() # Important! This step must always run because it controls job's exit code.
|
||||
uses: trunk-io/analytics-uploader@main
|
||||
with:
|
||||
junit-paths: ${{ inputs.input-path }}
|
||||
variant: ${{ inputs.variant || inputs.output-name }}
|
||||
org-slug: metabase
|
||||
dry-run: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == true }} # quarantine, but do not upload results
|
||||
token: ${{ inputs.trunk-api-token }}
|
||||
|
||||
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/**"
|
||||
|
||||
|
||||
6
.github/scripts/build-e2e-matrix.js
vendored
@@ -3,14 +3,8 @@
|
||||
// grouping some specific tests together, other tests are split into chunks
|
||||
|
||||
const DEFAULT_SPEC_PATTERN = "./e2e/test/scenarios/**/*.cy.spec.*";
|
||||
const EMBEDDING_SDK_SPEC_PATTERN =
|
||||
"./e2e/test/scenarios/embedding-sdk/**.cy.spec.*";
|
||||
|
||||
const specialTestConfigs = [
|
||||
{
|
||||
name: "embedding-sdk",
|
||||
specs: EMBEDDING_SDK_SPEC_PATTERN,
|
||||
},
|
||||
{
|
||||
name: "oss-subset",
|
||||
edition: "oss",
|
||||
|
||||
16
.github/scripts/build-e2e-matrix.unit.spec.js
vendored
@@ -11,7 +11,7 @@ describe("buildMatrix", () => {
|
||||
it("should calculate correct regular chunks with default parameters", () => {
|
||||
const result = testBuildMatrix("./e2e/test/scenarios/**/*.cy.spec.*", 30);
|
||||
|
||||
expect(result.regularChunks).toBe(26);
|
||||
expect(result.regularChunks).toBe(27);
|
||||
});
|
||||
|
||||
it("should create correct number of configuration objects", () => {
|
||||
@@ -23,7 +23,7 @@ describe("buildMatrix", () => {
|
||||
it("should apply default specs path when empty entry is provided", () => {
|
||||
const result = testBuildMatrix("", 6);
|
||||
|
||||
expect(result.regularChunks).toBe(2);
|
||||
expect(result.regularChunks).toBe(3);
|
||||
expect(result.config.length).toBe(6);
|
||||
});
|
||||
|
||||
@@ -39,14 +39,13 @@ describe("buildMatrix", () => {
|
||||
});
|
||||
|
||||
it("should handle case when input chunks equals special tests count", () => {
|
||||
const result = testBuildMatrix("./e2e/test/scenarios/**/*.cy.spec.*", 4);
|
||||
const result = testBuildMatrix("./e2e/test/scenarios/**/*.cy.spec.*", 3);
|
||||
|
||||
expect(result.regularChunks).toBe(0);
|
||||
expect(result.config.length).toBe(4);
|
||||
expect(result.config[0].name).toBe("embedding-sdk");
|
||||
expect(result.config[1].name).toBe("oss-subset");
|
||||
expect(result.config[2].name).toBe("mongo");
|
||||
expect(result.config[3].name).toBe("python");
|
||||
expect(result.config.length).toBe(3);
|
||||
expect(result.config[0].name).toBe("oss-subset");
|
||||
expect(result.config[1].name).toBe("mongo");
|
||||
expect(result.config[2].name).toBe("python");
|
||||
});
|
||||
|
||||
it("should correctly calculate regularChunks for non-default spec pattern", () => {
|
||||
@@ -84,7 +83,6 @@ describe("buildMatrix", () => {
|
||||
it("should return special test configs when default spec pattern is provided", () => {
|
||||
const result = testBuildMatrix("./e2e/test/scenarios/**/*.cy.spec.*", 50);
|
||||
|
||||
expect(result.config[46].name).toBe("embedding-sdk");
|
||||
expect(result.config[47].name).toBe("oss-subset");
|
||||
expect(result.config[48].name).toBe("mongo");
|
||||
expect(result.config[49].name).toBe("python");
|
||||
|
||||
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"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
19
.github/workflows/app-db.yml
vendored
@@ -137,11 +137,13 @@ jobs:
|
||||
image: cimg/mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MySQL Latest
|
||||
junit-name: be-tests-mysql-latest-ee
|
||||
image: mysql:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
enable-aws-iam-tests: 'true'
|
||||
job:
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
@@ -211,6 +213,14 @@ jobs:
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
MB_MYSQL_AWS_IAM_TEST: ${{ matrix.version.env.enable-aws-iam-tests }}
|
||||
MB_MYSQL_AWS_IAM_TEST_HOST: ${{ secrets.MYSQL_AURORA_IAM_INSTANCE_HOST }}
|
||||
MB_MYSQL_AWS_IAM_TEST_PORT: 3306
|
||||
MB_MYSQL_AWS_IAM_TEST_USER: my_iam_user
|
||||
MB_MYSQL_AWS_IAM_TEST_DBNAME: test_db
|
||||
MB_MYSQL_AWS_IAM_TEST_SSL_CERT: trust
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
@@ -249,11 +259,13 @@ jobs:
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: Postgres Latest
|
||||
junit-name: postgres-latest-ee
|
||||
docker-image: postgres:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
enable-aws-iam-tests: 'true'
|
||||
job:
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
@@ -305,6 +317,13 @@ jobs:
|
||||
MB_POSTGRES_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
|
||||
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
|
||||
MB_POSTGRES_AWS_IAM_TEST: ${{ matrix.version.env.enable-aws-iam-tests }}
|
||||
MB_POSTGRES_AWS_IAM_TEST_HOST: ${{ secrets.POSTGRES_AURORA_IAM_INSTANCE_HOST }}
|
||||
MB_POSTGRES_AWS_IAM_TEST_PORT: 5432
|
||||
MB_POSTGRES_AWS_IAM_TEST_USER: my_iam_user
|
||||
MB_POSTGRES_AWS_IAM_TEST_DBNAME: test_db
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ matrix.version.docker-image }}
|
||||
|
||||
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
|
||||
|
||||
4
.github/workflows/backend.yml
vendored
@@ -129,6 +129,10 @@ 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.
|
||||
continue-on-error: 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
|
||||
|
||||
8
.github/workflows/docs-generate-base.yml
vendored
@@ -60,6 +60,14 @@ jobs:
|
||||
clojure -M:ee:doc api-documentation
|
||||
continue-on-error: false
|
||||
|
||||
- name: Generate command documentation
|
||||
id: generate_command_docs
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
echo "Generating API documentation..."
|
||||
clojure -M:ee:doc command-documentation
|
||||
continue-on-error: false
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
|
||||
8
.github/workflows/docs-release-generate.yml
vendored
@@ -4,12 +4,18 @@ on:
|
||||
schedule:
|
||||
- cron: "0 2 * * 1-5" # Every Monday through Friday at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_branch:
|
||||
description: "Target branch for documentation update"
|
||||
required: false
|
||||
default: "release-x.56.x"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
update-release-docs:
|
||||
name: Call documentation update for release branch
|
||||
uses: ./.github/workflows/docs-generate-base.yml
|
||||
with:
|
||||
target_branch: "release-x.56.x"
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.56.x' }}
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
31
.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 }}
|
||||
@@ -433,21 +423,25 @@ jobs:
|
||||
image: circleci/mariadb:10.2.23
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
junit-name: be-tests-mariadb-latest-ee
|
||||
image: circleci/mariadb:latest
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MySQL Latest
|
||||
junit-name: be-tests-mysql-latest-ee
|
||||
image: mysql:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
enable-aws-iam-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
@@ -506,6 +500,14 @@ jobs:
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
MB_MYSQL_AWS_IAM_TEST: ${{ matrix.version.env.enable-aws-iam-tests }}
|
||||
MB_MYSQL_AWS_IAM_TEST_HOST: ${{ secrets.MYSQL_AURORA_IAM_INSTANCE_HOST }}
|
||||
MB_MYSQL_AWS_IAM_TEST_PORT: 3306
|
||||
MB_MYSQL_AWS_IAM_TEST_USER: my_iam_user
|
||||
MB_MYSQL_AWS_IAM_TEST_DBNAME: test_db
|
||||
MB_MYSQL_AWS_IAM_TEST_SSL_CERT: trust
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
@@ -796,11 +798,13 @@ jobs:
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: Postgres Latest
|
||||
junit-name: postgres-latest-ee
|
||||
docker-image: postgres:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
enable-aws-iam-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
@@ -840,6 +844,13 @@ jobs:
|
||||
MB_POSTGRES_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
|
||||
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
|
||||
MB_POSTGRES_AWS_IAM_TEST: ${{ matrix.version.env.enable-aws-iam-tests }}
|
||||
MB_POSTGRES_AWS_IAM_TEST_HOST: ${{ secrets.POSTGRES_AURORA_IAM_INSTANCE_HOST }}
|
||||
MB_POSTGRES_AWS_IAM_TEST_PORT: 5432
|
||||
MB_POSTGRES_AWS_IAM_TEST_USER: my_iam_user
|
||||
MB_POSTGRES_AWS_IAM_TEST_DBNAME: test_db
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ matrix.version.docker-image }}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
29
.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
|
||||
@@ -57,7 +59,6 @@ jobs:
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.name == 'mongo' }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "${{ inputs.name == 'embedding-sdk' }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -83,10 +84,6 @@ jobs:
|
||||
mysql: ${{ inputs.name != 'mongo' }}
|
||||
mongo: ${{ inputs.name == 'mongo' }}
|
||||
|
||||
- name: Run Embedding SDK Storybook
|
||||
if: inputs.name == 'embedding-sdk'
|
||||
uses: ./.github/actions/run-embedding-sdk-storybook
|
||||
|
||||
- name: Retrieve uberjar artifact for ${{ inputs.edition }}
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
@@ -144,10 +141,9 @@ jobs:
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
# 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) }}
|
||||
# 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.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Tagged EE Cypress tests on ${{ inputs.name }}
|
||||
if: ${{ inputs.specs }}
|
||||
@@ -161,10 +157,9 @@ jobs:
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
# 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) }}
|
||||
# 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.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
|
||||
16
.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
|
||||
@@ -88,8 +91,9 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend unit tests
|
||||
run: yarn run test-unit --silent --shard=${{ matrix.shard }}/${{ env.SHARDS }}
|
||||
# We want Trunk to still receive test reports even if this job fails.
|
||||
# It will filter out the quarantined tests, and will control the job exit code based on the analysis.
|
||||
# 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.
|
||||
continue-on-error: true
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
|
||||
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 }}
|
||||
|
||||
3
.github/workflows/release-embedding-sdk.yml
vendored
@@ -345,8 +345,9 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
- name: Edit PR adding useful information (using Metabase bot app)
|
||||
# metabase/embedding team is added automatically as code owner of updated `package.template.json` file
|
||||
run: |
|
||||
gh pr edit --add-reviewer @metabase/embedding,albertoperdomo --add-label no-backport
|
||||
gh pr edit --add-reviewer albertoperdomo --add-label no-backport
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_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
|
||||
|
||||
36
.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,26 +61,27 @@ jobs:
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: goodCommit,
|
||||
ref: releaseCommit,
|
||||
majorVersion: Number(majorVersion),
|
||||
});
|
||||
|
||||
if (nextPatch && goodCommit && !hasBeenReleased) {
|
||||
console.log(
|
||||
{ nextPatch, releaseCommit, 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,
|
||||
}
|
||||
});
|
||||
}).catch(console.error);
|
||||
} else {
|
||||
console.log(
|
||||
{ nextPatch, goodCommit, hasBeenReleased }
|
||||
);
|
||||
console.error(`No new patch version or no green commit found for v${majorVersion}`);
|
||||
}
|
||||
}
|
||||
@@ -88,7 +94,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));
|
||||
await Promise.all(AUTO_RELEASE_VERSIONS.map(version => releasePatchFor(version)));
|
||||
}
|
||||
|
||||
8
.github/workflows/release-tag.yml
vendored
@@ -189,14 +189,16 @@ jobs:
|
||||
- name: Upload to s3 ${{ inputs.tag_name }} path
|
||||
run: | # sh
|
||||
aws s3 cp \
|
||||
s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.source_path.outputs.result }}/metabase.jar \
|
||||
s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.upload_path.outputs.result }}/metabase.jar
|
||||
s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.source_path.outputs.result }}/ \
|
||||
s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.upload_path.outputs.result }}/ \
|
||||
--recursive \
|
||||
--include "*"
|
||||
|
||||
- name: Create cloudfront invalidation
|
||||
run: |
|
||||
aws cloudfront create-invalidation \
|
||||
--distribution-id ${{ vars.AWS_CLOUDFRONT_DOWNLOADS_ID }} \
|
||||
--paths /${{ steps.upload_path.outputs.result }}/metabase.jar
|
||||
--paths "/${{ steps.upload_path.outputs.result }}/*"
|
||||
|
||||
- name: Download the jars
|
||||
run: | # sh
|
||||
|
||||
6
.github/workflows/release.yml
vendored
@@ -211,13 +211,15 @@ jobs:
|
||||
return version_path;
|
||||
|
||||
- name: Upload to S3
|
||||
run: aws s3 cp ./metabase.jar s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.jar
|
||||
run: |
|
||||
aws s3 cp ./metabase.jar s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.jar
|
||||
aws s3 cp ./SHA256.sum s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.sha256
|
||||
|
||||
- name: Create cloudfront invalidation
|
||||
run: |
|
||||
aws cloudfront create-invalidation \
|
||||
--distribution-id ${{ vars.AWS_CLOUDFRONT_DOWNLOADS_ID }} \
|
||||
--paths /${{ steps.version_path.outputs.result }}/metabase.jar
|
||||
--paths "/${{ steps.version_path.outputs.result }}/*"
|
||||
|
||||
verify-s3-download:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
2
.github/workflows/tag-for-release.yml
vendored
@@ -111,7 +111,7 @@ jobs:
|
||||
uses: actions/github-script@v7
|
||||
id: release_branch
|
||||
with:
|
||||
result_encoding: string
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const { getReleaseBranch } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const version = '${{ inputs.version }}';
|
||||
|
||||
|
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 |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 85 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"}}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"org.apache.hadoop" {:resource "apache2_0.txt"}
|
||||
"org.clojure" {:resource "EPL.txt"}
|
||||
"org.eclipse.jetty" {:resource "apache2_0.txt"}
|
||||
"org.eclipse.jetty.ee" {:resource "apache2_0.txt"}
|
||||
"org.opensaml" {:resource "apache2_0.txt"}
|
||||
"org.ow2.asm" {:resource "BSD.txt"}
|
||||
"xalan" {:resource "apache2_0.txt"}}
|
||||
|
||||
49
deps.edn
@@ -46,17 +46,17 @@
|
||||
com.github.blagerweij/liquibase-sessionlock {:mvn/version "1.6.9"} ; session lock for liquibase migrations
|
||||
com.github.jknack/handlebars ^:antq/exclude ; 4.4.0 requires java 17+
|
||||
{:mvn/version "4.3.1"} ; templating engine
|
||||
com.github.oliyh/martian-clj-http {:mvn/version "0.1.31"} ; openapi client
|
||||
com.github.oliyh/martian-clj-http {:mvn/version "0.2.1"} ; openapi client
|
||||
com.github.pangloss/transducers {:git/sha "c91d39046c8c64dc31b84ebb27154a8084fcda4c" ; like medley for transducers
|
||||
:git/url "https://github.com/pangloss/transducers"}
|
||||
com.github.seancorfield/honeysql {:mvn/version "2.7.1340"} ; Honey SQL 2. SQL generation from Clojure data maps
|
||||
com.github.seancorfield/honeysql {:mvn/version "2.7.1350"} ; Honey SQL 2. SQL generation from Clojure data maps
|
||||
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1070"} ; Talk to JDBC DBs
|
||||
com.github.steffan-westcott/clj-otel-api {:mvn/version "0.2.8"} ; Telemetry library
|
||||
com.github.vertical-blank/sql-formatter {:mvn/version "2.0.5"} ; Java SQL formatting library https://github.com/vertical-blank/sql-formatter
|
||||
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,9 @@
|
||||
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
|
||||
software.amazon.jdbc/aws-advanced-jdbc-wrapper {:mvn/version "2.6.5"} ; AWS JDBC wrapper for IAM authentication
|
||||
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 +246,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 +257,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 +265,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 +411,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 +692,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 +707,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 +808,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 +822,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,8 +1,8 @@
|
||||
(ns dev.deps-graph
|
||||
(:require
|
||||
[clojure.core.memoize :as memoize]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.namespace.file :as ns.file]
|
||||
[clojure.tools.namespace.find :as ns.find]
|
||||
@@ -126,6 +126,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 +262,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."
|
||||
([]
|
||||
"Build a graph of module => set of modules it directly depends on."
|
||||
([deps]
|
||||
(letfn [(reduce-module-deps [module-deps module deps]
|
||||
(reduce
|
||||
(fn [module-deps {dep-module :module, :as _dep}]
|
||||
@@ -305,15 +311,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 +335,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 +381,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,21 +421,118 @@
|
||||
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
|
||||
"Print the diff between how the config would look if regenerated with [[generate-config]] versus how it looks in
|
||||
reality ([[kondo-config]]). Use this to suggest updates to make to the config file."
|
||||
[]
|
||||
(ddiff/pretty-print (kondo-config-diff)))
|
||||
[]
|
||||
(ddiff/pretty-print (kondo-config-diff)))
|
||||
|
||||
(comment
|
||||
(module-dependencies 'lib)
|
||||
(external-usages 'core)
|
||||
|
||||
(module-dependencies (dependencies) 'lib)
|
||||
|
||||
(module-usages-of-other-module 'lib 'models))
|
||||
|
||||
(defn all-module-deps-paths
|
||||
"Build a map of
|
||||
|
||||
dep => path-to-dep
|
||||
|
||||
for each dependency (direct or indirect) of a module, e.g.
|
||||
|
||||
(all-module-deps-paths 'settings)
|
||||
;; =>
|
||||
{api [] ; settings depends on api directly
|
||||
api-keys [permissions collections]} ; settings depends on permissions which depends on collections which depends on api-keys"
|
||||
([module]
|
||||
(all-module-deps-paths (dependencies) module))
|
||||
([deps module]
|
||||
(all-module-deps-paths deps module (sorted-map) (atom #{}) []))
|
||||
([deps module acc already-seen path]
|
||||
(let [module-deps (module-dependencies deps module)
|
||||
new-deps (remove @already-seen module-deps)
|
||||
acc (into acc
|
||||
(map (fn [dep]
|
||||
[dep path]))
|
||||
new-deps)]
|
||||
(swap! already-seen into new-deps)
|
||||
(reduce
|
||||
(fn [acc new-dep]
|
||||
(all-module-deps-paths deps new-dep acc already-seen (conj path new-dep)))
|
||||
acc
|
||||
new-deps))))
|
||||
|
||||
(defn module-dependencies-by-namespace
|
||||
"Return a map of external dependency of `module` => set of namespaces in `module` that use it:
|
||||
|
||||
(module-dependencies-by-namespace 'permissions)
|
||||
;; =>
|
||||
{api #{metabase.permissions.api
|
||||
metabase.permissions.models.collection.graph
|
||||
...}
|
||||
app-db #{metabase.permissions.api
|
||||
metabase.permissions.models.permissions-group
|
||||
...}
|
||||
...}"
|
||||
([module]
|
||||
(module-dependencies-by-namespace (dependencies) module))
|
||||
|
||||
([deps module]
|
||||
(into (sorted-map)
|
||||
(map (fn [dep]
|
||||
[dep (into (sorted-set) (keys (module-usages-of-other-module deps module dep)))]))
|
||||
(module-dependencies deps module))))
|
||||
|
||||
(defn dependencies-eliminated-by-removing-namespaces
|
||||
"Return the set of `module` dependencies that we could eliminate if we were to split namespace(s) off into a separate
|
||||
module.
|
||||
|
||||
;; if we move `metabase.permissions.api` into a separate module then `permissions` no longer has a dependency on
|
||||
;; `request`
|
||||
(dependencies-eliminated-by-removing-namespaces 'permissions 'metabase.permissions.api)
|
||||
;; =>
|
||||
#{request}"
|
||||
[module namespace-symb-or-set]
|
||||
(let [deps (dependencies)
|
||||
namespace-symbs (if (symbol? namespace-symb-or-set)
|
||||
#{namespace-symb-or-set}
|
||||
namespace-symb-or-set)
|
||||
dep->namespaces (module-dependencies-by-namespace deps module)]
|
||||
(into (sorted-set)
|
||||
(keep (fn [[dep namespaces]]
|
||||
(when (empty? (set/difference namespaces namespace-symbs))
|
||||
dep)))
|
||||
dep->namespaces)))
|
||||
|
||||
(defn leaf-modules
|
||||
"Modules that are leaf nodes in the module dependency tree -- nothing else depends on them."
|
||||
[]
|
||||
(let [deps (dependencies)]
|
||||
(into (sorted-set)
|
||||
(comp (map :module)
|
||||
(keep (fn [module]
|
||||
(when (zero? (count (external-usages deps module)))
|
||||
module))))
|
||||
deps)))
|
||||
|
||||
(defn non-dependencies
|
||||
"Modules that `module` does not depend on, either directly or indirectly -- changes to any of these modules should not
|
||||
affect `module`."
|
||||
[module]
|
||||
(let [deps (dependencies)
|
||||
all-modules (into (sorted-set) (map :module) deps)]
|
||||
(set/difference
|
||||
all-modules
|
||||
(set (keys (all-module-deps-paths deps module))))))
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ Some tips:
|
||||
- Whenever you want Metabot to do something completely different, you should reset the conversation, as Metabot might find that irrelevant historical context to be confusing.
|
||||
- Once Metabot creates a question for you, you can follow up with more questions or take over yourself. You can drill through the chart or step into the editor to tweak the query (both in the query builder and the SQL editor).
|
||||
- Metabot works best with English prompts. While it might understand other languages, you'll get the most reliable results by asking your questions in English.
|
||||
- Define domain-specific terms in the [glossary](../exploration-and-organization/data-model-reference.md#glossary) to help Metabot understand your organization's terminology.
|
||||
|
||||
### Metabot response menu
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ The best thing you can do to improve Metabot's performance is to prep your data
|
||||
- [Add models and metrics to your Metabot collection](#add-models-and-metrics-to-your-metabot-collection)
|
||||
- [Add descriptions for your data and content](#add-descriptions-for-your-data-and-content)
|
||||
- [Make sure the semantic types for each field are correct](#make-sure-the-semantic-types-for-each-field-are-correct)
|
||||
- [Define domain-specific terms in the glossary](#define-domain-specific-terms-in-the-glossary)
|
||||
- [Curate prompt suggestions](#curate-prompt-suggestions)
|
||||
|
||||
### Add models and metrics to your Metabot collection
|
||||
@@ -69,6 +70,12 @@ Make sure the semantic types for each field accurately describe the field's "mea
|
||||
|
||||
Metabase will try to set semantic types automatically, but you should confirm that each field has the relevant semantic type. See [Data types and semantic types](../data-modeling/semantic-types.md). You can also set semantic types for [models](../data-modeling/models.md#add-metadata-to-columns-in-a-model).
|
||||
|
||||
### Define domain-specific terms in the glossary
|
||||
|
||||
Add your organization's terminology, acronyms, and business-specific terms to the [glossary](../exploration-and-organization/data-model-reference.md#glossary). When Metabot receives a prompt, it can look up terms in the glossary to better understand your request.
|
||||
|
||||
For example, if you define "MRR" as "Monthly Recurring Revenue" in your glossary, Metabot will know what you mean when you ask "What's our MRR for Q4?" This is especially helpful for industry-specific jargon, internal product names, or abbreviations unique to your organization.
|
||||
|
||||
### Curate prompt suggestions
|
||||
|
||||
When you select a collection for Metabot to "learn", Metabot will suggest a series of prompts based on the content it finds in that collection. These prompts just give people a feel for the kinds of things people can ask Metabot to do.
|
||||
|
||||
28883
docs/api.html
30790
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,15 @@ 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.
|
||||
|
||||
You can quickly toggle dark mode from anywhere in Metabase by opening the [command palette](../exploration-and-organization/exploration.md#command-palette) and searching for "light" or "dark" or "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).
|
||||
|
||||
## Branding
|
||||
|
||||
@@ -74,7 +82,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.
|
||||
|
||||
|
||||
BIN
docs/data-modeling/images/check-dependencies.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
|
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
|
||||
|
||||
|
||||
@@ -4,14 +4,22 @@ title: Model persistence
|
||||
|
||||
# Model persistence
|
||||
|
||||
> Currently available for PostgreSQL, MySQL, and Redshift.
|
||||
|
||||
Metabase can persist the results of your models so that your models (and the questions based on those models) load faster.
|
||||
|
||||
Metabase will store model results in tables in a bespoke schema in your data warehouse (not the Metabase application database). When people ask questions based on your models, Metabase will use the tables with the stored results instead of re-running the model's query.
|
||||
|
||||
> Model persistence doesn't work with [row and column security](../permissions/row-and-column-security.md) or [impersonation](../permissions/impersonation.md).
|
||||
|
||||
## Databases that support model persistence
|
||||
|
||||
Currently, model persistence is only available for the following databases:
|
||||
|
||||
- PostgreSQL
|
||||
- MySQL
|
||||
- Redshift
|
||||
|
||||
Model persistence doesn't work with [row and column security](../permissions/row-and-column-security.md) or [impersonation](../permissions/impersonation.md).
|
||||
|
||||
## Turn on model persistence in Metabase
|
||||
|
||||
To persist models for faster loading, you'll need to turn on model persistence for:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -119,9 +119,21 @@ There are some limitations to this indexing:
|
||||
|
||||
You can edit a model's query by clicking on the down arrow next to the model's name and clicking on **Edit query definition**. When you're doing editing, be sure to save your changes. Unlike questions, which prompt you to save as a new question, any changes here will overwrite the existing model. If you want to create a new model from an existing model, select **Duplicate this model** from the model sidebar (the icon of two overlapping squares).
|
||||
|
||||
### Checking for breaking changes
|
||||
|
||||
{% include plans-blockquote.html feature="Checking for breaking changes" %}
|
||||
|
||||
When you save changes to a model, Metabase will try to detect whether your changes would break any other entities that depend on that model. For example, if you remove a column from the model, but other questions based on that model rely on that column, Metabase will warn you that those downstream questions will break.
|
||||
|
||||

|
||||
|
||||
Currently, Metabase will look for broken column references. If you rename or remove a column, Metabase will likely flag the change as breaking downstream entities. But Metabase can't detect other types of changes like changing the column type or computation logic as breaking changes.
|
||||
|
||||
## 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 +143,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 +153,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.
|
||||
|
||||
|
||||