Compare commits

...

130 Commits

Author SHA1 Message Date
Timofei
0b83180e96 add more debug #1 2025-11-26 14:08:35 +04:00
Timofei
ffaca12661 add more debug 2025-11-25 19:11:20 +04:00
Timofei
6acd24fc20 trigger sample app e2e 2025-11-25 17:09:49 +04:00
Timofei
2352d01b34 Adjust debug 2025-11-25 16:26:26 +04:00
Timofei
90b238f722 Adjust debug 2025-11-25 14:46:27 +04:00
Timofei
12e1e7a8ae Debug 2025-11-24 21:05:51 +04:00
Denis Berezin
0b42ad89a9 Fix trend viz dates diff calculation for a year unit (#65985) 2025-11-24 18:34:33 +03:00
dpsutton
857bdadd45 bump databricks (#66123)
* bump databricks

This moves over to a thin jar (or regular jar) which provides jdbc
classes and lists its dependencies. We can now bump or pin as we
like. The previous versions were uberjars that had their dependencies
packaged inside.

```clojure
clj -Sdeps '{:deps {com.databricks/databricks-jdbc-thin {:mvn/version "3.0.5"}}}'
Clojure 1.12.1
user=> (Class/forName "com.databricks.client.jdbc.Driver")
com.databricks.client.jdbc.Driver
```

```clojure
clj -Xdeps tree :extra '{:deps {com.databricks/databricks-jdbc-thin {:mvn/version "3.0.5"}}}'
org.clojure/clojure 1.12.1
  . org.clojure/spec.alpha 0.5.238
  . org.clojure/core.specs.alpha 0.4.74
com.databricks/databricks-jdbc-thin 3.0.5
  . com.databricks/databricks-sdk-java 0.69.0
    X org.slf4j/slf4j-api 2.0.9 :older-version
    . org.apache.commons/commons-configuration2 2.11.0 :newer-version
      X org.apache.commons/commons-lang3 3.14.0 :older-version
      . org.apache.commons/commons-text 1.12.0
        X org.apache.commons/commons-lang3 3.14.0 :older-version
      . commons-logging/commons-logging 1.3.2 :newer-version
    X com.fasterxml.jackson.core/jackson-databind 2.15.2 :older-version
...
```

contrasted with

```clojure
❯ clj -Xdeps tree :extra '{:deps {com.databricks/databricks-jdbc {:mvn/version "3.0.1"}}}'
org.clojure/clojure 1.12.1
  . org.clojure/spec.alpha 0.5.238
  . org.clojure/core.specs.alpha 0.4.74
com.databricks/databricks-jdbc 3.0.1
```

* bump to run drivers

* include test namespace change to trigger driver tests
2025-11-24 09:14:19 -06:00
Nemanja Glumac
eed6736230 Fix uberjar fetching for SDK host-apps (#66194) 2025-11-24 15:53:22 +01:00
Noah Moss
144b0b33f8 Move audit installation after search init; change some logs to debug (#66045)
* Move audit installation after search init; change some logs to debug

* change another warn to debug
2025-11-24 14:48:35 +00:00
Sébastien
dcca252247 Closes EMB-997 add a reset function to plugins (#65958)
* reorg

* added reinit fns to oss plugins

* ಠ_ಠ

* Added doc and eslint to prevent import of individual reinitialize functions

* added short doc to reinitialize functions to indicate they're internal
2025-11-24 15:15:15 +01:00
Denis Berezin
a183e8b5b4 Add translation for boolean dash filter value (#66050)
* Add translation for boolean dash filter value

* Fix CI
2025-11-24 16:55:44 +03:00
Edward Paget
3b837a15a0 fix: google auth returns given_name/family_name (#66151)
this was incorrectly using first_name/last_name. This also updates the
existing mocks to return the correct fields.
2025-11-24 13:01:08 +00:00
Nemanja Glumac
6679039b45 Revert "Pin the NodeJS version to the version 22 (#66152)" (#66187)
This reverts commit 58867cac0b.
2025-11-24 12:37:57 +01:00
Nemanja Glumac
58867cac0b Pin the NodeJS version to the version 22 (#66152) 2025-11-24 11:49:49 +01:00
Nemanja Glumac
2e4b7adbcb Parallelize embedding SDK package and uberjar builds (#66122) 2025-11-24 11:47:29 +01:00
Fraser
5aeffcf173 Skip BarChart Watermark loki (#66176) 2025-11-24 10:30:48 +00:00
Nemanja Glumac
721db4d40f Fetch uberjar using a proper action (#66165) 2025-11-21 23:58:57 +01:00
dpsutton
442fd726a3 Prevent driver tests (#66156)
Co-authored-by: Bryan Maass <bryan.maass@gmail.com>
2025-11-21 15:36:19 -07:00
Denis Berezin
9e65cc60bf Fix timeseries value detection (#66161) 2025-11-21 23:59:43 +03:00
Nemanja Glumac
44ab6decf0 Clean up backend-test matrix (#66158) 2025-11-21 20:40:40 +01:00
John Swanson
8b369333c3 Fix flaky Remote Sync test (#65862) 2025-11-21 11:29:00 -08:00
Nicolò Pretto
c9e602db2f chore(sdk): remove excessive logs in sdk fixup script (#66133) 2025-11-21 18:19:38 +01:00
Nemanja Glumac
b1fb103a3f Remove redundant static-viz files changed condition (#66138) 2025-11-21 17:41:36 +01:00
Jeff Bruemmer
b631ee88a4 docs - fix docstring (#66134)
* doc string

* edit
2025-11-21 11:39:49 -05:00
Riley Thompson
2df87d3a7f Add empty state to native query DB dropdown (#66090)
* add global search to db dropdown

* Add tests
2025-11-21 10:04:44 -05:00
Egor Grushin
f486c415f9 chore: Add egorgrushin as a team member (#66043)
* chore: Adds egorgrushin to ~/.github/team.json and ~/release/github-slack-map.json.

* chore: fixed the usernames order
2025-11-21 15:02:00 +00:00
Nemanja Glumac
ba5804041b Consolidate embedding CI workflows (#66075) 2025-11-21 11:40:00 +00:00
Nemanja Glumac
9d6564cdaf Fix backend cache paths (#66103) 2025-11-21 11:41:08 +01:00
Sébastien
591b453e4c Closes EMB-1037 preview_embed endpoint for filter search (#66077) 2025-11-21 09:11:52 +01:00
Sébastien
5d087d0d18 Closes EMB-1038 wrong endpoint for embed maps preview (#66063) 2025-11-21 09:11:43 +01:00
Riley Thompson
719ebd0a7b Fix caching of postgres domains in arrays (#66059)
* custom nippy handling for pgobject

* Add domain caching test

* simplify test comparison
2025-11-20 23:22:50 +00:00
Fraser
ff6ec328f3 Create use-page-title hook and remove SetTitle Hoc (#65782) 2025-11-21 10:59:40 +13:00
dpsutton
78978219a6 minimal tests to be run when the label is minimal-tests (#66024)
* minimal tests to be run when the label is `minimal-tests`

* include enterprise tests, fix syntax

* bump CI

* don't fail if label is absent
2025-11-20 15:31:11 -06:00
Jeff Bruemmer
7c8a8f3694 docs - update docs workflow (#66096)
* update docs workflow

* update perms

* more perms

* perms to delete pending reviews

* review deletion

* delete comments

* update skill
2025-11-20 16:23:51 -05:00
Jeff Bruemmer
45abe078f9 docs - supporting text (#66088)
* supporting text

* image

* edits
2025-11-20 15:06:42 -05:00
Jeff Bruemmer
82b95611cf docs - light weeding (#66023)
* spelling

* docstring
2025-11-20 14:21:28 -05:00
Noah Moss
f04fb1bff2 Fix template tag parsing for model references in transforms generated by metabot (#65630)
* Try to fix model references in transforms generated by metabot

* Add an e2e test

* move test later in file

* simplify implementation

* address bot comments

* add comment
2025-11-20 14:10:41 -05:00
Jeff Bruemmer
f200fbe20c docs - lychee action version (#66091)
* lychee action version

* bump versions
2025-11-20 14:09:01 -05:00
Jeff Bruemmer
3413313d3a docs - links workflow (#66087) 2025-11-20 13:46:09 -05:00
Nicolò Pretto
b5c0d93f8e chore(sdk): fix sdk storybook (#66078)
* chore(sdk): fix sdk storybook

* use rootRepoPath for appConfig
2025-11-20 18:24:13 +01:00
Edward Paget
71f7cbc41b fix: handle invalid email address in auth-identity (#66022)
...migrations.

Handles cases where emails were set to longer than the valid length of
254 bytes. Those emails would fail on this migration previously. We're
not currently using auth-identity to lookup email addresses so this
won't cause any incorrect behavior, but also if any emails like this
exist they weren't valid anyway.

Also sets the validCheckSum to ANY like some other SQL migrations to
handle cases where this migration has already been run successfully, as
on stats.
2025-11-20 10:59:43 -06:00
github-automation-metabase
c2619a5036 Auto-update documentation for master (#66056)
* Update generated docs (master)

* Update docs/configuring-metabase/environment-variables.md

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-11-20 14:24:59 +00:00
Nicolò Pretto
e4728d22b8 chore(sdk,ci): fix stress tests for sdk tests (#66065)
* chore(sdk,ci): fix stress tests for sdk tests

* fix baseUrl, cypress explodes if we pass it in ct test config
2025-11-20 11:37:10 +01:00
Timofey Kachalov
2d1b6c4a02 refactor(embed-js): Move EmbedJS + EmbedJS wizard files to OSS folder (#65973) 2025-11-20 14:32:30 +04:00
Ngoc Khuat
b8eb7d65f9 Add bottom padding for Attach results as files section (#62483) 2025-11-20 10:25:16 +01:00
Anderson
28b5fb79f1 Collect customer consent to grant instance access to Metabase success engineers (#65701)
* feat: add api for manage support access grants

* Setup frontend endpoint logic and start work on UI

* Implement create access grant; revoke; UI clean up

* Move section to tools > help / Adjust main UI

* feat: migration to new auth-identity module

* Fix schema migration tests

* Adjust backend fields structure on the backend / Update types and table in the UI

* Implement invitation mechanism and auth provider

* Create a route for the access grant modal

* Fix fe types / Fix clj spaces

* Add some test placeholders

* Remove stray #p

* Add missing index

* refix broken migration

* Change the way we temporarily make the new feature always enabled

* Add email sending on grant creation

* Add login and tests

* Make auths expirable

* Remove extraneous test

* Fix bad syntax in test

* Split two column migration into two migrations

* Fix test syntax issue

* Better expires at test

* Create e2e tests / Split troubleshooting.cy.spec and move to ts

* Add fe unit tests

* Add fe unit tests

* Fix kondo

* Fix config file and settings require

* Revert added disallowed exported namespaces

* Correctly test expiration

* Add tests for revoked at behavior

* Better better expires_at test

* Fix help.cy.spec.ts / Self review updates

* Fixup last issues with module config

* Make password reset is disabled for support users

Improve some email copy

* Real token check

* Move test relying on EE-code out of OSS code

* Send invitation notification after sso user creations

* Fix module config

* fixup! BE feedback

* fixup! altered not found msg

* Use user email as fallack when name is not set on support grants table

---------

Co-authored-by: Edward Paget <ed.paget@gmail.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
2025-11-19 23:59:02 -03:00
Sloan Sparger
53cf2ab305 [Metabot] Request cancellation (#65511)
* fix cut not removing text from input

* ensure we fetch all data for all possible models for smart links

* remove unused comment

* fixes smart links in metabot input sometimes degrading into their text representation and fixes some cases where wrong model name ("model" instead of "dataset") was used for metabot links

* fix unit tests

* ui for cancelling a request, handles state updates for canceled requests

* press esc to cancel metabot request, fix state being incorrect on requests after an aborted request

* add test coverage for ui, update test utils to enable aborting requests in unit tests, small bits of clean up

* add tests to processStream function

* update snapshot
2025-11-19 17:35:13 -06:00
Nemanja Glumac
135abb44a5 Consolidate caches (#66036) 2025-11-19 23:03:23 +01:00
Alex Yarosh
8699037548 Change images for development and production settings (#66039)
Updated images in the remote sync documentation to reflect new settings.
2025-11-19 20:50:12 +00:00
Alex Yarosh
03dad76c9f Update plans-blockquote inclusion for documents (#66038) 2025-11-19 15:49:12 -05:00
github-automation-metabase
dca22d0865 Bump org.postgresql/postgresql from 42.7.7 to 42.7.8 (#63804)
Inspect dependency changes here: https://github.com/pgjdbc/pgjdbc/blob/REL42.7.8/CHANGELOG.md

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-19 19:21:50 +00:00
github-automation-metabase
a6f98dbd68 Bump com.amazon.redshift/redshift-jdbc42 from 2.1.0.34 to 2.2.0 (#64830)
Inspect dependency changes here: https://github.com/aws/amazon-redshift-jdbc-driver/blob/v2.2.0/CHANGELOG.md

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-19 19:09:59 +00:00
github-automation-metabase
2fe9ebdec1 Bump org.apache.tika/tika-core from 3.2.2 to 3.2.3 (#63799)
Inspect dependency changes here:

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-19 19:03:52 +00:00
github-automation-metabase
c31762e244 Bump clj-kondo/clj-kondo from 2025.07.28 to 2025.10.23 (#65029)
* Bump clj-kondo/clj-kondo from 2025.07.28 to 2025.10.23

Inspect dependency changes here: https://github.com/clj-kondo/clj-kondo/blob/v2025.10.23/CHANGELOG.md

* Fix Kondo warning

---------

Co-authored-by: Cam Saul <github@camsaul.com>
2025-11-19 19:00:12 +00:00
Nicolò Pretto
5585815455 fix(docs): fix sdk docs not being generated correctly (#66030) 2025-11-19 11:58:18 -05:00
Jeff Bruemmer
2f8cc0f153 skills (#65676)
* skills

* docs review workflow

* update skills

* update bug fix workflow to use skills

* headings

* update code workflow to use skills

* updates

* add workflow dispatch to docs review

* edits
2025-11-19 16:54:05 +00:00
Jeff Bruemmer
d00a66f474 Fix YAML syntax for target_branch in workflow (#66018) 2025-11-19 09:50:38 -05:00
Braden Shepherdson
19ba6a165a [QP] Allow one-sided :number/between parameter values (#65987)
Fixes #65714.

These are sent as `:number/<=` or `:number/>=` and the validation was
too strict from 57 onward. Added a test for none, min, max, and both
ends of a Between filter.
2025-11-19 09:35:09 -05:00
Jeff Bruemmer
e0e3099787 Docs 57 latest (#65666)
* update release workflow to 57

* string
2025-11-19 09:27:52 -05:00
Jeff Bruemmer
6d62827416 docs - X version note and cleanup (#65756)
* X version note and cleanup

* note on hotfix x

* edit

* note on beta

* changelog link
2025-11-19 09:14:54 -05:00
Fraser
5a8ec6631a Cache yarn in CI (#65946) 2025-11-19 14:31:11 +01:00
Abed Habli
d5a7dc2e49 Docs: reusing snippet parameters (#65723)
* reusing snippet parameters

* Update docs/questions/native-editor/snippets.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* updating the section

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-11-19 13:25:17 +01:00
Thomas Schmidt
0a3a826537 Metabot tool endpoitns should support MBQL5 (#65844)
* Metabot tool endpoitns should support MBQL5

* Update filter tool to work with MBQL 5

* Fix find-outliers-wrong-query-test

Was attempting to assoc-in/dissoc-in MBQL4 query keys on an MBQL5 query.

---------

Co-authored-by: Mike Appleby <86076+appleby@users.noreply.github.com>
2025-11-19 10:32:39 +00:00
Fraser
04cbeab190 Remove ModalWithTrigger (#65994) 2025-11-19 10:22:51 +01:00
Nemanja Glumac
248a95e704 De-clutter the Cypress (nodejs) Module runner (#65881)
Running Cypress tests doesn't have to be hard.

This PR is moving us in a direction where (1) there's some logic that sets up the environment for e2e testing, and then (2) there's a simple command to invoke cypress (aka "decoupling" or "separation of concerns")

Cypress only needs to know which config to look at. It contains all the information it needs to run.

There are two ways to invoke Cypress:

1. Cypress cli
2. Cypress Module API (programmatic entry point)

They should be compatible and interchangeable. Anything you pass to the node module as options can be passed as individual flags to the cli.

```
npx cypress run --config-file path/to/some/config --spec path/to/spec.cy.spec.ts
```

is equivalent to

```
cypress.run({ configFile: "path/to/some/config", spec: "path/to/spec.cy.spec.ts" });
```

This PR removes all abstractions from the actual node module runner.
2025-11-19 10:20:14 +01:00
Kamil Mielnik
c7e2dcd254 GDGT-1235 Fix flaky dashboard parameters test (#65902)
* Fix flaky test

* Remove broken findDashCardAction helper
2025-11-19 15:17:19 +07:00
Fraser
fafd6bb3d8 Refactor ActionExecuteModal (#65573) 2025-11-19 14:38:44 +13:00
Fraser
4fe0ecbeb9 Refactor CustomURLPicker (#65574) 2025-11-19 14:23:46 +13:00
eliotmetabase
e1c31c0d8e Update label placeholder text to Optional for Gauge visualization (#65989)
* Update label placeholder text to Optional for Gauge visualization

* Update unit test
2025-11-18 17:04:14 -08:00
dpsutton
a8e592718f Turn off db routing for clickhouse and it's trick. (#65984)
* Turn off db routing for clickhouse and it's trick.

DB routing needs a "degree of freedom" to run the exact same query
against a different schema/db/instance whatever. Clickhouse only has
database.table. so to db route on the same clickhouse instance you have
to omit the database and let it resolve during query time.

`select * from db.table` can't work. Have to do `select * from table`
and let the connection dictate db1 or db2.

In 56, tables look like this:

```clojure
{:name "upload_records_10k_20251118164620", :schema "db_eafc4346850f435e"}
```

In 57, now they look like this for single db clickhouse things:

```clojure
{:name "upload_records_10k_20251118164620", :schema nil}
```

There are two critical problems here:
1. When uploading, it's not aware to omit the schema. So a table is
created with that schema. During the next sync that table is archived
and a new one with a nil'd schema is created. So the model created for
the uploaded CSV would break as it points to an inactive table. This has
a further compounding issue because now there "appear" to be two schema:
the faked nil one and also a new one like `db_eafc...`.
2. But _all_ tables will be archived in the same manner which were
initially found in 56 or below. These tables will be marked as inactive
and new ones created. Meaning all mbql queries based on clickhouse will
fail.

I wanted to revert this change but there's actually a bit more in the PR
and some conflicts. It also does more than just enabling db-routing so I
think we can perhaps undo the damage in this way more easily.

* remove outdated test
2025-11-18 18:45:46 -05:00
github-automation-metabase
0b5bbd260f Bump compojure/compojure from 1.7.1 to 1.7.2 (#63797)
Inspect dependency changes here: https://github.com/weavejester/compojure/blob/1.7.2/CHANGELOG.md

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-18 20:49:24 +00:00
github-automation-metabase
38c0aebd00 Bump io.github.eerohele/pp from 4998a1fdd9d3713d7034d04509e9212204773bf2 to ffc4edc482c057cd9412c62f018a41e9140c618b (#63473)
Inspect dependency changes here: 4998a1fdd9...ffc4edc482

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-18 20:40:55 +00:00
Riley Thompson
41b5904d52 Fix order by custom expression aggregations (#65937)
* fix bigquery order by aggregate custom expression

* add import

* make test work for all drivers
2025-11-18 13:58:53 -05:00
Alex Yarosh
a2975bdb44 Revise quickstart options for Embedded Analytics SDK (#65830)
Reorganized quickstart section for clarity based on user setup.
2025-11-18 12:41:33 -05:00
Edward Paget
e996561416 fix: allow analytics collection perms to be set (#65927)
* fix: allow analytics collection perms to be set

In #65098, we did not fully replicate the behavior of the old graph
endpoint where collection namespacing rules aren't applied to the
"analytics" namespace. Collections in this namespace can be modified as
though they are part of the root `nil` namespace. The bug was in
removing collections not part of the targetted namespace of the
`update-graph!` funciton from the graph to update.

This restores the old behavior by not excluding collection permissions
updates for collections in the "analytics" namespace in the
`remove-collections-from-other-namespaces` function.

it also updates our tests for the collection graph to make sure mock
analytics collections have the right namespace set rather than a `nil` namespace.

* fixup! reset revisions

* fixup! tests to EE module fix tests in EE module
2025-11-18 09:40:22 -06:00
Stas Gavrylov
10e76e3040 Update Table Editing e2e tests to use Postgres instead of SampleDB (#65955)
* Update Table Editing e2e tests to use Postgres instead of SampleDB

* Update table editing e2e tests to include different column types

* Address PR comments
2025-11-18 17:40:06 +02:00
Oleksandr Yakushev
25a97838db Bump Malli to 0.20.0 (#65909) 2025-11-18 17:15:12 +02:00
Brad Anderson
54c23276d7 Documents supporting text - Drag-and-drop (#65854)
Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
2025-11-18 10:14:27 -05:00
Denis Berezin
58df0d448e Fix incorrect tooltip metric sum in row chart (#65938)
* Fix incorrect tooltip metric sum in row chart

* Review fixes
2025-11-18 18:08:51 +03:00
Nicola Mometto
35c649f88b Nicola/glossary creator api (#65961)
* feat: glossary, actually add creator_id on create endpoints

* feat: add creator_id tests on api
2025-11-18 15:08:26 +00:00
Nicolò Pretto
d981e64f0a move sdk bundle code to the oss folder (#65753)
* move the actual sdk folders

* update rspack config and eslint config

* fix relative import in CommonSdkStoryWrapper

* allow default exports in preview.tsx

* update eslint config to allow imports from frontend/src/embedding-sdk-shared

* remove legacy eslint disable directive

* temporarily 'disable' eslint-disable-next-line no-external-references-for-sdk-package-code that has not been ported yet

* update rspack config to use new paths

* update jest config to use new paths

* add `embedding-sdk-package` to restricted imports

* copy eslint files to apply to the new sdk folders

* add eslint-disable-next-line no-restricted-imports to some places where we need emotion

* fix jest setup

* add and use setupEmbeddingSdkEnterprisePlugins

* use `useSetting` instead of `getApplicationColors` to remove enterprise import

* move defineMetabaseAuthConfig to embedding-sdk-shared

* move MetabotProvider to embedding-sdk-bundle

* fix docs generation issue about types

* fix prettier ignore file

* move back tests to ee folder

* remove ../../../ from imports

* fix eslint issues

* fix, again, jest config :sad:
2025-11-18 15:56:43 +01:00
Sébastien
425856f92a Closes EMB-1021 flaky test e2e tests metabase browser read only mode (#65851)
* .

* .

* .
2025-11-18 15:33:42 +01:00
Anderson
5c87a6c12b Make sure 'Pull changes' button is not truncated on remote sync settings (#65932) 2025-11-18 10:41:45 -03:00
Sébastien
a033c12089 Closes EMB-982 embedding stuck on 404 when navigating with postMessage (#65662)
* Closes EMB-982 postmessage 404

* actual fix

* .

* .

* .
2025-11-18 12:51:32 +01:00
Nicola Mometto
39821ae14e Add creator to transform content view, and include glossary (#65957)
* feat: add creator_id to content view for transforms

* feat: add creator to glossary

* feat: glossary entries in content

* add creator test

* missing creator_id
2025-11-18 11:37:27 +00:00
Fraser
739b062e7c Refactor QuestionRowCount (#65837) 2025-11-18 23:10:34 +13:00
Phoomparin Mano
ec73bb3bae Allow content-disposition header in cross-origin requests (#65918) 2025-11-18 14:59:38 +07:00
github-automation-metabase
566d6d1ad6 Bump thheller/shadow-cljs from 3.2.0 to 3.2.1 (#64165)
Inspect dependency changes here:

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-18 01:11:01 +00:00
github-automation-metabase
9a981a80b5 Bump org.apache.logging.log4j/log4j-core from 2.25.1 to 2.25.2 (#64153)
* Bump org.apache.logging.log4j/log4j-core from 2.25.1 to 2.25.2

Inspect dependency changes here:

* Consolidate log4j version bumps

---------

Co-authored-by: Cam Saul <github@camsaul.com>
2025-11-18 01:09:26 +00:00
github-automation-metabase
ea5d731e6a Bump org.bouncycastle/bcutil-jdk18on from 1.81 to 1.82 (#63808)
* Bump org.bouncycastle/bcutil-jdk18on from 1.81 to 1.82

Inspect dependency changes here:

* Consolidate deps bumps

---------

Co-authored-by: Cam Saul <github@camsaul.com>
Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-18 01:06:01 +00:00
github-automation-metabase
cea2da195e Bump io.trino/trino-jdbc from 476 to 477 (#64169)
Inspect dependency changes here:

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2025-11-18 01:05:19 +00:00
github-automation-metabase
be1889ac0a Bump ring/ring-core from 1.14.2 to 1.15.3 (#64160)
* Bump ring/ring-core from 1.14.2 to 1.15.3

Inspect dependency changes here: https://github.com/ring-clojure/ring/blob/1.15.3/CHANGELOG.md

* Consolidate Ring deps bumps

---------

Co-authored-by: Cam Saul <github@camsaul.com>
2025-11-18 01:05:00 +00:00
Cam Saul
94d2daaafd Backend module detangling pre Clojure/conj 2025 edition (#65681)
* Split warehouses and warehouses-rest

* Split warehouse-schema and warehouse-schema-rest

* Split actions and actions-rest

* Split collections and collections-rest

* Split `dashboards` and `dashboards-rest`

* Split public-sharing and public-sharing-rest

* Split embedding and embedding-rest; eliminated driver deps: xrays segments sample-data tiles

* Split users and users-rest

* Split setup and setup-rest

* Some more dev functions

* Update Kondo modules config

* Fix test namespace name

* Fix test failure 🔧
2025-11-17 16:35:15 -08:00
Jeff Bruemmer
85633efccf docs - remote sync rename (#65933) 2025-11-17 17:27:11 -05:00
Riley Thompson
e53d67c763 Update snowflake string filtering (#65853) 2025-11-17 22:18:45 +00:00
William
7a5d365a4d qp catches circular queries involving both sql and mbql cards (#65875) 2025-11-17 15:33:28 -06:00
Brad Anderson
6e0186703f Documents supporting text (#65705)
Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
Co-authored-by: Metabase Automation <github-automation@metabase.com>
2025-11-17 16:15:22 -05:00
Noah Moss
713a369825 And basic recent views info to metabot user context (#65618)
* add a models filter option to recent_views API

* Add recent views to metabot context with only basic metadata fields included

* clean up; add test
2025-11-17 15:43:05 -05:00
Cam Saul
52eeb00a2e Add MAGE module command; use module system for determining if we can skip driver tests (#65631)
* MAGE modules

* Trivial change

* Trivial change 2

* Add note

* Let's try this

* Don't rely on team label

* Update GH Action

* Fix shell script syntax

* Use same runner type for all jobs in drivers workflow

* We can skip Postgres tests too

* Fix Mage command??

* debug github output

* Test ignoring tests in Lib

* Can we skip fetching the base ref??

* Fix test-ignoring logic

* Ok so maybe we do need to fetch the base ref

* Try git-fetch --depth=1

* Fix drivers not waiting for job

* Remove TODO

* Cache Babashka dependencies

* Update step name

* Make sure Babashka deps are actually saved

* Flip the status codes around

* PR feedback and cleanup
2025-11-17 12:41:30 -08:00
Braden Shepherdson
17291f9d79 [QP] Allow multiple dashboard/card params to exist, if only one is set (#64428)
Fixes #64135.

Previously this code would throw if multiple dashboard and/or card
params were bound to the same SQL variable. Now multiple links are
allowed, provided that only one is actually given a value.

Actually, this logic de-duplicates the values, so if multiple filters
all set the same value, that also works.
2025-11-17 20:23:00 +00:00
Cam Saul
af139fd003 Run Codespell on docs/ (#65925)
* Run Codespell on docs/

* Only run of docs/
2025-11-17 11:29:29 -08:00
Nicola Mometto
97f104ecbf Better human readable table-data-event for audit-log (#65907)
* abstract action-v2-execute events behind table-data-edit for audit-log

* add test

* chore: still publish
2025-11-17 19:14:48 +00:00
dpsutton
d5e562665f Correctly sync routed databases in task (#65441)
* Correctly sync routed databases in task

Before:

```clojure
(sync-and-analyze-database*! 598)
2025-11-04 21:30:48,550 INFO task.sync-databases :: Starting sync task for Database 598.
2025-11-04 21:30:48,626 INFO sync.util :: STARTING: Sync metadata for sqlite Database 598 ''airlines'' (free: 0.54G, total: 1.02G, ratio: 0.53)
2025-11-04 21:30:48,650 ERROR sync.fetch-metadata :: Error while fetching metadata with 'db-metadata'
clojure.lang.ExceptionInfo: Anonymous users cannot access a database with routing enabled.
	at
        metabase_enterprise.database_routing.common$router_db_or_id__GT_destination_db_id.invokeStatic(common.clj:30)
        ....
2025-11-04 21:30:48,660 WARN sync.util :: Error in sync step Sync metadata for sqlite Database 598 ''airlines''
clojure.lang.ExceptionInfo: Anonymous users cannot access a database with routing enabled.
2025-11-04 21:30:48,668 INFO sync.util :: FINISHED: Sync metadata for sqlite Database 598 ''airlines'' (42.6 ms) (free: 0.54G, total: 1.02G, ratio: 0.53)
2025-11-04 21:30:48,669 INFO sync.util :: STARTING: Analyze data for sqlite Database 598 ''airlines'' (free: 0.54G, total: 1.02G, ratio: 0.53)
2025-11-04 21:30:48,678 INFO sync.util :: STARTING: step ''fingerprint-fields'' for sqlite Database 598 ''airlines'' (free: 0.54G, total: 1.02G, ratio: 0.53)
```

And afterwards:

```clojure
2025-11-04 21:31:57,593 INFO task.sync-databases :: Starting sync task for Database 598.
2025-11-04 21:31:57,643 INFO sync.util :: STARTING: Sync metadata for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.50)
2025-11-04 21:31:57,666 INFO sql-jdbc.execute :: Obtaining a fresh resilient connection
2025-11-04 21:31:57,671 INFO sync.describe-database :: "Tweets": SELECT privileges confirmed
2025-11-04 21:31:57,684 INFO sync.util :: STARTING: step ''sync-dbms-version'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,693 INFO sync.util :: FINISHED: step ''sync-dbms-version'' for sqlite Database 598 ''airlines'' (9.0 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,693 INFO sync.util :: STARTING: step ''sync-timezone'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,696 INFO sync-metadata.sync-timezone :: :sqlite database 598 default timezone is "UTC"
2025-11-04 21:31:57,700 INFO sync.util :: FINISHED: step ''sync-timezone'' for sqlite Database 598 ''airlines'' (7.1 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,701 INFO sync.util :: STARTING: step ''sync-tables'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,706 INFO sync-metadata.tables :: :is_writable of Table 6058 ''Tweets'' changed from null to null
2025-11-04 21:31:57,715 INFO sync.util :: FINISHED: step ''sync-tables'' for sqlite Database 598 ''airlines'' (14.1 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,715 INFO sync.util :: STARTING: step ''sync-fields'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,743 INFO sync.util :: FINISHED: step ''sync-fields'' for sqlite Database 598 ''airlines'' (27.3 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,743 INFO sync.util :: STARTING: step ''sync-fks'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,755 INFO sync.util :: FINISHED: step ''sync-fks'' for sqlite Database 598 ''airlines'' (12.0 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,755 INFO sync.util :: STARTING: step ''sync-indexes'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,758 INFO sync.util :: FINISHED: step ''sync-indexes'' for sqlite Database 598 ''airlines'' (3.1 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,758 INFO sync.util :: STARTING: step ''sync-metabase-metadata'' for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,762 INFO sync.util :: FINISHED: step ''sync-metabase-metadata'' for sqlite Database 598 ''airlines'' (4.0 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,771 INFO sync.util :: FINISHED: Sync metadata for sqlite Database 598 ''airlines'' (128.4 ms) (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,771 INFO sync.util :: STARTING: Analyze data for sqlite Database 598 ''airlines'' (free: 0.50G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,783 INFO sync.util :: STARTING: step ''fingerprint-fields'' for sqlite Database 598 ''airlines'' (free: 0.49G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,794 INFO sync.analyze :: fingerprint-fields Analyzed [************······································] 😒   25% Table 6058 ''Tweets''
2025-11-04 21:31:57,801 INFO sync.util :: FINISHED: step ''fingerprint-fields'' for sqlite Database 598 ''airlines'' (17.8 ms) (free: 0.49G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,801 INFO sync.util :: STARTING: step ''classify-fields'' for sqlite Database 598 ''airlines'' (free: 0.49G, total: 1.02G, ratio: 0.49)
2025-11-04 21:31:57,806 INFO sync.analyze :: classify-fields Analyzed [*************************·························] 😬   50% Table 6058 ''Tweets''
2025-11-04 21:31:57,809 INFO sync.util :: FINISHED: step ''classify-fields'' for sqlite Database 598 ''airlines'' (7.7 ms) (free: 0.49G, total: 1.02G, ratio: 0.48)
2025-11-04 21:31:57,809 INFO sync.util :: STARTING: step ''classify-tables'' for sqlite Database 598 ''airlines'' (free: 0.49G, total: 1.02G, ratio: 0.48)
2025-11-04 21:31:57,817 INFO sync.analyze :: classify-tables Analyzed [*************************************·············] 😋   75% Table 6058 ''Tweets''
2025-11-04 21:31:57,822 INFO sync.util :: FINISHED: step ''classify-tables'' for sqlite Database 598 ''airlines'' (13.0 ms) (free: 0.49G, total: 1.02G, ratio: 0.48)
2025-11-04 21:31:57,827 INFO sync.util :: FINISHED: Analyze data for sqlite Database 598 ''airlines'' (55.4 ms) (free: 0.49G, total: 1.02G, ratio: 0.48)
```

* add a test

this was the best i could do at first. Whats annoying is that sync steps
are so resilient to errors that it's hard to get their return values. So
kinda hate that i'm checking the database log table for task history but
here we are.

* way better tests

* reorder requires

* update module config

* some cloud dbs have multiple tables in the same catalog

the definition of the dataset is:

```clojure
[["t"
  [{:field-name "f", :base-type :type/Text}]
  [["original-foo"]
   ["original-bar"]]]]
```

But cloud databases often fake the separation with things like
prepending the dataset name to the table name to simulate separation
while using only a single catalog or equivalent

* typo

* assert positive integer on total tables
2025-11-17 12:05:11 -06:00
Phoomparin Mano
b6a8de1d29 EAJS wizard should derive a lighter dashboard background color (#65547) 2025-11-17 23:19:29 +07:00
Nathan Voxland
55abe262c9 Rename remote-sync modes (#65825) 2025-11-17 10:10:56 -06:00
Edward Paget
088a5354fc feat: migration to new auth-identity module (#65716)
* feat: migration to new auth-identity module

* Fix schema migration tests

* Make auths expirable

* Remove extraneous test

* Fix bad syntax in test

* Split two column migration into two migrations

* Fix test syntax issue

* Better expires at test

* Better better expires_at test

* Send invitation notification after sso user creations

* Fix module config
2025-11-17 09:48:08 -06:00
github-automation-metabase
a843870977 Update generated docs (master) (#65892)
Co-authored-by: github-actions <github-actions@github.com>
2025-11-17 09:24:50 -05:00
Nemanja Glumac
049723c814 Remove custom embedding SDK cors config (#65910) 2025-11-17 15:12:19 +01:00
Stas Gavrylov
25270fea3e UXW-1866: UX regression on dropdown list/search with multiple values (#64605)
* UXW-1866: Make MultiAutocomplete dropdown preserve open state when selecting options

* UXW-1866: Fix e2e test

* Add note on running e2e tests for translation functionality

* Fix e2e test

* Fix e2e test
2025-11-17 16:02:13 +02:00
Nemanja Glumac
33f9a3148d Simplify Snowplow Cypress helpers (#65859)
* Remove custom describeWithSnowplow constructs

* Update tests

* Update dev docs

* Log message when sending Snowplow request

* Rename empty describe to 'Snowplow analytics'

* Remove 'describeWithSnowplow' from ESLint rules
2025-11-17 09:11:55 +00:00
Mahatthana (Kelvin) Nomsawadi
2b69c463dd Fix dashboard.backgroundColor: transparent breaking dashboard PDF export (#65847) 2025-11-17 09:04:56 +00:00
Fraser
6845e31f38 Drop react draggable for dnd kit (#65268) 2025-11-17 13:53:05 +13:00
Nemanja Glumac
8b4fb71db4 Remove e2e tests for info popovers in field picker (#65883)
Removed multiple tests related to info popovers for field picker options and saved questions.

This behavior is already covered with a unit test at:
`frontend/src/metabase/common/components/MetadataInfo/ColumnInfoIcon/ColumnInfoIcon.unit.spec.tsx`
2025-11-17 00:48:38 +01:00
metamben
b889dd8e52 MBQL5 segments (#65566) 2025-11-15 21:47:37 +00:00
Fraser
c461d1296c Remove unused types (#65834) 2025-11-15 14:56:03 +13:00
John Swanson
4eda823252 Fix batch-upsert! method for heterogeneous entries (#65177)
* Fix the update keys for postgres search idx upsert

The upsert keys were calculated by looking at the first entry we were
inserting and removing `:id`, `:model`, and `:model_id`.

But the entries are not necessarily homogeneous. In this case, the first
entry is a dashboard (has no `:dashboard_id`) and the second entry is a
card (yes `:dashboard_id`).

This meant we never actually updated the dashboard_id when updating a card.

I attempted to add a test for this:

```
(deftest updates-work-properly
  (mt/with-test-user :rasta
    (mt/with-temp [:model/Dashboard {dash-id :id} {}]
      (let [card-name (str (random-uuid))
            card-id-1 (:id (mt/user-http-request :rasta :post 200 "card/" {:name card-name
                                                                           :collection_id nil
                                                                           :database_id 1
                                                                           :display "table"
                                                                           :creator_id (mt/user->id :rasta)
                                                                           :visualization_settings {}
                                                                           :dataset_query {:type "native" :native {:query "select 123"}
                                                                                           :database 1}}))
            card-id-2 (:id (mt/user-http-request :rasta :post 200 "card/" {:name card-name
                                                                           :collection_id nil
                                                                           :database_id 1
                                                                           :display "table"
                                                                           :creator_id (mt/user->id :rasta)
                                                                           :visualization_settings {}
                                                                           :dataset_query {:type "native" :native {:query "select 123"}
                                                                                           :database 1}}))]
        (mt/user-http-request :rasta :put 200 (str "card/" card-id-1) {:dashboard_id dash-id})
        (is (= [card-name]
               (map :name (:data (mt/user-http-request :rasta :get 200 "search" :q card-name)))))))))
```

The good news is that this test passes after my change. The bad news is
that it also passes without my change. I think what's going on is that
the behavior is different with `*force-sync*` on/off: in async mode, we
get the full batch that contains the updates for `[dashboard, card]`.
In synchronous mode, we get separate batches for `[dashboard]` and then
`[card]`.

I think we might want to rework tests somehow - it would be quite nice
to be able to still run things asynchronously but drain the queue before
making assertions, so the behavior wouldn't be different between
tests/prod.

* add a test
2025-11-14 23:27:45 +00:00
John Swanson
aa549d6910 Don't set last_used_param_values for public (#64432)
Fixes UXW-216
2025-11-14 15:20:19 -08:00
Oreon Lothamer
d5da0f4d38 self-hosting metabase stack (#65262)
* self-hosting metabase stack

* self-hosting config

* self-hosting wait for helm with --atomic
2025-11-14 13:02:53 -10:00
Riley Thompson
dded0d225a Replace snowflake test password auth with key pair auth (#65522)
* replace password with key pair auth

* re enable snowflake tests

* add private key env var

* update private key details for temp dbs

* small changes

* fix describe database test

* fix multi method params

* update test

* update test

* try fix

* check CI

i noticed that this tests pass if we don't put a connection-uri in
there. I'm not sure what the original intent was, but the following
works just fine:

```
{:url "jdbc❄️//<account>.snowflakecomputing.com/?schema=INFORMATION_SCHEMA",
 :properties {"use-password" "false",
              "connection-uri" "jdbc❄️//<account>.snowflakecomputing.com/?user=<user>CI&private_key_file=%2Fprivate%2Fvar%2Ffolders%2F1d%2F3ns5s1gs7xjgb09bh1yb6wpc0000gn%2FT%2Fmetabase-secret_8808579641027884666.tmp&schema=INFORMATION_SCHEMA",
              "client_session_keep_alive" "true",
              "timezone" "UTC",
              "warehouse" "<warehouse>",
              "user" "<user>",
              "ssl" "true",
              "private_key_file" "/var/folders/1d/3ns5s1gs7xjgb09bh1yb6wpc0000gn/T/metabase-secret_8808579641027884666.tmp",
              "week_start" "7",
              "application" "Metabase_Metabase",
              "account" "<account>",
              "db" "\"<db>\"",
              "time_output_format" "HH24:MI:SS.FF3",
              "client_metadata_request_use_connection_ctx" "true"}}
```

When it goes through clojure.java.jdbc. Somehow we are getting a
connection string that doesn't play nice. I _think_ it may just be an
issue with snowflake but i'm not positive.

* remove parallel test markers

* remove parallel marker

when running tests locally with these removed:

```clojure
(run/find-and-run-tests-repl {:only 'metabase.driver.snowflake-test})
...
Ran 49 tests in 116.525 seconds
502 assertions, 1 failure, 0 errors.
{:test 49, :pass 501, :fail 1, :error 0, :type :summary, :duration 116525.006833, :single-threaded 32, :parallel 17}
```
yields only one expected error. Previously using regular stuff also
worked

```clojure
snowflake-test=> (run-tests)

Testing metabase.driver.snowflake-test

Ran 49 tests containing 502 assertions.
1 failures, 0 errors.
{:test 49, :pass 501, :fail 1, :error 0, :type :summary}
```

(again the single test that failed). but using the CLI tool i would get
errors when running them in parallel.

* fixup stupid snowflake

1. i don't know why we are running into multithreading issues now. But
i've removed those tags and the issues are gone.
2.

```clojure
snowflake-test=> (let [spec {:connection-uri "jdbc❄️//account.snowflakecomputing.com/?schema=INFORMATION_SCHEMA&user=<user>&password=<password>&db=%22v5_airports%22"}]
                   [(jdbc/query spec ["select CURRENT_DATABASE() db"])
                    (jdbc/query spec ["select CURRENT_SCHEMA() db"])])
 [({:db "v5_airports"}) ({:db "INFORMATION_SCHEMA"})]
snowflake-test=> (let [spec {:connection-uri "jdbc❄️//account.snowflakecomputing.com/?schema=INFORMATION_SCHEMA&user=<user>&password=<password>&db=v5_airports"}]
                   [(jdbc/query spec ["select CURRENT_DATABASE() db"])
                    (jdbc/query spec ["select CURRENT_SCHEMA() db"])])
[({:db nil}) ({:db nil})]
```

In the above, note that putting the db in the connection string _and
url-encoding it_ (it's already quoted) is necessary.

* conditionally add db

in tests there's not a database so just be smart about it:

```clojure
{:warehouse "<warehouse>",
 :additional-options nil,
 :timezone "UTC",
 :db nil,
 :account "<account>",
 :use-password false,
 :time_output_format "HH24:MI:SS.FF3",
 :user "<user>",
 :subname "//<account>.snowflakecomputing.com/"}
```

* address pr comments

* remove outdated comment

pretty freaky comment eh? I added this when i commented out the
`handle-conn-uri` call underneath it and it caused tests to pass. That
was helpful in identifying the mechanism, but not a good solution. Put a
comment here so that that was not a final state, forgot to remove
comment when we got to a proper final state and `handle-conn-uri` was
fixed.

---------

Co-authored-by: dan sutton <dan@dpsutton.com>
2025-11-14 16:03:39 -05:00
Braden Shepherdson
51067eb586 [QP] Only label remaps for columns that actually need it (#65860)
Fixes #65726.

If you implicitly joined a field that happened to be the target of a
remapping, the QP was labeling it as `:remapped_from` the FK even
though (a) it wasn't really remapped and (b) there was no
corresponding FK column marked as `:remapped_to` the target column.

(Eg. `Orders.PRODUCT_ID` is remapped to `Products.TITLE`. Then I
implicitly join `Products.TITLE` directly and the QP marks it as
remapped from `Orders.PRODUCT_ID` even though it wasn't.)

That caused the FE to do odd things like drop and relabel columns.

This change adjusts the QP so it will ignore plausible remappings that
don't actually get returned from the last stage.
2025-11-14 18:57:02 +00:00
dpsutton
cd39f12e79 Suppress liquibase output (#65858)
We get a bunch of this useless stuff in CI:

```
Placeholder until other changesets

UPDATE SUMMARY
Run:                        641
Previously run:               0
Filtered out:                69
-------------------------------
Total change sets:          710

FILTERED CHANGE SETS SUMMARY
DBMS mismatch:               69
```

Looking for this in liquibase this is not logged but printed to the
console :(.

It goes through this:

```java
    private static void writeMessage(String message, UpdateSummaryOutputEnum showSummaryOutput, OutputStream outputStream) throws LiquibaseException {
        switch (showSummaryOutput) {
            case CONSOLE:
                writeToOutput(outputStream, message);
                break;
            case LOG:
                writeToLog(message);
                break;
            default:
                writeToOutput(outputStream, message);
                writeToLog(message);
        }
    }
```

So we want that enum to be LOG.

```clojure
liquibase=> (clojure.reflect/reflect liquibase.Liquibase)
{:bases #{java.lang.Object java.lang.AutoCloseable},
 :flags #{:public},
 :members #{ ,,,
             #clojure.reflect.Method{:name setShowSummaryOutput,
                                    :return-type void,
                                    :declaring-class liquibase.Liquibase,
                                    :parameter-types [liquibase.UpdateSummaryOutputEnum],
                                    :exception-types [],
                                    :flags #{:public}}
          ,,,}}
```

So easy fix. Set this enum and hopefully it shuts up.
2025-11-14 11:52:49 -06:00
Riley Thompson
854650cd86 Fix athena sync 65728 (#65777)
* claude fix

* simplify

* handle nested fields for athena

* nfc have position 0

* set non leafs as details only

* clean up parser and add tests

* whitespace

* disable nested fields

* kondo

* kondo

* disable nested schema test
2025-11-14 12:52:06 -05:00
Jeff Bruemmer
9cd92bf39c docs - document links new charts (#65828)
* links and new charts

* links

* edits

* Update docs/documents/introduction.md

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2025-11-14 12:09:24 -05:00
Chris Truter
55deb90598 Fix append for CSVs with really long column names with shared prefixes (#65793) 2025-11-14 15:51:48 +00:00
Nicolò Pretto
679170b832 Preparation for moving sdk code to oss folder (#65428)
* implement plugins for auth

* move some stuff to the oss folder as those file create issues later on

* make embedding-sdk:docs:generate pass

* move metabot to use plugins

* remove imports from package from other folders
2025-11-14 16:28:52 +01:00
Timofey Kachalov
5829365136 fix(sdk, embed-js, static): Fix Static embedding styles (#65796) 2025-11-14 18:24:04 +04:00
lbrdnk
0de9cf68de Revert param values from query results (#65841)
* Revert "Adjust cards-with-filters to work with action cards (#65805)"

This reverts commit d6b857a2c6.

* Revert "Fix mapping of params to later query stages (#65651)"

This reverts commit c98e640426.

* Revert "Compute param values from card results (#64318)"

This reverts commit 2693173e7f.
2025-11-14 15:08:14 +01:00
Nemanja Glumac
2e6c7d97e7 Remove projectId from Cypress config (#65748) 2025-11-14 07:32:34 +01:00
Mahatthana (Kelvin) Nomsawadi
cdad4c95ff fix(sdk-bundle): Fix EditableDashboard sometimes doesn't show the correct tab (#65802)
* Fix SDK save dashboard question to not show collection/dashboard picker

* Fix EditableDashboard sometimes doesn't show the correct tab on first render

* Add an assertion

* left some breadcrumbs

* Update unit tests to be async
2025-11-14 02:53:03 +00:00
1151 changed files with 22629 additions and 11256 deletions

View File

@@ -0,0 +1,75 @@
## Linting and Formatting
- **Lint PR:** `./bin/mage kondo-updated master` (or whatever target branch)
- Call the command one time at the beginning, record the results, then work through the problems one at a time.
- If the solution is obvious, then please apply the fix. Otherwise skip it.
- If you fix all the issues (and verify by rerunning the kondo-updated command):
- commit the change with a succinct and descriptive commit message
- **Lint File:** `./bin/mage kondo <file or files>`
- Use the linter as a way to know that you are adhering to conventions in place in the codebase
- **Lint Changes:** `./bin/mage kondo-updated HEAD`
- **Format:** `./bin/mage cljfmt-files [path]`
## Testing
- **Run a test:** `./bin/mage run-tests namespace/test-name`
- **Run all tests in a namespace:** `./bin/mage run-tests namespace`
## Code Readability
- **Check Code Readability:** `./bin/mage -check-readable <file> [line-number]`
- Run after every change to Clojure code
- Check specific line first, then entire file if readable
## REPL Usage
- **Evaluating Clojure Code:** `./bin/mage -repl '<code>'`
- See "Sending Code to the REPL" section for more details
### Sending Code to the REPL
- Send code to the metabase process REPL using: `./bin/mage -repl '(+ 1 1)'` where `(+ 1 1)` is your Clojure code.
- See `./bin/mage -repl -h` for more details.
- If the Metabase backend is not running, you'll see an error message with instructions on how to start it.
#### Working with Files and Namespaces
1. **Load a file and call functions with fully qualified names**:
To call `your.namespace/your-function` on `arg1` and `arg2`:
```
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
```
DO NOT use "require", "load-file" etc in the code string argument.
#### Understanding the Response
The `./bin/mage -repl` command returns three separate, independent outputs:
- `value`: The return value of the last expression (best for data structures)
- `stdout`: Any printed output from `println` etc. (best for messages)
- `stderr`: Any error messages (best for warnings and errors)
Example call:
```bash
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
```
Example response:
```
ns: user
session: 32a35206-871c-4553-9bc9-f49491173d1c
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
stdout: Hello, world!
stderr:
```
For effective REPL usage:
- Return data structures as function return values
- Use `println` for human-readable messages
- Print errors to stderr

View File

@@ -0,0 +1,153 @@
# Metabase Clojure Style Guide
This guide covers Clojure and ClojureScript coding conventions for Metabase. See also: `CLOJURE_STYLE_GUIDE.adoc` for the Community Clojure Style Guide.
## Naming Conventions
**General Naming:**
- Acceptable abbreviations: `acc`, `i`, `pred`, `coll`, `n`, `s`, `k`, `f`
- Use `kebab-case` for all variables, functions, and constants
**Function Naming:**
- Pure functions should be nouns describing the value they return (e.g., `age` not `calculate-age` or `get-age`)
- Functions with side effects must end with `!`
- Don't repeat namespace alias in function names
**Destructuring:**
- Map destructuring should use kebab-case local bindings even if the map uses `snake_case` keys
## Documentation Standards
**Docstrings:**
- Every public var in `src` or `enterprise/backend/src` must have docstring
- Format using Markdown conventions
- Reference other vars with `[[other-var]]` not backticks
**Comments:**
- `TODO` format: `;; TODO (Name M/D/YY) -- description`
## Code Organization
**Visibility:**
- Make everything `^:private` unless it is used elsewhere
- Try to organize namespaces to avoid `declare` (put public functions near the end)
**Size and Structure:**
- Break up functions > 20 lines
- Lines ≤ 120 characters
- No blank lines within definition forms (except pairwise `let`/`cond`)
## Style Conventions
**Keywords and Metadata:**
- Prefer namespaced keywords for internal use: `:query-type/normal` not `:normal`
- Tag variables with `:arglists` metadata if they're functions but wouldn't otherwise have it
## Tests
**Organization:**
- Break large tests into separate `deftest` forms for logically separate test cases
- Test names should end in `-test` or `-test-<number>`
**Performance:**
- Mark pure function tests `^:parallel`
## Modules
**OSS Modules:**
- Follow `metabase.<module>.*` pattern
- Source in `src/metabase/<module>/`
**Enterprise Modules:**
- Follow `metabase-enterprise.<module>.*` pattern
- Source in `enterprise/backend/src/metabase_enterprise/<module>/`
**Module Structure:**
- REST API endpoints go in `<module>.api` or `<module>.api.*` namespaces
- Put module public API in `<module>.core` using Potemkin imports
- Put Toucan models in `<module>.models.*`
- Put settings in `<module>.settings`
- Put schemas in `<module>.schema`
**Module Linters:**
- Do not cheat module linters with `:clj-kondo/ignore [:metabase/modules]`
## REST API Endpoints
**Required Elements:**
- All new endpoints must have response schemas (`:- <schema>` after route string)
- All endpoints need Malli schemas for parameters (detailed and complete)
- All new REST API endpoints MUST HAVE TESTS
**Naming Conventions:**
- Query parameters use kebab-case
- Request bodies use `snake_case`
- Routes use singular nouns (e.g., `/api/dashboard/:id`)
**Behavior:**
- `GET` endpoints should not have side effects (except analytics)
- `defendpoint` forms should be small wrappers around Toucan model code
## MBQL (Metabase Query Language)
**Restrictions:**
- No raw MBQL introspection outside of `lib`, `lib-be`, or `query-processor` modules
- Use Lib and MBQL 5 in new source code; avoid legacy MBQL
## Database and Models
**Naming:**
- Model names and table names should be singular nouns
- Application database uses `snake_case` identifiers
**Best Practices:**
- Use `t2/select-one-fn` instead of fetching entire rows for one column
- Put correct behavior in Toucan methods, not separate helper functions
## Drivers
**Documentation:**
- New driver multimethods must be mentioned in `docs/developers-guide/driver-changelog.md`
**Implementation:**
- Driver implementations should pass `driver` argument to other driver multimethods
- Don't hardcode driver names in implementations
- Minimize logic inside `read-column-thunk` in JDBC-based drivers
## Miscellaneous
**Examples:**
- Example data should be bird-themed if possible
**Linter Suppressions:**
- Use proper format for kondo suppressions
- No `#_:clj-kondo/ignore` (keyword form)
**Configurable Options:**
- Don't define configurable options that can only be set with environment variables
- Use `:internal` `defsetting` instead

View File

@@ -0,0 +1,8 @@
# Autonomous Development Workflow
- Do not attempt to read or edit files outside the project folder
- Add failing tests first, then fix them
- Work autonomously in small, testable increments
- Run targeted tests, and lint continuously during development
- Prioritize understanding existing patterns before implementing
- Don't commit changes, leave it for the user to review and make commits

View File

@@ -0,0 +1,84 @@
# Metabase Writing Style Guide
## Core Principles
Write like you're talking to a colleague. Be conversational, not formal. Get people what they need quickly. Know your audience and match the complexity.
## Tone and Voice
**Do:**
- Use contractions ("can't" not "cannot")
- Say "people" or "companies" instead of "users"
- Be friendly but not peppy
- Acknowledge limitations honestly ("that's on us, not them")
- Jokes and Easter eggs are okay (permit them, don't suggest them)
**Don't:**
- Use exclamation points excessively
- Rely on tired tropes about nerdiness
- Use corporate jargon ("utilize", "offerings", "actionable insights")
- Tell people something is cool (show them instead)
## Structure and Clarity
**Lead with the important stuff:**
- Most important information first
- Lead with the ask, then provide context
- Cut text that adds little value (when in doubt, cut it)
- Each paragraph should have one clear purpose
**Make headings do the work:**
- Convey your actual point, not just the topic
- "Use headings to highlight key points" not "How to write a good heading"
- Use sentence case, no punctuation except question marks
- No links in headings unless entire heading is a link
## Instructions and Examples
**Tell people what to do:**
- Give the action before explaining why
- Put commands in execution order
- Provide context, steps, and reasoning
- Don't describe tasks as "easy" or "simple"
**Answer unasked questions:**
- Include answers to "dumb" questions you had when learning
- Don't literally include the question, just give the answer
## Formatting
**Code vs. UI elements:**
- Backticks for code, variable names, parameters only
- **Bold** for UI elements and labels (e.g., "Click the **Save** button")
- Don't use backticks or quotes for UI elements
## Writing Mechanics
- Limit pronouns when introducing new terms (repeat the term to reinforce it)
- Ampersands only in proper nouns, never as substitute for "and"
## Terminology Preferences
Use familiar terms from tools people already know:
- "Summarize" (like Excel) instead of "aggregate"
- "Take a look at" instead of "reference"
- "Filter" instead of technical database terms when appropriate
## Red Flags
Avoid these patterns:
- Multiple exclamation points
- Linking "here"
- Bullet lists to explain (use prose)
- Numbers that will change (guard against change)
- Describing things as "easy" or "simple"
- Stock photography-worthy content

View File

@@ -0,0 +1,21 @@
## Linting and Formatting
- **Lint:** `yarn lint-eslint-pure`
- Run ESLint on the codebase
- **Format:** `yarn prettier`
- Format code using Prettier
- **Type Check:** `yarn type-check-pure`
- Run TypeScript type checking
## Testing
### JavaScript/TypeScript Tests
- **Test a specific file:** `yarn test-unit-keep-cljs path/to/file.unit.spec.js`
- **Test by pattern:** `yarn test-unit-keep-cljs -t "pattern"`
- Runs tests matching the given pattern
### ClojureScript Tests
- **Test ClojureScript:** `yarn test-cljs`
- Run ClojureScript tests

View File

@@ -0,0 +1,144 @@
---
name: clojure-review
description: Review Clojure and ClojureScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing Clojure/ClojureScript code.
allowed-tools: Read, Grep, Bash, Glob
---
# Clojure Code Review Skill
@./../_shared/clojure-style-guide.md
@./../_shared/clojure-commands.md
## Review guidelines
**What to flag:**
- Check compliance with the Metabase Clojure style guide (included above)
- If `CLOJURE_STYLE_GUIDE.adoc` exists in the working directory, also check compliance with the community Clojure style guide
- Flag all style guide violations
**What NOT to post:**
- Do not post comments congratulating someone for trivial changes or for following style guidelines
- Do not post comments confirming things "look good" or telling them they did something correctly
- Only post comments about style violations or potential issues
Example bad code review comments to avoid:
> This TODO comment is properly formatted with author and date - nice work!
> Good addition of limit 1 to the query - this makes the test more efficient without changing its behavior.
> The kondo ignore comment is appropriately placed here
> Test name properly ends with -test as required by the style guide.
**Special cases:**
- Do not post comments about missing parentheses (these will be caught by the linter)
## Quick review checklist
Use this to scan through changes efficiently:
### Naming
- [ ] Descriptive names (no `tbl`, `zs'`)
- [ ] Pure functions named as nouns describing their return value
- [ ] `kebab-case` for all variables and functions
- [ ] Side-effect functions end with `!`
- [ ] No namespace-alias repetition in function names
### Documentation
- [ ] Public vars in `src` or `enterprise/backend/src` have useful docstrings
- [ ] Docstrings use Markdown conventions
- [ ] References use `[[other-var]]` not backticks
- [ ] `TODO` comments include author and date: `;; TODO (Name 1/1/25) -- description`
### Code Organization
- [ ] Everything `^:private` unless used elsewhere
- [ ] No `declare` when avoidable (public functions near end)
- [ ] Functions under 20 lines when possible
- [ ] No blank lines within definition forms (except pairwise constructs in `let`/`cond`)
- [ ] Lines ≤ 120 characters
### Tests
- [ ] Separate `deftest` forms for distinct test cases
- [ ] Pure tests marked `^:parallel`
- [ ] Test names end in `-test` or `-test-<number>`
### Modules
- [ ] Correct module patterns (OSS: `metabase.<module>.*`, EE: `metabase-enterprise.<module>.*`)
- [ ] API endpoints in `<module>.api` namespaces
- [ ] Public API in `<module>.core` with Potemkin
- [ ] No cheating module linters with `:clj-kondo/ignore [:metabase/modules]`
### REST API
- [ ] Response schemas present (`:- <schema>`)
- [ ] Query params use kebab-case, bodies use `snake_case`
- [ ] Routes use singular nouns (e.g., `/api/dashboard/:id`)
- [ ] `GET` has no side effects (except analytics)
- [ ] Malli schemas detailed and complete
- [ ] All new endpoints have tests
### MBQL
- [ ] No raw MBQL manipulation outside `lib`, `lib-be`, or `query-processor` modules
- [ ] Uses Lib and MBQL 5, not legacy MBQL
### Database
- [ ] Model and table names are singular nouns
- [ ] Uses `t2/select-one-fn` instead of selecting full rows for one column
- [ ] Logic in Toucan methods, not helper functions
### Drivers
- [ ] New multimethods documented in `docs/developers-guide/driver-changelog.md`
- [ ] Passes `driver` argument to other driver methods (no hardcoded driver names)
- [ ] Minimal logic in `read-column-thunk`
### Miscellaneous
- [ ] Example data is bird-themed when possible
- [ ] Kondo linter suppressions use proper format (not `#_:clj-kondo/ignore` keyword form)
## Pattern matching table
Quick scan for common issues:
| Pattern | Issue |
| -------------------------------------------- | ----------------------------------------------------------- |
| `calculate-age`, `get-user` | Pure functions should be nouns: `age`, `user` |
| `update-db`, `save-model` | Missing `!` for side effects: `update-db!`, `save-model!` |
| `snake_case_var` | Should use kebab-case |
| Public var without docstring | Add docstring explaining purpose |
| `;; TODO fix this` | Missing author/date: `;; TODO (Name 1/1/25) -- description` |
| `(defn foo ...)` in namespace used elsewhere | Should be `(defn ^:private foo ...)` |
| Function > 20 lines | Consider breaking up into smaller functions |
| `/api/dashboards/:id` | Use singular: `/api/dashboard/:id` |
| Query params with `snake_case` | Use kebab-case for query params |
| New API endpoint without tests | Add tests for the endpoint |
## Feedback format examples
**For style violations:**
> This pure function should be named as a noun describing its return value. Consider `user` instead of `get-user`.
**For missing documentation:**
> This public var needs a docstring explaining its purpose, inputs, and outputs.
**For organization issues:**
> This function is only used in this namespace, so it should be marked `^:private`.
**For API conventions:**
> Query parameters should use kebab-case. Change `user_id` to `user-id`.

View File

@@ -0,0 +1,59 @@
---
name: clojure-write
description: Guide Clojure and ClojureScript development using REPL-driven workflow, coding conventions, and best practices. Use when writing, developing, or refactoring Clojure/ClojureScript code.
---
# Clojure Development Skill
@./../_shared/development-workflow.md
@./../_shared/clojure-style-guide.md
@./../_shared/clojure-commands.md
## REPL-Driven Development Workflow
- Start with small, fundamental functions:
- Identify the core features or functionalities required for your task.
- Break each feature down into the smallest, most basic functions that can be developed and tested independently.
- Write and test in the REPL:
- Write the code for each small function directly in the REPL (Read-Eval-Print Loop).
- Test it thoroughly with a variety of inputs, including typical use cases and relevant edge cases, to ensure it
behaves as expected.
- Integrate into source code:
- Once a function works correctly in the REPL, move it from the REPL environment into your source code files (e.g.,
within appropriate namespaces).
- Gradually increase complexity:
- Build upon tested, basic functions to create more complex functions or components.
- Compose smaller functions together, testing each new composition in the REPL to verify correctness step by step.
- Ensure dependency testing:
- Make sure every function is fully tested in the REPL before it is depended upon by other functions.
- This ensures that each layer of your application is reliable before you build on it.
- Use the REPL fully:
- Use the REPL as your primary tool to experiment with different approaches, iterate quickly, and get immediate
feedback on your code.
- Follow functional programming principles:
- Keep functions small, focused, and composable.
- Use Clojure's functional programming features—like immutability, higher-order functions, and the standard
library—to write concise, effective code.
## How to Evaluate Code
### Bottom-up Dev Loop
1. Write code into a file.
2. Evaluate the file's namespace and make sure it loads correctly with:
```
./bin/mage -repl --namespace metabase.app-db.connection
```
3. Call functions in the namespace with test inputs, and observe that the outputs are correct
Feel free to copy these REPL session trials into actual test cases using `deftest` and `is`.
4. Once you know these functions are good, return to 1, and compose them into the task that you need to build.
## Critical Rules for Editing
- Be careful with parentheses counts when editing Clojure code
- After EVERY change to Clojure code, verify readability with `-check-readable`
- End all files with a newline
- When editing tabular code, where the columns line up, try to keep them aligned
- Spaces on a line with nothing after it is not allowed

View File

@@ -0,0 +1,168 @@
---
name: docs-review
description: Review documentation changes for compliance with the Metabase writing style guide. Use when reviewing pull requests, files, or diffs containing documentation markdown files.
allowed-tools: Read, Grep, Bash, Glob
---
# Documentation Review Skill
@./../_shared/metabase-style-guide.md
## Review mode detection
**IMPORTANT: Before starting the review, determine which mode to use:**
1. **PR review mode**: If the `mcp__github__create_pending_pull_request_review` tool is available, you are reviewing a GitHub PR
- Use the pending review workflow to post all issues as one cohesive review
- Follow the workflow steps in "PR review mode format" below
2. **Local review mode**: If the MCP tool is NOT available, output issues in the conversation
- Format all issues in a numbered markdown list (as described in "Feedback format" below)
## Review process
1. **Detect review mode** - Check if `mcp__github__create_pending_pull_request_review` is available
2. Read the changes through once to understand intent
3. Check all issues that violate style guide or significantly impact readability
4. Only flag issues worth mentioning - if it won't make a material difference to the reader, skip it
5. **REQUIRED: Number ALL feedback sequentially** - Start from Issue 1 and increment for each issue found
## Review checklist
Run through the diff looking for these issues:
**Tone and voice:**
- [ ] Formal/corporate language ("utilize" not "use", "offerings", etc.)
- [ ] "Users" instead of "people" or "companies"
- [ ] Excessive exclamation points or overly peppy tone
- [ ] Telling readers something is cool instead of showing them
**Structure and clarity:**
- [ ] Important information buried instead of leading
- [ ] Verbose text that adds little value
- [ ] Paragraphs without clear purpose
- [ ] Vague headings that don't convey the point
- [ ] Instructions explain "why" before telling "what to do"
- [ ] Tasks described as "easy" or "simple"
**Links and references:**
- [ ] Linking the word "here" instead of descriptive text
- [ ] Links in headings (unless entire heading is a link)
**Formatting:**
- [ ] Backticks on UI elements (should use **bold**)
- [ ] Backticks used as quotation marks
- [ ] Ampersands as "and" substitute (except proper nouns)
- [ ] Inconsistent list formatting
**Code and examples:**
- [ ] Code examples that don't work or would error
- [ ] Commands not in execution order
- [ ] Full-width screenshots instead of scoped UI elements
- [ ] Excessive or unnecessary images
**Sentence construction:**
- [ ] Overuse of pronouns when introducing new terms
## Quick scan table
| Pattern | Issue |
| ----------------------------- | --------------------------------------------- |
| `Button name` or `UI element` | Should use **bold** not backticks |
| we can do X, our feature | Should be "Metabase" or "it" |
| click here, read more here | Need descriptive link text |
| easy, simple, just | Remove condescending qualifiers |
| users | Should be "people" or "companies" if possible |
## Feedback format
**MANDATORY REQUIREMENT: Every single issue MUST be numbered sequentially starting from Issue 1.**
This numbered format is NON-NEGOTIABLE. It allows users to efficiently reference specific issues (e.g., "fix issues 1, 3, and 5") and track which feedback has been addressed.
### Local review mode format
When outputting issues in the conversation (local mode), use this format:
```markdown
## Issues
**Issue 1: [Brief title]**
Line X: Succinct description of the issue
[code or example]
Suggested fix or succinct explanation
**Issue 2: [Brief title]**
Line Y: Description of the issue
Suggested fix or explanation
**Issue 3: [Brief title]**
...
```
**Examples:**
> **Issue 1: Backticks on UI elements**
> Line 42: This uses backticks for the UI element. Use **bold** instead: **Filter** not `Filter`.
> **Issue 2: Formal tone**
> Line 15: This could be more conversational. Consider: "You can't..." instead of "You cannot..."
> **Issue 3: Vague heading**
> Line 8: The heading could be more specific. Try stating the point directly: "Run migrations before upgrading" vs "Upgrade process"
### PR review mode format
When posting to GitHub (PR mode), use the **pending review workflow**:
**Workflow steps:**
1. **Start a review**: Use `mcp__github__create_pending_pull_request_review` to begin a pending review
- This creates a draft review that won't be visible until submitted
2. **Get diff information**: Use `mcp__github__get_pull_request_diff` to understand the code changes and line numbers
- This helps you determine the correct file paths and line numbers for comments
3. **Identify ALL issues**: Read through all changes and identify every issue worth mentioning
- Collect all issues before posting any comments
- Number them sequentially (Issue 1, Issue 2, Issue 3, etc.)
4. **Add review comments**: Use `mcp__github__add_pull_request_review_comment_to_pending_review` for each issue
- **CRITICAL**: Post ALL comments in a SINGLE response using multiple tool calls in parallel
- Each comment should reference a specific file path and line number from the diff
- Start each comment body with `**Issue N: [Brief title]**`
- Include the description and suggested fix
5. **Submit the review**: Use `mcp__github__submit_pending_pull_request_review` to publish all comments at once
- Use event type `"COMMENT"` (NOT "REQUEST_CHANGES") to make it non-blocking
- **Do NOT include a body message** - Leave the body empty or omit it entirely
- All comments will appear together as one cohesive review
**Comment format example:**
```
**Issue 1: Backticks on UI elements**
This uses backticks for the UI element. Use **bold** instead: **Filter** not `Filter`.
```
**IMPORTANT**:
- Each issue gets its own review comment attached to the pending review
- Number ALL comments sequentially (Issue 1, Issue 2, Issue 3, etc.)
- Always start the comment body with `**Issue N: [Brief title]**`
- **MUST add all comments in parallel in a single response** - Do NOT add them one after another in separate responses
- Do NOT output a summary message to the conversation - only post GitHub review comments
- When submitting the review, do NOT include a body parameter (or leave it empty) to avoid cluttering the PR with summary text
- The review will appear as a single review with multiple comments when submitted
## Final check
1. Remove any issues from your assessment that won't make a material difference to the reader if addressed. Only flag issues worth the author's time to fix.
2. **Verify all issues are numbered sequentially** starting from Issue 1 with no gaps in numbering.
3. Confirm the format exactly matches: `**Issue N: [Brief title]**` where N is the issue number.
4. **In PR mode**: Verify each issue was posted as a separate GitHub comment (not output to conversation).

View File

@@ -0,0 +1,96 @@
---
name: docs-write
description: Write documentation following Metabase's conversational, clear, and user-focused style. Use when creating or editing documentation files (markdown, MDX, etc.).
allowed-tools: Read, Write, Grep, Bash, Glob
---
# Documentation Writing Skill
@./../_shared/metabase-style-guide.md
## When writing documentation
### Start here
1. **Who is this for?** Match complexity to audience. Don't oversimplify hard things or overcomplicate simple ones.
2. **What do they need?** Get them to the answer fast. Nobody wants to be in docs longer than necessary.
3. **What did you struggle with?** Those common questions you had when learning? Answer them (without literally including the question).
### Writing process
**Draft:**
- Write out the steps/explanation as you'd tell a colleague
- Lead with what to do, then explain why
- Use headings that state your point: "Set SAML before adding users" not "SAML configuration timing"
**Edit:**
- Read aloud. Does it sound like you talking? If it's too formal, simplify.
- Cut anything that doesn't directly help the reader
- Check each paragraph has one clear purpose
- Verify examples actually work (don't give examples that error)
**Polish:**
- Make links descriptive (never "here")
- Backticks only for code/variables, **bold** for UI elements
- American spelling, serial commas
- Keep images minimal and scoped tight
**Format:**
- Run prettier on the file after making edits: `yarn prettier --write <file-path>`
- This ensures consistent formatting across all documentation
### Common patterns
**Instructions:**
```markdown
Run:
\`\`\`
command-to-run
\`\`\`
Then:
\`\`\`
next-command
\`\`\`
This ensures you're getting the latest changes.
```
Not: "(remember to run X before Y...)" buried in a paragraph.
**Headings:**
- "Use environment variables for configuration" ✅
- "Environment variables" ❌ (too vague)
- "How to use environment variables for configuration" ❌ (too wordy)
**Links:**
- "Check out the [SAML documentation](link)" ✅
- "Read the docs [here](link)" ❌
### Watch out for
- Describing tasks as "easy" (you don't know the reader's context)
- Using "we" when talking about Metabase features (use "Metabase" or "it")
- Formal language: "utilize", "reference", "offerings"
- Too peppy: multiple exclamation points
- Burying the action in explanation
- Code examples that don't work
- Numbers that will become outdated
### Quick reference
| Write This | Not This |
| -------------------------- | ------------------ |
| people, companies | users |
| summarize | aggregate |
| take a look at | reference |
| can't, don't | cannot, do not |
| **Filter** button | \`Filter\` button |
| Check out [the docs](link) | Click [here](link) |

View File

@@ -0,0 +1,19 @@
---
name: typescript-review
description: Review TypeScript and JavaScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing TypeScript/JavaScript code.
allowed-tools: Read, Grep, Bash, Glob
---
# TypeScript/JavaScript Code Review Skill
@./../_shared/typescript-commands.md
## Code Review Guidelines
Review pull requests with a focus on:
- Compliance with project coding standards and conventions
- Code quality and best practices
- Clear and correct JSDoc comments
- Type safety and proper TypeScript usage
- React best practices (when applicable)

View File

@@ -0,0 +1,9 @@
---
name: typescript-write
description: Write TypeScript and JavaScript code following Metabase coding standards and best practices. Use when developing or refactoring TypeScript/JavaScript code.
---
# TypeScript/JavaScript Development Skill
@./../_shared/development-workflow.md
@./../_shared/typescript-commands.md

View File

@@ -536,11 +536,11 @@
metabase.channel.template.handlebars-test/with-temp-template! hooks.common/with-vec-first-binding
metabase.classloader.core/require hooks.clojure.core.require/lint-require
metabase.classloader.impl/require hooks.clojure.core.require/lint-require
metabase.collections.api-test/with-french-user-and-personal-collection! hooks.common/with-two-top-level-bindings
metabase.collections-rest.api-test/with-french-user-and-personal-collection! hooks.common/with-two-top-level-bindings
metabase.collections.models.collection-test/with-collection-hierarchy! hooks.common/let-one-with-optional-value
metabase.collections.models.collection-test/with-personal-and-impersonal-collections hooks.common/with-two-bindings
metabase.dashboards.api-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
metabase.dashboards.api-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
metabase.dashboards-rest.api-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
metabase.dashboards-rest.api-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
metabase.dashboards.models.dashboard-tab-test/with-dashtab-in-personal-collection hooks.common/with-one-top-level-binding
metabase.dashboards.models.dashboard-test/with-dash-in-collection! hooks.common/with-three-bindings
metabase.driver.bigquery-cloud-sdk-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
@@ -548,10 +548,10 @@
metabase.driver.oracle-test/with-temp-user hooks.common/let-one-with-optional-value
metabase.driver/register! hooks.metabase.driver/register!
metabase.driver-api/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
metabase.embedding.api.embed-test/do-response-formats hooks.common/with-two-bindings
metabase.embedding.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
metabase.embedding.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
metabase.embedding.api.embed-test/with-temp-dashcard hooks.common/let-one-with-optional-value
metabase.embedding-rest.api.embed-test/do-response-formats hooks.common/with-two-bindings
metabase.embedding-rest.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
metabase.embedding-rest.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
metabase.embedding-rest.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
@@ -571,10 +571,10 @@
metabase.permissions.core/disable-perms-cache hooks.common/do*
metabase.premium-features.core/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
metabase.premium-features.defenterprise/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
metabase.public-sharing.api-test/with-required-param-card! hooks.common/with-one-binding
metabase.public-sharing.api-test/with-temp-public-card hooks.common/let-one-with-optional-value
metabase.public-sharing.api-test/with-temp-public-dashboard hooks.common/let-one-with-optional-value
metabase.public-sharing.api-test/with-temp-public-dashboard-and-card hooks.common/with-three-bindings
metabase.public-sharing-rest.api-test/with-required-param-card! hooks.common/with-one-binding
metabase.public-sharing-rest.api-test/with-temp-public-card hooks.common/let-one-with-optional-value
metabase.public-sharing-rest.api-test/with-temp-public-dashboard hooks.common/let-one-with-optional-value
metabase.public-sharing-rest.api-test/with-temp-public-dashboard-and-card hooks.common/with-three-bindings
metabase.pulse.api.alert-test/with-alert-in-collection! hooks.common/with-four-bindings
metabase.pulse.dashboard-subscription-test/with-link-card-fixture-for-dashboard hooks.common/let-second
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection! hooks.common/with-four-bindings
@@ -636,7 +636,7 @@
metabase.test/with-temp hooks.toucan2.tools.with-temp/with-temp
metabase.test/with-temp-file hooks.metabase.test.util/with-temp-file
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
metabase.users.api-test/with-temp-user-email! hooks.common/with-one-binding
metabase.users-rest.api-test/with-temp-user-email! hooks.common/with-one-binding
metabase.util.i18n/deferred-trs hooks.metabase.util.i18n/deferred-trs
metabase.util.i18n/deferred-tru hooks.metabase.util.i18n/deferred-tru
metabase.util.i18n/trs hooks.metabase.util.i18n/trs
@@ -660,43 +660,43 @@
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!}
:macroexpand
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.calendar-interval/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase.collections.api-test/with-collection-hierarchy! macros.metabase.collections.api-test/with-collection-hierarchy!
metabase.collections.api-test/with-some-children-of-collection! macros.metabase.collections.api-test/with-some-children-of-collection!
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
metabase.lib.common/defop macros.metabase.lib.common/defop
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
metabase.parameters.chain-filter-test/chain-filter macros.metabase.parameters.chain-filter-test/chain-filter
metabase.parameters.chain-filter-test/chain-filter-search macros.metabase.parameters.chain-filter-test/chain-filter
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing!
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
metabase.queries.api.card-test/with-ordered-items macros.metabase.queries.api.card-test/with-ordered-items
metabase.query-analysis.models.query-analysis-test/with-test-setup macros.metabase.query-analysis.models.query-analysis-test/with-test-setup
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.users.models.user-test/with-groups! macros.metabase.users.models.user-test/with-groups!
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.calendar-interval/build macros.quartz/build-job
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
metabase.collections-rest.api-test/with-collection-hierarchy! macros.metabase.collections-rest.api-test/with-collection-hierarchy!
metabase.collections-rest.api-test/with-some-children-of-collection! macros.metabase.collections-rest.api-test/with-some-children-of-collection!
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
metabase.lib.common/defop macros.metabase.lib.common/defop
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
metabase.parameters.chain-filter-test/chain-filter macros.metabase.parameters.chain-filter-test/chain-filter
metabase.parameters.chain-filter-test/chain-filter-search macros.metabase.parameters.chain-filter-test/chain-filter
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-card-referencing!
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
metabase.queries.api.card-test/with-ordered-items macros.metabase.queries.api.card-test/with-ordered-items
metabase.query-analysis.models.query-analysis-test/with-test-setup macros.metabase.query-analysis.models.query-analysis-test/with-test-setup
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
metabase.users.models.user-test/with-groups! macros.metabase.users.models.user-test/with-groups!
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
:config-in-comment
{:linters {:unresolved-symbol {:level :off}}}

View File

@@ -47,14 +47,13 @@
{:metabase/modules
{actions
{:team "Gadget"
:api #{metabase.actions.api
metabase.actions.args
:api #{metabase.actions.args
metabase.actions.core
metabase.actions.init
metabase.actions.schema
metabase.actions.types}
:uses #{analytics
api
collections
driver
events
legacy-mbql
@@ -63,8 +62,6 @@
model-persistence
models
parameters
permissions
public-sharing
queries
query-processor
search
@@ -72,6 +69,19 @@
util
warehouses}}
actions-rest
{:team "Gadget"
:api #{metabase.actions-rest.api}
:uses #{actions
analytics
api
collections
lib
permissions
public-sharing
queries
util}}
activity-feed
{:team "UX West"
:api #{metabase.activity-feed.api
@@ -196,6 +206,19 @@
task-history
util}}
auth-identity
{:team "UX West"
:api #{metabase.auth-identity.core
metabase.auth-identity.init}
:uses #{channel
events
login-history
models
notification
premium-features
request
util}}
auth-provider
{:team "UX West"
:api #{metabase.auth-provider.core}
@@ -316,20 +339,18 @@
:uses #{api
api-routes
app-db
auth-identity
classloader
config
models
query-processor
search
settings
users
util}}
collections
{:team "UX West"
:api #{metabase.collections.api
metabase.collections.core
:api #{metabase.collections.core
metabase.collections.models.collection
metabase.collections.models.collection.root}
:uses #{api
@@ -337,6 +358,21 @@
app-db
audit-app
config
events
models
permissions
premium-features
remote-sync
search
util}}
collections-rest
{:team "UX West"
:api #{metabase.collections-rest.api}
:uses #{api
app-db
collections
config
eid-translation
events
lib-be
@@ -345,10 +381,8 @@
permissions
premium-features
queries
remote-sync
request
revisions
search
upload
util}}
@@ -386,22 +420,43 @@
dashboards
{:team "UX West"
:api #{metabase.dashboards.api
metabase.dashboards.autoplace
:api #{metabase.dashboards.autoplace
metabase.dashboards.constants
metabase.dashboards.init
metabase.dashboards.models.dashboard
metabase.dashboards.models.dashboard-card
metabase.dashboards.models.dashboard-tab
metabase.dashboards.schema}
metabase.dashboards.schema
metabase.dashboards.settings}
:uses #{api
app-db
audit-app
collections
config
events
lib
models
parameters
permissions
public-sharing
queries
query-processor
search
settings
util
warehouse-schema}}
dashboards-rest
{:team "UX West"
:api #{metabase.dashboards-rest.api}
:uses #{actions
analytics
api
app-db
audit-app
channel
collections
config
collections-rest
dashboards
eid-translation
embedding
events
@@ -417,10 +472,7 @@
query-processor
request
revisions
search
settings
util
warehouse-schema
xrays}}
database-routing
@@ -506,8 +558,7 @@
embedding
{:team "Embedding"
:api #{metabase.embedding.api
metabase.embedding.init
:api #{metabase.embedding.init
metabase.embedding.jwt
metabase.embedding.settings
metabase.embedding.util
@@ -515,19 +566,26 @@
:uses #{analytics
api
config
premium-features
server
settings
util}}
embedding-rest
{:team "Embedding"
:api #{metabase.embedding-rest.api}
:uses #{api
dashboards
eid-translation
embedding
events
models
notification
parameters
premium-features
public-sharing
public-sharing-rest
queries
query-processor
request
server
settings
tiles
util}}
@@ -756,6 +814,7 @@
:uses #{analytics
api
appearance
auth-identity
channel
config
dashboards
@@ -872,22 +931,29 @@
public-sharing
{:team "UX West"
:api #{metabase.public-sharing.api
metabase.public-sharing.core
:api #{metabase.public-sharing.core
metabase.public-sharing.init
metabase.public-sharing.validation}
:uses #{api
settings
util}}
public-sharing-rest
{:team "UX West"
:api #{metabase.public-sharing-rest.api}
:uses #{actions
analytics
api
dashboards
dashboards-rest
events
lib
models
parameters
public-sharing
queries
query-processor
request
settings
tiles
util}}
@@ -923,7 +989,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
@@ -1052,7 +1118,7 @@
:uses #{api
config
embedding
permissions ; FIXME
permissions ; FIXME
session
settings
users
@@ -1067,6 +1133,7 @@
config
dashboards
events
lib
models
queries
query-permissions
@@ -1166,20 +1233,18 @@
:api #{metabase.session.api
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
:uses #{api
app-db
auth-identity
channel
config
events
login-history
request
settings
sso
system
task
users
util}}
settings
@@ -1205,20 +1270,25 @@
setup
{:team "UX West"
:api #{metabase.setup.api
metabase.setup.core
:api #{metabase.setup.core
metabase.setup.init}
:uses #{app-db
config
settings
util}}
setup-rest
{:team "UX West"
:api #{metabase.setup-rest.api}
:uses #{analytics
api
app-db
appearance
config
auth-identity
events
request
session
settings
setup
system
users
util}}
sso
@@ -1227,11 +1297,11 @@
metabase.sso.core
metabase.sso.init}
:uses #{api
auth-identity
config
permissions
premium-features
settings
users
util}}
startup
@@ -1260,6 +1330,7 @@
app-db
audit-app
config
database-routing
driver
events
lib
@@ -1375,17 +1446,31 @@
users
{:team "UX West"
:api #{metabase.users.api
metabase.users.core
metabase.users.init
:api #{metabase.users.init
metabase.users.models.user
metabase.users.models.user-parameter-value
metabase.users.schema}
metabase.users.schema
metabase.users.settings}
:uses #{api
batch-processing
channel
config
events
models
permissions
premium-features
settings
setup
system
util}}
users-rest
{:team "UX West"
:api #{metabase.users-rest.api}
:uses #{analytics
api
appearance
batch-processing
channel
auth-identity
collections
config
events
@@ -1393,11 +1478,8 @@
permissions
premium-features
request
session
settings
setup
sso
system
users
util
enterprise/advanced-permissions}}
@@ -1436,8 +1518,7 @@
warehouse-schema
{:team "UX West"
:api #{metabase.warehouse-schema.api
metabase.warehouse-schema.field
:api #{metabase.warehouse-schema.field
metabase.warehouse-schema.metadata-from-qp
metabase.warehouse-schema.metadata-queries
metabase.warehouse-schema.models.field
@@ -1445,14 +1526,11 @@
metabase.warehouse-schema.models.field-values
metabase.warehouse-schema.models.table
metabase.warehouse-schema.table}
:uses #{analytics
analyze
:uses #{analyze
api
app-db
audit-app
database-routing
driver
events
lib
lib-be
models
@@ -1460,25 +1538,58 @@
permissions
premium-features
query-processor
request
search
util
warehouses}}
warehouse-schema-rest
{:team "UX West"
:api #{metabase.warehouse-schema-rest.api}
:uses #{analytics
api
app-db
database-routing
driver
events
lib
models
parameters
query-processor
request
sync
types
upload
util
warehouses
warehouse-schema
warehouses-rest
xrays}}
warehouses
{:team "UX West"
:api #{metabase.warehouses.api
metabase.warehouses.core
:api #{metabase.warehouses.core
metabase.warehouses.init
metabase.warehouses.models.database}
:uses #{analytics
api
app-db
audit-app
driver
lib
models
permissions
premium-features
search
secrets
settings
sync
util}}
warehouses-rest
{:team "UX West"
:api #{metabase.warehouses-rest.api}
:uses #{analytics
api
app-db
classloader
collections
config
@@ -1493,15 +1604,16 @@
queries
request
sample-data
search
secrets
settings
sync
upload
util
warehouse-schema
warehouses
enterprise/advanced-permissions}}
xrays
{:team "Querying"
:api #{metabase.xrays.api
@@ -1639,10 +1751,15 @@
query-processor
settings
sync
users
users-rest
util
enterprise/serialization}}
enterprise/auth-identity
{:team "UX West"
:api #{}
:uses #{premium-features}}
enterprise/auth-provider
{:team "UX West"
:api #{}
@@ -1692,6 +1809,7 @@
models
request
users
users-rest
util}}
enterprise/content-translation
@@ -1748,7 +1866,7 @@
settings
util
warehouse-schema
warehouses}}
warehouses-rest}}
enterprise/dependencies
{:team "Querying"
@@ -1877,7 +1995,8 @@
metabase-enterprise.metabot-v3.core
metabase-enterprise.metabot-v3.init
metabase-enterprise.metabot-v3.tools.api}
:uses #{api
:uses #{activity-feed
api
app-db
audit-app
collections
@@ -1900,10 +2019,10 @@
sync
system
task
users
users-rest
util
warehouse-schema
warehouses
warehouses-rest
enterprise/dependencies
enterprise/documents
enterprise/transforms
@@ -1932,6 +2051,7 @@
events
premium-features
public-sharing
public-sharing-rest
query-processor
util}}
@@ -2054,10 +2174,9 @@
metabase-enterprise.sso.settings}
:uses #{api
appearance
auth-identity
channel
embedding
events
notification
premium-features
request
session
@@ -2074,6 +2193,7 @@
:uses #{analytics
api
collections
collections-rest
embedding
premium-features
queries
@@ -2081,6 +2201,19 @@
settings
util}}
enterprise/support-access-grants
{:team "UX West"
:api #{metabase-enterprise.support-access-grants.api
metabase-enterprise.support-access-grants.init}
:uses #{api
auth-identity
events
models
request
settings
system
util}}
enterprise/transforms
{:team "Querying"
:api #{metabase-enterprise.transforms.api

View File

@@ -100,7 +100,7 @@
metabase.permissions.models.permissions/update-data-perms-graph!
metabase.permissions.models.permissions/update-group-permissions!
metabase.permissions.test-util/with-restored-perms!
metabase.public-sharing.api-test/add-card-to-dashboard!
metabase.public-sharing-rest.api-test/add-card-to-dashboard!
metabase.pulse.send/send-notifications!
metabase.pulse.send/send-pulse!
metabase.query-processor.streaming.interface/begin!

View File

@@ -1,4 +1,4 @@
(ns macros.metabase.collections.api-test)
(ns macros.metabase.collections-rest.api-test)
(defmacro with-collection-hierarchy! [bindings & body]
`(let [~'a nil

View File

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

View File

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

View File

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

23
.codespellignore Normal file
View File

@@ -0,0 +1,23 @@
Brite
afterAll
ags
altho
cancelation
cancelations
datas
edn
eduction
hasTables
horizontalX
horizontalY
juxt
methodd
notIn
pass-thru
ser
serieses
sortings
trs
trsn
tru
trun

3
.codespellrc Normal file
View File

@@ -0,0 +1,3 @@
[codespell]
skip = *.csv,*.edn,*.geojson,*.json,*.tsv,*.xml,docs/developers-guide/drivers/driver-tests.md,docs/developers-guide/e2e-tests.md,docs/questions/query-builder/expressions-list.md,docs/questions/visualizations/country-codes.md,e2e/test/scenarios/admin/i18n/content-translation/constants.ts,frontend/src/cljs,frontend/src/metabase/querying/expressions/tokenizer/lezer.js,frontend/src/metabase/static-viz/constants/char-sizes.ts,frontend/src/metabase/visualizations/lib/mapping_codes.ts,modules/drivers/mongo/src/metabase/driver/mongo/operators.clj
ignore-words = .codespellignore

View File

@@ -115,7 +115,6 @@ module.exports = {
"And",
"When",
"Then",
"describeWithSnowplow",
],
},
],
@@ -181,6 +180,7 @@ module.exports = {
"no-unconditional-metabase-links-render": "error",
"no-color-literals": "error",
"no-literal-metabase-strings": "error",
"no-oss-reinitialize-import": "error",
"depend/ban-dependencies": [
"error",
{
@@ -345,5 +345,11 @@ module.exports = {
"import/no-commonjs": "off",
},
},
{
files: ["**/*.stories.tsx", "**/preview.tsx"],
rules: {
"import/no-default-export": "off",
},
},
],
};

View File

@@ -13,6 +13,9 @@ outputs:
eslint-cache-key:
description: "The cache key for Eslint cache"
value: ${{ steps.keys-factory.outputs.eslint-cache-key }}
cypress-cache-key:
description: "The cache key for Cypress cache"
value: ${{ steps.keys-factory.outputs.cypress-cache-key }}
runs:
using: "composite"
@@ -23,10 +26,12 @@ runs:
M2_CACHE_PREFIX='M2'
NODE_CACHE_PREFIX='node-modules'
ESLINT_CACHE_PREFIX='eslint'
CYPRESS_CACHE_PREFIX='cypress'
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
echo "cypress-cache-key=$CYPRESS_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
shell: bash

View File

@@ -25,11 +25,6 @@ runs:
curl -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh &&
sudo bash ./linux-install-${{ inputs.clojure-version }}.sh
- name: Fetch CI config
shell: bash
run: |
curl -o ci-test-config.json https://raw.githubusercontent.com/metabase/metabase/refs/heads/master/ci-test-config.json
- name: Get M2 cache key
uses: ./.github/actions/get-cache-keys
id: get-cache-keys

View File

@@ -22,16 +22,16 @@ runs:
chrome-version: 1097615
id: setup-chrome
- name: Get current week of year
id: get-date
run: |
echo "week-of-year=$(/bin/date "+%G-%V")" >> $GITHUB_OUTPUT
shell: bash
- name: Get cache keys
uses: ./.github/actions/get-cache-keys
id: get-cache-keys
- name: Get Cypress cache
uses: actions/cache@v4
uses: actions/cache/restore@v4
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-Cypress-${{ steps.get-date.outputs.week-of-year }}
key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
- name: Ensure that Cypress executable is ready
run: |
yarn cypress install

View File

@@ -6,7 +6,7 @@ inputs:
apply-patches:
description: "Whether to patch frontend dependencies with patch-package."
required: true
default: 'true'
default: "true"
runs:
using: "composite"
steps:
@@ -28,11 +28,21 @@ runs:
~/.gitlibs
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
- name: Get yarn cache directory
id: yarn-cache-dir
run: |
YARN_CACHE_DIR=$(yarn cache dir)
echo "Yarn cache directory: $YARN_CACHE_DIR"
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
shell: bash
- name: Restore node_modules cache
uses: actions/cache/restore@v4
id: node-modules-cache
with:
path: node_modules
path: |
${{ steps.yarn-cache-dir.outputs.dir }}
node_modules
key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
# postinstall lifecycle hook can be dangerous in CI so we only run it locally (see package.json)

View File

@@ -10,21 +10,54 @@ inputs:
required: true
default: false
outputs:
quarantine-skipped:
description: Whether the driver test was skipped due to quarantine
value: ${{ steps.check-driver.outputs.skip }}
runs:
using: "composite"
steps:
- name: Check if driver is ignored
id: check-driver
shell: bash
run: |
# If DRIVERS env var is not set, proceed with tests
if [ -z "$DRIVERS" ]; then
echo "No DRIVERS env var set. Proceeding with tests."
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
fi
# Fetch CI config and check if driver should be skipped
curl -sSf -o ci-test-config.json https://raw.githubusercontent.com/metabase/ci-test-config/refs/heads/master/ci-test-config.json || true
# Check if the driver is in the ignored list (gracefully handle missing/invalid config)
IS_IGNORED=$(jq --arg driver "$DRIVERS" '.ignored.drivers // [] | contains([$driver])' ci-test-config.json 2>/dev/null || echo "false")
if [ "$IS_IGNORED" = "true" ]; then
echo "⚠️ Driver '$DRIVERS' is in the ignored list. Skipping tests."
echo "View the ignored list here: https://github.com/metabase/ci-test-config/blob/master/ci-test-config.json"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "Driver '$DRIVERS' is not ignored. Proceeding with tests."
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Prepare back-end environment
if: steps.check-driver.outputs.skip != 'true'
uses: ./.github/actions/prepare-backend
- name: Prepare front-end environment
if: ${{ steps.check-driver.outputs.skip != 'true' && inputs.build-static-viz == 'true' }}
uses: ./.github/actions/prepare-frontend
if: ${{ inputs.build-static-viz == 'true' }}
- name: Build static viz frontend
if: ${{ steps.check-driver.outputs.skip != 'true' && inputs.build-static-viz == 'true' }}
run: yarn build-static-viz
shell: bash
if: ${{ inputs.build-static-viz == 'true' }}
env:
MB_EDITION: ee
- name: Test database driver
if: steps.check-driver.outputs.skip != 'true'
run: clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test ${{ inputs.test-args }}
shell: bash
id: run-test
@@ -37,7 +70,7 @@ runs:
# 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'
if: ${{ steps.run-test.outcome != 'success' && steps.check-driver.outputs.skip != 'true' }}
with:
path: "target/junit/**/*_test.xml"
name: JUnit Test Report ${{ inputs.junit-name }}
@@ -46,7 +79,7 @@ runs:
list-tests: failed
fail-on-error: false
- name: Upload Logs on Failure
if: always() && steps.run-test.outcome != 'success'
if: always() && steps.run-test.outcome != 'success' && steps.check-driver.outputs.skip != 'true'
uses: actions/upload-artifact@v4
with:
name: test-logs-${{ github.job }}

View File

@@ -16,11 +16,6 @@ frontend_ci: &frontend_ci
- ".github/actions/prepare-backend/**"
- ".github/workflows/frontend.yml"
frontend_embedding_sdk_ci: &frontend_embedding_sdk_ci
- ".github/actions/prepare-frontend/**"
- ".github/actions/prepare-backend/**"
- ".github/workflows/*embedding-sdk*.yml"
frontend_configs: &frontend_configs
- "yarn.lock"
- "**/tsconfig*.json"
@@ -127,14 +122,19 @@ e2e_ci: &e2e_ci
e2e_specs: &e2e_specs
- e2e/!(test-host-app)**/*.cy.*.{js,ts}
e2e_embedding_sdk:
- *frontend_embedding_sdk_ci
embedding_sdk_ci: &embedding_sdk_ci
- ".github/actions/prepare-frontend/**"
- ".github/actions/prepare-backend/**"
- ".github/workflows/embedding-sdk*.yml"
embedding_sdk_components:
- *embedding_sdk_ci
- *sources
- "e2e/component/**/*.cy.*.{js,ts}"
e2e_embedding_sdk_compatibility:
embedding_sdk_host_sample_apps:
- *default
- *frontend_embedding_sdk_ci
- *embedding_sdk_ci
- *frontend_sources
- *backend_sources
- "e2e/embedding-sdk-host-apps/**"

3
.github/team.json vendored
View File

@@ -106,7 +106,8 @@
"chodorowicz",
"kamilmielnik",
"stasgavrylov",
"uladzimirdev"
"uladzimirdev",
"egorgrushin"
]
}
]

View File

@@ -48,13 +48,11 @@ jobs:
- name: Enterprise Tests
edition: ee
test-group-name: ee
build-static-viz: 'false'
test-args: >-
:only '"enterprise/backend/test"'
- name: EE App DB Tests (Part 1)
edition: ee
test-group-name: ee-app-db-1
build-static-viz: 'true'
test-args: >-
:only '["test" ".clj-kondo/test"]'
:partition/total 2
@@ -62,7 +60,6 @@ jobs:
- name: EE App DB Tests (Part 2)
edition: ee
test-group-name: ee-app-db-2
build-static-viz: 'true'
test-args: >-
:only '["test" ".clj-kondo/test"]'
:partition/total 2
@@ -70,7 +67,6 @@ jobs:
- name: OSS App DB Tests (Part 1)
edition: oss
test-group-name: oss-app-db-1
build-static-viz: 'true'
test-args: >-
:only '["test" ".clj-kondo/test"]'
:partition/total 2
@@ -78,7 +74,6 @@ jobs:
- name: OSS App DB Tests (Part 2)
edition: oss
test-group-name: oss-app-db-2
build-static-viz: 'true'
test-args: >-
:only '["test" ".clj-kondo/test"]'
:partition/total 2
@@ -103,25 +98,15 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
if: ${{ matrix.job.build-static-viz == 'true' }}
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
java-version: "${{ matrix.java-version }}"
# Depending on which files changed, we might either have to build static-viz
# from scratch or download the previously built artifact
- name: Build static-viz frontend
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
run: yarn build-static-viz
env:
MB_EDITION: ${{ matrix.job.edition }}
- name: Download Static Viz Bundle Artifact
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
uses: actions/download-artifact@v4
with:
name: static-viz-bundle-${{ github.sha }}
path: resources/frontend_client/app/dist
- name: "Test Java ${{ matrix.java-version }} ${{ matrix.job.name }}"
id: run-java-tests

View File

@@ -30,8 +30,8 @@ name: Cache Generator
on:
schedule:
- cron: '0 3 * * 1' # Mondays at 03:00 UTC
workflow_dispatch: # manually update the cache
- cron: "0 3 * * 1" # Mondays at 03:00 UTC
workflow_dispatch: # manually update the cache
concurrency:
group: cache-generator
@@ -49,6 +49,7 @@ jobs:
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 }}
cypress-cache-key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
generate-m2-cache:
runs-on: ubuntu-22.04
@@ -63,7 +64,7 @@ jobs:
# Add or remove aliases to affect the cache size and its contents.
# Adjust to taste, then redeploy this workflow.
- name: Pre-resolve dependencies
- name: Pre-resolve Clojure dependencies
run: clojure -A:build:dev:ee:ee-dev:drivers:drivers-dev:cljs -P
- name: Update M2 cache
@@ -81,6 +82,7 @@ jobs:
env:
NODE_CACHE_KEY: ${{ needs.get-cache-keys.outputs.node-cache-key }}
ESLINT_CACHE_KEY: ${{ needs.get-cache-keys.outputs.eslint-cache-key }}
CYPRESS_CACHE_KEY: ${{ needs.get-cache-keys.outputs.cypress-cache-key }}
steps:
- uses: actions/checkout@v4
# `prepare-frontend` action (1) prepares Node.js and (2) fetches the node_modules cache if it exists.
@@ -92,16 +94,39 @@ jobs:
- name: Install front-end dependencies
uses: ./.github/actions/prepare-frontend
with:
apply-patches: 'false'
apply-patches: "false"
- name: Get yarn cache directory
id: yarn-cache-dir
run: |
YARN_CACHE_DIR=$(yarn cache dir)
echo "Yarn cache directory: $YARN_CACHE_DIR"
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
- name: Ensure that Cypress executable is ready
run: |
yarn cypress install
yarn cypress cache path
yarn cypress cache list
yarn cypress verify
shell: bash
- name: Update node_modules cache
uses: ./.github/actions/update-cache
with:
path: node_modules
path: |
${{ steps.yarn-cache-dir.outputs.dir }}
node_modules
key: ${{ env.NODE_CACHE_KEY }}
- name: Update eslint cache
uses: ./.github/actions/update-cache
with:
path: .eslintcache
path: ${{ github.workspace}}/.eslintcache
key: ${{ env.ESLINT_CACHE_KEY }}
- name: Update Cypress cache
uses: ./.github/actions/update-cache
with:
path: ~/.cache/Cypress
key: ${{ env.CYPRESS_CACHE_KEY }}

View File

@@ -159,15 +159,15 @@ jobs:
1. **Analyze the issue description and comments** - Understand what the issue is describing
a. Pay particular attention to reproduction steps and if there's an agreed upon fix.
2. **Search the codebase** - Use Grep and Glob tools to find relevant files
3. **Implement a fix** - Follow existing code patterns and conventions
4. **Test the fix** - Run appropriate tests using the commands in CLAUDE.md:
- JavaScript/TypeScript: `yarn test-unit path/to/file.unit.spec.js`
- Clojure: `clojure -X:dev:test :only namespace/test-name`
- Lint: `yarn lint-eslint-pure` for JS/TS, `./bin/mage kondo <file>` for Clojure
3. **Implement a fix** - Use the appropriate write skill based on the file types:
- Use clojure-write for Clojure/ClojureScript files
- Use typescript-write for TypeScript/JavaScript files
- Follow the coding standards and patterns in each skill
4. **Test the fix** - Run appropriate tests as specified in the skills
5. **Verify the fix** - Ensure tests pass and code follows conventions
## Important Guidelines
- Follow the development patterns in CLAUDE.md
- Use the appropriate write skill for the language you're working in
- Write failing tests first, then fix them
- Work in small, testable increments
- Don't commit changes - leave that for the workflow

View File

@@ -79,17 +79,14 @@ jobs:
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
Review this pull request with a focus on:
Review this pull request for compliance with project coding standards and conventions.
- Compliance with project coding standards and conventions
- Code quality and best practices
- Clear and correct docstrings
- Clear and correct comments explaining what the code is doing
**Use the appropriate review skill based on the file types changed:**
- Use clojure-review for Clojure/ClojureScript files
- Use typescript-review for TypeScript/JavaScript files
- Follow the review guidelines and checklists in each skill
DO NOT post any comments confirming that something is correct or congratulating someone for following our
guidelines -- only post comments for style violations or concerns.
Follow additional guidelines in the "Code Review Guidelines" section of CLAUDE.md .
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
Note: The PR branch is already checked out in the current working directory.

100
.github/workflows/claude-docs-review.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: "Claude Docs Review"
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- 'docs/**/*.md'
- 'docs/**/*.mdx'
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: number
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-claude-docs-review
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
# Make sure the value of GITHUB_TOKEN will not be persisted in repo's config
persist-credentials: false
- name: Generate GitHub App Token for Claude
uses: actions/create-github-app-token@v1
id: claude-app-token
with:
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
permission-contents: read # Required for reading repository files and PR diffs
permission-pull-requests: write # Required for creating inline comments and PR comments
- name: Delete old Claude docs review comments
env:
GH_TOKEN: ${{ steps.claude-app-token.outputs.token }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
shell: bash
run: |
# Delete pending reviews first (these can be deleted entirely)
pending_review_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login=="metabase-bot[bot]" and .state=="PENDING") | .id')
if [ -n "$pending_review_ids" ]; then
echo "Deleting pending reviews: $pending_review_ids"
for id in $pending_review_ids; do
gh api -X DELETE "/repos/$REPO/pulls/$PR_NUMBER/reviews/$id"
echo "Deleted pending review $id"
done
fi
# Delete individual inline comments from submitted reviews (can't delete the review itself)
old_line_comment_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
if [ -n "$old_line_comment_ids" ]; then
echo "Deleting line comments: $old_line_comment_ids"
for id in $old_line_comment_ids; do
gh api -X DELETE "/repos/$REPO/pulls/comments/$id"
echo "Deleted line comment $id"
done
fi
# Delete top-level review comments
old_top_level_comment_ids=$(gh api "/repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
if [ -n "$old_top_level_comment_ids" ]; then
echo "Deleting top-level comments: $old_top_level_comment_ids"
for id in $old_top_level_comment_ids; do
gh api -X DELETE "/repos/$REPO/issues/comments/$id"
echo "Deleted top-level comment $id"
done
fi
- uses: anthropics/claude-code-action@v1
timeout-minutes: 10
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.claude-app-token.outputs.token }}
show_full_output: true
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
Review the documentation changes in this pull request.
Use the docs-review skill to review all changed documentation files for compliance with the Metabase writing style guide.
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
claude_args: |
--allowedTools "Skill,mcp__github__get_pull_request,mcp__github__get_pull_request_files,mcp__github__create_pending_pull_request_review,mcp__github__get_pull_request_diff,mcp__github__add_comment_to_pending_review,mcp__github__add_pull_request_review_comment_to_pending_review,mcp__github__delete_pending_pull_request_review,mcp__github__submit_pending_pull_request_review"

19
.github/workflows/codespell.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Codespell
on:
push:
branches:
- 'master'
- 'release-**'
pull_request:
jobs:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: codespell-project/actions-codespell@v1
with:
# keep this list in sync with the list in `mage/src/mage/codespell.yml`
path: >-
docs

View File

@@ -2,8 +2,6 @@ name: Check doc links
on:
pull_request:
paths:
- "docs/**"
workflow_dispatch:
jobs:
@@ -15,7 +13,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
sparse-checkout: |
docs
@@ -29,7 +27,7 @@ jobs:
- name: Run link checker
id: link-check
uses: lycheeverse/lychee-action@v2
uses: lycheeverse/lychee-action@v2.3.0
with:
fail: true
args: docs --config ./.lychee/config.toml --offline

View File

@@ -8,7 +8,7 @@ on:
target_branch:
description: "Target branch for documentation update"
required: false
default: "release-x.56.x"
default: "release-x.57.x"
type: string
jobs:
@@ -16,6 +16,6 @@ jobs:
name: Call documentation update for release branch
uses: ./.github/workflows/docs-generate-base.yml
with:
target_branch: ${{ inputs.target_branch || 'release-x.56.x' }}
target_branch: ${{ inputs.target_branch || 'release-x.57.x' }}
secrets:
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}

View File

@@ -17,7 +17,7 @@ jobs:
determine-driver-skips:
runs-on: ubuntu-latest
outputs:
skip: ${{ steps.check_team_and_branch.outputs.skip }}
skip: ${{ steps.check-branch-and-files-changed.outputs.skip }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -25,49 +25,31 @@ jobs:
if: github.event_name == 'pull_request' && github.event.pull_request.base.ref != ''
run: |
# Fetch the base branch to ensure its history is available for diffing
git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
git fetch --depth=1 origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
# Only run this if it's a pull request and the base ref exists
- name: Check if tier3 drivers should run
id: check_team_and_branch
- name: Check if driver tests should run
id: check-branch-and-files-changed
run: |
IS_MASTER_OR_RELEASE="${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') }}"
HAS_TEAM_LABEL="${{ contains(github.event.pull_request.labels.*.name, '.Team/Drivers') || contains(github.event.pull_request.labels.*.name, '.Team/Querying') || contains(github.event.pull_request.labels.*.name, '.Team/SemanticLayer') || contains(github.event.pull_request.labels.*.name, '.Team/DevEx') }}"
echo "Is master or release: ${IS_MASTER_OR_RELEASE}"
echo "Has '.Team/Drivers' or '.Team/Querying' or '.Team/SemanticLayer' label: ${HAS_TEAM_LABEL}"
if [[ "${IS_MASTER_OR_RELEASE}" == "true" ]]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "Merging into master or a release branch. Tier 3 driver tests will run."
elif [[ "${HAS_TEAM_LABEL}" == "true" ]]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "A relevant team label is present. Tier 3 driver tests will run."
echo "Merging into master or a release branch. All driver tests will run."
else
echo "Checking for changes in relevant folders..."
CHANGED_FILES=$(git diff --name-only $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) ${{ github.sha }})
# Initialize an empty array to store folders with changes
CHANGED_FOLDERS=()
for folder in "src/metabase/lib" "src/metabase/query_processor" "modules/drivers" "src/metabase/driver"; do
if echo "$CHANGED_FILES" | grep -q "$folder"; then
CHANGED_FOLDERS+=("$folder")
fi
done
if [[ ${#CHANGED_FOLDERS[@]} -gt 0 ]]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "Changes detected in the following folders: ${CHANGED_FOLDERS[*]}."
echo "Tier 3 driver tests will run."
echo "Checking for changes in relevant modules..."
if ./bin/mage can-skip-driver-tests "${{ github.event.pull_request.base.ref }}"; then
echo 'skip=true' >> $GITHUB_OUTPUT
else
echo "skip=true" >> $GITHUB_OUTPUT
echo "No relevant team label, not merging into master/release, and no changes in relevant folders. Tier 3 driver tests will be skipped."
echo 'skip=false' >> $GITHUB_OUTPUT
fi
fi
shell: bash
be-tests-athena-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -85,6 +67,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Athena driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-athena-ee'
@@ -93,7 +76,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -104,8 +87,9 @@ jobs:
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-bigquery-cloud-sdk-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -146,7 +130,7 @@ jobs:
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -163,8 +147,9 @@ jobs:
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
be-tests-clickhouse-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -216,7 +201,7 @@ jobs:
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -235,7 +220,7 @@ jobs:
be-tests-druid-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -251,6 +236,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Druid driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-druid-ee'
@@ -259,7 +245,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -272,7 +258,7 @@ jobs:
be-tests-druid-jdbc-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -288,6 +274,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Druid JDBC driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-druid-jdbc-ee'
@@ -296,7 +283,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -309,7 +296,7 @@ jobs:
be-tests-databricks-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -325,6 +312,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Databricks driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-databricks-ee'
@@ -333,7 +321,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -346,7 +334,7 @@ jobs:
be-tests-databricks-multi-catalog-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -363,6 +351,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Databricks driver with multi-catalog
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-databricks-multi-catalog-ee'
@@ -371,7 +360,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -383,7 +372,7 @@ jobs:
be-tests-h2:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -400,7 +389,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -411,8 +400,9 @@ jobs:
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-mysql-mariadb:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -528,7 +518,7 @@ jobs:
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -547,7 +537,7 @@ jobs:
be-tests-mongo:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -597,7 +587,7 @@ jobs:
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -616,7 +606,7 @@ jobs:
be-tests-mongo-ssl:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -660,6 +650,7 @@ jobs:
curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metaca.crt \
-o ./test_resources/ssl/mongo/metaca.crt
- name: Test ${{ matrix.version.name }}
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: ${{ matrix.version.junit-name }}
@@ -668,7 +659,7 @@ jobs:
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -681,7 +672,7 @@ jobs:
be-tests-mongo-sharded-cluster-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -695,6 +686,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test MongoDB driver (Sharded cluster)
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-mongo-sharded-cluster-ee'
@@ -702,7 +694,7 @@ jobs:
:only metabase.driver.mongo.sharded-cluster-test
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -715,7 +707,7 @@ jobs:
be-tests-oracle:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -767,6 +759,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test ${{ matrix.version.name }}
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: ${{ matrix.version.junit-name }}
@@ -775,7 +768,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -786,8 +779,9 @@ jobs:
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-postgres:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -877,7 +871,7 @@ jobs:
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -896,7 +890,7 @@ jobs:
be-tests-presto-jdbc-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -945,6 +939,7 @@ jobs:
-file /tmp/presto-ssl-ca.der
-trustcacerts
- name: Test Presto JDBC driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-presto-jdbc-ee'
@@ -953,7 +948,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -964,8 +959,9 @@ jobs:
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
be-tests-redshift-ee:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
@@ -1015,7 +1011,7 @@ jobs:
${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: ${{ !matrix.job.skip && !cancelled() }}
if: ${{ !matrix.job.skip && !cancelled() && steps.test-driver.outputs.quarantine-skipped != 'true' }}
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -1031,80 +1027,80 @@ jobs:
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
# dan sutton 2025-10-23: disable snowflake because of account
# issues. https://linear.app/metabase/issue/DEV-997/reenable-snowflake-in-ci. Note that the transforms tests were
# disabled previously
# be-tests-snowflake-ee:
# if: ${{ !inputs.skip }}
# runs-on: ubuntu-22.04
# timeout-minutes: 60
# strategy:
# fail-fast: false
# matrix:
# job:
# - name: Driver Tests
# test-args: >-
# :only-tags [:mb/driver-tests]
# :exclude-tags [:mb/transforms-python-test]
be-tests-snowflake-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
# DS 2025-09-26 temporarily disabled while we diagnose flakes
# - name: Transforms Python Tests
# test-args: >-
# :only-tags [:mb/transforms-python-test]
# needs-python-runner: true
# env:
# CI: 'true'
# DRIVERS: snowflake
# MB_SNOWFLAKE_TEST_USER: METABASE CI
# MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
# MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
# MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
# MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
# MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
# MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
# MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
# MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
env:
CI: 'true'
DRIVERS: snowflake
MB_SNOWFLAKE_TEST_USER: METABASE CI
MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
MB_SNOWFLAKE_TEST_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PRIVATE_KEY }}
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
# # RSA Role testing:
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
# name: Snowflake ${{ matrix.job.name }}
# steps:
# - uses: actions/checkout@v4
# - name: Setup python-runner
# if: ${{ matrix.job.needs-python-runner == true }}
# uses: ./.github/actions/setup-python-runner
# with:
# metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
# - name: Test Snowflake driver
# id: test-driver
# uses: ./.github/actions/test-driver
# with:
# junit-name: 'be-tests-snowflake-ee'
# test-args: ${{ matrix.job.test-args }}
# - name: Upload Test Results
# uses: ./.github/actions/upload-test-results
# if: always()
# with:
# input-path: ./target/junit/
# output-name: ${{ github.job }}
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
# aws-region: ${{ vars.AWS_REGION }}
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
# - name: Cleanup python-runner
# if: ${{ always() && matrix.job.needs-python-runner == true }}
# uses: ./.github/actions/cleanup-python-runner
# with:
# upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
# logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
# RSA Role testing:
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
name: Snowflake ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
uses: ./.github/actions/setup-python-runner
with:
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Test Snowflake driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-snowflake-ee'
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
if: ${{ always() && matrix.job.needs-python-runner == true }}
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-sparksql-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -1118,6 +1114,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test Spark driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-sparksql-ee'
@@ -1126,7 +1123,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -1139,7 +1136,7 @@ jobs:
be-tests-sqlite-ee:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -1148,6 +1145,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test SQLite driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-sqlite-ee'
@@ -1156,7 +1154,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -1169,7 +1167,7 @@ jobs:
be-tests-sqlserver:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
@@ -1221,7 +1219,7 @@ jobs:
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -1241,7 +1239,7 @@ jobs:
# be-tests-starburst-ee:
# needs: determine-driver-skips
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
# runs-on: ubuntu-22.04
# runs-on: ubuntu-latest
# timeout-minutes: 60
# env:
# CI: 'true'
@@ -1279,7 +1277,7 @@ jobs:
login-to-ecr:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
id-token: write
@@ -1309,7 +1307,7 @@ jobs:
be-tests-vertica-ee:
needs: [determine-driver-skips, login-to-ecr]
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
@@ -1328,6 +1326,7 @@ jobs:
- name: Make plugins directory
run: mkdir plugins
- name: Test Vertica driver
id: test-driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-vertica-ee'
@@ -1336,7 +1335,7 @@ jobs:
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
@@ -1363,8 +1362,7 @@ jobs:
- be-tests-postgres
- be-tests-presto-jdbc-ee
- be-tests-redshift-ee
# dan: disabled for account reasons. https://linear.app/metabase/issue/DEV-997/reenable-snowflake-in-ci
# - be-tests-snowflake-ee
- be-tests-snowflake-ee
- be-tests-sparksql-ee
- be-tests-sqlite-ee
- be-tests-sqlserver

View File

@@ -1,185 +0,0 @@
# 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
on:
workflow_call:
inputs:
cached-embedding-sdk-dist-artifact-name:
required: true
type: string
jobs:
files-changed:
name: "SDK backward-compatibility E2E > Check which files changed"
runs-on: ubuntu-22.04
timeout-minutes: 3
outputs:
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
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
decide-target-ref:
runs-on: ubuntu-latest
outputs:
target-ref: ${{ steps.get-target-ref.outputs.value }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Decide ref to checkout
id: get-target-ref
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
else
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
fi
# if this is a test on a release branch, we need to check the build requirements
get-build-requirements:
name: "SDK backward-compatibility E2E > [Backport only] Get build requirements"
needs: [decide-target-ref]
if: |
!cancelled() &&
contains(github.base_ref || github.ref, 'release-x')
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get build dependencies
uses: actions/github-script@v7
id: dependencies
with:
script: | # js
const {
getBuildRequirements,
getVersionFromReleaseBranch,
} = require('${{ github.workspace }}/release/dist/index.cjs');
const targetBranchName = '${{ github.base_ref || github.ref }}';
const version = getVersionFromReleaseBranch(targetBranchName);
const requirements = getBuildRequirements(version);
return {
java_version: requirements.java,
node_version: requirements.node,
};
e2e-tests:
name: "SDK backward-compatibility E2E > Run e2e tests"
needs: [files-changed, decide-target-ref, get-build-requirements]
if: |
!cancelled() &&
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 45
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.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
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
with:
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
- name: Build uberjar with ./bin/build.sh
run: ./bin/build.sh
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.cached-embedding-sdk-dist-artifact-name }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- name: Make app db snapshot
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
- name: Run component tests for Embedding SDK
run: |
node e2e/runner/run_cypress_ci.js component \
--browser ${{ steps.cypress-prep.outputs.chrome-path }} \
--env grepTags="-@skip-backward-compatibility",grepOmitFiltered=true
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit
output-name: e2e-embedding-sdk
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-embedding-sdk-cross-version
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Publish Summary
if: failure()
uses: actions/github-script@v7
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();

View File

@@ -1,204 +0,0 @@
name: E2E Component Tests for Embedding SDK
on:
workflow_call:
inputs:
cached-embedding-sdk-dist-artifact-name:
required: true
type: string
jobs:
files-changed:
name: "SDK e2e > Check which files changed"
runs-on: ubuntu-22.04
timeout-minutes: 3
outputs:
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
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
# if this is a test on a release branch, we need to check the build requirements
get-build-requirements:
name: "SDK e2e > [Backport only] Get build requirements"
if: |
!cancelled() &&
contains(github.base_ref || github.ref, 'release-x')
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get build dependencies
uses: actions/github-script@v7
id: dependencies
with:
script: | # js
const {
getBuildRequirements,
getVersionFromReleaseBranch,
} = require('${{ github.workspace }}/release/dist/index.cjs');
const targetBranchName = '${{ github.base_ref || github.ref }}';
const version = getVersionFromReleaseBranch(targetBranchName);
const requirements = getBuildRequirements(version);
return {
java_version: requirements.java,
node_version: requirements.node,
};
build-uberjar:
name: "SDK e2e > Build uberjar"
needs: [files-changed, get-build-requirements]
if: |
!cancelled() &&
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 25
env:
MB_EDITION: ee
INTERACTIVE: false
# make sure that builds on release branches get licenses, because we use them for releases
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
with:
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
- name: Build uberjar with ./bin/build.sh
run: ./bin/build.sh
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
e2e-tests:
name: "SDK e2e > Run e2e tests (React ${{ matrix.react-version }})"
needs: [get-build-requirements, build-uberjar]
if: |
!cancelled() &&
needs.build-uberjar.result == 'success'
runs-on: ubuntu-22.04
timeout-minutes: 45
strategy:
matrix:
react-version: [18, 19]
fail-fast: false
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.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
CYPRESS_CI: true
steps:
- uses: actions/checkout@v4
- name: Retrieve uberjar artifact for ee
uses: actions/download-artifact@v4
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK ${{ needs.get-build-requirements.outputs.java_version || 21 }}
uses: actions/setup-java@v4
with:
java-version: ${{ needs.get-build-requirements.outputs.java_version || 21 }}
distribution: "temurin"
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.cached-embedding-sdk-dist-artifact-name }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- name: Make app db snapshot
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
- name: Change React version to ${{ matrix.react-version }}
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
- name: Run component tests for Embedding SDK
run: node e2e/runner/run_cypress_ci.js component --browser ${{ steps.cypress-prep.outputs.chrome-path }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: "SDK e2e > Upload Cypress Artifacts upon failure"
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: "SDK e2e > Publish Summary"
if: failure()
uses: actions/github-script@v7
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();

View File

@@ -37,26 +37,26 @@ on:
default: true
jobs:
determine-suite:
name: Determine test suite
determine-test-type:
name: Determine test type
runs-on: ubuntu-22.04
timeout-minutes: 5
outputs:
suite: ${{ steps.detect.outputs.suite }}
type: ${{ steps.detect.outputs.type }}
steps:
- name: Detect suite from spec path
- name: Detect test type from spec path
id: detect
run: |
SPEC='${{ inputs.spec }}'
if [[ "$SPEC" == e2e/test-component/scenarios/embedding-sdk/* ]]; then
echo "suite=component" >> $GITHUB_OUTPUT
echo "type=component" >> $GITHUB_OUTPUT
else
echo "suite=e2e" >> $GITHUB_OUTPUT
echo "type=e2e" >> $GITHUB_OUTPUT
fi
workflow-summary:
name: Stress test inputs
needs: [determine-suite]
needs: [determine-test-type]
runs-on: ubuntu-22.04
timeout-minutes: 5
steps:
@@ -66,7 +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 '- `test_type` (inferred): ${{ needs.determine-test-type.outputs.type }}' >> $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
@@ -75,7 +75,7 @@ jobs:
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
stress-test-flake-fix:
needs: [determine-suite]
needs: [determine-test-type]
runs-on: ubuntu-22.04
timeout-minutes: 60
name: Stress test E2E flake fix
@@ -122,12 +122,12 @@ jobs:
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
if: ${{ needs.determine-test-type.outputs.type == 'component' }}
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "cljs"
- name: Build Embedding SDK package
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
if: ${{ needs.determine-test-type.outputs.type == 'component' }}
run: yarn build-embedding-sdk-package
- name: Prepare JDK 21
@@ -148,13 +148,12 @@ 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' }}
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js ${{ needs.determine-suite.outputs.suite }} \
node e2e/runner/run_cypress_ci.js ${{ needs.determine-test-type.outputs.type }} \
--spec '${{ github.event.inputs.spec }}' \
--env burn=${{ github.event.inputs.burn_in }} \
--config-file e2e/support/cypress-stress-test.config.js \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--config-file e2e/support/cypress-stress-test.config.js
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4

View File

@@ -120,6 +120,8 @@ jobs:
run: node e2e/runner/run_cypress_ci.js start
- name: Generate database snapshots
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js snapshot
- name: Run auto-split Cypress tests on ${{ inputs.name }}
@@ -133,13 +135,13 @@ jobs:
# do not write cypress-split summary to github
SPLIT_SUMMARY: false
SPEC_PATH: ${{ env.specs || inputs.specs }}
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
shell: bash
run: |
echo "Running tests with specs: $SPEC_PATH"
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="-@mongo+-@python+-@OSS+-@skip",grepOmitFiltered=true \
--spec "$SPEC_PATH" \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env grepTags="-@mongo+-@python+-@OSS+-@skip" \
--spec "$SPEC_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.
@@ -150,12 +152,12 @@ jobs:
shell: bash
env:
SPEC_PATH: ${{ env.specs || inputs.specs }}
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
echo "Running tests with specs: $SPEC_PATH"
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}",grepOmitFiltered=true \
--spec "$SPEC_PATH" \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}" \
--spec "$SPEC_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.

View File

@@ -0,0 +1,250 @@
name: Embedding SDK Component Tests
on:
workflow_call:
jobs:
sdk-component-tests:
name: "SDK component tests (React ${{ matrix.react-version }})"
runs-on: ubuntu-22.04
timeout-minutes: 45
strategy:
matrix:
react-version: [18, 19]
fail-fast: false
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.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
steps:
- uses: actions/checkout@v4
- name: Retrieve uberjar artifact for ee
uses: ./.github/actions/fetch-artifact
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
extract-path: "target/uberjar"
output-name: "metabase.jar"
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v4
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: "temurin"
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- name: Make app db snapshot
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js snapshot
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
- name: Change React version to ${{ matrix.react-version }}
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
- name: Run component tests for Embedding SDK
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js component
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: "SDK component tests > Upload Cypress Artifacts upon failure"
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: "SDK component tests > Publish Summary"
if: failure()
uses: actions/github-script@v7
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();
decide-target-ref:
runs-on: ubuntu-latest
outputs:
target-ref: ${{ steps.get-target-ref.outputs.value }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Decide ref to checkout
id: get-target-ref
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
else
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
fi
# This job ensures that the Embedding SDK package from the current commit (N) works with the
# Embedding SDK Bundle (Metabase JAR) from the previous commit (N-1).
# N - 1 is a target of a pull request, or a literal previous commit when this job is run on push in a branch.
sdk-component-tests-backward-compatibility:
name: "SDK component tests (Backward compatibility)"
needs: [decide-target-ref]
runs-on: ubuntu-22.04
timeout-minutes: 45
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.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
steps:
- name: Check out the code from the previous commit
uses: actions/checkout@v4
with:
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
- name: Get the actual commit SHA from the checked out branch
id: get-commit-sha
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Attempt to retrieve the ee uberjar artifact from the previous commit
id: download-uberjar
uses: ./.github/actions/fetch-artifact
with:
name: metabase-ee-${{ steps.get-commit-sha.outputs.sha }}-uberjar
extract-path: "target/uberjar"
output-name: "metabase.jar"
continue-on-error: true # do not break the whole job if this step doesn't succeed
# Build the uberjar from the previous commit only if we couldn't fetch it.
# `step.foo.OUTCOME` is important because it looks at the step status *before* continue-on-error is applied.
# Do not confuse it with the `step.foo.conclusion`.
- name: Build MB uberjar from the commit N-1
if: steps.download-uberjar.outcome != 'success'
run: ./bin/build.sh
- name: Retrieve Embedding SDK dist artifact built from the current commit N
uses: actions/download-artifact@v4
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- name: Make app db snapshot
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js snapshot
- name: Run component tests for Embedding SDK
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js component \
--env grepTags="-@skip-backward-compatibility"
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit
output-name: e2e-embedding-sdk
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-embedding-sdk-cross-version
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Publish Summary
if: failure()
uses: actions/github-script@v7
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();

View File

@@ -1,78 +1,14 @@
name: "[NOT REQUIRED] SDK Compatibility tests"
on:
push:
branches:
- "master"
- "release-**"
pull_request:
types: [opened, synchronize, reopened]
branches:
- "master"
- "release-**"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
workflow_call:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
timeout-minutes: 3
outputs:
e2e_embedding_sdk_compatibility: ${{ steps.changes.outputs.e2e_embedding_sdk_compatibility }}
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
# if this is a test on a release branch, we need to check the build requirements
get-build-requirements:
if: |
!cancelled() &&
contains(github.base_ref || github.ref, 'release-x')
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get build dependencies
uses: actions/github-script@v7
id: dependencies
with:
script: | # js
const {
getBuildRequirements,
getVersionFromReleaseBranch,
} = require('${{ github.workspace }}/release/dist/index.cjs');
const targetBranchName = '${{ github.base_ref || github.ref }}';
const version = getVersionFromReleaseBranch(targetBranchName);
const requirements = getBuildRequirements(version);
return {
java_version: requirements.java,
node_version: requirements.node,
};
get-sample-apps-data:
if: |
!cancelled()
runs-on: ubuntu-22.04
timeout-minutes: 10
env:
# we must support last two stable releases
# we must support the last two stable releases
SUPPORTED_RELEASES_COUNT: 2
TEST_SUITES: |
[
@@ -145,51 +81,6 @@ jobs:
matrix: []
};
build:
needs: [files-changed, get-build-requirements, get-sample-apps-data]
if: |
!cancelled() &&
needs.files-changed.outputs.e2e_embedding_sdk_compatibility == 'true' &&
fromJSON(needs.get-sample-apps-data.outputs.matrix)[0] != null
runs-on: ubuntu-22.04
timeout-minutes: 25
env:
MB_EDITION: ee
INTERACTIVE: false
# make sure that builds on release branches get licenses, because we use them for releases
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
with:
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
- name: Build uberjar with ./bin/build.sh
run: ./bin/build.sh
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
- name: Build Embedding SDK package
run: |
# Remove the matchers to not report false positives on every PR
# unrelated to embedding (EMB-765)
echo "::remove-matcher owner=tsc::"
yarn build-embedding-sdk-package
- name: Prepare Embedding SDK artifact
uses: actions/upload-artifact@v4
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: resources/embedding-sdk
# In order to check compatibility between Sample Apps and Embedding SDK, the job for each sample app:
# - pulls a Sample App
# - starts it
@@ -197,13 +88,13 @@ jobs:
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
# For more details see `Sample Apps compatibility with Embedding SDK tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
e2e-sample-apps-tests:
needs: [get-build-requirements, get-sample-apps-data, build]
needs: [get-sample-apps-data]
strategy:
matrix:
test_suite: ${{ fromJSON(needs.get-sample-apps-data.outputs.matrix) }}
environment: ["production", "development"]
if: |
!cancelled() && needs.build.result == 'success'
!cancelled() && needs.get-sample-apps-data.result == 'success'
runs-on: ubuntu-22.04
timeout-minutes: 45
name: e2e-sample-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
@@ -216,7 +107,6 @@ jobs:
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
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
@@ -241,9 +131,11 @@ jobs:
echo "sample_app_name=${test_suite%-e2e}" >> "$GITHUB_OUTPUT"
- name: Retrieve uberjar artifact for ee
uses: actions/download-artifact@v4
uses: ./.github/actions/fetch-artifact
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
extract-path: "target/uberjar"
output-name: "metabase.jar"
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v4
@@ -299,7 +191,10 @@ jobs:
id: run-e2e-tests
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
continue-on-error: true
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }} --browser ${{ steps.cypress-prep.outputs.chrome-path }}
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }}
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
@@ -323,7 +218,6 @@ jobs:
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
# For more details see `Embedding SDK integration tests with Host Apps` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
e2e-host-apps-tests:
needs: [get-build-requirements, build]
strategy:
matrix:
test_suite:
@@ -334,8 +228,6 @@ jobs:
"angular-20-host-app-e2e",
]
environment: ["production", "development"]
if: |
!cancelled() && needs.build.result == 'success'
runs-on: ubuntu-22.04
timeout-minutes: 45
name: e2e-host-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
@@ -348,7 +240,6 @@ jobs:
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
HOST_APP_ENVIRONMENT: ${{ matrix.environment }}
@@ -359,9 +250,11 @@ jobs:
- uses: actions/checkout@v4
- name: Retrieve uberjar artifact for ee
uses: actions/download-artifact@v4
uses: ./.github/actions/fetch-artifact
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
extract-path: "target/uberjar"
output-name: "metabase.jar"
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v4
@@ -394,7 +287,9 @@ jobs:
- name: Run e2e tests for Host App
id: run-e2e-tests
continue-on-error: true
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }} --browser ${{ steps.cypress-prep.outputs.chrome-path }}
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }}
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4

View File

@@ -0,0 +1,31 @@
name: Embedding SDK Package Build
on:
workflow_call:
workflow_dispatch:
concurrency:
group: embedding-sdk-package-${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
build-sdk-package:
runs-on: ubuntu-22.04
timeout-minutes: 25
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 Embedding SDK package
run: |
# Remove the matchers to not report false positives on every PR
# unrelated to embedding (EMB-765)
echo "::remove-matcher owner=tsc::"
yarn build-embedding-sdk-package
- name: Prepare Embedding SDK artifact
uses: actions/upload-artifact@v4
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: resources/embedding-sdk

View File

@@ -2,61 +2,35 @@ name: SDK
on:
workflow_call:
inputs:
cli-snippets-type-check:
description: "Whether to run cli snippets type check."
required: true
type: boolean
docs-snippets-type-check:
description: "Whether to run docs snippets type check."
required: true
type: boolean
documentation-validation:
description: "Whether to run documentation generation validation check."
required: true
type: boolean
component-tests:
description: "Whether to run component tests."
required: true
type: boolean
host-sample-apps-tests:
description: "Whether to run host and sample apps compatibility tests."
required: true
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
timeout-minutes: 3
outputs:
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_sources }}
embedding_documentation: ${{ steps.changes.outputs.embedding_documentation }}
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
build:
needs: [files-changed]
if: |
!cancelled() &&
(needs.files-changed.outputs.e2e_embedding_sdk == 'true' ||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
needs.files-changed.outputs.embedding_documentation == 'true')
runs-on: ubuntu-22.04
timeout-minutes: 25
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 Embedding SDK package
run: |
# Remove the matchers to not report false positives on every PR
# unrelated to embedding (EMB-765)
echo "::remove-matcher owner=tsc::"
yarn build-embedding-sdk-package
- name: Prepare Embedding SDK artifact
uses: actions/upload-artifact@v4
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: resources/embedding-sdk
embedding-sdk-cli-snippets-type-check:
needs: [files-changed, build]
if: |
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' &&
needs.build.result == 'success'
if: inputs.cli-snippets-type-check
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
@@ -72,10 +46,7 @@ jobs:
run: yarn run embedding-sdk:cli-snippets:type-check
embedding-sdk-docs-snippets-type-check:
needs: [files-changed, build]
if: |
needs.files-changed.outputs.embedding_documentation == 'true' &&
needs.build.result == 'success'
if: inputs.docs-snippets-type-check
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
@@ -91,10 +62,7 @@ jobs:
run: yarn run embedding-sdk:docs-snippets:type-check
embedding-sdk-api-documentation-generation-validation:
needs: [files-changed, build]
if: |
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' &&
needs.build.result == 'success'
if: inputs.documentation-validation
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
@@ -140,29 +108,24 @@ jobs:
run: exit 1
# See `Component e2e tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
sdk-e2e-tests:
name: "SDK e2e tests"
uses: ./.github/workflows/e2e-component-tests-embedding-sdk.yml
needs: [build]
embedding-sdk-component-tests:
name: "SDK component tests"
if: inputs.component-tests
uses: ./.github/workflows/embedding-sdk-component-tests.yml
secrets: inherit
with:
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
e2e-component-tests-embedding-sdk-backward-compatibility:
name: "SDK e2e backward compatibility tests"
uses: ./.github/workflows/e2e-component-tests-embedding-sdk-backward-compatibility.yml
needs: [build]
not-required-embedding-sdk-apps-compatibility-tests:
name: "SDK e2e tests for compatibility with (host/sample) apps"
if: inputs.host-sample-apps-tests
uses: ./.github/workflows/embedding-sdk-host-sample-apps-tests.yml
secrets: inherit
with:
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
embedding-sdk-tests-result:
needs:
- embedding-sdk-cli-snippets-type-check
- embedding-sdk-docs-snippets-type-check
- embedding-sdk-api-documentation-generation-validation
- sdk-e2e-tests
- e2e-component-tests-embedding-sdk-backward-compatibility
- embedding-sdk-component-tests
if: always() && !cancelled()
runs-on: ubuntu-latest
timeout-minutes: 5

View File

@@ -42,7 +42,7 @@ jobs:
- name: Restore ESLint cache
uses: actions/cache/restore@v4
with:
path: .eslintcache
path: ${{ github.workspace}}/.eslintcache
key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
- name: Compile CLJS

227
.github/workflows/minimal-tests.yml vendored Normal file
View File

@@ -0,0 +1,227 @@
name: Minimal Tests
on:
pull_request:
types: [opened, synchronize, reopened, labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
# Only run if the PR has the minimal-tests label
check-label:
runs-on: ubuntu-latest
timeout-minutes: 1
outputs:
should-run: ${{ steps.check.outputs.result }}
steps:
- name: Check for minimal-tests label
id: check
run: |
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'minimal-tests') }}" == "true" ]]; then
echo "result=true" >> $GITHUB_OUTPUT
else
echo "result=false" >> $GITHUB_OUTPUT
fi
# Postgres app-db tests - single version, single job
postgres-app-db-test:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: postgres
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
MB_POSTGRESQL_TEST_USER: mb_test
services:
postgres:
image: postgres:latest
ports:
- "5432:5432"
env:
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
name: Postgres Latest EE App DB Tests (Part 1)
steps:
- uses: actions/checkout@v4
- name: Test Postgres App DB
uses: ./.github/actions/test-driver
with:
build-static-viz: true
junit-name: 'minimal-be-tests-postgres-ee'
test-args: >-
:only '["test" "enterprise/backend/test"]'
:exclude-tags '[:mb/driver-tests :mb/transforms-python-test]'
:partition/total 2
:partition/index 0
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
# Postgres driver tests
postgres-driver-test:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: postgres
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
MB_POSTGRESQL_TEST_USER: mb_test
services:
postgres:
image: postgres:latest
ports:
- "5432:5432"
env:
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
name: Postgres Latest Driver Tests
steps:
- uses: actions/checkout@v4
- name: Test Postgres Driver
uses: ./.github/actions/test-driver
with:
junit-name: 'minimal-be-tests-postgres-driver-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
# Frontend unit tests
frontend-unit-tests:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 30
env:
JEST_JUNIT_OUTPUT_DIR: ./target/junit
JEST_JUNIT_OUTPUT_NAME: test-report-frontend-unit.xml
name: Frontend Unit Tests
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: Run frontend unit tests
run: yarn run test-unit --silent
continue-on-error: true
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit
output-name: frontend-unit
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
# Summary job
minimal-tests-result:
needs:
- postgres-app-db-test
- postgres-driver-test
- frontend-unit-tests
runs-on: ubuntu-latest
timeout-minutes: 5
name: minimal-tests-result
if: always() && !cancelled()
steps:
- name: Check job status
uses: actions/github-script@v7
env:
needs: ${{ toJson(needs) }}
with:
script: | # js
const needs = JSON.parse(process.env.needs);
const jobs = Object.entries(needs).map(
([jobName, jobValues]) => ({
name: jobName,
result: jobValues.result
}));
// Check if all jobs either succeeded or were skipped
// Skipped jobs are acceptable (they were intentionally skipped when the label is absent)
const allPassedOrSkipped = jobs.every(job =>
job.result === 'success' || job.result === 'skipped'
);
// Check if any jobs actually failed (not skipped, not cancelled)
const anyFailed = jobs.some(job =>
job.result === 'failure'
);
if (allPassedOrSkipped && !anyFailed) {
console.log("");
console.log(" _------. ");
console.log(" / , \_ ");
console.log(" / / /{}\ |o\_ ");
console.log(" / \ `--' /-' \ ");
console.log(" | \ \ | ");
console.log(" | |`-, | ");
console.log(" / /__/)/ ");
console.log(" | | ");
console.log("");
console.log("All minimal tests have passed. Fast feedback achieved!");
process.exit(0);
}
// otherwise, something failed
console.log("");
console.log(" .::::::::::. .::::::::::. ");
console.log(" .::::''''''::::. .::::''''''::::. ");
console.log(" .:::' `::::.... ....::::' `:::. ");
console.log(" .::' `:::::::| |:::::::' `::. ");
console.log(" .::| |::::::|_ ___ __|::::::| |::. ");
console.log(" `--' |::::::|_()__()_|::::::| `--' ");
console.log(" ::: |::-o::| |::o-::| ::: ");
console.log(" `::. .|::::::| |::::::|. .::' ");
console.log(" `:::. .::\-----' `-----/::. .:::' ");
console.log(" `::::......::::' `::::......::::' ");
console.log(" `::::::::::' `::::::::::' ");
console.log("");
console.log("Minimal tests have failed.");
jobs.forEach((job) => {
if (job.result !== 'success' && job.result !== 'skipped') {
console.log(`${job.name} - ${job.result}`);
}
});
process.exit(1);

View File

@@ -10,6 +10,15 @@ concurrency:
jobs:
pr-env:
if: ${{ github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env') || github.event.label.name == 'PR-Env' }}
if: ${{ (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env') && !contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')) || github.event.label.name == 'PR-Env' }}
uses: ./.github/workflows/pr-env-destroy.yml
secrets: inherit
with:
self_hosting: false
pr-env-self-hosting:
if: ${{ (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')) || github.event.label.name == 'PR-Env-self-hosting' }}
uses: ./.github/workflows/pr-env-destroy.yml
secrets: inherit
with:
self_hosting: true

View File

@@ -3,6 +3,12 @@ run-name: Destroying Dynamic PR Environment for ${{ github.ref_name }} by @${{ g
on:
workflow_call:
inputs:
self_hosting:
description: 'Self-hosting stack'
required: false
type: boolean
default: false
jobs:
destroy_pr:
@@ -54,9 +60,20 @@ jobs:
namespace: default
user: oidc-token
- name: Destroy PR Review ENV
if: ${{ !inputs.self_hosting }}
run: |
kubectl delete metabase -n hosting-pr${{ github.event.number }} hosting-pr${{ github.event.number }}
kubectl delete ns hosting-pr${{ github.event.number }}
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
with:
version: 'latest'
- name: Destroy PR Review ENV (self-hosting)
if: ${{ inputs.self_hosting }}
run: |
helm uninstall hosting-pr${{ github.event.number }} --namespace hosting-pr${{ github.event.number }} || true
kubectl delete ns hosting-pr${{ github.event.number }} || true
- name: Setup psql client
run: |
sudo apt-get update

View File

@@ -10,7 +10,7 @@ concurrency:
jobs:
uberjar:
if: ${{ github.event.label.name == 'PR-Env' }}
if: ${{ github.event.label.name == 'PR-Env' || github.event.label.name == 'PR-Env-self-hosting' }}
uses: ./.github/workflows/uberjar.yml
secrets: inherit
@@ -19,3 +19,13 @@ jobs:
if: ${{ github.event.label.name == 'PR-Env' }}
uses: ./.github/workflows/pr-env.yml
secrets: inherit
with:
self_hosting: false
pr-env-self-hosting:
needs: [uberjar]
if: ${{ github.event.label.name == 'PR-Env-self-hosting' }}
uses: ./.github/workflows/pr-env.yml
secrets: inherit
with:
self_hosting: true

View File

@@ -3,6 +3,12 @@ run-name: Building Dynamic PR Environment for ${{ github.ref_name }} by @${{ git
on:
workflow_call:
inputs:
self_hosting:
description: 'Deploy self-hosting stack'
required: false
type: boolean
default: false
jobs:
build:
runs-on: ubuntu-latest
@@ -113,8 +119,10 @@ jobs:
| grep -q 1 \
|| psql -c "CREATE DATABASE hosting_pr${{ github.event.number }}"
- name: Download Deployment YAML template
if: ${{ !inputs.self_hosting }}
run: aws s3 cp s3://metabase-pr-env/metabase.yml.tmpl ./metabase.yml.tmpl
- name: Render Deployment YAML
if: ${{ !inputs.self_hosting }}
uses: nowactions/envsubst@v1
with:
input: ./metabase.yml.tmpl
@@ -125,8 +133,52 @@ jobs:
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.PR_ENV_MB_PREMIUM_EMBEDDING_TOKEN }}
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Deploy PR Review ENV
if: ${{ !inputs.self_hosting }}
run: |
kubectl apply -f ./metabase.yml
- name: Download Helm values template (self-hosting)
if: ${{ inputs.self_hosting }}
run: aws s3 cp s3://metabase-pr-env/values.yaml.tmpl ./values.yaml.tmpl
- name: Render Helm values (self-hosting)
if: ${{ inputs.self_hosting }}
uses: nowactions/envsubst@v1
with:
input: ./values.yaml.tmpl
output: ./values.yaml
env:
IMAGE_TAG: pr${{ github.event.number }}
PR_NUMBER: ${{ github.event.number }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.PR_ENV_MB_PREMIUM_EMBEDDING_TOKEN }}
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
OPEN_AI_API_KEY: ${{ secrets.OPEN_AI_API_KEY }}
DB_HOST: ${{ secrets.PR_ENV_DB_HOST }}
DB_USERNAME: ${{ secrets.PR_ENV_DB_USER }}
DB_PASSWORD: ${{ secrets.PR_ENV_DB_PASSWORD }}
CONFIG: ${{ secrets.PR_ENV_CONFIG }}
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
with:
version: 'latest'
- name: Login to Quay.io
if: ${{ inputs.self_hosting }}
run: |
helm registry login quay.io \
--username ${{ secrets.QUAY_USERNAME }} \
--password ${{ secrets.QUAY_PASSWORD }}
- name: Deploy PR Review ENV (self-hosting)
if: ${{ inputs.self_hosting }}
run: |
helm upgrade --install hosting-pr${{ github.event.number }} \
oci://quay.io/enterprisemetabase/chart-metabase-stack \
--namespace hosting-pr${{ github.event.number }} \
--create-namespace \
--values ./values.yaml \
--atomic \
--timeout 10m
preview_links:
runs-on: ubuntu-latest
needs: [deploy_pr]
@@ -139,3 +191,4 @@ jobs:
✅ Preview:
https://pr${{ github.event.number }}.coredev.metabase.com
🗒️ [login instructions](https://www.notion.so/metabase/Ephemeral-PR-Environments-c74a3710c87a460dbf4fc0007a001458)
📦 Deployment type: ${{ inputs.self_hosting && 'PR-Env-self-hosting' || 'PR-Env' }}

View File

@@ -194,19 +194,21 @@ jobs:
- name: Run a few important OSS Cypress tests as sanity check
if: ${{ matrix.edition == 'oss' }}
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="@smoke+-@EE",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env grepTags="@smoke+-@EE" \
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)'
- name: Run a few important EE Cypress tests as sanity check
if: ${{ matrix.edition == 'ee' }}
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="@smoke+-@OSS",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env grepTags="@smoke+-@OSS" \
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)'
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4

View File

@@ -12,7 +12,7 @@ on:
jobs:
files-changed:
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') && !contains(github.event.pull_request.labels.*.name,'minimal-tests') }}
name: Check which files changed
runs-on: ubuntu-22.04
timeout-minutes: 5
@@ -25,6 +25,11 @@ jobs:
frontend_all: ${{ steps.changes.outputs.frontend_all }}
frontend_sources: ${{ steps.changes.outputs.frontend_sources }}
documentation: ${{ steps.changes.outputs.documentation }}
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_sources }}
embedding_documentation: ${{ steps.changes.outputs.embedding_documentation }}
embedding_sdk_components: ${{ steps.changes.outputs.embedding_sdk_components }}
embedding_sdk_host_sample_apps: ${{ steps.changes.outputs.embedding_sdk_host_sample_apps }}
embedding_sdk_ci: ${{ steps.changes.outputs.embedding_sdk_ci }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
@@ -36,49 +41,19 @@ jobs:
list-files: csv
filters: .github/file-paths.yaml
static-viz-files-changed:
name: Check whether static-viz files changed
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
static_viz: ${{ steps.static_viz.outputs.static_viz }}
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: Upload Static Viz Bundle Artifact
uses: actions/upload-artifact@v4
with:
name: static-viz-bundle-${{ github.sha }}
path: resources/frontend_client/app/dist
- name: Check for static viz changes
uses: dorny/paths-filter@v3.0.0
id: static_viz
with:
token: ${{ github.token }}
filters: .github/static-viz-sources.yaml
uberjar:
needs: [files-changed, static-viz-files-changed]
needs: [files-changed]
if: ${{ !cancelled() && !(needs.files-changed.outputs.documentation == 'true' && needs.files-changed.outputs.backend_all == 'false' && needs.files-changed.outputs.frontend_all == 'false' && needs.files-changed.outputs.e2e_all == 'false') }}
uses: ./.github/workflows/uberjar.yml
secrets: inherit
backend-tests:
if: ${{ !cancelled() && needs.files-changed.result == 'success' }}
needs: [files-changed, static-viz-files-changed]
needs: [files-changed]
uses: ./.github/workflows/backend.yml
secrets: inherit
with:
skip: ${{ needs.files-changed.outputs.backend_all != 'true' && needs.static-viz-files-changed.outputs.static_viz != 'true' }}
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
semantic-search-tests:
needs: files-changed
@@ -122,18 +97,51 @@ jobs:
# list of changed specs files, then only specs are changed and we can run only those specs
specs: ${{ needs.files-changed.outputs.e2e_all_files == needs.files-changed.outputs.e2e_specs_files && needs.files-changed.outputs.e2e_specs_files || '' }}
embedding-sdk-package:
needs: [files-changed]
if: |
!cancelled() &&
(needs.files-changed.outputs.embedding_sdk_components == 'true' ||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
needs.files-changed.outputs.embedding_documentation == 'true' ||
needs.files-changed.outputs.embedding_sdk_ci == 'true')
uses: ./.github/workflows/embedding-sdk-package-build.yml
secrets: inherit
sdk-tests:
needs: files-changed
needs: [uberjar, embedding-sdk-package, files-changed]
if: |
needs.embedding-sdk-package.result == 'success' &&
needs.uberjar.result == 'success'
uses: ./.github/workflows/embedding-sdk.yml
secrets: inherit
with:
cli-snippets-type-check: ${{ needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' }}
docs-snippets-type-check: ${{ needs.files-changed.outputs.embedding_documentation == 'true' }}
documentation-validation: ${{ needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' }}
component-tests: ${{ needs.files-changed.outputs.embedding_sdk_components == 'true' }}
host-sample-apps-tests: ${{ needs.files-changed.outputs.embedding_sdk_host_sample_apps == 'true' }}
pr-env:
needs: [uberjar]
if: |
!cancelled() &&
contains(github.event.pull_request.labels.*.name, 'PR-Env')
contains(github.event.pull_request.labels.*.name, 'PR-Env') &&
!contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')
uses: ./.github/workflows/pr-env.yml
secrets: inherit
with:
self_hosting: false
pr-env-self-hosting:
needs: [uberjar]
if: |
!cancelled() &&
contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')
uses: ./.github/workflows/pr-env.yml
secrets: inherit
with:
self_hosting: true
containerize:
needs: [uberjar]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,5 +1,8 @@
accept = ["200..=204", "429"]
exclude_path = ["docs/embedding/sdk"]
exclude = ["\\.html(#.*)?$"]
exclude_path = ["docs/util"]
no_progress = true
include_fragments = true
verbose = "error"
verbose = "error"
include_verbatim = false
cache = false

View File

@@ -2,4 +2,4 @@ frontend/src/cljs
docs/configuring-metabase/environment-variables.md
docs/api/*
docs/api-documentation.md
enterprise/frontend/src/embedding-sdk-shared/.storybook/msw-public/mockServiceWorker.js
frontend/src/embedding-sdk-shared/.storybook/msw-public/mockServiceWorker.js

724
CLAUDE.md
View File

@@ -1,722 +1,20 @@
# Metabase Development Guide
## Autonomous Development Workflow
# Skills
- Do not attempt to read or edit files outside the project folder
- Add failing tests first, then fix them
- Work autonomously in small, testable increments
- Run targeted tests, and lint continuously during development
- Prioritize understanding existing patterns before implementing
- Don't commit changes, leave it for the user to review and make commits
For detailed guidance on writing and reviewing code and documentation, see the skills in [.claude/skills/](.claude/skills/):
## Quick Commands
## Clojure
### JavaScript/TypeScript
- **[clojure-write](.claude/skills/clojure-write/SKILL.md)** - Clojure/ClojureScript development with REPL-driven workflow and coding conventions
- **[clojure-review](.claude/skills/clojure-review/SKILL.md)** - Clojure/ClojureScript code review guidelines and style enforcement
- **Lint:** `yarn lint-eslint-pure`
- **Test:** `yarn test-unit-keep-cljs path/to/file.unit.spec.js` or `yarn test-unit-keep-cljs -t "pattern"`
- **Format:** `yarn prettier`
- **Type Check:** `yarn type-check-pure`
## TypeScript
### Clojure
- **[typescript-write](.claude/skills/typescript-write/SKILL.md)** - TypeScript/JavaScript development and best practices
- **[typescript-review](.claude/skills/typescript-review/SKILL.md)** - TypeScript/JavaScript code review guidelines
- **Lint PR:** `./bin/mage kondo-updated master` (or whatever target branch)
- Call the command one time at the beginning, record the results, then work through the problems one at a time.
- If the solution is obvious, then please apply the fix. Otherwise skip it.
- If you fix all the issues (and verify by rerunning the kondo-updated command):
- commit the change with a succinct and descriptive commit message
- **Lint File:** `./bin/mage kondo <file or files>` (or whatever target branch)
- Use the linter as a way to know that you are adhering to conventions in place in the codebase
- **Lint Changes:** `./bin/mage kondo-updated HEAD`
- **Format:** `./bin/mage cljfmt-files [path]`
- **Run a test:** `./bin-mage run-tests namespace/test-name`
- **Run all tests in a namespace:** `./bin-mage run-tests namespace`
- **Check Code Readability** `./bin/mage -check-readable` with optional line-number
- Run this after every change to Clojure code, only accept readable code
- **Evaluating Clojure Code** `./bin/mage -repl '<code>'`
- See `Sending code to the REPL` for more details
## Documentation
### ClojureScript
- **Test:** `yarn test-cljs`
## Clojure REPL-driven development
- Start with small, fundamental functions:
- Identify the core features or functionalities required for your task.
- Break each feature down into the smallest, most basic functions that can be developed and tested independently.
- Write and test in the REPL:
- Write the code for each small function directly in the REPL (Read-Eval-Print Loop).
- Test it thoroughly with a variety of inputs, including typical use cases and relevant edge cases, to ensure it
behaves as expected.
- Integrate into source code:
- Once a function works correctly in the REPL, move it from the REPL environment into your source code files (e.g.,
within appropriate namespaces).
- Gradually increase complexity:
- Build upon tested, basic functions to create more complex functions or components.
- Compose smaller functions together, testing each new composition in the REPL to verify correctness step by step.
- Ensure dependency testing:
- Make sure every function is fully tested in the REPL before it is depended upon by other functions.
- This ensures that each layer of your application is reliable before you build on it.
- Leverage the REPL fully:
- Use the REPL as your primary tool to experiment with different approaches, iterate quickly, and get immediate
feedback on your code.
- Follow functional programming principles:
- Keep functions small, focused, and composable.
- Leverage Clojure's functional programming features—like immutability, higher-order functions, and the standard
library—to write concise, effective code.
### How to evaluate code
#### Keeping Code Readable
The `./bin/mage -check-readable <file> <optional: line-number>` command checks if your Clojure code can be properly parsed.
This ensures your changes maintain valid syntax and structure.
- Edit Clojure files one step at a time.
- After EVERY change to a Clojure form, call `mage -check-readable src/metabase/thefile.clj <line-number>` with the line number.
- If it's readable then call `mage -check-readable dev/src/dev.clj` without the line number to check the entire file.
- If the change results in unreadable code, try again until it is readable.
- To overcome errors about parens, pay close attention to them. Count opening/closing parens you add/remove.
#### Bottom-up dev loop
1. Write code into a file.
2. Evaluate the file's namespace and make sure it loads correctly with:
```
mage -repl --namespace metabase.app-db.connection
```
3. Call functions in the namespace with test inputs, and observe that the outputs are correct 3.1
Feel free to copy these REPL session trials into actual test cases using `deftest` and `is`.
4. Once you know these functions are good, return to 1, and compose them into the task that you need to build.
#### Sending code to the REPL
- Send code to the metabase process REPL using: `./bin/mage -repl '(+ 1 1)'` where `(+ 1 1)` is your Clojure code.
- See `./bin/mage -repl -h` for more details.
- If the Metabase backend is not running, you'll see an error message with instructions on how to start it.
##### Working with files and namespaces
1. **Load a file and call functions with fully qualified names**:
To call `your.namespace/your-function` on `arg1` and `arg2`:
```
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
```
DO NOT use "require", "load-file" etc in the code string argument.
##### Understanding the response
The `./bin/mage -repl` command returns three separate, independent outputs:
- `value`: The return value of the last expression (best for data structures)
- `stdout`: Any printed output from `println` etc. (best for messages)
- `stderr`: Any error messages (best for warnings and errors)
Example call:
```bash
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
```
Example response:
```
ns: user
session: 32a35206-871c-4553-9bc9-f49491173d1c
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
stdout: Hello, world!
stderr:
```
For effective REPL usage:
- Return data structures as function return values
- Use `println` for human-readable messages
- Print errors to stderr
## Tips
- End all files with a newline.
- When editing tabular code, where the columns line up, try to keep them aligned.
- Spaces on a line with nothing after it is not allowed
## Critical REPL Usage Rules
- Be careful with parentheses counts when editing Clojure code
- After EVERY change to Clojure code, verify readability with `-check-readable`
# Code Review Guidelines
Review pull requests with a focus on:
- Compliance with project coding standards and conventions
- Code quality and best practices
- Clear and correct docstrings
- Clear and correct comments explaining what the code is doing
## More Instructions
- 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
Guide.
- Do not post comments congratulating someone for trivial changes, or for following the style guidelines, or
confirming that things "look good", or telling them that they did something correctly; only post comments about
style violations or potential issues. Example bad code review comments to avoid:
> This TODO comment is properly formatted with author and date - nice work!
> Good addition of limit 1 to the query - this makes the test more efficient without changing its behavior.
> The kondo ignore comment is appropriately placed here
> The kondo ignore is properly used here. The comment on lines 42-45 explains why hardcoded driver names are needed,
> which is good context.
> Test name properly ends with -test as required by the style guide.
- Do not post comments about missing parentheses.
# Code Conventions and Style Guide
## Clojure and ClojureScript
### General Conventions
- Prefer longer, more verbose names for functions and variables; avoid abbreviations unless they are well-known and
conventional in the Clojure world. `acc`, `i`, `pred`, `coll`, `n`, `s`, `k`, and `f` are examples of well-known
conventions; any Clojure developer has seen them before and can tell you what they mean. Avoid unconventional
abbreviations like `tbl` and unclear variable names like `zs'`. A good function or variable name should make its
purpose immediately clear. Error on the side of longer variable names if needed.
```clj
;; too cryptic
(defn mayb+1 [n]
(when n
(inc n)))
;; just right
(defn maybe-inc [n]
(when n
(inc n)))
```
**Why?** Code is read many more times than it is written, and clearer variable names make using and tweaking your
code easier for others.
Conventions used widely in our codebase (like `mp` for a Metadata Provider) are acceptable; one-offs like `zs'` are
not.
- Avoid misleading variable and function names. The names of a variable or function should clearly and unambiguously
describe its purpose and match what it does.
```clj
;; bad
(defn nil-or-maplist? [v] ; coll would be a better variable name because it's more specific
(or (nil? v)
(and (sequential? v) ; v can actually be an array, vector, list, or lazy seq
(every? map? v))))
;; good
(defn nil-or-sequence-of-maps? [coll]
(or (nil? coll)
(and (sequential? coll)
(every? map? coll))))
```
**Why?** Poorly-named functions are prone to being used in cases where they're inappropriate or avoided in cases
when they would be suitable.
- Pure function names should be nouns describing the value they return.
For example, a function to compute a user's age based on their birthdate should be called `age`, not `calculate-age`
or `get-age`.
**Why?** A pure function is one which can be replaced with its value without affecting the result, so the name
should reflect that.
- Don't repeat the usual alias of the namespace a function belongs to in the name of a function itself.
```clj
(ns metabase.config)
;; bad
(defn config-is-dev? [] ...)
;; good
(defn is-dev? [] ...)
```
**Why?** It's obvious that `is-dev?` in the example above is referring to `dev`, because it's in the `config`
namespace. It's also needlessly noisy when using the function in another namespace:
```clj
;; bad
(when (config/config-is-dev?)
...)
;; good
(when (config/is-dev?)
...)
```
In some cases, following this rule will require you to use a `(:refer-clojure :exclude [...])` form in your namespace
declaration. This is acceptable, and should be taken as a sign that you're following this rule correctly.
- If you shadow a `clojure.core` var, make sure you use `(:refer-clojure :exclude ...)` in the `ns` form.
- Make everything `^:private` unless it is used elsewhere.
Don't make things public just for the sake of tests. Use the var form (e.g. `#'redshift/execute!`) instead in your
tests.
**Why?** It's much easier to read and refactor code when you know its scope is limited to the current namespace.
- Tag variables with `:arglists` metadata if they are functions but wouldn't otherwise have it, such as when using
`def` to define partial functions or function compositions e.g.
```clj
(def ^{:arglists '([n])} plus-one (partial + 1))
```
**Why?** Good editors use this metadata show the expected arguments to a function as you're writing code.
- Try to organize namespaces in such a way that you don't need to use `declare`. This usually means putting the public
portion of a namespace near the end of a file.
**Why?** Avoiding declare when unnecessary forces us to read and write code in a consistent manner, that is, from
top to bottom. When code is written in this consistent order we can safely assume referenced functions sit somewhere
above their reference in the namespace; this makes the code easier to navigate.
- 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
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
functions used across many namespaces it should have docstrings.
Schemas defined with `mr/def` e.g. `(mr/def ::my-schema)` do not require docstrings.
- Format docstrings according to Markdown conventions. (https://guide.clojure.style/#markdown-docstrings)
- Mentions of other vars in docstrings should use `[[some-other-var]]` (for vars in the same namespace) or
`[[metabase.namespace/some-other-var]]` (for vars in a different namespace) instead of backticks; references in
docstrings should be valid (i.e., point to something that exists).
(https://guide.clojure.style/#document-references)
- Judiciously use comments to explain sections of code that would not immediately be clear to someone else. Avoid
comments that do little more than repeat what the code already says.
- Make sure to update comments and docstrings when you change the code they describe.
- `TODO` comments should include the author and date, for example
```clj
;; TODO (Cam 10/7/25) -- this is a properly formatted TODO comment
(...)
```
- Break up larger functions (> 20 lines) in source code (`src` or `enterprise/backend/src`) whenever possible. Small
functions are much easier to test, understand, and tweak. Tests are allowed to be longer, especially when they
contain mock data, but they should probably never be more than 100 lines.
- Try to keep lines 120 characters wide or less; use this as a guideline when formatting docstrings or comments.
- **No Blank Lines Within Definition Forms** Do not place blank lines in the middle of a function or macro definition.
An exception can be made to indicate grouping of pairwise constructs as found in e.g. `let` and `cond`, in case
those don’t fit on the same line. `deftest` is **NOT** an exception to this rule.
(https://guide.clojure.style/#no-blank-lines-within-def-forms) Verify that the line in question is **actually
blank** (i.e., only contains whitespace for the **entire line**) before warning about this. **A line that contains
any non-whitespace character is not a blank line.** You will be shut down if you make this mistake too many times,
so be careful.
- Use `kebab-case` names for variables and defs, including constants.
(https://guide.clojure.style/#naming-functions-and-variables)
```clj
;;; BAD
(def MY_CONSTANT 100)
;;; GOOD
(def my-constant 100)
;;; BAD
(defn myFunction [my_arg] ...)
;;; GOOD
(defn my-function [my-arg] ...)
```
- Map destructuring should use kebab-case local bindings even if the map it was destructured from uses `snake_case`
keys or if it is returned as a value for a `snake_case` key.
```clj
;; Good
(let [{database-id :database_id} some-object]
{:database_id database-id, :table_id 100})
;; Bad
(let [{database_id :database_id} some-object]
{:database_id database_id, :table_id 100})
```
- Prefer namespaced keywords for keywords that are used internally (i.e., not returned by the REST API or persisted by
the app DB):
```clj
;;; good
(defn query-type [x]
(if (some-pred? x)
:query-type/normal
:query-type/crazy))
;;; bad
(defn query-type [x]
(if (some-pred? x)
:normal
:crazy))
```
These are easy to search across the entire application and makes their origin clearer.
- Functions that have side-effects such as writing to the application database or mutating the global state of the
application should have names that end in exclamation points. Exclamation points should be considered "sticky", so
if a function uses another function with a name ending in an exclamation point, it too should have a name that ends
in an exclamation point. (https://guide.clojure.style/#naming-unsafe-functions)
An exception is functions that write log messages or other output to the console; these don't need exclamation
points.
### Tests
- Large tests should be broken out into separate `deftest` forms when they consist of several logically separate test
cases.
```clj
(deftest ^:parallel my-test
(testing "Some logically discrete test case"
(is ...)))
(deftest ^:parallel my-other-test
(testing "Another logically discrete test case"
(is ...)))
(deftest ^:parallel my-third-different-test
(testing "A third logically discrete test case"
(is ...)))
(deftest ^:parallel my-amazing-test
(testing "A fourth logically discrete test case"
(is ...)))
```
is preferable to
```clj
(deftest ^:parallel my-test
(testing "Some logically discrete test case"
(is ...))
(testing "Another logically discrete test case"
(is ...))
(testing "A third logically discrete test case"
(is ...))
(testing "A fourth logically discrete test case"
(is ...)))
```
- Mark pure function tests `^:parallel`.
- Test utility functions that are not thread-safe/safe in `^:parallel` tests should have names that end in an
exclamation mark.
- Test names in `deftest` forms should end in `-test` or `-test-<number>` e.g. `whatever-test` or `whatever-test-2` or
even `whatever-test-2b`. (https://guide.clojure.style/#test-naming)
### Modules
- The backend codebase is broken out into separate modules.
- The module configuration file lives in `.clj-kondo/config/modules/config.edn`. There is one entry for each module.
The entry has several keys, but the important ones are `:api` -- the list of namespaces this module provides for use
outside of the module -- and `:uses` -- the list of modules this module directly relies on.
- An OSS follows the pattern `metabase.<module>.*` (for the Clojure namespace) and `src/metabase/<module>/**`(for the
source files) with tests inside the corresponding `test/metabase/<module>/` directory, e.g. the `dashboards` module
is everything inside `src/metabase/dashboards/` and `test/metabase/dashboards/`; it might have a
`metabase.dashboards.api` namespace that corresponds to the file `src/metabase/dashboards/api.clj`.
- An EE module follows the pattern `metabase-enterprise.<module>.*` (for the Clojure namespaces) and
`enterprise/backend/src/metabase_enterprise/<module>/**` (for the source files). The module name uses the
`enterprise/` prefix. For example, `enterprise/billing` is everything in the
`enterprise/backend/src/metabase_enterprise/billing/` directory (for source code) and
`enterprise/backend/test/metabase_enterprise/billing/` (for tests); it might have a namespace called
`metabase-enterprise.billing.api` which corresponds to the file
`enterprise/backend/src/metabase_enterprise/billing/api.clj`.
- REST API endpoints (defined by the `defendpoint` macro) should live in a `<module>.api` or `<module>.api.*`
namespace, e.g. `metabase.dashboards.api` or `metabase.dashboards.api.x`.
- When you add a new API endpoint namespace, you need to add a mapping for it in `metabase.api-routes.routes`.
- Put any functions used by other modules (the API meant for the rest of the backend code) in `<module>.core` .
`.core` should import stuff with Potemkin/ `metabase.util.namespace` and not be used inside the module itself. It’s
also nice to put a `:consistent-alias` entry for this namespace in the Kondo config.
- The `<module>.core` namespace should generally be empty other than importing things from other namespaces with
Potemkin (`potemkin/import-vars` and the like).
- Put Toucan models related to a feature in `<module>.models.*` and add mappings in `metabase.models.resolution`.
- Put scheduled Quartz tasks in `<module>.task.*`.
- Put event handlers (things that use the events subsystem in `metabase.events.core`) in `<module>.event.*`.
- Put `defsetting`s (Settings) in `<module>.settings`.
- Quartz tasks, event handlers, and Settings all need to be loaded on launch, so if you have any of the above add
them to a `<module>.init` namespace and require it in `metabase[-enterprise].core.init`.
- Don't try to cheat the module linters by using things like `#_{:clj-kondo/ignore [:metabase/modules]}`. Note that
this does not apply to using `:clj-kondo/ignore` to disable warnings for other linters besides `:metabase/modules`.
- Put Malli schemas in `<module>.schema`.
- Aim to keep the number of namespaces used outside of a module small. A module should only need at most a subset of
`<module>.api`, `<module>.settings`, `<module>.schema`, `<module>.init`, and `<module>.core`.
- Try to minimize the direct dependencies of a module as well as the indirect dependencies. Modules that are used by
lots of other modules (such as `util`) ideally will have no dependencies on other modules. Our goal is to make as
many modules as possible be "leaf nodes".
- Module names should be match the customer-facing name of the feature they concern, usually a plural noun, for
example, `dashboards` rather than `dashboard`.
### Settings
- Don't define configurable options that can only be set with environment variables; use an `:internal` `defsetting`
instead. We have lots of tooling around `defsetting`.
### REST API Endpoints
- All new REST API Endpoints (defined by `defendpoint`) should have a response schema (denoted by `:- <schema>` after
the route string).
```clj
;;; BAD
(api.macros/defendpoint :get "/"
"Get a list of all transform tags."
...)
;;; GOOD
(api.macros/defendpoint :get "/" :- [:sequential ::whatever-this-returns]
"Get a list of all transform tags."
...)
```
- 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.
- HTTP request bodies should use `snake_case`.
- REST API endpoints should have routes that use singular nouns, for example `GET /api/dashboard/:id` to get a
Dashboard rather than `GET /api/dashboards/:id`.
- `GET` endpoints should not have side effects outside of analytics like updating last-viewed-at timestamps or
recording usage metrics. A `GET` endpoint should not be creating new rows in the application database representing
user-facing objects, for example `GET /api/dashboard` should not attempt to populate empty Dashboards with content
by creating new rows in the application database.
- `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.
- 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.
- Malli schemas for REST API endpoints should be detailed enough that someone can look at them and know how to use the
endpoint.
Example: for an endpoint like this:
```clj
(api.macros/defendpoint :post "/check_transform"
"Check a proposed edit to a transform, and return the card, transform, etc. IDs for things that will break."
[_route-params
_query-params
{:keys [id source target] :as _body} :- ::transform-body]
...)
```
This is a bad schema for the request body:
```clj
;;; Bad, does not specify the shape of :source and :target in any real detail
(mr/def ::transform-body
[:map
[:id {:optional false} ms/PositiveInt]
[:name {:optional true} :string]
[:source {:optional true} [:maybe ms/Map]]
[:target {:optional true} [:maybe ms/Map]]])
```
While this is better:
```clj
;;; Better, because it tells us the expected shape of :source and :target
(mr/def ::transform-body
[:map
[:id {:optional false} ms/PositiveInt]
[:name {:optional true} :string]
[:source {:optional true} [:maybe [:map [:x pos-int?] [:y pos-int?]]]]
[:target {:optional true} [:maybe [:map [:a pos-int?] [:b pos-int?]]]]])
```
- All new REST API endpoints **MUST HAVE TESTS**.
### MBQL
- No raw MBQL introspection or manipulation should be done outside of Lib (the `lib` and `lib-be` modules) or the
Query Processor (the `query-processor` module) modules. MBQL maps include the `:model/Card` `dataset_query`, you can
usually recognize it when you see a map with a `:database` key and either `:type` or `:lib/type`. You should treat
this map as an opaque object outside of the aforementioned modules; pretend you didn't know it has a `:database` or
`:type` key.
- Use Lib and MBQL 5 in all new **source** code instead of legacy MBQL; avoid use of the `legacy-mbql` module, or the
`metabase.query-processor.store` namespace, in new code. Any code that checks whether a query `:type` is `:native`
or `:query` is a gigantic code smell. Exception: we will allow legacy MBQL in tests... for now.
- Use of `qp.store/with-metadata-provider` or `mt/with-metadata-provider` should be discouraged and phased out.
## Models and the Application Database
- Model names should be singular nouns, e.g. `:model/Dashboard` and not `:model/Dashboards`.
- Table names in the application database should be singular nouns, e.g. `transform` instead of `transforms`.
- The application database should use `snake_case` identifiers for table and column names.
## Using Toucan
- Never fetch an entire row from the application database only to immediately discard everything except for the value
of one column, this is super inefficient and also icky. Use `t2/select-one-fn` instead.
```clj
;;; Bad
(:database_id (t2/select-one :model/Card :id 1))
;;; Good
(t2/select-one-fn :database_id :model/Card :id 1)
;;; Even better -- this does SELECT database_id instead of SELECT *
(t2/select-one-fn :database_id [:model/Card :database_id] :id 1)
```
- One of the big ideas behind Toucan is there's never supposed to be a question of "what function to I use to create
an X correctly" or "what function do I use to update a Y correctly" -- the answer is always supposed to be
`t2/select`, `t2/update!`, etc. That's why they're backed by multimethods, so you can put the correct behavior in
the model's method and then it becomes impossible for you to forget to call the correct function. Avoid adding
functions like `select-dashboards` or `update-dashboard!` -- put this functionality in Toucan methods for the model
in question. This includes things like firing off events.
- Avoid the `mi/define-batched-hydration-method` and `mi/define-simple-hydration-method` helpers in new code; define
method implementations with `(methodical/defmethod t2/batched-hydrate ...)` directly instead.
### Drivers
- All new driver multimethods must be mentioned in `docs/developers-guide/driver-changelog.md`.
- All new driver multimethods should use Lib-style kebab-cased metadata and MBQL 5 queries.
- Driver multimethod implementations (`defmethod` forms) should explicitly pass any `driver` argument to any other
driver multimethods it invokes, rather than hardcoding the driver name.
```clj
;;; Good
(defmethod driver/driver-method :postgres
[driver x]
(driver/some-other-method driver x))
;;; Bad
(defmethod driver/driver-method :postgres
[_driver x]
(driver/some-other-method :postgres x))
```
This is because other drivers may inherit from this driver and we need to propagate the driver name passed in to any
method implementations it inherits.
- Drivers should only use methods from the `driver` or `driver-api` modules.
- Be sure to minimize the amount of logic you're doing inside the `read-column-thunk` in JDBC-based drivers as much as
possible. Here's our Athena code for java.sql.Types/TIMESTAMP_WITH_TIMEZONE for example:
```clj
(defmethod sql-jdbc.execute/read-column-thunk [:athena Types/TIMESTAMP_WITH_TIMEZONE]
[_driver ^ResultSet rs _rs-meta ^Long i]
(fn []
;; Using ZonedDateTime if available to conform tests first. OffsetDateTime if former is not available.
(when-some [^Timestamp timestamp (.getObject rs i Timestamp)]
(let [timestamp-instant (.toInstant timestamp)
results-timezone (driver-api/results-timezone-id)]
...))))
```
Here's a tiny change that will avoid calling (driver-api/results-timezone-id) potentially 10 million times (for a
potential million-row result with 10 timestamp columns)
```clj
(defmethod sql-jdbc.execute/read-column-thunk [:athena Types/TIMESTAMP_WITH_TIMEZONE]
[_driver ^ResultSet rs _rs-meta ^Long i]
(let [results-timezone (driver-api/results-timezone-id)]
(fn []
;; Using ZonedDateTime if available to conform tests first. OffsetDateTime if former is not available.
(when-some [^Timestamp timestamp (.getObject rs i Timestamp)]
(let [timestamp-instant (.toInstant timestamp)]
...)))))
```
- Avoid defining new datasets with `defdataset` as much as possible, since loading test data in cloud-based databases
is crazy slow.
### Misc
- Example data should be bird-themed if possible.
- Any comments that are written by or mention `Cam` should be given bonus points.
### Kondo
- Kondo linter warnings are suppressed with a comment proceeding the form to ignore warnings in e.g.
```clj
#_{:clj-kondo/ignore [:metabase/disallow-hardcoded-driver-names-in-tests]}
(do-x ...)
```
to ignore the `:metabase/disallow-hardcoded-driver-names-in-tests` Linter inside the `(do-x ...)` form. You do not
need to point these out to us, we know what they do.
```clj
^{:clj-kondo/ignore [:metabase/disallow-hardcoded-driver-names-in-tests]}
(do-x ...)
```
is an acceptable alternative.
- Avoid ignoring **everything** with a `#_:clj-kondo/ignore` (a keyword instead of a map).
- **[docs-write](.claude/skills/docs-write/SKILL.md)** - Documentation writing with Metabase style guide
- **[docs-review](.claude/skills/docs-review/SKILL.md)** - Documentation review checklist

26
bb.edn
View File

@@ -83,6 +83,18 @@
:requires [[mage.check]]
:task (task! (mage.check/check (:arguments parsed)))}
codespell
{:doc "Check for misspellings in the source code."
:examples [["./bin/mage codespell"]]
:requires [[mage.codespell]]
:task (task! (mage.codespell/codespell (:arguments parsed)))}
codespell-interactive
{:doc "Check for misspellings in the source code, and interactively prompt to fix them."
:examples [["./bin/mage codespell-interactive"]]
:requires [[mage.codespell]]
:task (task! (mage.codespell/codespell-interactive (:arguments parsed)))}
start-maildev
{:doc "Start Maildev"
:examples [["./bin/mage start-maildev -h" "print help for start-maildev"]]
@@ -192,6 +204,20 @@
test)]
(nrepl-eval "mb.hawk.core" (str code) (:port options)))}
affected-modules
{:doc "Prints information about which backend modules have changes compared to a Git ref e.g. `master`"
:examples [["./bin/mage affected-modules master"]
["./bin/mage affected-modules"]]
:requires [[mage.modules]]
:task (task! (mage.modules/cli-print-affected-modules (:arguments parsed)))}
can-skip-driver-tests
{:doc "Returns zero (success) if we can skip driver tests based on changes related to a Git ref e.g. `master`"
:examples [["./bin/mage can-skip-driver-tests master"]
["./bin/mage can-skip-driver-tests"]]
:requires [[mage.modules]]
:task (task! (mage.modules/cli-can-skip-driver-tests (:arguments parsed)))}
ls {:doc "List all mage public and private tasks"
:examples [["./bin/mage ls" "List all tasks"]]
:requires [[mage.util :as u]]

View File

@@ -44,7 +44,7 @@
["target/classes/metabase/request/util.clj"] true
;; Both a FE and a BE path
["frontend/src/metabase/browse/components/TableBrowser/TableBrowser.jsx:145"
"metabase/warehouses/api.clj:178"] true
"metabase/warehouses_rest/api.clj:178"] true
;; FE-only paths
["frontend/src/metabase/components/ActionButton.jsx:31"] false
["frontend/src/metabase/entities/collections/forms.js:22"] false

View File

@@ -20,8 +20,9 @@ const REPLACES_MAP = {
"metabase-types": "frontend/src/metabase-types",
metabase: "frontend/src/metabase",
"embedding-sdk-package": "enterprise/frontend/src/embedding-sdk-package",
"embedding-sdk-bundle": "enterprise/frontend/src/embedding-sdk-bundle",
"embedding-sdk-shared": "enterprise/frontend/src/embedding-sdk-shared",
"embedding-sdk-bundle": "frontend/src/embedding-sdk-bundle",
"embedding-sdk-shared": "frontend/src/embedding-sdk-shared",
"embedding-sdk-ee": "enterprise/frontend/src/embedding-sdk-ee",
cljs: "target/cljs_release",
};
@@ -31,13 +32,15 @@ const API_EXTRACTOR_CONFIG_PATH = path.join(
);
const getLogger = (prefix) => {
const verbose = process.env.SDK_FIXUP_VERBOSE_LOGS === "true";
return {
verbose: (message) => verbose && console.log(`[${prefix}] ${message}`),
log: (message) => console.log(`[${prefix}] ${message}`),
error: (message) => console.error(`[${prefix}] ${message}`),
};
};
const { log } = getLogger("dts fixup");
const { log, verbose } = getLogger("dts fixup");
const getRelativePath = (fromPath, toPath) => {
const relativePath = path.relative(path.dirname(fromPath), toPath);
@@ -67,7 +70,7 @@ const replaceAliasedImports = (filePath) => {
});
fs.writeFileSync(filePath, fileContent, { encoding: "utf-8" });
log(`Edited file: ${filePath}`);
verbose(`Edited file: ${filePath}`);
};
const removeUnresolvedReexports = (filePath) => {
@@ -94,7 +97,7 @@ const removeUnresolvedReexports = (filePath) => {
!fs.existsSync(`${path.resolve(dirPath, target)}.d.ts`);
if (isUnresolved) {
log(`Removing unresolved re-export: ${line}`);
verbose(`Removing unresolved re-export: ${line}`);
isModified = true;
}
@@ -117,7 +120,7 @@ const fixupTypesAfterCompilation = ({ isWatchMode }) => {
removeUnresolvedReexports(filePath);
});
log("Done!");
console.log("[dts fixup] Done!");
if (!isWatchMode) {
generateDtsRollup();
@@ -176,7 +179,7 @@ const generateDtsRollup = () => {
});
});
log("Dts rollup done!");
console.log("[dts rollup] Dts rollup done!");
};
const watchFilesAndFixThem = () => {

View File

@@ -74,7 +74,7 @@
:exclusions [commons-beanutils/commons-beanutils
commons-digester/commons-digester
commons-logging/commons-logging]}
compojure/compojure {:mvn/version "1.7.1" ; HTTP Routing library built on Ring
compojure/compojure {:mvn/version "1.7.2" ; HTTP Routing library built on Ring
: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"}
@@ -85,9 +85,9 @@
hiccup/hiccup {:mvn/version "2.0.0"} ; HTML templating
inflections/inflections {:mvn/version "0.15.0"} ; Clojure/Script library used for prularizing words
instaparse/instaparse {:mvn/version "1.5.0"} ; Make your own parser
io.github.camsaul/toucan2 {:mvn/version "1.0.568"}
io.github.camsaul/toucan2 {:mvn/version "1.0.569"}
io.github.eerohele/pp {#_#_:git/tag "2024-11-13.77" ; super fast pretty-printing library
:git/sha "4998a1fdd9d3713d7034d04509e9212204773bf2"
:git/sha "ffc4edc482c057cd9412c62f018a41e9140c618b"
:git/url "https://github.com/eerohele/pp"}
;; TODO: replace back to normal artifact
io.github.metabase/macaw {:mvn/version "0.2.29"} ; Parse native SQL queries
@@ -108,7 +108,7 @@
org.bouncycastle/bcprov-jdk18on]}
metabase/throttle {:mvn/version "1.0.2"} ; Tools for throttling access to API endpoints and other code pathways
methodical/methodical {:mvn/version "1.0.127"} ; drop-in replacements for Clojure multimethods and adds several advanced features
metosin/malli {:mvn/version "0.19.2"} ; Data-driven Schemas for Clojure/Script and babashka
metosin/malli {:mvn/version "0.20.0"} ; Data-driven Schemas for Clojure/Script and babashka
nano-id/nano-id {:mvn/version "1.1.0"} ; NanoID generator for generating entity_ids
net.cgrand/macrovich {:mvn/version "0.2.2"} ; utils for writing macros for both Clojure & ClojureScript
net.clojars.wkok/openai-clojure {:mvn/version "0.23.0"
@@ -121,15 +121,15 @@
net.thisptr/jackson-jq {:mvn/version "1.6.0"} ; Java implementation of the JQ json query language
org.apache.commons/commons-compress {:mvn/version "1.28.0"} ; compression utils
org.apache.commons/commons-lang3 {:mvn/version "3.18.0"} ; helper methods for working with java.lang stuff
org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.25.1"} ; apache logging framework
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.1"} ; add compatibility with log4j 1.2
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.1"} ; apache logging framework
org.apache.logging.log4j/log4j-jcl {:mvn/version "2.25.1"} ; allows the commons-logging API to work with log4j 2
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.1"} ; java.util.logging (JUL) -> Log4j2 adapter
org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.25.2"} ; apache logging framework
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.2"} ; add compatibility with log4j 1.2
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.2"} ; apache logging framework
org.apache.logging.log4j/log4j-jcl {:mvn/version "2.25.2"} ; allows the commons-logging API to work with log4j 2
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.2"} ; java.util.logging (JUL) -> Log4j2 adapter
org.apache.logging.log4j/log4j-layout-template-json
{:mvn/version "2.25.1"} ; allows the custom json logging format
{:mvn/version "2.25.2"} ; allows the custom json logging format
org.apache.logging.log4j/log4j-slf4j2-impl
{:mvn/version "2.25.1"} ; allows the slf4j2 API to work with log4j 2
{:mvn/version "2.25.2"} ; allows the slf4j2 API to work with log4j 2
org.apache.poi/poi {:mvn/version "5.4.1"} ; Work with Office documents (e.g. Excel spreadsheets) -- newer version than one specified by Docjure
org.apache.poi/poi-ooxml {:mvn/version "5.4.1"
:exclusions [org.bouncycastle/bcpkix-jdk15on
@@ -138,10 +138,10 @@
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"}
org.apache.tika/tika-core {:mvn/version "3.2.3"}
org.apache.xmlgraphics/batik-all {:mvn/version "1.19"} ; SVG -> image
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.81"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.81"}
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.82"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.82"}
org.clj-commons/claypoole {:mvn/version "1.2.2"} ; Threadpool tools for Clojure
org.clj-commons/dirigiste {:mvn/version "1.0.4"} ; Instrumented object pooling
org.clj-commons/hickory {:mvn/version "0.7.7" ; Parse HTML into Clojure data structures
@@ -180,7 +180,7 @@
org.mariadb.jdbc/mariadb-java-client ^:antq/exclude ; 3.x only support jdbc:mariadb, so using 2.x for now
{:mvn/version "2.7.10"} ; MySQL/MariaDB driver
org.mindrot/jbcrypt {:mvn/version "0.4"} ; Crypto library
org.postgresql/postgresql {:mvn/version "42.7.7"} ; Postgres driver
org.postgresql/postgresql {:mvn/version "42.7.8"} ; Postgres driver
org.slf4j/slf4j-api {:mvn/version "2.0.17"} ; abstraction for logging frameworks -- allows end user to plug in desired logging framework at deployment time
org.tcrawley/dynapath {:mvn/version "1.1.0"} ; Dynamically add Jars (e.g. Oracle or Vertica) to classpath
org.threeten/threeten-extra {:mvn/version "1.8.0"} ; extra Java 8 java.time classes like DayOfMonth and Quarter
@@ -192,8 +192,8 @@
redux/redux {:git/url "https://github.com/metabase/redux"
:sha "4a37feaf817a2a6b5ef688c927f6fd4375964433"}
riddley/riddley {:mvn/version "0.2.0"} ; code walking lib -- used interally by Potemkin, manifold, etc.
ring/ring-core {:mvn/version "1.14.2"} ; HTTP abstraction
ring/ring-jetty-adapter {:mvn/version "1.14.2" ; Ring adapter for Jetty
ring/ring-core {:mvn/version "1.15.3"} ; HTTP abstraction
ring/ring-jetty-adapter {:mvn/version "1.15.3" ; Ring adapter for Jetty
:exclusions [org.eclipse.jetty/jetty-server
org.eclipse.jetty/jetty-unixdomain-server
org.eclipse.jetty.ee9/jetty-ee9-servlet
@@ -249,7 +249,7 @@
{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`
clj-kondo/clj-kondo {:mvn/version "2025.10.23"} ; included here mainly to facilitate working on the stuff in `.clj-kondo/src`
cloverage/cloverage {:mvn/version "1.2.4"}
com.clojure-goes-fast/clj-async-profiler
{:mvn/version "1.6.2"} ; Enables local profiling and heat map generation
@@ -274,7 +274,7 @@
:exclusions [org.clojure/clojure
org.clojure/clojurescript]} ; lets you write test fixtures that are clear, concise, and easy to maintain (clojure.spec)
rewrite-clj/rewrite-clj {:mvn/version "1.2.50"} ; clojure source parsing and rewriting
ring/ring-devel {:mvn/version "1.14.2"} ; reload clojure files on the fly
ring/ring-devel {:mvn/version "1.15.3"} ; reload clojure files on the fly
ring/ring-mock {:mvn/version "0.6.2"} ; creating Ring request maps for testing purposes
talltale/talltale {:mvn/version "0.5.14"}} ; generates fake data, useful for prototyping or load testing
:extra-paths ["dev/src" "local/src" "test" "test_resources" ".clj-kondo/src" ".clj-kondo/test"]
@@ -419,7 +419,7 @@
org.clojars.mmb90/cljs-cache {:mvn/version "0.1.4"}
refactor-nrepl/refactor-nrepl {:mvn/version "3.11.0"}
;; keep it in sync with package.json
thheller/shadow-cljs {:mvn/version "3.2.0"}}}
thheller/shadow-cljs {:mvn/version "3.2.1"}}}
;; for local dev -- include the drivers locally with :dev:drivers
:drivers
@@ -467,7 +467,7 @@
;; Use this to only run Kondo against specific files.
:kondo
{:replace-deps
{clj-kondo/clj-kondo {:mvn/version "2025.07.28"}}
{clj-kondo/clj-kondo {:mvn/version "2025.10.23"}}
:main-opts
["-m" "clj-kondo.main"]}

View File

@@ -356,8 +356,8 @@
([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)))]
(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]
@@ -433,10 +433,10 @@
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
(external-usages 'core)
@@ -461,12 +461,12 @@
([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)]
(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]
@@ -532,7 +532,46 @@
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))))))
all-modules (into (sorted-set) (map :module) deps)
module-deps (set (keys (all-module-deps-paths deps module)))]
(printf "Module %s depends on %d/%d (%.1f%%) other modules.\n"
module
(count module-deps)
(count all-modules)
(double (* (/ (count module-deps) (count all-modules)) 100)))
(flush)
(set/difference all-modules module-deps)))
(defn- simulate-rename
"Create a new version of `deps` as they would appear if you renamed namespace(s).
(simulate-rename (dependencies) '{metabase.users.api metabase.users-rest.api})"
([deps old-namespace new-namespace]
(for [dep deps]
(-> dep
(cond-> (= (:namespace dep) old-namespace)
(assoc :namespace new-namespace))
(update :deps (fn [deps]
(for [dep deps]
(if (= (:namespace dep) old-namespace)
{:namespace new-namespace, :module (module new-namespace)}
dep)))))))
([deps old-namespace->new-namespace]
(reduce
(fn [deps [old-namespace new-namespace]]
(simulate-rename deps old-namespace new-namespace))
deps
old-namespace->new-namespace)))
(defn dependencies-eliminated-by-renaming-namespaces
"Calculate the set of dependencies of `module` (both explicit and transient) that would be eliminated by renaming
`old-namespaces->new-namespaces`.
(dependencies-eliminated-by-renaming-namespaces 'users '{metabase.users.api metabase.users-rest.api})"
[module old-namespace->new-namespace]
(let [deps (dependencies)
old-module-deps (into (sorted-set) (keys (all-module-deps-paths deps module)))
new-deps (simulate-rename deps old-namespace->new-namespace)
new-module-deps (into (sorted-set) (keys (all-module-deps-paths new-deps module)))]
(set/difference old-module-deps new-module-deps)))

View File

@@ -3853,7 +3853,7 @@
"required" : [ "type", "subject", "body" ]
} ]
},
"metabase.collections.api.DashboardQuestionCandidate" : {
"metabase.collections-rest.api.DashboardQuestionCandidate" : {
"type" : "object",
"properties" : {
"description" : {
@@ -3885,13 +3885,13 @@
},
"required" : [ "id", "name", "sole_dashboard_info" ]
},
"metabase.collections.api.DashboardQuestionCandidatesResponse" : {
"metabase.collections-rest.api.DashboardQuestionCandidatesResponse" : {
"type" : "object",
"properties" : {
"data" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidate"
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidate"
}
},
"total" : {
@@ -3900,7 +3900,7 @@
},
"required" : [ "data", "total" ]
},
"metabase.collections.api.MoveDashboardQuestionCandidatesResponse" : {
"metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse" : {
"type" : "object",
"properties" : {
"moved" : {
@@ -9211,7 +9211,7 @@
"type" : "string",
"enum" : [ "events" ]
},
"metabase.users.api.user-group-membership" : {
"metabase.users-rest.api.user-group-membership" : {
"description" : "Group Membership info of a User.\n In which :is_group_manager is only included if `advanced-permissions` is enabled.",
"type" : "object",
"properties" : {
@@ -9267,12 +9267,12 @@
},
"required" : [ "schedule_type" ]
},
"metabase.warehouse-schema.api.table.data-authority-write" : {
"metabase.warehouse-schema-rest.api.table.data-authority-write" : {
"description" : "Schema for writing a valid table data authority.",
"type" : "string",
"enum" : [ "computed", "authoritative", "unconfigured", "ingested" ]
},
"metabase.warehouses.api.available-settings" : {
"metabase.warehouses-rest.api.available-settings" : {
"type" : "object",
"additionalProperties" : {
"type" : "object",
@@ -12530,7 +12530,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidatesResponse"
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidatesResponse"
}
}
}
@@ -12656,7 +12656,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/metabase.collections.api.MoveDashboardQuestionCandidatesResponse"
"$ref" : "#/components/schemas/metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse"
}
}
}
@@ -12914,7 +12914,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidatesResponse"
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidatesResponse"
}
}
}
@@ -13049,7 +13049,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/metabase.collections.api.MoveDashboardQuestionCandidatesResponse"
"$ref" : "#/components/schemas/metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse"
}
}
}
@@ -15119,7 +15119,7 @@
"type" : "object",
"properties" : {
"settings" : {
"$ref" : "#/components/schemas/metabase.warehouses.api.available-settings"
"$ref" : "#/components/schemas/metabase.warehouses-rest.api.available-settings"
}
},
"required" : [ "settings" ]
@@ -16252,7 +16252,7 @@
"required" : true,
"schema" : {
"type" : "string",
"enum" : [ "metabase-ai", "python-execution" ]
"enum" : [ "metabase-ai", "metabase-ai-tiered", "python-execution" ]
}
} ],
"responses" : {
@@ -16272,6 +16272,9 @@
"schema" : {
"type" : "object",
"properties" : {
"quantity" : {
"type" : "integer"
},
"terms_of_service" : {
"type" : "boolean"
}
@@ -20141,7 +20144,7 @@
},
"remote-sync-type" : {
"type" : "string",
"enum" : [ "production", "development" ]
"enum" : [ "read-only", "read-write" ]
},
"remote-sync-url" : {
"type" : "string"
@@ -28674,25 +28677,6 @@
"tags" : [ "/api/setup" ]
}
},
"/api/setup/user_defaults" : {
"get" : {
"summary" : "GET /api/setup/user_defaults",
"description" : "Returns object containing default user details for initial setup, if configured,\n and if the provided token value matches the token in the configuration value.",
"parameters" : [ ],
"responses" : {
"2XX" : {
"description" : "Successful response"
},
"4XX" : {
"description" : "Client error response"
},
"5XX" : {
"description" : "Server error response"
}
},
"tags" : [ "/api/setup" ]
}
},
"/api/slack/bug-report" : {
"post" : {
"summary" : "POST /api/slack/bug-report",
@@ -28830,7 +28814,7 @@
"enum" : [ "technical", "hidden", "cruft" ]
},
"data_authority" : {
"$ref" : "#/components/schemas/metabase.warehouse-schema.api.table.data-authority-write"
"$ref" : "#/components/schemas/metabase.warehouse-schema-rest.api.table.data-authority-write"
},
"show_in_getting_started" : {
"type" : "boolean"
@@ -28993,7 +28977,7 @@
"enum" : [ "technical", "hidden", "cruft" ]
},
"data_authority" : {
"$ref" : "#/components/schemas/metabase.warehouse-schema.api.table.data-authority-write"
"$ref" : "#/components/schemas/metabase.warehouse-schema-rest.api.table.data-authority-write"
},
"show_in_getting_started" : {
"type" : "boolean"
@@ -30489,7 +30473,7 @@
"user_group_memberships" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/metabase.users.api.user-group-membership"
"$ref" : "#/components/schemas/metabase.users-rest.api.user-group-membership"
}
}
},
@@ -30627,7 +30611,7 @@
"user_group_memberships" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/metabase.users.api.user-group-membership"
"$ref" : "#/components/schemas/metabase.users-rest.api.user-group-membership"
}
}
}

View File

@@ -79,7 +79,7 @@ For example, say you're running two Metabase instances, A and B, that both use t
Then the number of user accounts would total 8 for that day.
If you're on Metabase Cloud, counting active users works the same: each day we count the maxium number of user accounts at any one time over the previous 36 hours.
If you're on Metabase Cloud, counting active users works the same: each day we count the maximum number of user accounts at any one time over the previous 36 hours.
We refresh the user count you see in your [Metabase Store account page](https://store.metabase.com) every day. Since the refresh only happens once a day, there might be a delay between when you adjust the number of user accounts in your Metabase and when your accounts sync with your Store page.

View File

@@ -48,7 +48,7 @@ If you're migrating from a Metabase Cloud Pro plan to a self-hosted Pro plan, yo
2. **Download the snapshot from Metabase Store**. The email from Metabase Success Engineering will have a link to your download. You'll need to log in to the Metabase Store to access the download.
The snapshot of your Metabase Cloud application database will be an H2 file database: a file with the extension `.mv.db`. (Metabase Cloud instances use PostgresSQL for application databases, but snapshots are saved in H2 format, so that you can restore to either PostgresSQL or MysQL application databases).
The snapshot of your Metabase Cloud application database will be an H2 file database: a file with the extension `.mv.db`. (Metabase Cloud instances use PostgreSQL for application databases, but snapshots are saved in H2 format, so that you can restore to either PostgreSQL or MysQL application databases).
Snapshots expire quickly, but don't sweat it; if your snapshot has expired, just request a new one.

View File

@@ -209,6 +209,7 @@ config:
not-behind-proxy: false
notification-link-base-url: null
notification-system-event-thread-pool-size: 5
notification-temp-file-size-max-bytes: 10485760
notification-thread-pool-size: 3
persisted-model-refresh-cron-schedule: 0 0 0/6 * * ? *
persisted-models-enabled: false

View File

@@ -1329,7 +1329,7 @@ Force all traffic to use HTTPS via a redirect, if the site URL is HTTPS.
- 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.
Whether to automatically import from the remote git repository. Only applies if remote-sync-type is :read-only.
### `MB_REMOTE_SYNC_AUTO_IMPORT_RATE`
@@ -1337,7 +1337,7 @@ Whether to automatically import from the remote git repository. Only applies if
- 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.
If remote-sync-type is :read-only 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`
@@ -1599,7 +1599,7 @@ Used for encrypting and checking whether SDK requests are signed.
- Default: `null`
When using the appdb engine against postgresql, override the language used for stemming in to_tsvector.
Value must be a valid configured langauge option in your database such as 'english' or 'simple'.
Value must be a valid configured language option in your database such as 'english' or 'simple'.
### `MB_SEARCH_TYPEAHEAD_ENABLED`

View File

@@ -139,4 +139,4 @@ This setting doesn't affect [SQL queries](../questions/native-editor/writing-sql
## Localizing embedded Metabase
You can tranlate both Metabase UI elements (like button labels) and content (like dashboard names) in static embeds. See [Setting the language for static embeds](../embedding/static-embedding-parameters.md#setting-the-language-for-a-static-embed) and [Translating embedded dashboards and questions](../embedding/translations.md).
You can translate both Metabase UI elements (like button labels) and content (like dashboard names) in static embeds. See [Setting the language for static embeds](../embedding/static-embedding-parameters.md#setting-the-language-for-a-static-embed) and [Translating embedded dashboards and questions](../embedding/translations.md).

View File

@@ -55,7 +55,7 @@ If performance degrades, or you'd rather keep the JSON contained in the original
4. Select the database that contains the field you want to update.
5. Select the table that contains the field.
6. Select the field containing the original JSON
7. In the **Behavior** section, locate the **Unfold JSON** option and select **Yes** or **No**. If the column was unfolded, Metabase will have hidden this JSON columnn from view, so if you want the JSON column to be visible again, you'll need to change the column's visibility to **Everywhere**.
7. In the **Behavior** section, locate the **Unfold JSON** option and select **Yes** or **No**. If the column was unfolded, Metabase will have hidden this JSON column from view, so if you want the JSON column to be visible again, you'll need to change the column's visibility to **Everywhere**.
8. Scroll to the top and click the **Filter values** button and select **Re-scan this field**.
![Unfolded JSON setting](./images/unfold-json-setting.png)

View File

@@ -65,7 +65,7 @@ You'll add an ampersand (`&`) to separate each parameter. For example, In the **
sslmode=verify-full&sslrootcert=/path/to/certificate.pem
```
Replace `/path/to/certifcate.pem` with the full path for the certificate you downloaded from your provider.
Replace `/path/to/certificate.pem` with the full path for the certificate you downloaded from your provider.
You can learn more about [SSL support for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).

View File

@@ -63,7 +63,7 @@ GRANT analytics TO metabase;
-- GRANT SELECT ON "your_table" IN SCHEMA "your_schema" TO analytics;
```
Depending on how you use Metabase, you can also additonally grant:
Depending on how you use Metabase, you can also additionally grant:
- `TEMPORARY` privileges to create temp tables.
- `EXECUTE` privileges to use stored procedures or user-defined functions.

View File

@@ -79,7 +79,7 @@ title: API changelog
- `POST /api/user/:id/send_invite` has been removed.
- `GET /:id/fields` now includes the Table ID.
- APIs under `/api/pulse` and `/api/alert` will be removed in a future version as we're tranitioning to a new architecture.
- APIs under `/api/pulse` and `/api/alert` will be removed in a future version as we're transitioning to a new architecture.
## Metabase 0.51.0

View File

@@ -176,7 +176,7 @@ several properties that can be set as environment variables or in a `deps.edn`.
"-Dmb.db.pass="]}}}
```
You could also pass a full conection string in as the `mb.db.connection.uri`:
You could also pass a full connection string in as the `mb.db.connection.uri`:
```
"-Dmb.db.connection.uri=postgres://<user>:<password>@localhost:5432/<dbname>"

View File

@@ -157,13 +157,13 @@ title: Driver interface changelog
## Metabase 0.54.11
- The multimethods `metabase.driver.sql-jdbc.sync.interface/active-tables` and `metabase.driver.sql-jdbc.sync.interface/filtered-syncable-schemas`, aswell as the functions
- The multimethods `metabase.driver.sql-jdbc.sync.interface/active-tables` and `metabase.driver.sql-jdbc.sync.interface/filtered-syncable-schemas`, as well as the functions
`metabase.driver.sql-jdbc.sync.describe_database/fast-active-tables`, `metabase.driver.sql-jdbc.sync.describe_database/have-select-privilege-fn` and `metabase.driver.sql-jdbc.sync.describe_database/db-tables` now take a database spec instead of a `java.sql.Connection` object.
## Metabase 0.54.10
- Add `metabase.driver/table-known-to-not-exist?` for drivers to test if an exception is due to a query on a table that no longer exists
- Add `metabase.driver.sql-jdbc/impl-table-known-to-not-exist?` for JDBC drivers. This is the implemenation of table-known-to-not-exist for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
- Add `metabase.driver.sql-jdbc/impl-table-known-to-not-exist?` for JDBC drivers. This is the implementation of table-known-to-not-exist for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
## Metabase 0.54.0
@@ -195,7 +195,7 @@ title: Driver interface changelog
## Metabase 0.53.12
- Add `metabase.driver/query-canceled?` for drivers to test if an exception is due to a query being canceled due to user action
- Add `metabase.driver.sql-jdbc/impl-query-canceled?` for JDBC drivers. This is the implemenation of query-canceled for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
- Add `metabase.driver.sql-jdbc/impl-query-canceled?` for JDBC drivers. This is the implementation of query-canceled for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
## Metabase 0.53.10
@@ -641,7 +641,7 @@ title: Driver interface changelog
to be assigned to specific database roles which are set before any queries are executed, so that access to tables can
be restricted at the database level instead of (or in conjunction with) Metabase's built-in permissions system.
- The multimethod `metabase.driver.sql-jdbc.sync.describe-table/get-table-pks` is changed to return a vector instea
- The multimethod `metabase.driver.sql-jdbc.sync.describe-table/get-table-pks` is changed to return a vector instead
of a set.
- The function `metabase.query-processor.timezone/report-timezone-id-if-supported` has been updated to take an additional
@@ -839,8 +839,8 @@ differences between the library versions.
**Note: we expect these breaking changes to be fixed before 0.46.0 ships. This will be updated if they are.**
The classes `metabase.util.honeysql_extensions.Identifer` and `metabase.util.honeysql_extensions.TypedHoneySQLForm`
have been moved to `metabase.util.honey_sql_1.Identifer` and `metabase.util.honey_sql_1.TypedHoneySQLForm`,
The classes `metabase.util.honeysql_extensions.Identifier` and `metabase.util.honeysql_extensions.TypedHoneySQLForm`
have been moved to `metabase.util.honey_sql_1.Identifier` and `metabase.util.honey_sql_1.TypedHoneySQLForm`,
respectively. On the off chance that your driver directly referencing these class names, you may need to update things
to use the new class names.

View File

@@ -174,7 +174,7 @@ You'll almost certainly be running your database in a local Docker container. Ra
Tells Metabase to look for the environment variable `MB_MYSQL_TEST_USER`; if not found, default to `"root"`. The name of the environment variable follows the pattern `MB_<driver>_TEST_<property>`, as passed into the function as first and second args, respectively. You don't need to specify a default value for `tx/db-test-env-var`; perhaps `user` is an optional parameter; and if `MB_MYSQL_TEST_USER` isn't specified, you don't need to specify it in the connection details.
But what about properties you want to require, but do not have sane defaults? In those cases, you can use `tx/db-test-env-var-or-throw`. It the corresponding enviornment variable isn't set, these will throw an Exception, ultimately causing tests to fail.
But what about properties you want to require, but do not have sane defaults? In those cases, you can use `tx/db-test-env-var-or-throw`. It the corresponding environment variable isn't set, these will throw an Exception, ultimately causing tests to fail.
```clj
;; If MB_SQLSERVER_TEST_USER is unset, the test suite will quit with a message saying something like
@@ -190,7 +190,7 @@ Besides `tx/db-test-env-var`, `metabase.test.data.interface` has several other h
There's a few other things Metabase needs to know when comparing test results. For example, different databases name tables and columns in different ways; methods exist to let Metabase know it should expect something like the `venues` table in the `test-data` Database Definition to come back as `VENUES` for a database that uppercases everything. (We consider such minor variations in naming to still mean the same thing.) Take a look at `tx/format-name` and other methods like that and see which ones you need to implement.
## What about DBMSes that don't let you create new databases programatically?
## What about DBMSes that don't let you create new databases programmatically?
This is actually a common problem, and luckily we have figured out how to work around it. The solution is usually something like using different _schemas_ in place of different databases, or prefixing table names with the database name, and creating everything in the same database. For SQL-based databases, you can implement `sql.tx/qualified-name-components` to have tests use a different identifier instead of what they would normally use, for example `"shared_db"."test-data_venues".id` instead of `"test-data".venues.id`. The SQL Server and Oracle test extensions are good examples of such black magic in action.

View File

@@ -159,7 +159,7 @@ driver:
# Whether loading this driver can be deferred until the first
# attempt to connect to a database of this type. Default: true. Only
# set this to false if absolutely neccesary.
# set this to false if absolutely necessary.
lazy-load: true
# Parent driver, if any.

View File

@@ -4,7 +4,7 @@ title: End-to-end tests with Cypress
# End-to-end tests with Cypress
Metabase uses Cypress for “end-to-end testing”, that is, tests that are executed against the application as a whole, including the frontend, backend, and application database. These tests are essentially scripts written in JavaScript that run in the web browser: visit different URLs, click various UI elements, type text, and assert that things happen as expected (for example, an element appearing on screen, or a network request occuring).
Metabase uses Cypress for “end-to-end testing”, that is, tests that are executed against the application as a whole, including the frontend, backend, and application database. These tests are essentially scripts written in JavaScript that run in the web browser: visit different URLs, click various UI elements, type text, and assert that things happen as expected (for example, an element appearing on screen, or a network request occurring).
_Please, get familiar with the [Cypress best practices](https://docs.cypress.io/app/core-concepts/best-practices) before you proceed._
@@ -155,17 +155,15 @@ export MB_SNOWPLOW_URL=http://localhost:9090
We have a few helpers for dealing with tests involving snowplow
1. You can use `describeWithSnowplow` (or `describeWithSnowplowEE` for EE edition) method to define tests that only
run when a Snowplow instance is running
1. Use `resetSnowplow()` test helper before each test to clear the queue of processed events.
1. Use `expectSnowplowEvent({ ...payload }, count=n)` to assert that exactly `count` snowplow events match (partially)
2. Use `expectSnowplowEvent({ ...payload }, count=n)` to assert that exactly `count` snowplow events match (partially)
the payload provided (count defaults to 1)
1. Use `expectUnstructuredSnowplowEvent` to assert that exactly `count` snowplow events are unstructured events that
3. Use `expectUnstructuredSnowplowEvent` to assert that exactly `count` snowplow events are unstructured events that
partial-match the payload provided. This is simply a convenience function for comparing
`event.unstruct_event.data.data` rather than the entire `event`. Most of our events are unstructured events, so this is handy.
1. Use `assertNoUnstructuredSnowplowEvent({ ...eventData })` is the inverse of `expectUnstructuredSnowplowEvent`, and asserts that
4. Use `assertNoUnstructuredSnowplowEvent({ ...eventData })` is the inverse of `expectUnstructuredSnowplowEvent`, and asserts that
_no_ unstructured events match the payload.
1. Use `expectNoBadSnowplowEvents()` after each test to assert that no invalid events have been sent.
5. Use `expectNoBadSnowplowEvents()` after each test to assert that no invalid events have been sent.
### Running tests that require SMTP server
@@ -175,6 +173,10 @@ Some of our tests depend on the email being set up, and require a local SMTP ser
docker run -d -p 1080:1080 -p 1025:1025 maildev/maildev:latest
```
### Running tests that require translation dictionaries
Some of the tests are checking content translation functionality. These tests require to run `./bin/i18n/build-translation-resources` command before running the tests to precompile JSON files with translations.
### Cypress comes with `Lodash` for free
We don't need to have [Lodash](https://lodash.com/) in our direct dependencies to be able to [use it with Cypress](https://docs.cypress.io/api/utilities/_). It is aliased with an underscore and its methods can be accessed with `Cypress._.method()`. We can use `_.times` method to stress-test a certain test (or a set of tests) locally.

View File

@@ -128,7 +128,6 @@ For the most part we follow the [Airbnb React/JSX Style Guide](https://github.co
- Prefer React [function components over class components](https://reactjs.org/docs/components-and-props.html#function-and-class-components)
- Avoid creating new components within the `containers` folder, as this approach has been deprecated. Instead, store both connected and view components in the `components` folder for a more unified and efficient organization. If a connected component grows substantially in size and you need to extract a view component, opt for using the `View` suffix.
- For control components, typically we use `value` and `onChange`. Controls that have options (e.x. `Radio`, `Select`) usually take an `options` array of objects with `name` and `value` properties.
- Components named like `FooModal` and `FooPopover` typically refer to the modal/popover _content_ which should be used inside a `Modal`/`ModalWithTrigger` or `Popover`/`PopoverWithTrigger`
- Components named like `FooWidget` typically include a `FooPopover` inside a `PopoverWithTrigger` with some sort of trigger element, often `FooName`
- Use arrow function instance properties if you need to bind a method in a class (instead of `this.method = this.method.bind(this);` in the constructor), but only if the function needs to be bound (e.x. if you're passing it as a prop to a React component)

View File

@@ -4,9 +4,7 @@ title: Metabase release versioning
# Metabase release versioning
We follow our own flavor of the [semantic versioning guidelines](https://semver.org/) in order to distinguish the [open-source version](https://www.metabase.com/product/starter) of Metabase from the paid, source-available version of Metabase (available in the [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans).
Semantic versioning typically follows the format: `Major.Minor.Point.Patch`. For example, version `3.15.2` or `3.15.2.1`.
We follow our own flavor of the [semantic versioning guidelines](https://semver.org/). Standard semantic versioning uses `Major.Minor.Patch` format, but we've adapted it to distinguish the [open-source version](https://www.metabase.com/product/starter) of Metabase from the paid, source-available version of Metabase (available in the [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans).
With Metabase releases, we prefix the version with a `0` or `1`, depending on the license.
@@ -16,13 +14,13 @@ With Metabase releases, we prefix the version with a `0` or `1`, depending on th
License.Major.Point.Hotfix
```
E.g.,
For example:
```
v0.46.3.1
v0.57.3.1
```
`v0.46.3.1` would be for a hotfix (`1`) for the third (`3`) point release of Metabase `46`, the open-source edition (`0`).
`v0.57.3.1` would be for a hotfix (`1`) for the third (`3`) point release of Metabase `57`, the open-source edition (`0`).
### License
@@ -31,33 +29,39 @@ v0.46.3.1
### Major
We release major version when we introduce new features or breaking changes.
We release major versions when we introduce new features or breaking changes. See [release announcements](https://www.metabase.com/releases).
### Point
Sometimes called a minor release, we issue point releases when we add bug fixes and refinements to existing features.
Sometimes called a minor release, we issue point releases when we add bug fixes and refinements to existing features. See the [changelog](https://www.metabase.com/changelog)
### Hotfix
Sometimes called a patch release, we issue these hotfix releases to fix security issues in a timely manner, or to undo a horrific regression.
Sometimes called a patch release, we issue these releases nightly so we can fix bugs or security issues as soon as we can.
You'll see releases ending as `.x` in [releases on GitHub](https://github.com/metabase/metabase/releases). These are placeholder links. The actual link will go to the latest hotfix. For example:
- `metabase/metabase:v0.57.2.x` will automatically pull the latest patch version in for the latest minor version (like `v0.57.2.1`, `v0.57.2.2`, etc.).
## Other release terms
### The Gold Release
### The beta release
The gold release is the first release of a new major version of Metabase. So for Metabase version 46, the gold releases would be:
The beta release is the beta release of a new major version of Metabase. So for Metabase version 57, the gold releases would be:
- `v0.46.0` (the OSS version)
- `v1.46.0` (the EE version)
- `v0.57.0` (the OSS version)
- `v1.57.0` (the EE version)
### Release Candidates
### The gold release
We usually publish release candidates to kick the tires on new features before releasing a new major version (a gold release). To distinguish these release candidates, we append an `-RC#` at the end.
The gold release is the first stable release of a new major version of Metabase. So for Metabase version 57, the gold releases would be:
E.g.,
- `v0.57.1` (the OSS version)
- `v1.57.1` (the EE version)
- `v1.46.0-RC1` (the first release candidate for the EE version)
- `v0.46.0-RC3` (the third release candidate for the OSS version)
### Cloud builds
If you see a version followed by an `-X`, like `v1.56.2-X01`, these are version numbers related to builds for customers on [Metabase Cloud](https://www.metabase.com/cloud/).
## Further reading

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@@ -5,7 +5,7 @@ summary: Create documents to combine text and charts for long-form analysis. Add
# Documents
{% include plans-blockquote.html feature="Documents" %}
{% include plans-blockquote.html feature="Documents" is_plural=true%}
![Document with comment](./images/document-with-comment.png)
@@ -23,35 +23,52 @@ Documents let you tell a story with your data by combining text, charts, and oth
Type `/` in your document to see available commands:
- **Ask Metabot** to chat with [Metabot](../ai/metabot.md) in the document.
- **Chart** to insert a table or chart from an existing question or model. You can't add dashboards or other documents, but you can mention them (see the next section).
- **Link**. You can mention other items in your Metabase.
- **Chart** to insert a table or chart from an existing question or model, or create a new question directly in the document. You can't add dashboards or other documents, but you can mention them (see the next section).
- **Link** to mention other items in your Metabase.
## Mention items with `@`
You can type `@` to mention items from your Metabase. (The mentions get a fancy little icon next to them).
You can type `@` to mention items from your Metabase (mentions get a fancy little icon next to them).
## Add charts to documents
You can add [questions](../questions/start.md) and [models](../data-modeling/models.md) to a document.
You can add [questions](../questions/start.md) and [models](../data-modeling/models.md) to a document. You can either insert existing questions from your collections or create new questions directly in the document.
### Creating new questions in documents
To create a new question directly in a document:
1. Type `/` to open the command menu.
2. Select **Chart**.
3. Select **New chart**.
4. Choose either:
- **New Question** to use the [query builder](../questions/query-builder/editor.md).
- **New SQL query** to use the [native SQL editor](../questions/native-editor/writing-sql.md).
5. Build your question and click **Save and use** to add it to the document.
Questions created this way are saved directly to the document and don't appear in your collections unless you explicitly save them there later. Deleting charts made in documents is permanent. You can't recover deleted charts from the [trash](../exploration-and-organization/delete-and-restore.md).
To enter the full-screen editor for a question, click on the question's title.
### Adding a chart copies it to the document
When you add a chart from a collection, Metabase creates a copy in the document. The original question remains in the collection. Changes to one copy won't affect the other; they're independent.
If however, you delete the document's copy, that copy is permanently deleted. It can't be recovered from the [trash](../exploration-and-organization/delete-and-restore.md).
If you delete the document's copy, that copy is permanently deleted and can't be recovered from the [trash](../exploration-and-organization/delete-and-restore.md).
### Charts generated by Metabot only exist in the document
### Charts created in documents only exist in the document
If [Metabot](../ai/metabot.md) generates a chart, Metabase saves that question to the document itself. If you delete it, it's gone forever. You won't be able to recover the question from the trash.
Questions that are created directly in the document (either through [Metabot](../ai/metabot.md) or by creating a new question via the Chart command) are saved to the document itself and don't appear in your collections. If you delete these questions from the document, they're permanently deleted and can't be recovered from the trash.
## Editing charts
To edit a chart, click the **three-dot** menu in the chart's upper right. You can:
- Edit visualization
- Edit query
- Replace the chart with another question or model
- [Download results](../questions/exporting-results.md#exporting-results-from-document-cards)
- **Add supporting text** to provide context next to the chart
- **Edit visualization** to change how the chart displays data
- **Edit query** to modify the underlying question
- **Replace** the chart with another question or model
- [**Download results**](../questions/exporting-results.md#exporting-results-from-document-cards)
Edits only affect the chart in the document. If you added the chart from a collection, Metabase will have created a copy of the chart. Any changes made to the chart in the document won't affect the original question.
@@ -61,16 +78,31 @@ Edits only affect the chart in the document. If you added the chart from a colle
To rearrange charts, simply click and hold on the chart and drag it to where you want it in the document.
You can put up to three charts side by side (more than three charts side-by-side gets crowded). To resize charts, you can click on the edges to adjust their size.
When there are multiple charts in the same row, you can resize them horizontally by clicking between the charts and sliding left or right to resize.
You can put up to three items side by side in a group. This includes both charts and supporting text blocks. When you have multiple items in the same row, you can resize them horizontally by clicking between the items and sliding left or right to resize.
To resize a chart's height, click the bottom of the chart and resize the chart up or down.
For now, you can only put charts side-by-side (not text elements).
After arranging your charts, save your changes!
## Adding supporting text to charts
![Supporting text next to a chart](./images/supporting-text.png)
You can add a supporting text block next to charts to provide context.
To add supporting text to a chart:
1. Hover over the chart.
2. Click the **three-dot** menu in the chart's upper right.
3. Select **Add supporting text**.
4. Type your text in the supporting text block.
You can only have one supporting text block per group of charts. A group can contain up to three items total (charts and supporting text block combined).
You can drag and drop a supporting text block to reorder them within a group of charts. You can only move supporting text within its current group; you can't drag it out to create a standalone text block.
Supporting text blocks support the same Markdown formatting as the rest of the document, including headings, lists, blockquotes, and code blocks. But you can't add charts in supporting text.
## Formatting text
Documents support Markdown syntax for:
@@ -84,7 +116,22 @@ You can also access formatting options by typing `/`.
## Links
If you paste a link into a document, Metabase will turn it into a clickable link. For now, Markdown links aren't supported, and you can't change the link text.
You can add links to documents in several ways:
- **Select text and add a link**: Select any text, then click the **Link** button in the formatting menu. Enter a URL and press Enter.
- **Paste a URL**: Paste a link into a document, and Metabase will turn it into a clickable link.
- **Use Markdown syntax**: Type a link using Markdown format: `[link text](url)`. When you type the closing parenthesis, Metabase will convert it to a clickable link.
### Editing links
To edit a link:
1. Hover over the link.
2. Click the **pencil icon** in the popup.
3. Update the URL.
4. Click the **check icon** to save, or the **trash icon** to remove the link entirely.
To edit the link's visible text, edit the text in the document itself.
## Commenting on documents
@@ -118,7 +165,7 @@ If you never want to see a comment again:
3. Click on the **three-dot** menu.
4. Select **Delete**.
This deletion is permanent.
This deletion is permanent.
## Printing documents

View File

@@ -160,7 +160,7 @@ Until the Embedded analytics SDK is fully loaded and initialized, the hook retur
### `CreateDashboardModal`
#### API Refernce
#### API Reference
- [Component](./api/CreateDashboardModal.html)
- [Props](./api/CreateDashboardModalProps.html)

View File

@@ -26,9 +26,11 @@ Here's the [Shoppy source code](https://github.com/metabase/shoppy).
## Quickstarts
- [Quickstart](./quickstart.md) (If you have Metabase and an app)
- [Quickstart CLI](./quickstart-cli.md) (If you have an app, but no Metabase)
- [Quickstart with sample React app](./quickstart-with-sample-app.md) (If you don't have either)
The best way to get started with Embedded Analytics SDK depends on what you already have:
- You have an app and a Metabase instance: go to [main quickstart](./quickstart.md)
- You have an app but no Metabase: go to [quickstart with CLI](./quickstart-cli.md)
- You don't have an app: go to [quickstart with a sample React app](./quickstart-with-sample-app.md)
## Installation

View File

@@ -51,7 +51,7 @@ Once the database status is `Available`, you need to click on the DB identifier:
![RDS DB Identifier](images/RDSDBIdentifier.png)
On the page that appears after you click on the database identifer, you will see on the center of the page the **Connectivity & Security** section. This section will provide you with the **Endpoint** that Metabase needs to connect to the application database we just created.
On the page that appears after you click on the database identifier, you will see on the center of the page the **Connectivity & Security** section. This section will provide you with the **Endpoint** that Metabase needs to connect to the application database we just created.
![RDS Connection Data](images/RDSConnectionData.png)

View File

@@ -11,7 +11,7 @@ summary: Create development instances of Metabase for testing without paying per
On Pro and Enterprise plans, you can purchase a development instance or development token as an add-on to your plan.
- **Self-hosted**: you can purchase a dev token so you don't get billed for every test user acccount. You can use the same dev token for one or more self-hosted development instances (meaning: you can reuse the same token for multiple dev instances at no additional cost). The development token will have access to the same set of paid features as your production token.
- **Self-hosted**: you can purchase a dev token so you don't get billed for every test user account. You can use the same dev token for one or more self-hosted development instances (meaning: you can reuse the same token for multiple dev instances at no additional cost). The development token will have access to the same set of paid features as your production token.
- **Cloud**: you can purchase a development instance hosted on [Metabase Cloud](https://www.metabase.com/cloud/). The development instance will have access to the same set of paid features as your production token.
Dev instances are only available to Pro and Enterprise plans; they're not included in the base plans. Dev instances must be purchased as an add-on to your plan, after you've completed your trial and made your first payment.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@@ -17,11 +17,11 @@ Remote Sync lets you develop analytics content in a collection in a development
Here's a basic remote-sync workflow:
1. Create a dashboard in a **Metabase configured in Development mode**.
1. Create a dashboard in a **Metabase configured in Read-write mode**.
2. Push it to a Git branch.
3. Open a pull request for review.
4. Merge the PR to production.
5. Your **Metabase configured in Production mode** automatically pulls in the changes.
5. Your **Metabase configured in Read-only mode** automatically pulls in the changes.
We'll cover [setting up Remote Sync](#setting-up-remote-sync), an [example dev-to-production workflow](#an-example-dev-to-production-workflow), [branch management](#branch-management), and some other odds and ends.
@@ -29,10 +29,10 @@ We'll cover [setting up Remote Sync](#setting-up-remote-sync), an [example dev-t
**Remote sync has two modes for different roles**:
- **Development mode**: Create and edit content. You can [push](#pushing-changes-to-git) and [pull](#pulling-changes-from-git) changes to and from your repository. Multiple Metabase instances can connect in Development mode, each working on [different branches](#branch-management).
- **Production mode**: Serves read-only content to users. Production instances only [pull](#pulling-changes-from-git) changes (typically from your main branch) and don't allow direct editing of synced content. You can set up [auto-sync](#pulling-changes-automatically) to automatically pull approved changes every five minutes.
- **Read-write mode**: Create and edit content. You can [push](#pushing-changes-to-git) and [pull](#pulling-changes-from-git) changes to and from your repository. Multiple Metabase instances can connect in Read-write mode, each working on [different branches](#branch-management).
- **Read-only mode**: Serves read-only content to users. Read-only instances only [pull](#pulling-changes-from-git) changes (typically from your main branch) and don't allow direct editing of synced content. You can set up [auto-sync](#pulling-changes-automatically) to automatically pull approved changes every five minutes.
**Only the synced collection is tracked**: Each Metabase connected to Remote Sync has one special [collection that syncs with Git](#how-the-synced-collection-works-in-development-mode). When you connect in Development mode, Metabase creates a collection called "Synced Collection" (you can rename it). Everything inside this collection (including sub-collections) is versioned and synchronized with your repository.
**Only the synced collection is tracked**: Each Metabase connected to Remote Sync has one special [collection that syncs with Git](#how-the-synced-collection-works-in-read-write-mode). When you connect in Read-write mode, Metabase creates a collection called "Synced Collection" (you can rename it). Everything inside this collection (including sub-collections) is versioned and synchronized with your repository.
**The synced collection must be self-contained**: Everything a dashboard or question needs must be [inside the synced collection](#items-in-the-synced-collection-cant-depend-on-items-outside-of-it). Content outside the synced collection won't sync to your repository or appear in other Metabase instances.
@@ -67,9 +67,9 @@ Copy the token immediately after generating it.
### 3. Connect your development Metabase to your repository
![Development settings](./images/development-settings.png)
![Development settings](./images/read-write-settings.png)
You can put any Metabase into development mode. We also offer [Development instances](./development-instance.md) that you can use for Remote Sync or any other kind of development.
You can put any Metabase into Read-write mode. We also offer [Development instances](./development-instance.md) that you can use for Remote Sync or any other kind of development.
In the Metabase instance that you use for development:
@@ -79,7 +79,7 @@ In the Metabase instance that you use for development:
- For example, `https://github.com/your-org/your-repo`. The repository must already exist and be initialized with at least one commit.
3. Select **Development mode**.
3. Select **Read-write mode**.
4. Add your access token:
@@ -91,7 +91,7 @@ In the Metabase instance that you use for development:
### 4. Add an item to your synced collection
When you first connect in Development mode, Metabase automatically creates a **synced collection** called "Synced Collection"—any content you add to it will be tracked in Git and can be pushed to your repository.
When you first connect in Read-write mode, Metabase automatically creates a **synced collection** called "Synced Collection"—any content you add to it will be tracked in Git and can be pushed to your repository.
You can rename the Synced Collection if you want, and you can add sub-collections within it to organize your content.
@@ -130,7 +130,7 @@ Copy the token immediately after generating it — you'll need to paste it into
### 7. Connect your production Metabase to your repository
![Production settings](./images/production-settings.png)
![Production settings](./images/read-only-settings.png)
In your production Metabase instance:
@@ -140,7 +140,7 @@ In your production Metabase instance:
- Use the same repository as your development Metabase, for example, `https://github.com/your-org/your-repo`.
3. Select **Production mode**.
3. Select **Read-only mode**.
4. Add your access token:
@@ -154,9 +154,9 @@ In your production Metabase instance:
- Click "Pull changes" to immediately sync content from your repository.
- To keep your production instance automatically updated, toggle on "Auto-sync with Git". Metabase will pull changes from your main branch every five minutes.
In Production mode, the synced collection appears in the regular collections list with a special icon to indicate it's versioned and read-only.
In Read-only mode, the synced collection appears in the regular collections list with a special icon to indicate it's versioned and read-only.
![Production Metabase](./images/production-view.png)
![Production Metabase](./images/read-only-view.png)
At this point, you should be all set up. Exit Admin settings, then reload your browser. You should see your synced collection in your production Metabase.
@@ -195,7 +195,7 @@ On your production Metabase instance:
2. The "Megafauna Analytics" dashboard appears in production with all its questions.
3. The content is read-only for users (they can view and use it, but can't edit it).
## How the synced collection works in Development mode
## How the synced collection works in Read-write mode
- [The synced collection in the UI](#the-synced-collection-in-the-ui)
- [Moving and deleting content in the synced collection](#moving-and-deleting-content-in-the-synced-collection)
@@ -203,13 +203,13 @@ On your production Metabase instance:
### The synced collection in the UI
When you first connect in Development mode, Metabase creates a synced collection called "Synced Collection". You can add items and sub-collections to it. The synced collection shows its current state with visual indicators: a yellow dot indicates unsynced local changes that need to be committed, and up/down arrows provide sync controls for pulling and pushing changes.
When you first connect in Read-write mode, Metabase creates a synced collection called "Synced Collection". You can add items and sub-collections to it. The synced collection shows its current state with visual indicators: a yellow dot indicates unsynced local changes that need to be committed, and up/down arrows provide sync controls for pulling and pushing changes.
In Production mode, the synced collection appears in the regular collections list (not in a separate "Synced Collections" section) with a special icon to indicate it's versioned and read-only.
In Read-only mode, the synced collection appears in the regular collections list (not in a separate "Synced Collections" section) with a special icon to indicate it's versioned and read-only.
### Moving and deleting content in the synced collection
**Deletions sync to production:** When you remove content from the synced collection in Development mode and push that change, the content will also be removed from your Production instance when it syncs. This applies to moving content out of the synced collection or deleting it entirely.
**Deletions sync to production:** When you remove content from the synced collection in Read-write mode and push that change, the content will also be removed from your Production instance when it syncs. This applies to moving content out of the synced collection or deleting it entirely.
Content in other Metabases that depended on this item may break since the dependency will no longer be in the synced collection.
@@ -239,7 +239,7 @@ If you're using Metabase for embedding, you might want synced content to appear
For groups with these permissions, the sub-collections they can access will appear at the top level of navigation, as if they were root-level collections. They won't see the top-level Synced Collection that you have in your development Metabase.
What you see in Development mode:
What you see in Read-write mode:
```
Collections
@@ -248,7 +248,7 @@ Collections
├── Giant Sloth Statistics
```
What embedding groups see in Production mode (with no access to Synced Collection, but access to Mammoth Statistics and Giant Sloth Statistics):
What embedding groups see in Read-only mode (with no access to Synced Collection, but access to Mammoth Statistics and Giant Sloth Statistics):
```
Collections
@@ -280,7 +280,7 @@ Remote Sync uses the same serialization format as the [Metabase CLI serializatio
## Branch management
Branching is only available in Development mode.
Branching is only available in Read-write mode.
### Creating a branch
@@ -313,13 +313,13 @@ If you have unsynced changes, Metabase will show a dialog asking what you want t
The dialog shows you exactly which items have changed, so you can make an informed decision.
If you switch modes (from Development to Production or vice versa) with unpushed changes, you'll be prompted to save or discard them. You cannot switch to Production mode with uncommitted changes.
If you switch modes (from Read-write to Read-only or vice versa) with unpushed changes, you'll be prompted to save or discard them. You cannot switch to Read-only mode with uncommitted changes.
If changes don't appear after switching modes: Hard refresh your browser (Cmd/Ctrl + Shift + R).
## Pushing changes to Git
You can only push changes in a Metabase with Remote Sync set to Development mode.
You can only push changes in a Metabase with Remote Sync set to Read-write mode.
### Committing and pushing your changes
@@ -329,13 +329,13 @@ When you make changes to items in the synced collection, a yellow dot appears on
2. Enter a descriptive commit message explaining your changes.
3. Click "Continue" to push your changes to Git.
If you see a message that "Remote is ahead of local", that means someone else pushed to the branch from another Metabase in Development mode. Pull the latest changes before pushing again.
If you see a message that "Remote is ahead of local", that means someone else pushed to the branch from another Metabase in Read-write mode. Pull the latest changes before pushing again.
## Pulling changes from Git
You can pull changes when in Development or Production mode.
You can pull changes when in Read-write or Read-only mode.
In Development mode, you can get the latest changes from your Git repository:
In Read-write mode, you can get the latest changes from your Git repository:
1. Click the down arrow (pull) icon next to your synced collection in the left sidebar.
2. Review any summary of incoming changes if shown.
@@ -348,7 +348,7 @@ If changes don't appear after pulling:
- Hard refresh your browser (Cmd/Ctrl + Shift + R).
- If you encounter sync errors, review error messages in the sync dialog, manually resolve conflicts in your Git repository, then pull again.
In Production mode, go to **Admin settings** > **Settings** > **Remote sync** and click **Pull changes**.
In Read-only mode, go to **Admin settings** > **Settings** > **Remote sync** and click **Pull changes**.
### Handling unsynced changes
@@ -362,7 +362,7 @@ When in doubt, create a new branch and push changes to that branch. That way you
### Pulling changes automatically
In Production mode, you can set Metabase to auto-sync changes from your main branch.
In Read-only mode, you can set Metabase to auto-sync changes from your main branch.
1. Navigate to **Admin settings** > **Settings** > **Remote sync**.
2. Enable Auto-sync with Git.
@@ -396,7 +396,7 @@ Keep doing what you're doing.
If you want to switch fully to Remote Sync, we recommend starting with a new repo:
1. Check out a new branch in your Metabase instance in Development mode.
1. Check out a new branch in your Metabase instance in Read-write mode.
2. Import your data to your Metabase instance with the serialization command as you normally would.
3. Move the content you want to sync into the synced collection.
4. Push up your changes to the new repo.

View File

@@ -136,7 +136,7 @@ Metabase will do all this automatically.
If you need to downgrade after a major version upgrade, you'll either need to restore from a backup, or manually migrate to a lower version, otherwise Metabase may refuse to start (see the next section).
Durning a **minor version upgrade** (e.g., 54.1 to 54.2), the new Metabase container or Jar will just work. Only in rare cases will it have to perform a migration, but, like with major version upgrades, Metabase will perform the migration automatically. And of course, you're backing up your application database each time you upgrade, right?
During a **minor version upgrade** (e.g., 54.1 to 54.2), the new Metabase container or Jar will just work. Only in rare cases will it have to perform a migration, but, like with major version upgrades, Metabase will perform the migration automatically. And of course, you're backing up your application database each time you upgrade, right?
## Rolling back an upgrade or to an older version
@@ -181,7 +181,7 @@ Once the migration process completes, start up Metabase using the JAR or contain
If you're running Metabase in a cluster:
1. Reduce the number of nodes to a single node. You can't upgrade all nodes at the same time because the ugprade process works by acquiring a migration lock on the application database from a single client, which performs the migration. If you keep more than one node active when you do a major version upgrade, the application won't behave correctly, as schema changes to the application database could cause problems for nodes that are still running the older version of Metabase.
1. Reduce the number of nodes to a single node. You can't upgrade all nodes at the same time because the upgrade process works by acquiring a migration lock on the application database from a single client, which performs the migration. If you keep more than one node active when you do a major version upgrade, the application won't behave correctly, as schema changes to the application database could cause problems for nodes that are still running the older version of Metabase.
2. Perform the upgrade as normal (as outlined above).
3. Raise the number of nodes to the same number you had before.

View File

@@ -15,7 +15,7 @@ This page covers the [View data](./data.md#view-data-permissions) permission lev
For now, impersonation access is only available for the following databases:
- ClickHouse
- MySQL
- PostgreSQL. If you're using views in PostgresSQL, the row-level security policies on views will only work on Postgres versions 15 and higher.
- PostgreSQL. If you're using views in PostgreSQL, the row-level security policies on views will only work on Postgres versions 15 and higher.
- Redshift
- Snowflake
- SQL Server
@@ -179,6 +179,6 @@ Blue group's more permissive access would override the impersonated access.
Admins won't ever see the effects of impersonation effects, because their privileges will override those of any other group they're a member of.
Metabase's default Administrators group has "Can view" access to all databases, and Metabase uses the most permissive access for any person in multple groups, so any admin will have "Can view" - not "Impersonated" - access to the database.
Metabase's default Administrators group has "Can view" access to all databases, and Metabase uses the most permissive access for any person in multiple groups, so any admin will have "Can view" - not "Impersonated" - access to the database.
To test impersonation, create a test user, assign them a user attribute with the database role, and add them to the impersonated group. Then, log in as the test user and verify the data access.

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