Compare commits

...

163 Commits

Author SHA1 Message Date
Jakub Chodorowicz
42e3a98805 add split openapi to the repo 2026-03-06 16:02:32 +01:00
Jakub Chodorowicz
245825e10e fix paths in openapi scripts 2026-03-02 10:21:11 +01:00
Jakub Chodorowicz
b0b2b597ab cleanup openapi scripts 2026-03-02 10:12:50 +01:00
Jakub Chodorowicz
3eaf107895 split openapi into separate components, paths 2026-03-02 10:07:50 +01:00
Romeo Van Snick
d54e6b64e2 Convert parameters to typescript (#70137)
* Convert parameters to TypeScript

* Fix types

* Fix types in normalizeValue

* Fix types in dashboard tests

* Fix more types

* Fix test

* Remove duplicate check

* Naming
2026-03-02 17:07:10 +11:00
metabase-bot[bot]
763d5e1777 [chore] Update E2E test timings with recent data (#70236)
Update E2E test timings with averaged data

Collected timing data from 7 days of test runs.
All specs with timing data were included (simple averages).

🤖 Generated automatically by update-e2e-timings workflow

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
2026-03-02 12:16:14 +13:00
Fraser
b746306e12 Convert VisualizationError utils to TypeScript (#69062)
* Convert VisualizationError utils to TypeScript

Closes DEV-1407

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-02 11:53:39 +13:00
Fraser
f0c90980ff Convert DefaultParametersSection to TypeScript (#69075)
* Convert DefaultParametersSection to TypeScript

Closes DEV-1420

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-02 11:53:21 +13:00
Egor Grushin
e53f226b40 GDGT-1852 fix ui issues in Data Summary tab in Transform Inspector (#70150)
* GDGT-1852 fix ui issues
2026-03-01 22:48:32 +00:00
Romeo Van Snick
40cbf2ffb5 Avoid updating url when navigating to query builder (#68982)
* Use Question.serializeCardForUrl for consistency

* Convert metabase/lib/card to typescript

* Remove broken createCard helper

* Fix types

* Remove unused parameter

* Remove entityId from cards

* Remove createCard tests

* Avoid renaming Card to CardObject for no reason

* Also allow serializing UnsavedCard

* Make options optional

* Simplify serializeCardForUrl

* Make serializeCardForUrl implementation match the Question.serializeCardForUrl implementation

* Use serializeCardForUrl from metabase/lib/card

* Use serializeCardForUrl from metabase/lib/card

* Use stable stringify for url hashes

* Remove unnecessary _serializeForUrl helper

* Correctly ignore possibly infinite type instantiations

* Fix unit tests

* Allow passing parameterValues

* Fix comparison

* Coerce values to undefined

* Update unit tests

* Remove duplicate key

* Make cards optional
2026-03-02 08:38:55 +11:00
Braden Shepherdson
55a0afbd2a [MBQL lib] Stop using ID-based refs for fields from joined cards (#70116)
Fixes QUE2-383.

Uses `:lib/card-id` in metadata to check if a column came from a card at any point.
If so, it should use a name-based `:field` ref rather than an ID-based one.
This makes the refs to joined columns consistent with those to main source columns,
based on whether the source is a table or card.
2026-02-28 20:10:19 +00:00
Robert Kosara
f759d9f473 Docs: Tenant attributes and collections improvements (#70179)
* Tenant attributes and collections improvements

First round of edits to add the ability to create tenant attributes using SSO.

* Revise tenant-specific access for non-admin internal users

* Edits based on Alberto's comments

* Move tenant claims info to claims section, link to it from attributes

* Apply suggestion from @alexyarosh

Co-authored-by: Alex Yarosh <alexandra@metabase.com>

* Moved tenant attribute logic and simplified sentence

* Fix a sentence, improve code example, and clean up permissions bullet points

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-02-27 13:46:20 -08:00
Uladzimir Havenchyk
39951a570a chore: make entity copy modal generic (#70018)
* chore: convert dashcard related redux components to typescript

* fixup

* address review

* another review iteration

* chore: make entity copy modal generic

* chore: convert dashcard related redux components to typescript

* fixup

* address review

* another review iteration

* add a missing import

* type cleanup

* address review
2026-02-28 00:07:56 +03:00
Alex Yarosh
6b369ef818 docs: writeable connection (#70210)
* writeable connection

* Optimized 1 images, saved 89.0 KB (46.9% smaller)

* routing notes

* typos

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-02-27 14:33:37 -05:00
Nemanja Glumac
f3c352d920 Increase viewport to reduce "extract column" flakes (#70206) 2026-02-27 20:06:00 +01:00
Anderson
9c0576cc5c Add snowplow event for opening Data Studio (#69880)
* Add snowplow event for when opening Data Studio

* Update triggered_from
2026-02-27 18:21:02 +00:00
Nemanja Glumac
fc00027fca Migrate away from deprecated Cypress.env() API (#70189)
As of Cypress 15.10.0, `Cypress.env()` is deprecated. This commit migrates
all usages to the new recommended APIs:

- Non-sensitive config values → `Cypress.expose()` (synchronous)
- Sensitive token values → `cy.env()` (asynchronous)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-27 19:03:29 +01:00
Ngoc Khuat
3be69174e6 Improve logging around notifications (#70099) 2026-02-27 16:55:43 +00:00
Nicola Mometto
11a73d8621 Add position field to dashboard parameters for stable serialization (#70107)
* revert sorting by id on parameters serialization, parameters ordering is significant and must be preserved

* revert, we're storing position

* feat: add :position to parameters, backfill

* feat: backfill position on imported parameters

* chore: tests

* FE: update position

* chore: gitignore python-sources.lock

* chore: ensure we get position

* restrict to dashboard params

* tests

* simpler approach: just sort by position on serialization export/import
2026-02-27 16:06:32 +00:00
Dan Stone
d1e7b483dd Prefer SQL DML for insert-from-source! to fix bigquery metadata consistency (#69753)
The legacy .insertAll API (and newer Storage API) do not work well with
tables that are created/dropped frequently. This is because their
metadata caches can take a long time to up date.

Python transforms use the drop-recreate fallback strategy for bigquery
so we observe this inconsistency for every run after the first, even
with absurd multi minute timeouts.

The SQL API does not have a problem, so we'll use that. We expect a
performance regression but its better to work slowly than not work at
all.

Other changes:

- Removed now redundant bigquery retry loop

- Dropped dictionary support, we cannot support it easily and it cannot be
considered higher prio than bigquery working at all.

- Added correct parameter typing for JSON.

- Fixed ordering issues in tests (assumed row order when it is
non-deterministic)
2026-02-27 15:47:58 +00:00
Ngoc Khuat
aa66ab25d6 Simplify notification result data flow (#70115) 2026-02-27 21:58:54 +07:00
Edward Paget
8207fc3181 feat: error code on streaming response throw/fail (#62351)
* fix: real error codes in streaming responses

* fixup! try faking old errors deeper in the stack

* fixup! wrap fetch endpoint in embedding too

* fixup! treat string only errors differently

* fixup! handle errors in a couple other places

* fixup! hard to figure out what kind of error it is

* fixup! typecasts

* fixup! test arity

* fixup! remove blanket 403 page from dataset

* fixup! simplify backend 403 responses

* fixup! a couple missed tests

* fixup! add mockresponse for mockclient

* fixup! other failing tests

* fixup! last failing test

* fixup! add another OSS check

* fixup! one more oss test

* fixup! update status codes on some more tests

* fixup! use committed function
2026-02-27 09:42:20 -05:00
Nicolò Pretto
b429839f0a remove sdk import error on core app e2e tests (#70188)
try to untangle imports
2026-02-27 14:41:19 +01:00
Jakub Chodorowicz
451f080ede add new variants to action icon and use it in Inspector Header cell (#70180)
* add new variants to action icon and use it in Inspector Header cell

* add is-active state
2026-02-27 12:10:07 +01:00
Dan Stone
4f00289729 Fix bigquery routing test (#70154)
- Was using wrong project / creds
- Using two different sha names only worked when shas were not used as
qualifiers in the query (they are for bigquery)

Added the ability to override the hash-key used for tx/hash-dataset to
support forcing a hash collision (as is required by routing).
2026-02-27 10:58:14 +00:00
Maksym Yakubych
369d410d4d fix: new SQL questions automatically open DataReference sidebar (#70120) 2026-02-26 21:09:43 +00:00
Nicola Mometto
b8c13482df fix: replace usage of source-table-id with primary-source-table-id (#70162) 2026-02-26 14:24:20 -05:00
Sloan Sparger
126eb6b1b1 [Metabot] Prevent OSS SQL gen UI from showing in cloud migrated instances (#69649)
prevent oss sql gen ui from showing for customsers with had set it up locally and migrated to cloud
2026-02-26 12:54:41 -06:00
Noah Moss
4ab19dce4d Ring middleware for public sharing endpoints (#70153)
* Ring middleware for public sharing endpoints

* Refactor test

* move +public-sharing-enabled to address module lint

* Fix documents test
2026-02-26 12:59:54 -05:00
Braden Shepherdson
39b4a21830 [MBQL lib] Refactor a common pattern into new lib functions (#68474)
Many uses of `lib.util/source-card-id` and `source-table-id` were
followed by calling `lib.metadata/card` or `lib.metadata/table`
respectively.

This PR adds two new functions `lib.metadata.calculation/primary-source-card`
and `primary-source-table` which look up the card/table directly.

It also adds `lib.metadata.calculation/primary-source` for the handful
of places where whichever of `primary-source-card` or `primary-source-table`
returns non-nil gets used, such as in some drill-thrus.
2026-02-26 17:32:57 +00:00
Eric Normand
3ba0f90cf0 Automate mutation testing end-to-end with run! orchestrator (#70126)
- Adds a fully automated `run!` entry point to `dev.mutation-testing` that orchestrates the entire mutation testing workflow: generate report → create Linear project → group functions → write tests via `claude -p` → verify → retry →
  commit → create draft PRs
  - Replaces the previous manual, step-by-step SKILL.md instructions (which required the operator to execute each step) with Clojure functions that handle the full pipeline automatically
  - Key new functions: `parse-namespace`, `group-functions`, `build-test-prompt`, `invoke-claude!`, `verify-and-retry!`, `process-group!`, `print-summary!`

  ## Motivation

  The mutation testing workflow previously required ~20 manual steps per namespace, with the Claude operator interpreting skill instructions and running each step. This was slow, error-prone, and required constant supervision. Now you just
  call `(mut-test/run! 'metabase.lib.order-by)` and walk away.

  ## How it works

  1. **`parse-namespace`** — derives source/test paths, short names, auto-detects `.cljc` vs `.clj`
  2. **`group-functions`** — clusters private functions with their public entry points based on test overlap, skips fully-covered functions
  3. **`build-test-prompt`** — assembles a prompt with function source, existing tests, and surviving mutations
  4. **`invoke-claude!`** — shells out to `claude -p` with scoped tool access (`Edit`, `Read`, `Bash(clj-nrepl-eval*)`)
  5. **`verify-and-retry!`** — checks which mutations were killed, retries up to 2x with targeted prompts for survivors
  6. **`process-group!`** — full per-group pipeline: branch → invoke Claude → verify → commit & push → Linear issue → draft PR
  7. **`run!`** — ties it all together, catches errors per group so one failure doesn't stop the batch
2026-02-26 09:51:59 -07:00
Nemanja Glumac
6e799b2ef6 Bump Cypress retries to two (#70157) 2026-02-26 17:42:51 +01:00
Dan Stone
dcd2d7394d Fix snowflake integer type (#70031)
BigInteger is observed
2026-02-26 16:04:33 +00:00
Aleksandr Lesnenko
3c693179e4 fix content translations break visualization settings (#70054)
* fix content translation produces unstable translated keys in viz settings
2026-02-26 10:54:11 -05:00
Braden Shepherdson
b01732fd69 [perf] Tools to repro query + metadata locally, given instance API access (#67952)
Given browser or API access to a Metabase instance, the tools in this PR
can recursively fetch cards and all the metadata (tables, fields, etc.)
required to run them through QP preprocessing and compilation.

Of course this cannot actually _execute_ the compiled form, since you
don't have access to the user's DWH, but it enables debugging of
hard-to-reproduce user bug reports in your local REPL.

This works by making API calls like `/api/card` and
`/api/card/query_metadata` until a complete picture of the metadata is
acquired. This is poured into a `MetadataProvider`, which is all the QP
needs to analyze and compile the query.
2026-02-26 15:49:56 +00:00
Nicola Mometto
a75ee49fd1 restrict a transform inspector test to sql (#70152)
fix: restrict to sql drivers
2026-02-26 15:23:21 +00:00
Eric Normand
dbfe313b4a Filter transforms based on permissions GHY-3152 (#70125)
Filter transforms based on permissions

We were leaking transforms in the :bad_transforms key
2026-02-26 14:55:12 +00:00
github-automation-metabase
088bd180d5 Auto-update documentation for master (#70133)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-26 09:46:51 -05:00
metabase-bot[bot]
0226eaa5d2 [chore] Update E2E test timings with recent data (#69996)
Update E2E test timings with averaged data

Collected timing data from 7 days of test runs.
All specs with timing data were included (simple averages).

🤖 Generated automatically by update-e2e-timings workflow

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
2026-02-26 15:04:49 +01:00
Nicola Mometto
3d68aa1ac4 feat: restrict join-analysis to only table-level join analysis (#70105) 2026-02-26 13:08:34 +00:00
Nemanja Glumac
c18c08b02c Pin patched libraries to their patched version (#70143) 2026-02-26 13:02:41 +01:00
Katya Voropaieva
851bedf5bb Update v59 sample Examples visualizations and dashboard. (#70075)
Adds Box Plot viz and a Scalar viz with conditional colors to the Example dashboard.
The resources/sample-content.edn was regenerated from the live instance content.
2026-02-26 10:45:57 +01:00
Uladzimir Havenchyk
9981cd5323 chore: convert dashboard reducer to typescript and rtk (#69842)
* chore: convert dashboard reducer to typescript

* convert to rtk

* use immer when possible

* fixup

* Apply suggestion from @kamilmielnik

I checked it locally and it is indeed unnecessary guard

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2026-02-26 11:47:52 +03:00
Uladzimir Havenchyk
264604b61e chore: convert dashcard mapping files to ts (#69836) 2026-02-26 10:30:10 +03:00
Sébastien
a5a0773eaf Migrate ChartTypeDropdown to use Mantine ComboBox (#69734)
* Migrate ChartTypeDropdown to use Mantine ComboBox

* test fixes

* .

* .

* .

* .

* screenshot
2026-02-26 10:56:02 +07:00
Romeo Van Snick
c463756c60 Remove deprecated unit test helpers (#69275)
* Replace createQueryWithClauses with createTestQuery

# Conflicts:
#	frontend/src/metabase-lib/test-helpers.ts
#	frontend/src/metabase/querying/filters/hooks/use-date-filter/use-date-filter.unit.spec.ts

* Remove unnused helpers

# Conflicts:
#	frontend/src/metabase-lib/test-helpers.ts

* WIP

* Export createMetadataProvider

* Use Lib.createTestHelper

* Remove createQuery helper usage

* Remove createQuery helper

* Remove unneeded getJoinQueryHelpers

* Fix test

* Remove DEFAULT_QUERY

* Move or remove unneeded test helpers

* Fix lint

* Allow auto binning in test spec

* Fix test

* Allow disamiguating column name by index

* Fix tests

* Fix imports
2026-02-26 13:09:27 +11:00
Romeo Van Snick
0f7e8983ca Metakey helper (#68987)
* Use H.metaKey where applicable

* Add helper for meta key options

* Fix import

* Update more instances

* Fix test
2026-02-26 00:51:03 +00:00
Sloan Sparger
3401c9e902 Fix e2e dashboard-questions.cy.spec.js scrollTo flakes (#70114)
* invert assertions about scrollTo hash param and instead assert on visibility

* update e2e/test/scenarios/question/saved.cy.spec.js

* try to improve flakiness

* trying something dumb
2026-02-25 16:22:00 -06:00
Eric Normand
0aeda87a3d [Mutation Testing] Kill mutations in metabase.lib.field/fingerprint-based-default-unit (#69319)
Part of the mutation testing project for metabase.lib.field.

Closes QUE2-338

Functions
Stats
Surviving mutations before: 16
Tests added: 1
Mutations killed by this PR: 16
Mutations remaining: 0
Mutations killed
Replace fingerprint with nil
Replace :type with :type__
Replace :type/DateTime with :DateTime__
Replace let with comment
Replace earliest with nil
Replace latest with nil
Replace when-not with when
Replace days with nil
Replace > with >=
Replace > with nil
Replace days with nil
Replace 1 with 0
Replace :minute with :minute__
Replace 31 with 0
Replace 365 with 0
Replace :week with :week__
Mutations not killed (with rationale)
Replace :day with :day__ — available-temporal-buckets-for-type has its own internal default of :day for datetime types, so the :day return value from fingerprint-based-default-unit is redundant with the fallback
2026-02-25 22:07:25 +00:00
metamben
b1ece80c0d Support DB-local settings in the JVM and JS metadata-providers (#70117)
Closes QUE2-292
2026-02-25 21:52:17 +00:00
metamben
486a6fc826 Add test to prove that #35842 has been fixed (#70080)
Closes #35842
2026-02-25 21:25:19 +00:00
Eric Normand
29f75c1251 test: mutation tests for column-metadata-effective-type and nest-display-name (#69328)
* [Mutation Testing] Add tests for metabase.lib.field/fieldable-columns

* test: add mutation tests for column-metadata-effective-type and nest-display-name

Adds tests that exercise:
- type-of fallback to :base-type when :effective-type is absent
- type-of preference for :effective-type over :base-type
- temporal extraction returning :type/Integer
- temporal truncation preserving effective type
- nested field with nil parent display-name graceful fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:03:39 +00:00
metamben
66dc6b6cdc Add test to prove that #57978 has been fixed (#70076)
Closes #57978
2026-02-25 23:37:16 +03:00
metamben
7433f38a22 Make visible-columns for cards respect field visibility (#70040)
Closes QUE2-5
2026-02-25 23:36:59 +03:00
Cam Saul
71606de98c Test to prove #28060 is fixed 🔧 (#69865)
* Test to prove #28060 is fixed 🔧

* Update src/metabase/driver/mysql.clj

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>

* Update src/metabase/driver/mysql.clj

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>

* Fix syntax error from PR feedback suggestion

* Test fix 🔧

* Fix 🔧

---------

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-02-25 17:25:35 +00:00
Braden Shepherdson
d2e8fcac5b [MBQL lib] Export some healthy API functions; drop some API nses (#69666)
Fixes QUE2-350.

There are two changes here:
- Exporting some legit public functions from `lib.core`
- "Befriending" the `enterprise/sandbox` and `lib-be` modules, which are
  deeply coupled to lib and MBQL in any case.

That allows us to remove several exported namespaces:
- `lib.binning`
- `lib.convert`
- `lib.field`
- `lib.metadata.cached-provider`
- `lib.metadata.column`
- `lib.metadata.invocation-tracker`
- `lib.metadata.result-metadata`
- `lib.normalize`
- `lib.query`
- `lib.temporal-bucket`
- `lib.walk`

The functions newly exported from `lib.core` are:

- `lib.field/infer-has-field-values`, called from some BE API handlers
  and AppDB readers, producing a "cooked" value the FE wants rather than
  exposing details of how `:model/FieldValues` is stored. Single-use,
  but a legitimate export.
- `lib.field/json-field?`, checks if the passed in field is a JSON field,
  that's a valid export used in a few places.
- `lib.query/native?`, checks if the first stage of this query is native,
  that's another legit export used in a few places.

`lib.normalize/normalize` and `lib.query/query` were already exported, I
updated some usages of those functions to `lib/foo` instead.
2026-02-25 10:37:07 -05:00
github-automation-metabase
e4d1706837 Auto-update documentation for master (#70096)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-25 10:14:22 -05:00
Uladzimir Havenchyk
cc1361936a convert dashboard redux related files to typescript (#69797)
* chore: convert dashcard related redux components to typescript

* fixup

* address review

* another review iteration

* add a missing import

* cleanup
2026-02-25 17:40:38 +03:00
Nemanja Glumac
69c0a2ca90 Upgrade Cypress to v15.10.0 (#69839) 2026-02-25 15:14:45 +01:00
Ngoc Khuat
3d3bbe22db Move pulse preview endpoints to dev-only (#70068) 2026-02-25 11:15:00 +00:00
Romeo Van Snick
ba0ddc9a32 Only switch to full-width native preview at small breakpoint (#70048)
* Only switch to full-width native preview at small breakpoint

* Add e2e test for metabase#67767
2026-02-25 20:22:46 +11:00
Fraser
da223ea0a1 Updated Chromium to v144 in Cypress (#69721)
* Bump chrome to v144
2026-02-25 21:07:41 +13:00
Noah Moss
f53c8d1513 Always run Metabot-triggered refingerprinting in admin context to match sync behavior (#70087)
* always run fingerprinting in admin perms context

* address comment
2026-02-24 17:47:22 -05:00
Fraser
51d7669d7d Convert admin/permissions/utils/urls to TypeScript (#69432)
* Convert `admin/permissions/utils/urls` to TypeScript

Part of the Accelerate TypeScript Migration project.

Closes DEV-1510

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improve type guard

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Uladzimir Havenchyk <uladzimir@metabase.com>
2026-02-25 10:18:09 +13:00
Anton Kostenko
eb20991259 Fix PR envs cleaning (#70024)
* Fix PR envs cleaning

* Add manual dispatch for pr env destroy
2026-02-24 15:16:50 -05:00
Nicolò Pretto
ec4874cfe5 re-generate lock file to remove stale deps (#70062)
* re-generate lock file to remove stale deps

* add sample apps to the deps for the package
2026-02-24 19:02:35 +01:00
Ngoc Khuat
8f442ca39b Add diagnostic logging for duplicate alert investigation (#70020) 2026-02-24 17:20:02 +00:00
Nicola Mometto
8c6e4aa60b Ensure we can resolve map lookups (#70060)
* feat: explicit handlebars context

* chore: unit tests
2026-02-24 13:53:19 +00:00
Nemanja Glumac
3d3d49dfca Automate Crowdin context harvester and trigger pre-translation (#70035) 2026-02-24 11:11:15 +01:00
Nicolò Pretto
bba60088cb emb-1300: use plugin for handleLink in eajs and fix double sdk plugin init (#69961)
* emb-1300: use plugin for handleLink in eajs and fix double sdk plugin init

* typo and fix unit test

* restore old comment
2026-02-24 09:25:38 +01:00
metamben
981113ef47 Use lib namespace on properties instead of real namespace names (#69808)
* Use lib namespace on properties instead of real namespace names

Closes QUE2-279
2026-02-23 22:53:37 +00:00
Braden Shepherdson
9c8e505c9f [QP] Move logging-only work inside the log/* call (#68362)
Move some logging-only computations into the `(log/* ...)` call so it won't be executed
unless the log level is sufficiently verbose for the log to actually be output.
2026-02-23 16:19:03 -05:00
Noah Moss
f4bbf2984f Add some better defaults & limits to agent API (#70033) 2026-02-23 15:35:18 -05:00
metamben
273f1677a1 Add tests for calculated display_name and fix nested name handling (#69982)
Closes QUE2-288
2026-02-23 23:23:58 +03:00
metamben
12e6b46118 Support all kinds of identity types on sqlserver (#69871)
Closes QUE2-251
2026-02-23 23:23:17 +03:00
Fraser
f27597b904 Convert AddEditSidebar to TypeScript (#69421)
* Convert AddEditSidebar to TypeScript (DEV-1499)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:45:56 +13:00
bryan
b2889824b3 Fix flaky count-of-grouping-test for Redshift by using UTC timezone (#67196)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 19:18:07 +00:00
Jeff Bruemmer
28cfae2e67 docs - table variables and snippets (#69983)
table variables and snippets
2026-02-23 11:34:02 -05:00
github-automation-metabase
60707e20ec Auto-update documentation for master (#70009)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-23 11:21:09 -05:00
bryan
a4e418f280 Fix OpenAPI 3.1.0 validation errors and add CI workflow (#69948) 2026-02-23 09:13:33 -07:00
Stas Gavrylov
c20bf2a943 GDGT-1755: Migrate common components to TypeScript (partial) (#69354)
* GDGT-1755: Migrate Text to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate Label to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate Subhead to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate IconWrapper to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate LabelIcon to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate use-scroll-on-mount to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Missed TS fixes

* GDGT-1755: Migrate QueryButton to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Migrate IconBorder to TS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Remove Text.tsx wrapper, import from metabase/ui directly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Adjust line height in places using new Text component

* GDGT-1755: Fix styling inconsistencies

* Update frontend Claude guidelines for typecheck/linting to use bun

* GDGT-1755: Address PR comments

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-23 18:13:01 +02:00
Alexander Polyankin
c66d31fec1 Drop table created in a e2e test (#70023)
* drop table created in a e2e test

* fix
2026-02-23 10:43:37 -05:00
Egor Grushin
93cb3bc327 lock a card display type for the VisualizationCard (#70016)
lock a card display type for the VisualizationCard in the transform inspector
2026-02-23 14:55:23 +00:00
Timothy S. Dean
52325a7503 Write Connections for Actions, Model Persistence, Uploads (#69878) 2026-02-23 09:15:35 -05:00
Fraser
dadf042982 Convert ExpandableString to TypeScript (#69084)
Closes DEV-1429

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-23 15:49:29 +02:00
Dan Stone
18e1fb02aa Revert turning on snowflake/bq tests for transform (#70017)
Revert "Re-enable Python transform tests for BigQuery and Snowflake (#67685)"

This reverts commit b6c1a51630.
2026-02-23 12:41:22 +00:00
Egor Grushin
5dd8b26770 getting rid of annoying warnings (#69975) 2026-02-23 12:17:08 +00:00
Dan Stone
b6c1a51630 Re-enable Python transform tests for BigQuery and Snowflake (#67685)
These tests were temporarily disabled on 2025-09-26 due to flakes.
Re-enabling them now to restore full test coverage.

Lets monitor and see what happens
2026-02-23 10:38:56 +00:00
Nicola Mometto
09ee4178ca Transforms inspector (#68675)
* wip: transform inspector BE

* WIP: inspect tables

* wip

* format join info as code

* wip: join info

* style

* add join stats type and display match rate

* chore: row

* chore: remove limit

* wip

* handle differerent cases for match rate and row count

* add joins table display and icons

* fix copy

* fix missing join table names

* cleanup

* cleanup

* extend match

* fix missing source table type

* add join tooltip

* chore: with join info

* wip

* wip

* add field statistics dummy component

* small ui adjustments

* small ui adjustments

* add filled rows

* order input columns like joins

* update visualization card styling

* wip: update join calc to be iterative

* add target and source stats

* add target and source stats, a filter by output fields and a tab for unmapped fields

* add target and source stats, a filter by output fields and a tab for unmapped fields

* chore: sort by order

* fix: inverse map

* add cross product warning

* add null join warning

* small QoL UI improvements

* wip: render output_row_count

* wip: native joins

* fix a bug with conditional rendering

* wip: try to pull out useful fields

* WIP: claude'd UI selection of visited_fields

* use default automatic display selection logic

* add transform mock page with mock summary section

* add mock api console log

* fix fetch wrong query metadata

* add mock lenses

* add mock lenses api

* REVERTME: ignore for deploy

* feat: schemas

* require

* feat: degeneracy check

* WIP cljc api

* wip context

* wip lenses

* update comments

* chore: visited-fields

* wip

* wip: base

* wip: triggered lens

* wip: lib

* distinguish drill lenses

* fix: return visited-fields

* wip: v2 api

* WIP: vibe'd FE

* tabs/remove mock

* account for params

* params

* ui improvements

* fix

* show fingerprinting

* fingerprint layout

* use BE fallback

* handle more complex joins

* fallback to interesting fields

* fix trigger via cljc

* remove debug

* different text

* compute-card-results in cljs

* condition trigger in cljc

* semi working

* fix card_id correlation

* remove dead code

* rename BE to just inspector

* switch to new

* ensure transform is deleted

* chore: cleanup schema

* bring back the field fingerprint info

* move cljs out of lib

* fix: schema

* minor cleanup

* appease kondo

* kondo

* remove dead code

* feat: centralize native analysis in query_analisis

* chore: remove #p

* clean up

* Gdgt 1663 move fingerprints out of the lens to data inspect (#68996)

* [transforms inspector] depend on trunk macaw (#69022)

depend on trunk macaw

* fix lint and type issues (#69017)

* remove unused lib function

* fix infinite rendering loop

* fix type lib.interestingFields

* fix

* fix: ui bug

* chore: disable visited fields for now

* chore: better unmatched-rows

* chore: exclude not nulls

* minor tweaks

* minor tweaks

* rename

* chore: tighter contract

* minor optimisation

* Gdgt 1669 make cards clickable (#69116)

* Add card link

* link to hash of question

* add clickable title

* remove tooltip

* Use MBQL info to match columns whe npossible (#69120)

use mbql info for column matching

* Gdgt 1675 disable unmatched rows view details button in inspector (#69152)

* remove row index column

* adapt viz UI, hide row index column

* Unmatched rows lens: return cards for both LHS or RHS null scenarios (#69123)

* feat: handle both cases of no join match

* feat: simple degeneracy

* export function

* remove dead fields

* only propose drill lenses that are applicable (#69169)

* Transforms inspector consistent casing for FE (#69174)

* chore: consistency

* chore: some docstrings updats

* feat: snake_case for the FE

* feat: pass visitedFields to interestingFields, remove ad-hoc logic in FE (#69175)

feat: pass visitedFields to interestingFields, remove ad-hoc logic in the FE

* feat: namespace card-ids with params (#69180)

* add estimated lens complexity (hardcoded for now)  (#69181)

* add estimated lens complexity (hardcoded for now)

* refactor: use with-metadata

* add metadata to triggers, not just cards (#69182)

* appease kondo

* chore: don't depend on git sha macaw

* reinstate metabase-enterprise.transforms.core from merge conflict

* Gdgt 1722 new UI for data summary new (#69220)

* Transforms inspector BE tests  (#69225)

* feat: inspector tests

* fix: impls

* fix: nil safe

* feat: integration tests

* make it work for both FE and BE

* fix card-results for non oute rjoin

* e2e test

* fixes

* fix: quoting

* fix for mysql/bigquery/redshift

* gdgt-1721 rework lenses navigation (#69205)

* Transforms inspector module (#69278)

* split transforms inspector into its own module, introduce transforms-rest

* handle kondo modules warnings

* fix: shadow_cljs

* kondo: fixes

* Transforms inspector be review (#69339)

* chore: align

* remove useless comments

* feat: add schema to context

* feat: simplify column-matching

* fix: visited-fields

* fix test

* remove unused

* tighter schema

* extrac-sources test

* fix: missing metadata-provider

* Gdgt 1744 combine separate averages columns into one for data summary (#69355)

* gdgt-1723 New UI for Joins Analysis (#69325)

* Gdgt 1725 allow running transform from empty screen (#69394)

* chore: move crud functions on transforms, avoid -rest modules dependencies

* fix tests

* appease kondo

* fix modules

* fix: small UI bug

* Transforms orphaned columns (#69497)

* add orphaned columns to unmatched-rows analysis

* chore: update tests

* Transforms inspector analytics (#69484)

* feat: flow discover/lens through transform events

* track transform_id in query execution

* feat: add transform-inspector ids/params to query info

* feat: add inspector specific /query endpoint

* feat: hook FE to new api

* feat: add lens_id/params to view

* feat: add them to ua

* fix: pass params too

* fix: use text.type

* fix test

* chore: remove unused requires

* chore: events

* fix FE part

---------

Co-authored-by: Egor Grushin <egor@metabase.com>

* fix: use lib functions instead of manual mbql construction (#69519)

* fix: use lib functions instead of manual mbql construction

* chore: refactor utils

* cleanup

* more manipulation with lib

* bugfix

* strip fields/filters early

* do it in util function

* fix: bad merge resolution

* kondo

* another bad merge resolution

* chore: indent

* Gdgt 1754 handle slow loading lens indicator (#69334)

* lens loading indicator

# Conflicts:
#	frontend/src/metabase/transforms/pages/TransformInspectPage/TransformInspectPage.tsx
#	frontend/src/metabase/transforms/pages/TransformInspectPage/components/LensContent/LensContent.tsx

* add onCardLoaded handler to context

* Gdgt 1724 new UI column (#69282)

* Gdgt 1783 fix wrong args passed to lens query (#69546)

* fix wrong args being passed to lens queries

* improve naming of query parameters

* remove whitespaces from merge resolution

* add transforms api test

* feat: i18n the inspector strings (#69565)

* fix dark mode color issues (#69570)

fix bg fixed to white

* Gdgt 1737 use card metadata instead of parsing card ids (#69583)

* use card metadata instead of parsing ids

* improve way of getting the table name from cards

* use macaw still, TODO

* port some query-analysis to sql-tools (#69732)

* chore: port some query-analysis to sql-tools

* remove sql-tools from transforms

* chore: vendor macaw functions, appease kondo

* refactor

* chore: refactor

* side agnostic

* revert manual delete, closes https://linear.app/metabase/issue/GDGT-1730/pre-merge-blocker-revert-t2delete-on-transform-once-uxw-3021-is-fixed

* fix: always sync audit db on change

* dead code

* fix: use honeysql instead of building sql manually (#69774)

* GDGT-1791 Maximum depth error in console (#69754)

GDGT-1791 fix and refactor useCardLoadingTracker

* Gdgt 1651 add frontend e2e tests for transform inspector UI (#69677)

* GDGT-1823 make guarantee that LensContent is always stores one lens data (#69800)

* fix: 404 on lens not found (#69838)

* fix: 404 on lens not found

* chore: don't pmap

* fix: remove nil values from statistics

* fix

* Transforms inspector self review be (#69843)

* style

* chore: reuse resolve-join-sides

* add from-table-id for mbql queries too

* split context in mbql/native, don't union

* whitespace

* fix: preprocess native queries

* fix: update tests

* fix

* revert unrelated commit that made in the branch erroneously

* Gdgt 1812 better handle missing sources in lens inspector (#69848)

* Gdgt 1817 add snowplow product events fe (#69801)

* Add analytics events

# Conflicts:
#	frontend/src/metabase/transforms/analytics.ts

* add analytics tests

* simplify and improve lens loaded tracking event

* Move the idempotence check to useLensLoadedTracking

* GDGT-1831 replace unstyled button with a box to get rid of button in button error

* chore: transform lens_params

* GDGT-1786: Parametric lens navigation improvement (#69821)

* GDGT-1757: Refactor Column Comparison utils grouping and scoring logic (#69853)

* Improve types with entity ids (#69891)

* add concrete type declarations and usage for InspectorLensId, InspectorCardId, InspectorSectionId

* add better type to lens params

* cleanup of utils

* improve type for displaySettings

* use entity aliases in more places

* use existing type aliases for source and target

* add comment about InspectorTriggerCondition type

* unify prefix for inspector types

* remove spurious catch

* deduplicate types between metabase-api and lib

* Remove unused query

* fix provided tags for transform inspector

* add alerts and drill buttons test for default lens sections (#69899)

* restrict join analysis to single condition joins

* fix: bad merge reoslution

* fix types in lens test

* use isa function to detect type

* use concrete table id type alias

* use Urls.openInNewTab util

* remove empty mocks file

* use concrete table id type

* move lib for transform inspector to src/transforms/lib

* remove superflous log in e2e tests

* add proper SchemaName type

* remove superflous export

* revert move of isFetchBaseQueryError

* remove obsolete test

* use correct SchemaName type alias

* use correct VisualizationDisplay type

* remove leftover unused type

* remove superflous export

* Use CardId type alias

* discern lens also by based on params when tracking loading

* GDGT-1839 addressing the PR comments

* GDGT-1839 addressing the PR comments

* GDGT-1839 addressing the PR comments

* GDGT-1839 move getLensKey and LensHandle from api/tags directory

---------

Co-authored-by: Egor Grushin <egor@metabase.com>
Co-authored-by: Jakub Chodorowicz <jakub.chodorowicz@metabase.com>
Co-authored-by: Jakub Chodorowicz <jakub@chodorowicz.com>
2026-02-23 10:16:35 +00:00
Ngoc Khuat
a68b644ec8 Add prominent CTA button per failed transform in failure email (#69458) 2026-02-23 14:51:28 +07:00
Fraser
ca002fd953 Convert app-embed and app-public to TypeScript (#69438)
* Convert `app-embed` to TypeScript

Part of the Accelerate TypeScript Migration project.

Closes DEV-1516

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:49:58 +13:00
Anderson
ba5de0f2fa Show Python upsell on self hosted (#69759)
* Show Python upsell on self hosted

* Add e2e tests

* Fix unit test

* Simplify TransformsUpsellPage a bit more, fix tests

* Simplify logic

* Fix e2e and unit test

* Update self-hosted python upsell as per new design / Code clean up

* Fix unit test and also add a new one for useTransformsBilling

* Remove e2e tests to be moved to a new PR

* Fix cta link and bullet points icon alignment
2026-02-20 22:42:10 +00:00
Nathan Voxland
b4f717609e Add support for OpenID Connect (#69538) 2026-02-20 16:18:58 -06:00
Oleksandr Yakushev
3f898be45a [match] Implement replace-lite and use it instead of replace (#69270) 2026-02-20 23:39:57 +02:00
Edward Paget
071fc345b4 fix: better check for dirty collection state (#69974)
Our frontend code that prevents sending unchanged collections missed
tenant collections being added to the payload. This fixes both the
frontend to make sure we don't send when there are no changed
collections and it updates the backend actually check if collections are
being changed rather than just checking if the collections map exists.
2026-02-20 13:13:27 -06:00
Timofey Kachalov
08c4a89f91 fix(EmbedJS): Downloads fail on guest embedded questions (#69972) 2026-02-20 23:04:33 +04:00
Edward Paget
7d2d6571cb fix: transforms collections set setting on import (#69852)
* fix: transforms collections set setting on import

Also turns off the transforms sync setting if there are not transform
related models in the import

* fix: transform/snippet settings on collection sync

Updates our check for 'did transforms or snippets change on remote' to
examine collections in the respective namespaces so an import with only
transform collections (or snippet collection) creates the correct
settings on the importing instance.

* fixup! better handling for custom vs builtin tags

* fixup! better conflict checking of namespaced cols

* fixup! explicit connectionb for token cache

* fixup! PR feedback
2026-02-20 10:54:57 -06:00
Ryan Laurie
f6f2a31b6a Enable automated patch releases (#69971)
enable automated patch releases
2026-02-20 16:26:26 +00:00
github-automation-metabase
1342901722 Auto-update documentation for master (#69953)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-20 09:53:52 -05:00
Timofey Kachalov
a554da88bd Change the way of building the base_ref embedjs bundle (#69910) 2026-02-20 15:55:19 +04:00
Mahatthana (Kelvin) Nomsawadi
7fa10713ea Support passing dataPicker to SDK components to force staged data picker (#69631)
* Adding dataPicker to embedding-data-picker redux store

* Refactor: move `dataPicker` redux

Move from interactive embedding store to embedding data picker store

* Add `dataPicker` type for the SDK

* Fix storybook id value

* Accept `dataPicker` in modular embedding SDK

* Make the doc more suitable for markdown table in the docs site

* Add test for InteractiveQuestion

* Fix drillThroughQuestionProps type

* Add tests for drillThroughQuestionProps.dataPicker

* Fix SDK components can't specify different `entityTypes` or `dataPicker`

* Fix doc error

* Fix comment grammar

* Fix wrong file name

* Clarify redux slice usage with a comment

* Make context more specific to avoid misuse

* Add `question` back to SDK's `entityTypes` on the API doc
2026-02-20 11:27:46 +00:00
Nemanja Glumac
43e3fa694d Revert "Run container as non-root, simplify H2 handling in entrypoint (#64977)" (#69958)
This reverts commit 54bd11c5b9.
2026-02-20 10:42:48 +00:00
lbrdnk
370a4dff14 Enable check_snippet endpoint (#69892)
* Enable the check_snippet endpoint

* Param

* Test

* Test
2026-02-20 10:04:58 +01:00
Uladzimir Havenchyk
dc319ca76a feat(dashboard): move subscriptions to action menu (#64950)
* feat(dashboard): move subscriptions to action menu

* fix unit tests

* fix e2e tests

* fix console warning about uncontrolled component

* fix more tests

* fix remaining test

* bring test back
2026-02-20 08:39:55 +00:00
Romeo Van Snick
b08d62e316 Transforms with params UI (#69728)
* Validate transform queries can be preprocessed

* remove prn

* update backend error messages

* Add support for custom toggleTemplateTagEditor

* Allow passing setTemplateTagConfig to TagEditorParam

* Add support for storing parameter values when question changes

* Add template-tags tab

* Add template tag sidebar

* Allow saving optional template tags or tags with a default value

* Show error message on save

* Add new spec for transform template tags

* Avoid sharing state when navigating

* Add combination test for template tag types in transforms

* Add definitionTab helper

* Add test for multiple template tags

* Fix error message

* Fix tooltips in test

* Remove tooltip check in tests

* Use getErrorMessage to get error message in toast

* Only return message if it is not empty

* Wrap error message in form

* Use H.createTestNativeQuery helper

* Move variableTemplateTags and isVariableTemplateTag to Lib

* Use more conventional key to reset uistate

* update backend error messages again

* Revert "Wrap error message in form"

This is now handled in the api

This reverts commit 9efecaf261.

* Fix test

---------

Co-authored-by: William Zimrin <william@metabase.com>
2026-02-20 13:15:02 +11:00
Alexander Polyankin
ba302fc0ef Diagnostics cleanup pulled from the metrics branch (#69926) 2026-02-20 01:15:01 +00:00
Anderson
716a26ef75 Remove Exit sidebar menu item now that app switcher exists (#69886) 2026-02-19 23:53:20 +00:00
dpsutton
f1de74d0d2 [master] security fixes (#69924)
* security fixes

* Use actions dataset

Symptom:
We started having tests failing

like:

```
expected: {:has_more_values false,
           :values [["African"] ["American"] ["Artisan"]]}
  actual: {:has_more_values false, :values (("American") ("Artisan") ("Asian"))}
    diff: - {:values [["African"] ["American"] ["Artisan"]]}
          + {:values [["American"] ["Artisan"] ["Asian"]]}
```

and

```
expected: {:has_more_values true,
           :values [["African"] ["American"] ["Artisan"] ["Asian"] ["BBQ"]]}
  actual: {:has_more_values true,
           :values (("American")
                    ("Artisan")
                    ("Asian")
                    ("BBQ")
                    ("European Shop"))}
    diff: - {:values [["African"] ["American"] ["Artisan"] ["Asian"] ["BBQ"]]}
          + {:values [["American"]
                      ["Artisan"]
                      ["Asian"]
                      ["BBQ"]
                      ["European Shop"]]}
```

Identify:
```
modified   src/metabase/actions/execution.clj
@@ -35,6 +35,10 @@
                                                               [:model_id      ::lib.schema.id/card]
                                                               [:dataset_query ::lib.schema/native-only-query]]
    request-parameters]
+  (let [db-name (-> ((requiring-resolve 'metabase.test/db)) :name)]
+    (when-not (.startsWith db-name "actions-test-data")
+      (println clojure.test/*testing-vars*)
+      #_(clojure.repl/pst (Exception. "how here?") 500)))
   (log/tracef "Executing action\n\n%s" (u/pprint-to-str action))
   (try
     (let [parameters (for [parameter (:parameters action)]
@@ -61,6 +65,10 @@
 (defn- execute-custom-action! [action request-parameters]
   (let [{action-type :type} action]
     (actions/check-actions-enabled! action)
+    (let [db-name (-> ((requiring-resolve 'metabase.test/db)) :name)]
+      (when-not (.startsWith db-name "actions-test-data")
+        (println clojure.test/*testing-vars*)
+        #_(clojure.repl/pst (Exception. "how here?") 500)))
     (let [model (t2/select-one :model/Card :id (:model_id action))]
       (when (and (= action-type :query) (not= (:database_id model) (:database_id action)))
         ;; the above check checks the db of the model. We check the db of the query action here
@@ -165,8 +173,14 @@
   (let [k (keyword (:kind action-instance))]
     (legacy->current k k)))

+(require 'clojure.repl)
+
 (defn- execute-implicit-action!
   [action request-parameters]
+  (let [db-name (-> ((requiring-resolve 'metabase.test/db)) :name)]
+    (when-not (.startsWith db-name "actions-test-data")
+      (println clojure.test/*testing-vars*)
+      #_(clojure.repl/pst (Exception. "how here?") 500)))
   (let [model-id        (:model_id action)
         implicit-action (parse-implicit-action action)
         {:keys [query row-parameters]} (build-implicit-query action implicit-action request-parameters)
```

I modified the action execution to print out test vars that weren't
using the correct dataset. A few need cross datasets to ensure can't run
on different dbs, and a few don't matter because they assert that
permissions fail or data is missing. But this
execute-action-permission-test didn't, and it's last branch asserts
"Admin can still execute actions" and that seems to have been the cause
of our troubles
2026-02-19 15:16:26 -08:00
Rodrigo López Dato
54bd11c5b9 Run container as non-root, simplify H2 handling in entrypoint (#64977)
* Run container as non-root, simplify H2 setup

* Permissions

* add to bin/Dockerfile

* Update Dockerfile

Co-authored-by: metabase-bot[bot] <109303359+metabase-bot[bot]@users.noreply.github.com>

* Update bin/docker/Dockerfile

Co-authored-by: metabase-bot[bot] <109303359+metabase-bot[bot]@users.noreply.github.com>

* bad bot :(

* docs

* bring back JAVA_TIMEZONE

* don't hardcode default h2 directory in command

* fix link

* update openapi

---------

Co-authored-by: metabase-bot[bot] <109303359+metabase-bot[bot]@users.noreply.github.com>
2026-02-19 19:25:16 -03:00
Nemanja Glumac
2e3efef978 Use core.setFailed instead of throw for missing artifacts (#69897)
When an artifact doesn't exist (expected for new commits), the
fetch-artifact action was using throw new Error() which causes
GitHub Actions to report "Unhandled error" - confusing and noisy.

Using core.setFailed() cleanly marks the step as failed without
the scary error message.

Fixes: DEV-1581

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 23:15:33 +01:00
Rodrigo López Dato
9d9433b5b0 docs: Fix step numbering + add details on how to confirm if using H2 (#69581)
* Fix step numbering + add details on how to confirm if using H2

* fix anchor links
2026-02-19 18:19:46 -03:00
Rodrigo López Dato
f56c4e39cb Mention that the appdb schema must always be public (#69905) 2026-02-19 18:19:16 -03:00
bryan
f3fa8226cf Add debug echo to driver-decisions CI step (#69881)
Prints the mage command and args before execution so we can quickly
see what labels/flags were passed when debugging CI driver issues.

Closes: DEV-1577
2026-02-19 21:10:20 +00:00
Rodrigo López Dato
4376ecc379 Update model index limit in docs (#69933) 2026-02-19 18:08:16 -03:00
Braden Shepherdson
2c04482d27 [MBQL lib] Move leaky column logic from models.interface into lib (#69665)
Fixes QUE2-353.

`mi/normalize-result-metadata-column` called some internals to modernize
a possibly-lib, possibly-legacy column from `:result_metadata` on a
card. Pull that logic into Lib as a new exported function.
2026-02-19 20:52:27 +00:00
bryan
be492dc637 Remove unused HTTP action code paths (#69764)
* Remove dead code for actions

* Disable HTTP action type at API level

* clarify thrown exceptions

* Remove HTTP action tests

HTTP actions are now blocked at the API level; these tests cover
functionality that's no longer accessible.

* Remove remaining HTTP action references from api_test

The previous commit missed several HTTP action usages:
- all-actions-default helper function
- list-actions-test action-1 binding
- action-parameters-test (now uses query action)
2026-02-19 20:07:24 +00:00
Anderson
b2fb97a98a Replace 'Set up git sync' with 'Set up remote sync' on Data Studio sidebar (#69883) 2026-02-19 19:11:52 +00:00
William
347f3f11b4 Transforms with parameters (#69592)
We want to support transforms with template tags, as long as those tags aren't required or have default values. However, if we do that, the backend should actually verify that we do in fact have values for all relevant template tags. This pr adds basic validation logic to transform creation and update routes -- essentially, we just run qp.preprocess/preprocess and see if we get an error.
2026-02-19 12:03:00 -06:00
Edward Paget
d59d58e61a Fix narrow symbol downloads with tests (#69811)
* Fix downloading currency values in narrowSymbol format (#69779)

* Add tests for narrowSymbol currency style in downloads

---------

Co-authored-by: Will Bryant <will.bryant@gmail.com>
2026-02-19 11:39:11 -06:00
Timofey Kachalov
c8d8369fec feat(embed-js): Add docs hint for additional wizard properties (#69906) 2026-02-19 21:14:10 +04:00
Sébastien
ecaff38ac0 improved doc generation for EAJS (#69890) 2026-02-19 17:30:20 +01:00
Nemanja Glumac
951f74665d Do not cache UV (#69902) 2026-02-19 17:18:14 +01:00
Dan Stone
acf4530cc6 Move transform metering back into EE module (#69840)
* Move transform metering back into EE module

The defenterprise shadowing was not working as the metering function was
moved

* Add test of EE stats
2026-02-19 16:05:06 +00:00
Rodrigo López Dato
8eccd8a0f9 Capitalize Jar -> JAR (#69900) 2026-02-19 11:26:31 -03:00
Nemanja Glumac
83fdafad01 Introduce Copilot code review instructions (#69846) 2026-02-19 11:14:54 +01:00
github-automation-metabase
71cfad67bb Auto-update documentation for master (#69888)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-18 20:43:17 -05:00
Fraser
103e9056e1 Convert QueryButton to TypeScript (#69419)
* Convert QueryButton to TypeScript (DEV-1497)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Tighten types
2026-02-19 14:15:56 +13:00
Cam Saul
984cf44eca More robust visualization settings normalization (#69860)
* More robust visualization settings normalization

* Remove stray `println`
2026-02-18 16:20:00 -08:00
Jeff Bruemmer
d2f7234d7f docs - sql table variable (#69817) 2026-02-18 17:07:30 -05:00
Timothy S. Dean
4ab6f9df02 Transforms DB Connection (#68883)
Problem: When a database is configured for both SQL access and Transforms, any feature with SQL access (like raw SQL questions) inherits the elevated permissions (CREATE, DROP, INSERT, etc.) that Transforms need.

Solution: Admins can _optionally_ configure write credentials alongside existing read credentials. One logical database, two connection types. Specific code paths opt in to using write credentials; by default everything uses a read-only connection.

How: This PR adds a new `write_data_details` column on `Database`. If `write_data_details` isn't configured, everything will still use `details` as before. When it is configured, it acts as a set of overrides on top of `details`, but only in code paths that require write permissions. There should be no behavioral change for existing deployments.
2026-02-18 13:57:16 -07:00
Edward Paget
4c0c9aa08f fix: log parameter not nil value (#68200) 2026-02-18 14:29:50 -06:00
Rodrigo López Dato
32a00c99e5 Explain how DB routing and query caching work together (#69855)
* Explain how DB routing and query caching work together

* Update docs/configuring-metabase/caching.md

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

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-02-18 19:38:19 +00:00
github-automation-metabase
559c2f27de Auto-update documentation for master (#69833)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-18 11:44:49 -05:00
Nemanja Glumac
fd775c440d Split long running e2e specs into smaller chunks (#69844) 2026-02-18 17:18:36 +01:00
Nemanja Glumac
4a79872533 Clean up more unneeded dependency "resolutions" (#69837) 2026-02-18 17:14:53 +01:00
Timofey Kachalov
127132f8a6 Measure Embedding SDK Hosted Bundle bundle size (#69743) 2026-02-18 19:57:09 +04:00
Nathan Voxland
140d1adcb7 Remote-Sync: Support other git providers (#69675) 2026-02-18 09:37:25 -06:00
lbrdnk
639c8debc9 Enable native entity validation (#69784)
* Enable native entity validation

* Adjust tests

* Adjust test

* Adjuts tests

* Enable check_card and check_transform for native entities

* Adjust test to check child staleness

* Adjust test

* Adjust e2e test
2026-02-18 14:23:18 +01:00
Oleksandr Yakushev
795fa21b9d Add &parents to match-lite and rewrite remaining match/match-one usages (#69259) 2026-02-18 15:04:04 +02:00
lbrdnk
2f043ea9d2 Sqlglot review follow-up (#69788)
* Review remarks (incl. .sqlglot-version removal)

* Use uv.lock

* Restore batch-lookup-table-ids

* Adjust test

* Apply suggestions from code review

Co-authored-by: bryan <bryan.maass@gmail.com>

* Add sqlglot version parsing test

---------

Co-authored-by: bryan <bryan.maass@gmail.com>
2026-02-18 13:23:29 +01:00
Dan Stone
0e113a5c68 Accept string target :type in transform test helper (#69804)
It makes sense to use strings in http tests because that is what the
http-client will accept as json.

If a string target :type is supplied, we convert it to a keyword to
ensure we can dispatch to the intended method.
2026-02-18 11:06:04 +00:00
Uladzimir Havenchyk
e3ec99133a chore: convert IconBorder component to typescript (#69735)
* chore: convert IconBorder component to typescript

* pass rest parameters

* inline component

* fixup
2026-02-18 06:19:21 +00:00
Fraser
cd8c4b1e98 Convert lib/syncing to TypeScript (#69428)
* Convert `lib/syncing` to TypeScript

Part of the Accelerate TypeScript Migration project.

Closes DEV-1507
2026-02-18 16:16:10 +13:00
Romeo Van Snick
ead4e7d4d9 Fix unreferenced dependency flakes (#69535)
* Scroll item into view

* Fix sidebar test

* Wait for page to be visible
2026-02-18 14:15:52 +11:00
Fraser
f3fe43bb86 Convert LoginHistory to TypeScript (#69412)
Convert LoginHistory to TypeScript (DEV-1491)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:36:39 +13:00
Fraser
e385103a21 Convert LimitPopover to TypeScript (#69076)
* Convert LimitPopover to TypeScript

Closes DEV-1421

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-18 14:10:11 +13:00
Nathan Voxland
13681a004a Improve event creation remote-sync (#69768) 2026-02-17 18:05:08 -06:00
Fraser
a8715223ae Convert embedding/saved-entity-picker/utils to TypeScript (#69442)
Part of the Accelerate TypeScript Migration project.

Closes DEV-1520

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:29:25 +13:00
Fraser
3cbf8919a7 Convert lib/string to TypeScript (#69424)
Part of the Accelerate TypeScript Migration project.

Closes DEV-1505

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:29:11 +13:00
Oleksandr Yakushev
d4702e10e0 Merge remaining match -> match-many rewrites (#69686)
* [match] Implement match-many as a replacement for match

* [match] Add proper docs to match-lite and match-many
2026-02-17 23:56:04 +02:00
Braden Shepherdson
c8cd828410 [MBQL lib] Don't export a dynamic var; add with-card-clean-hook macro (#69662)
Fixes QUE2-354.

`lib/with-card-clean-hook` can be used to bind a callback function to
any material changes made by the `lib.convert/clean` process, which
generally indicates errors.
2026-02-17 21:36:48 +00:00
Edward Paget
aa5c870dc4 fix: cancelling remote sync tasks dismisses modal (#67613)
* fix: cancelling remote sync tasks dismisses modal

Previously we didn't close this modal until the task reached a
'cancelled' state which could take several seconds.

* fixup! PR feedback
2026-02-17 15:08:18 -06:00
Kevin Kelleher
f4aa0cc722 Visualizer trend lines and bug fixes: colors, settings, legends, axes (#69296) 2026-02-17 15:54:47 -05:00
metamben
764befaffb Document :metabase.lib.field/simple-display-name (#69590)
* Document :metabase.lib.field/simple-display-name

Closes QUE2-286

* Fix merge
2026-02-17 23:08:32 +03:00
metamben
dbced47022 Name the default join strategy (#69748)
Closes QUE2-285

The original idea was making the :strategy key required and leaving the setting of the value to the normalization, but
I decided not to go down that road. Here's why: there are multiple places on the path of constructing a join where we
can specify the join strategy but don't have to. These steps are not going through normalization and introducing
normalization would be inefficient and feel strange. Also, it would need to be done at several points, so it doesn't
seem a gain in the end. Because of this, all this PR does is naming the default strategy, so that changing it should
be possible at a single point. (Doing this would be a bad idea, so this PR is meant to be a documentation
improvement.)
2026-02-17 23:08:21 +03:00
Edward Paget
224d17965d fix: allow uploads when schema is unrestricted (#69657)
* fix: allow uploads when schema is unrestricted

* fixup! add upload enabled flag

* Update impl.clj

* fixup! where upload flag is set
2026-02-17 13:26:36 -06:00
Abed Habli
0b644a038f Docs v59: Nav UI update -- Grid icon and Admin rename (#69803)
* Update gear icon

* Update docs/data-modeling/json-unfolding.md

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

* Update docs/data-modeling/json-unfolding.md

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

* Update docs/embedding/sdk/quickstart-with-sample-app.md

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

* Update docs/embedding/sdk/quickstart.md

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

* Update docs/embedding/full-app-embedding-quick-start-guide.md

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

* Update docs/embedding/full-app-embedding-quick-start-guide.md

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

* Update docs/people-and-groups/user-provisioning.md

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

* Update docs/permissions/application.md

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

* Update docs/permissions/data.md

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

* Update docs/util/resources/introduction.md

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

* Update docs/usage-and-performance-tools/audit.md

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

* Apply suggestion from @jeff-bruemmer

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

* Update docs/embedding/public-links.md

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

* Update docs/embedding/public-links.md

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

* Update docs/people-and-groups/api-keys.md

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

* Update docs/people-and-groups/api-keys.md

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

* Update docs/people-and-groups/api-keys.md

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

* Remove few "ons"

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-02-17 20:16:14 +01:00
github-automation-metabase
65c102daf2 Auto-update documentation for master (#69781)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-02-17 11:14:20 -05:00
Nathan Voxland
5da65b6c52 Un-publishing remote-sync'ed library tables should remove them from the git repo (#69757) 2026-02-17 09:57:31 -06:00
Jeff Bruemmer
5f66a26a53 docs - one-shot sql gen (#69766) 2026-02-17 15:36:16 +00:00
Jakub Chodorowicz
75db6b5973 split large data model shared spec cypress file (#69744)
* split large data model shared spec cypress file

* split 2nd group further, rename files
2026-02-17 16:31:27 +01:00
Uladzimir Havenchyk
085fc52dca chore: cleanup relationships which we never show (#69786) 2026-02-17 17:24:11 +03:00
Uladzimir Havenchyk
b404c13d0c chore: convert dashboard actions to typescript (#69737)
* chore: convert dashboard actions to typescript

* cleanup

* fix unit test

* address review

* fix typecheck
2026-02-17 17:14:08 +03:00
2511 changed files with 112824 additions and 67785 deletions

View File

@@ -5,227 +5,110 @@ description: Run mutation testing on a Clojure namespace, generate tests to kill
# Mutation Testing Skill
This skill runs mutation testing on a target namespace, writes tests to kill surviving mutations, and creates draft PRs with Linear issues for each function.
This skill runs mutation testing end-to-end: generates a coverage report, groups functions, shells out to `claude -p` to write tests, verifies mutations are killed, and creates draft PRs with Linear issues.
## Prerequisites
- A running nREPL connected to the Metabase dev environment
- `LINEAR_API_KEY` environment variable set with a valid Linear personal API key
- `gh` CLI authenticated with GitHub
- `claude` CLI available on PATH
## Reference Files
- `dev/src/dev/coverage.clj` — mutation testing tool (generates reports, runs mutations)
- `dev/src/dev/mutation_testing.clj` — Linear API client and PR template helpers
- `mutation-testing-report.lib.card.md` — example report from the pilot
- `dev/src/dev/coverage.clj` — mutation testing engine (generates reports, runs mutations)
- `dev/src/dev/mutation_testing.clj` — orchestration, Linear API, PR helpers
## Invocation
The argument is a Clojure namespace, e.g.:
The argument is a Clojure namespace, optionally followed by a base branch:
```
/mutation-testing metabase.lib.order-by
/mutation-testing metabase.lib.order-by --base-branch release-x.52.x
/mutation-testing metabase.lib.order-by --project-id abc-123
/mutation-testing metabase.lib.order-by --base-branch release-x.52.x --project-id abc-123
```
## Workflow
- `--base-branch`: defaults to the value in config (set via `set-config!`), or `"master"` if not configured.
- `--project-id`: if provided, issues are added to this existing Linear project instead of creating a new one.
### Step 1: Parse the namespace
## Steps
From the argument (e.g., `metabase.lib.order-by`), derive:
- **Test namespace**: append `-test` (e.g., `metabase.lib.order-by-test`)
- **Short name**: the last segment (e.g., `order-by`)
- **Source path**: `src/metabase/lib/order_by.cljc` (replace `-` with `_` in path segments)
- **Test path**: `test/metabase/lib/order_by_test.cljc`
### 1. Parse arguments
### Step 2: Set up Linear and load tools
Parse `$ARGUMENTS` to extract:
- **namespace** (required) — the first positional argument
- **`--base-branch`** (optional) — if present, the next argument is the base branch name
- **`--project-id`** (optional) — if present, the next argument is an existing Linear project ID
### 2. Load and configure
```clojure
(require '[dev.coverage :as cov] :reload)
(require '[dev.mutation-testing :as mut-test] :reload)
(require '[dev.coverage :as cov] :reload)
```
If this is the first invocation (or the REPL was restarted), set up the Linear team:
```clojure
;; Find the team
(mut-test/list-teams!)
;; Set team ID from the output
(mut-test/set-config! {:team-id "<team-id>"})
(mut-test/set-config! {:team-id "<id>"})
```
If config is already set from a previous invocation, skip this.
### Step 3: Generate baseline report and create Linear project
### 3. Run
```clojure
(cov/generate-report
'<target-ns>
['<test-ns>]
"mutation-testing-report.lib.<short-name>.before.md")
(mut-test/run! '<target-ns>)
;; Or with options:
(mut-test/run! '<target-ns> {:base-branch "release-x.52.x"})
(mut-test/run! '<target-ns> {:project-id "abc-123"})
(mut-test/run! '<target-ns> {:base-branch "release-x.52.x" :project-id "abc-123"})
```
Read the generated report file. It has three sections:
- **Uncovered Functions** — never called by any test
- **Partially Covered Functions** — called but with surviving mutations
- **Fully Covered Functions** — all mutations killed
Pass the opts map with `:base-branch` and/or `:project-id` if those flags were provided in the arguments.
Then create a Linear project for this namespace (uses report stats in the description):
This will:
1. Generate a baseline mutation testing report
2. Create a Linear project for the namespace
3. Group functions by coverage relationships
4. For each group: create branch → invoke Claude to write tests → verify → retry if needed → commit & push → create Linear issue → create draft PR
5. Print a summary with PR links
### 4. Handle failures
If a group fails mid-processing, `run!` catches the error and continues to the next group. To retry a single group manually:
```clojure
(mut-test/create-project-for-namespace!
"<target-ns>"
"mutation-testing-report.lib.<short-name>.before.md")
;; => {:project-id "...", :name "Mutation Testing: metabase.lib.order-by"}
;; Automatically sets :project-id in config for subsequent create-issue! calls
;; Get the parsed namespace info
(def parsed (mut-test/parse-namespace '<target-ns>))
;; Run coverage to get the data
(def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)]))
;; Group and find the one you want
(def groups (mut-test/group-functions coverage-results))
;; Process just that group
(mut-test/process-group! parsed (nth groups <index>))
```
### Step 4: Plan the work
### 5. Verify the prompt (dry run)
Before creating branches, analyze the report to plan how to group the work:
1. **Identify killable functions.** For each function with surviving mutations, assess whether any mutations are killable. Run `cov/test-mutations` to see the full mutation list. If ALL mutations for a function are unkillable (schema-only, semantically equivalent, etc.), skip that function — no branch or PR needed.
2. **Group private functions by public entry point.** Private functions cannot be tested directly. Group them with the public function that exercises them. Create one branch/PR per group, not per private function.
3. **Track mutations per group.** The `:mutations-before` and `:not-killed` fields in the PR should only reflect mutations for functions covered by that specific PR — never mutations from other functions.
### Step 5: Process each group
For each group of functions that has killable mutations:
#### 5a. Create a branch
To inspect what Claude will see without invoking it:
```clojure
(mut-test/create-branch! "<target-ns>" "<primary-fn-name>")
;; => "mutation-testing-lib-order-by-orderable-columns"
(def parsed (mut-test/parse-namespace '<target-ns>))
(def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)]))
(def groups (mut-test/group-functions coverage-results))
(println (mut-test/build-test-prompt
(merge (select-keys parsed [:target-ns :test-ns :source-path :test-path])
(select-keys (first groups) [:fn-names :mutations]))))
```
Use the primary public function name for the branch. If the group covers private functions too, name the branch after the public entry point.
#### 5b. Read context
- Read the source function(s) from the source file
- Read the full test file to understand existing test patterns, helpers, and metadata providers used
- If the function is private, identify which public function(s) call it
#### 5c. Write tests
Generate the **simplest tests** that kill the surviving mutations while still making semantic sense. Guidelines:
- **Never call private functions directly.** Always test through public API functions. Use the coverage data to identify which public functions exercise the private function.
- **Keep tests simple.** Each test should verify one meaningful behavior. Don't over-engineer tests just to chase mutation kills.
- **Follow existing patterns.** Match the style of the existing tests: same metadata providers, same helper functions, same assertion patterns.
- **Insert tests near related existing tests** for the same function, not at the end of the file. This minimizes merge conflicts between branches. If there are no existing tests for the function, find the most logical location based on the order of functions in the source file.
**IMPORTANT: Use `file_edit` (not `clojure_edit`) when editing test files.** The `clojure_edit` tool reformats the entire file, introducing unnecessary whitespace changes that pollute the diff. Use `file_edit` with exact string matching to make surgical insertions that only touch the lines you intend to change.
#### 5d. Verify mutations are killed
Use the REPL to check that the new tests kill the targeted mutations:
```clojure
;; Reload the test namespace to pick up new tests
(require '<test-ns> :reload)
;; Test mutations for the specific function
(cov/test-mutations
'<target-ns>/<fn-name>
;; Set of test names that cover this function
#{'<test-ns>/<test-name-1> '<test-ns>/<test-name-2>})
```
Check the `:survived` key in the result. If mutations still survive:
- Try writing additional tests
- If a mutation is truly unkillable (semantically equivalent), note it for the PR description
- If a mutation reveals dead/unreachable code, consider suggesting removal as a code improvement
#### 5e. Handle unkillable mutations and improvements
**Do NOT edit the source namespace just for documentation.** Instead:
- **Unkillable mutations**: Note them in the PR description with rationale
- **Code improvements** (dead code removal, expression simplification) that directly relate to surviving mutants:
1. Make the change in the source file
2. Verify all tests still pass with the change
3. Note the file path, line range, and the improved code
4. **Reset the change**: `git checkout -- <source-file>` — do NOT commit it
5. After creating the PR, use `add-suggested-change!` to post the improvement as a GitHub suggested change (Step 5h)
6. The reviewer decides whether to accept it
#### 5f. Commit and push
```clojure
;; Only commit the test file — never commit source changes for improvements
(mut-test/commit-and-push! "<target-ns>" "<primary-fn-name>"
["test/metabase/lib/<short_name>_test.cljc"])
```
#### 5g. Create Linear issue and draft PR
```clojure
;; Create a Linear issue (use the primary function name)
(def issue (mut-test/create-issue-for-function! "<target-ns>" "<primary-fn-name>"))
;; => {:identifier "QUE-1234", :url "https://linear.app/...", ...}
;; Create draft PR linked to the Linear issue
;; :fn-names lists ALL functions covered by this PR
;; :mutations-before counts only mutations for functions in this PR
;; :not-killed lists only unkillable mutations for functions in this PR
(def pr-url
(mut-test/create-draft-pr!
{:target-ns "<target-ns>"
:fn-name "<primary-fn-name>"
:linear-identifier (:identifier issue)
:mutations-before 9 ;; surviving mutations for these functions only
:tests-added 2 ;; number of new test functions
:killed ["Replace :segment-id with :segment-id__"
"Replace cycle-path with nil"]
:not-killed [{:description "Replace acc with nil"
:rationale "reduce always has single iteration"}]
:suggested-changes []}))
;; => "https://github.com/metabase/metabase/pull/12345"
```
#### 5h. Add code improvement suggestions (if any)
If you identified code improvements in step 5e (and reset them), post each as a GitHub suggested change:
```clojure
(mut-test/add-suggested-change!
{:pr-url pr-url
:path "src/metabase/lib/<short_name>.cljc"
:start-line 42 ;; first line of the code to replace
:end-line 45 ;; last line of the code to replace
:suggestion "(improved-code-here)" ;; the replacement code
:comment "**Suggested improvement:** <description of the change and why it's related to the surviving mutant>"})
```
### Step 6: Return to master and repeat
```clojure
(mut-test/return-to-master!)
```
Repeat Step 5 for the next group.
### Step 7: Summary
Print a summary of what was done:
- Link to the Linear project for this namespace
- Number of functions processed
- Number of functions skipped (all mutations unkillable)
- Number of draft PRs created
- Number of mutations killed vs. unkillable
- List of test PRs with links
- List of mutation rule PRs with links (if any were created)
## Suggesting New Mutation Rules
If you notice patterns in surviving mutations that suggest the mutation testing tool should have additional rules (e.g., a common Clojure form that isn't being mutated), open a separate PR proposing additions to `dev.coverage/mutation-rules` with an explanation.
## Configuration
The `dev.mutation-testing` namespace reads `LINEAR_API_KEY` from the environment and stores team/project IDs in an atom.
- **Team ID**: Set once per REPL session via `(mut-test/list-teams!)` and `(mut-test/set-config! {:team-id "..."})` (Step 2)
- **Project ID**: Set automatically when `create-project-for-namespace!` is called (Step 3) — one project per namespace
- **Team ID**: Set once per REPL session via `(mut-test/list-teams!)` and `(mut-test/set-config! {:team-id "..."})`
- **Project ID**: Set automatically when `run!` calls `create-project-for-namespace!`
- **Base Branch**: Defaults to `"master"`. Override via `(mut-test/set-config! {:base-branch "release-x.52.x"})` or pass `{:base-branch "..."}` as the second arg to `run!`
- **Project ID**: If set (via `set-config!` or `run!` opts), `run!` reuses the existing Linear project instead of creating a new one

View File

@@ -12,8 +12,9 @@ allowed-tools: Read, Grep, Bash, Glob
Review pull requests with a focus on:
- Readability and maintainability
- Appropriate test coverage
- 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)
- React best practices (when applicable)

View File

@@ -227,7 +227,9 @@
;;
:invalid-arity {:skip-args [metabase.lib.util.match/match
metabase.lib.util.match/match-lite]} ; TODO (cam): can we fix these?
metabase.lib.util.match/match-lite
metabase.lib.util.match/match-many
metabase.lib.util.match/replace-lite]} ; TODO (cam): can we fix these?
:refer-all {:level :warning, :exclude [clojure.test]}
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
@@ -246,13 +248,17 @@
(metabase.lib.util.match/match)
(metabase.lib.util.match/match-one)
(metabase.lib.util.match/match-lite)
(metabase.lib.util.match/match-many)
(metabase.lib.util.match/replace)
(metabase.lib.util.match/replace-in)
(metabase.lib.util.match/replace-lite)
(metabase.driver-api.core/match)
(metabase.driver-api.core/match-one)
(metabase.driver-api.core/match-lite)
(metabase.driver-api.core/match-many)
(metabase.driver-api.core/replace)
(metabase.driver-api.core/replace-in)
(metabase.driver-api.core/replace-lite)
(metabase.query-processor.middleware.cache-backend.interface/with-cached-results)
(metabase.util.regex/rx [opt])
(metabase.util/prog1 [<>])
@@ -304,6 +310,9 @@
metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
metabase.lib.util.match/match {:message "Use lib.util.match/match-many instead of lib.util.match/match."}
metabase.lib.util.match/match-one {:message "Use lib.util.match/match-lite instead of lib.util.match/match-one."}
metabase.lib.util.match/replace {:message "Use lib.util.match/replace-lite instead of lib.util.match/replace."}
metabase-enterprise.transforms.interface/execute! {:message "Use metabase-enterprise.transforms.execute/execute! instead of metabase-enterprise.transforms.interface/execute!"}
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}
toucan2.core/debug {:message "Don't commit code using t2/debug"}

View File

@@ -547,6 +547,8 @@
metabase.driver.common
metabase.driver.common.parameters
metabase.driver.common.parameters.parse
metabase.driver.connection
metabase.driver.connection.workspaces
metabase.driver.ddl.interface
metabase.driver.h2
metabase.driver.impl
@@ -569,7 +571,8 @@
metabase.driver.sql.util
metabase.driver.sync
metabase.driver.util}
:uses #{app-db
:uses #{analytics
app-db
appearance
auth-provider
classloader
@@ -733,21 +736,12 @@
lib
{:team "Querying"
:api #{metabase.lib.binning
metabase.lib.convert ; Only so [[metabase.queries.models.card]] can set dynamic vars.
metabase.lib.core
:api #{metabase.lib.core
metabase.lib.equality
metabase.lib.field
metabase.lib.init
metabase.lib.metadata
metabase.lib.metadata.cached-provider
metabase.lib.metadata.column
metabase.lib.metadata.invocation-tracker
metabase.lib.metadata.protocols
metabase.lib.metadata.result-metadata
metabase.lib.normalize
metabase.lib.options
metabase.lib.query
metabase.lib.schema
metabase.lib.schema.actions
metabase.lib.schema.aggregation
@@ -769,17 +763,19 @@
metabase.lib.schema.test-spec
metabase.lib.schema.util
metabase.lib.schema.validate
metabase.lib.temporal-bucket
metabase.lib.types.isa
metabase.lib.util
metabase.lib.util.match
metabase.lib.walk}
metabase.lib.util.match}
:uses #{config
graph
legacy-mbql
types
util}
:friends #{query-processor legacy-mbql}}
:friends #{legacy-mbql ;; Reuses internal schemas and a few helper functions. Deeply coupled to MBQL.
lib-be ;; BE-specific extension of lib; tight coupling is acceptable.
query-processor ;; Necessarily manipulates MBQL; better to pull most logic into a new module.
enterprise/sandbox ;; Really part of the QP, though its lives in the enterprise/ tree.
}}
lib-be
{:team "Querying"
@@ -1181,6 +1177,7 @@
metabase.query-processor.middleware.add-remaps
metabase.query-processor.middleware.annotate
metabase.query-processor.middleware.cache-backend.db
metabase.query-processor.middleware.catch-exceptions
metabase.query-processor.middleware.constraints
metabase.query-processor.middleware.limit
metabase.query-processor.middleware.permissions
@@ -1574,12 +1571,10 @@
transforms
{:team "Querying"
:api #{metabase.transforms.api.transform
metabase.transforms.api.transform-job
metabase.transforms.api.transform-tag
metabase.transforms.core
:api #{metabase.transforms.core
metabase.transforms.feature-gating
metabase.transforms.init
metabase.transforms.interface
metabase.transforms.schema
metabase.transforms.util}
:uses #{analytics
@@ -1594,10 +1589,8 @@
premium-features
queries
query-processor
request
revisions
settings
sql-tools
sync
task
task-history
@@ -1606,6 +1599,35 @@
enterprise/transforms-python
enterprise/workspaces}}
transforms-inspector
{:team "Gadget"
:api #{metabase.transforms-inspector.core
metabase.transforms-inspector.schema}
:uses #{driver
lib
lib-be
query-processor
sql-tools
transforms
util}}
transforms-rest
{:team "Gadget"
:api #{metabase.transforms-rest.api.transform
metabase.transforms-rest.api.transform-job
metabase.transforms-rest.api.transform-tag}
:uses #{api
driver
events
models
query-processor
request
server
sql-tools
transforms
transforms-inspector
util}}
types
{:team "Querying"
:api #{metabase.types.core
@@ -2433,7 +2455,7 @@
enterprise/transforms
{:team "Querying"
:api #{}
:uses #{}}
:uses #{premium-features}}
enterprise/transforms-python
{:team "Querying"

View File

@@ -3,7 +3,7 @@
;; TODO -- seems silly to maintain different blacklists and whitelists here than we use for the deftest `^:parallel`
;; checker...
{:explicitly-safe
#{ ;; these toucan methods might actually set global values if it's used outside of a transaction,
#{;; these toucan methods might actually set global values if it's used outside of a transaction,
;; but since mt/with-temp runs in a transaction, so we'll ignore them in this case.
toucan2.core/delete!
toucan2.core/update!

View File

@@ -121,7 +121,9 @@
metabase.driver-api.core/match [[:inner 0]]
metabase.driver-api.core/match-one [[:inner 0]]
metabase.driver-api.core/match-lite [[:inner 0]]
metabase.driver-api.core/match-many [[:inner 0]]
metabase.driver-api.core/replace [[:inner 0]]
metabase.driver-api.core/replace-lite [[: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]]
@@ -157,7 +159,9 @@
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-many [[:inner 0]]
metabase.lib.util.match/replace [[:inner 0]]
metabase.lib.util.match/replace-lite [[: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]]

View File

@@ -45,7 +45,8 @@ runs:
});
if (!artifacts.data?.artifacts?.[0]?.id) {
throw new Error(`No artifacts found for ${artifactName}`);
core.setFailed(`No artifacts found for ${artifactName}`);
return;
}
const artifact_id = artifacts.data.artifacts[0].id;

View File

@@ -1,14 +1,13 @@
name: Prepare back-end environment
description: |
Prepare the backend. Installs jdk, Clojure, and fetches the cache for m2.
The CI config fetches `ci-test-config.json` from master and lets us ignore tests at the var level if they are flaking.
inputs:
java-version:
required: true
default: '21'
default: "21"
clojure-version:
required: true
default: '1.12.0.1488'
default: "1.12.0.1488"
runs:
using: "composite"
@@ -17,7 +16,7 @@ runs:
uses: actions/setup-java@v4
with:
java-version: ${{ inputs.java-version }}
distribution: 'temurin'
distribution: "temurin"
- name: Install Clojure CLI
shell: bash
continue-on-error: true
@@ -31,6 +30,10 @@ runs:
- name: Install uv (for Python package installation)
uses: astral-sh/setup-uv@v7
with:
enable-cache: false
version: "0.10.2" # keep in sync with mise.toml
github-token: ${{ github.token }}
- name: Restore M2 cache
uses: actions/cache/restore@v4

View File

@@ -15,11 +15,10 @@ outputs:
runs:
using: "composite"
steps:
- name: Install Chrome v111
- name: Install Chrome v144
uses: browser-actions/setup-chrome@v2
with:
# https://chromium.cypress.io/linux/stable/111.0.5563.146
chrome-version: 1097615
chrome-version: 144
id: setup-chrome
- name: Get cache keys

View File

@@ -0,0 +1,77 @@
name: Embedding SDK Bundle Size Check
description: |
Downloads the current branch's uberjar and builds the base ref's
embedding-sdk.js from source, compares their sizes,
and sets output if the size change exceeds the threshold.
inputs:
edition:
description: 'Metabase edition (ee or oss)'
required: true
base-ref-commit:
description: 'Commit SHA of the base ref (e.g. merge base).'
required: true
threshold-percent:
description: 'Percent threshold for bundle size change'
required: true
outputs:
status:
description: "Bundle size status: increased, decreased, or stable"
value: ${{ steps.compare.outputs.status }}
size_change_percent:
description: "size change percent (positive = increase, negative = decrease)"
value: ${{ steps.compare.outputs.size_change_percent }}
runs:
using: "composite"
steps:
- name: Prepare backend
uses: ./.github/actions/prepare-backend
- name: Prepare frontend
uses: ./.github/actions/prepare-frontend
- name: Download current uberjar
uses: actions/download-artifact@v4
with:
name: metabase-${{ inputs.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
path: temp/current-uberjar
- name: Extract embedding-sdk.js from current uberjar
shell: bash
run: unzip -j temp/current-uberjar/target/uberjar/metabase.jar frontend_client/app/embedding-sdk.js -d temp/current-bundle
- name: Checkout base ref
uses: actions/checkout@v4
with:
ref: ${{ inputs.base-ref-commit }}
path: temp/base-ref-src
- name: Build base-ref embedding-sdk.js
shell: bash
run: |
cd temp/base-ref-src
bun install
MB_EDITION=${{ inputs.edition }} bun run build-release
- name: Compare sizes and set output
id: compare
shell: bash
run: |
current_size=$(wc -c < temp/current-bundle/embedding-sdk.js) || { echo "::error::Failed to read current bundle"; exit 1; }
base_ref_size=$(wc -c < temp/base-ref-src/resources/frontend_client/app/embedding-sdk.js) || { echo "::error::Failed to read base-ref bundle"; exit 1; }
[ "$current_size" -gt 0 ] && [ "$base_ref_size" -gt 0 ] || { echo "::error::Bundle file is empty"; exit 1; }
diff=$((current_size - base_ref_size))
percent=$(( diff * 100 / base_ref_size ))
echo "Current size: $current_size bytes"
echo "Base ref size: $base_ref_size bytes"
echo "Difference: $diff bytes ($percent%)"
threshold="${{ inputs.threshold-percent }}"
if [ "$percent" -gt "$threshold" ]; then
status="increased"
elif [ "$percent" -lt "$(( -threshold ))" ]; then
status="decreased"
else
status="stable"
fi
echo "status=$status" >> $GITHUB_OUTPUT
echo "size_change_percent=$percent" >> $GITHUB_OUTPUT

View File

@@ -1,52 +1,55 @@
# Metabase Developer Assistant Instructions
## Frontend Development
For detailed coding standards and conventions, see `CLAUDE.md` and the skills in `.claude/skills/`.
### Repository Structure
## Code Review Standards
```
frontend/src/
├── metabase/ # Main application components and pages
├── metabase-lib/ # Query building and data modeling utilities
├── metabase-types/ # TypeScript type definitions
└── metabase/ui/ # Design system components
Review focus areas: Security → Performance → Testing → Documentation (in priority order)
e2e/ # Cypress end-to-end tests
frontend/test/ # Jest unit tests
```
When reviewing code, use these emoji prefixes to categorize feedback:
- 🔒 Security concerns
- ⚡ Performance opportunities
- 🧹 Cleanup needs
- 📚 Documentation gaps
- 🚨 Blocking issues
- 💭 Clarification questions
### Technology Stack
### Security Critical Issues
- **Framework**: React 18 with TypeScript
- **State**: Redux Toolkit
- **Styling**: CSS Modules (preferred) > Emotion styled-components
- **UI**: `metabase/ui` components built with Mantine v8
- **Build**: Rspack (primary), Webpack (legacy)
- **Testing**: Jest + React Testing Library, Cypress
- Check for hardcoded secrets, API keys, or credentials
- Look for SQL injection and XSS vulnerabilities
- Verify proper input validation and sanitization
- Review authentication and authorization logic
- Ensure enterprise features use the plugin system (no enterprise code in OSS)
### Coding Standards
### Performance Red Flags
#### Component Preferences
- Identify N+1 database query problems
- Spot inefficient loops and algorithmic issues
- Check for memory leaks and resource cleanup
- Review caching opportunities for expensive operations
- Prefer `metabase/ui`components over `metabase/common/components`
- Use `.tsx` for components, `.ts` for utilities
### Code Quality Essentials
#### Styling
- Functions should be focused and appropriately sized
- Use clear, descriptive naming conventions
- Ensure proper error handling throughout
- Remove dead code and unused imports
ALWAYS prefer Mantine style props,then CSS modules. DO NOT suggest styled components, they are deprecated.
### Review Style
#### TypeScript Migration
- Only flag actual issues worth mentioning
- Do not post "looks good" comments or congratulate following conventions
- Be specific and actionable in feedback
- Explain the "why" behind recommendations
- Ask clarifying questions when code intent is unclear
When heavily editing `.js`/`.jsx` files, create a separate PR to convert to TypeScript first, then implement changes.
### Review Format
#### Enterprise Features
- Condense feedback into as few comments as possible
- Prefer a single summary comment over dozens of inline comments
- Only use inline comments for critical issues that require immediate attention
- For non-critical issues, link to the specific line from the summary instead
- Delete all stale comments and summaries on each new push or CI run
Enterprise functionality MUST use the plugin system. It is very important to not expose enterprise code in the OSS version.
#### Testing Requirements
All PRs should include tests. Prefer Unit tests over E2E tests.
#### Localization
All user-facing strings MUST be localized using the ttag library. Localized strings should be complete phrases, do not concatenate a few separately localized strings. You should add context to strings where the meaning of the string might not be obvious in isolation: e.g. "Home" might have different words in some languages depending on whether you're talking about a dwelling or the landing page for a website.
Language-specific rules are in `.github/instructions/*.instructions.md`.

View File

@@ -199,3 +199,15 @@ cljs:
- *shared_specs
- ".github/workflows/cljs.yml"
- "shadow-cljs.edn"
openapi:
- ".github/workflows/openapi.yml"
- "src/metabase/api/**"
- "src/metabase/api_routes/**"
- "src/metabase/*_rest/api.clj"
- "src/metabase/*/api.clj"
- "src/metabase/*/api/*.clj"
- "enterprise/backend/src/metabase_enterprise/**/api.clj"
- "enterprise/backend/src/metabase_enterprise/**/api/*.clj"
- "resources/openapi/**"
- "package.json"

View File

@@ -0,0 +1,9 @@
---
applyTo: "**/*.{clj,cljs,cljc}"
---
# Clojure/ClojureScript Code Review
When reviewing Clojure or ClojureScript code, apply the guidelines in these files:
- `.claude/skills/_shared/clojure-style-guide.md`
- `.claude/skills/clojure-review/SKILL.md`

View File

@@ -0,0 +1,9 @@
---
applyTo: "docs/**/*.md"
---
# Documentation Review
When reviewing documentation, apply the guidelines in these files:
- `.claude/skills/_shared/metabase-style-guide.md`
- `.claude/skills/docs-review/SKILL.md`

View File

@@ -0,0 +1,11 @@
---
applyTo: "**/*.{ts,tsx,js,jsx}"
---
# TypeScript/JavaScript Code Review
When reviewing TypeScript or JavaScript code, apply the guidelines in these files:
- `.claude/skills/typescript-review/SKILL.md`
- `docs/developers-guide/frontend.md`
- `frontend/CLAUDE.md`

View File

@@ -77,6 +77,9 @@ jobs:
--pr-labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
--skip="${{ inputs.skip }}"
)
echo "Running:"
echo "./bin/mage -driver-decisions ${MAGE_ARGS[@]}"
echo ""
# First call: show human-readable output for debugging
./bin/mage -driver-decisions "${MAGE_ARGS[@]}"
# Second call: output only key=value lines for GITHUB_OUTPUT

View File

@@ -45,8 +45,9 @@ jobs:
name: e2e-tests-${{ inputs.name }}-${{ inputs.edition }}
env:
MB_EDITION: ${{ inputs.edition }}
# 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")`
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
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 }}

View File

@@ -19,8 +19,9 @@ jobs:
react-version: [18, 19]
fail-fast: false
env:
# 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")`
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
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 }}
@@ -155,8 +156,9 @@ jobs:
env:
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")`
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
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 }}

View File

@@ -23,6 +23,10 @@ on:
description: "Whether to run host and sample apps compatibility tests."
required: true
type: boolean
bundle-size-check:
description: "Whether to run bundle size check."
required: true
type: boolean
skip:
description: Whether to skip running tests due to one of the artifacts failing to build.
required: true
@@ -124,8 +128,52 @@ jobs:
uses: ./.github/workflows/embedding-sdk-host-sample-apps-tests.yml
secrets: inherit
embedding-sdk-bundle-size-check:
if: inputs.bundle-size-check
continue-on-error: true
runs-on: ubuntu-22.04
timeout-minutes: 20
env:
BUNDLE_SIZE_THRESHOLD_PERCENT: 2
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sdk-bundle-size-check
id: compare
with:
edition: ee
base-ref-commit: ${{ github.event.pull_request.base.sha }}
threshold-percent: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}
- name: Comment on PR if bundle size has increased
uses: ./.github/actions/notify-pull-request
if: success() && steps.compare.outputs.status == 'increased'
with:
remove-previous-comment: true
add-new-comment: true
message: "⚠️ Embedding SDK bundle size increased by ${{ steps.compare.outputs.size_change_percent }}% compared to the ${{ github.base_ref }} branch (threshold: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}%). Please contact Embedding team."
include-log: false
- name: Send notification to Slack via Zapier
if: success() && steps.compare.outputs.status == 'increased'
shell: bash
run: |
curl -s -X POST "${{ secrets.EMBEDDING_ZAPIER_WEBHOOK_URL }}" \
-d '⚠️ Embedding SDK bundle size increased by ${{ steps.compare.outputs.size_change_percent }}% (threshold: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}%)
PR #${{ github.event.pull_request.number }} ${{ github.event.pull_request.title }} (${{ github.event.pull_request.html_url }})'
- name: Comment on PR if bundle size has decreased
uses: ./.github/actions/notify-pull-request
if: success() && steps.compare.outputs.status == 'decreased'
with:
remove-previous-comment: true
add-new-comment: true
message: "🎉 Embedding SDK bundle size decreased by ${{ steps.compare.outputs.size_change_percent }}% compared to the ${{ github.base_ref }} branch. Nice work!"
include-log: false
embedding-sdk-tests-result:
needs:
- embedding-sdk-bundle-size-check
- embedding-sdk-cli-snippets-type-check
- embedding-sdk-docs-snippets-type-check
- embedding-sdk-api-documentation-generation-validation

46
.github/workflows/openapi.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Validate OpenAPI
on:
push:
branches:
- "master"
- "release-**"
pull_request:
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:
openapi: ${{ steps.changes.outputs.openapi }}
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
validate-openapi:
needs: files-changed
if: needs.files-changed.outputs.openapi == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 10
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: Generate OpenAPI spec
run: bun run openapi:generate
- name: Validate OpenAPI spec
run: bun run openapi:lint
- name: Split OpenAPI spec
run: bun run openapi:split

View File

@@ -5,8 +5,8 @@ on:
types: [ closed, unlabeled ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: false
jobs:
pr-env:

View File

@@ -0,0 +1,29 @@
name: PR Env Destroy (Manual)
on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to destroy environment for'
required: true
type: string
self_hosting:
description: 'Self-hosting stack'
type: boolean
default: false
jobs:
approve:
runs-on: ubuntu-latest
environment: pr-env-admin
steps:
- name: Approved
run: echo "Destroying PR environment for PR #${{ inputs.pr_number }}"
destroy:
needs: approve
uses: ./.github/workflows/pr-env-destroy.yml
secrets: inherit
with:
self_hosting: ${{ inputs.self_hosting }}
pr_number: ${{ inputs.pr_number }}

View File

@@ -9,6 +9,13 @@ on:
required: false
type: boolean
default: false
pr_number:
description: 'PR number (required for manual dispatch)'
required: false
type: string
env:
PR_NUMBER: ${{ inputs.pr_number || github.event.number }}
jobs:
destroy_pr:
@@ -62,8 +69,8 @@ jobs:
- 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 }}
kubectl delete metabase -n hosting-pr${{ env.PR_NUMBER }} hosting-pr${{ env.PR_NUMBER }}
kubectl delete ns hosting-pr${{ env.PR_NUMBER }}
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
@@ -72,8 +79,8 @@ jobs:
- 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
helm uninstall hosting-pr${{ env.PR_NUMBER }} --namespace hosting-pr${{ env.PR_NUMBER }} || true
kubectl delete ns hosting-pr${{ env.PR_NUMBER }} || true
- name: Setup psql client
run: |
sudo apt-get update
@@ -85,4 +92,4 @@ jobs:
-h ${{ secrets.PR_ENV_DB_HOST }} \
-U ${{ secrets.PR_ENV_DB_USER }} \
-d ${{ secrets.PR_ENV_DB_NAME }} \
-c "DROP DATABASE IF EXISTS hosting_pr${{ github.event.number }};"
-c "DROP DATABASE IF EXISTS hosting_pr${{ env.PR_NUMBER }};"

View File

@@ -13,8 +13,8 @@ on:
type: string
required: false
default: ''
# schedule:
# - cron: '0 21 * * 1-5' # every weekday at 4 pm EST/5 pm EDT
schedule:
- cron: '0 21 * * 1-5' # every weekday at 4 pm EST/5 pm EDT
jobs:
auto-patch-trigger:

View File

@@ -103,7 +103,8 @@ jobs:
(needs.files-changed.outputs.embedding_sdk_components == 'true' ||
needs.files-changed.outputs.frontend_sources == 'true' ||
needs.files-changed.outputs.embedding_documentation == 'true' ||
needs.files-changed.outputs.embedding_sdk_ci == 'true')
needs.files-changed.outputs.embedding_sdk_ci == 'true' ||
needs.files-changed.outputs.embedding_sdk_host_sample_apps == 'true')
uses: ./.github/workflows/embedding-sdk-package-build.yml
secrets: inherit
@@ -113,6 +114,7 @@ jobs:
uses: ./.github/workflows/embedding-sdk.yml
secrets: inherit
with:
bundle-size-check: ${{ github.event_name == 'pull_request' && needs.uberjar.result == 'success' && needs.files-changed.outputs.frontend_sources == 'true' }}
cli-snippets-type-check: ${{ needs.files-changed.outputs.frontend_sources == 'true' }}
docs-snippets-type-check: ${{ needs.files-changed.outputs.embedding_documentation == 'true' }}
documentation-validation: ${{ needs.files-changed.outputs.frontend_sources == 'true' }}

View File

@@ -0,0 +1,50 @@
name: Crowdin Context Harvester
on:
workflow_dispatch:
schedule:
- cron: "30 6 * * 2" # Tuesday at 06:30 UTC, one hour after translation-update.yml
jobs:
harvest:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Harvest and write AI context to Crowdin
timeout-minutes: 60
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
OPENAI_KEY: ${{ secrets.CROWDIN_OPENAI_API_KEY }}
run: |
npx --yes crowdin-context-harvester@latest harvest \
--project="$CROWDIN_PROJECT_ID" \
--ai="openai" \
--model="gpt-5.1" \
--croql='not (context contains "✨ AI Context")' \
--output="crowdin" \
--concurrency=10
# We only want to trigger pre-translations, not wait for them to finish running upstream.
# Weirdly, Crowdin keeps this step running as long as it runs pre-translations on their servers.
# Hence, the three minute timeout and continue-on-error.
- name: Trigger pre-translations
timeout-minutes: 3
uses: crowdin/github-action@v2
# https://crowdin.github.io/crowdin-cli/commands/crowdin-pre-translate
with:
command: "pre-translate"
command_args: >-
--method ai
--ai-prompt ${{ secrets.CROWDIN_AI_PROMPT_ID }}
--translate-untranslated-only
--token ${{ secrets.CROWDIN_TOKEN }}
--project-id ${{ secrets.CROWDIN_PROJECT_ID }}
--config=locales/crowdin.yml
--no-progress
continue-on-error: true

3
.gitignore vendored
View File

@@ -16,6 +16,7 @@
.nrepl-port
.portal
.vscode
/.zed
/*.h2.db
/*.lock.db
/*.mv.db
@@ -43,6 +44,7 @@ cypress.env.json
/docs/**/package.json
/docs/**/yarn.lock
/docs/embedding/sdk/api/
/docs/embedding/eajs/snippets/
/frontend/test/snapshots/*
/e2e/snapshots/*
/e2e/tmp/*
@@ -170,4 +172,3 @@ CLAUDE.local.md
/resources/python-sources/sqlglot-*.dist-info/
/resources/python-sources/.lock
__pycache__/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,151 @@
import {
ArrayType,
Comment,
Converter,
IntrinsicType,
LiteralType,
ReflectionFlag,
ReflectionKind,
UnionType,
} from "typedoc";
/**
* Check if a UnionType has ALL members as string LiteralTypes.
*/
function isAllStringLiteralUnion(type) {
if (!(type instanceof UnionType)) {
return false;
}
return (
type.types.length > 0 &&
type.types.every(
(t) => t instanceof LiteralType && typeof t.value === "string",
)
);
}
/**
* Extract the text content from a Comment tag's content array.
*/
function getTagText(tag) {
if (!tag.content || tag.content.length === 0) {
return "";
}
return tag.content.map((part) => part.text).join("");
}
/**
* Transform @remarks text into a human-readable sentence.
*
* "Guest embed" → "Available in Guest embed."
* "Pro/Enterprise" → "Available in Pro/Enterprise."
* "Pro/Enterprise, Guest embed" → "Available in Pro/Enterprise and Guest embed."
*/
function transformRemarks(text) {
const trimmed = text.trim();
if (trimmed.includes(",")) {
const parts = trimmed.split(",").map((s) => s.trim());
const last = parts.pop();
return `Available in ${parts.join(", ")} and ${last}.`;
}
return `Available in ${trimmed}.`;
}
/**
* A TypeDoc plugin that transforms property reflections before markdown
* rendering to produce cleaner props documentation tables.
*
* For each property on interfaces/type aliases:
* 1. Moves optional `?` markers into the description as "Optional."
* 2. Extracts @defaultValue tags into the description as "Default: `value`."
* 3. Replaces all-string-literal union types with `string` and lists possible values
* 4. Transforms @remarks tags into human-readable availability notes
*/
export function load(app) {
app.converter.on(Converter.EVENT_RESOLVE_END, (context) => {
const project = context.project;
for (const reflection of project.getReflectionsByKind(
ReflectionKind.Property,
)) {
const prefix = [];
// 1. Optional: move ? into description
if (reflection.flags.isOptional) {
prefix.push("Optional");
reflection.setFlag(ReflectionFlag.Optional, false);
}
// 2. Default value: extract @defaultValue tag
if (reflection.comment) {
const defaultTag = reflection.comment.getTag("@defaultValue");
if (defaultTag) {
const defaultText = getTagText(defaultTag).trim();
// If the value already contains backticks, use as-is
if (defaultText.includes("`")) {
prefix.push(`Default: ${defaultText}`);
} else {
prefix.push(`Default: \`${defaultText}\``);
}
reflection.comment.removeTags("@defaultValue");
}
}
// 3. Complex union types: simplify all-string-literal unions
let unionType = null;
if (isAllStringLiteralUnion(reflection.type)) {
unionType = reflection.type;
reflection.type = new IntrinsicType("string");
} else if (
reflection.type instanceof ArrayType &&
isAllStringLiteralUnion(reflection.type.elementType)
) {
unionType = reflection.type.elementType;
reflection.type = new ArrayType(new IntrinsicType("string"));
}
if (unionType) {
const values = unionType.types
.map((t) => `\`"${t.value}"\``)
.join(", ");
prefix.push(`Possible values: ${values}`);
}
// 4. Remarks: transform into availability notes
if (reflection.comment) {
const remarksTag = reflection.comment.getTag("@remarks");
if (remarksTag) {
const remarksText = getTagText(remarksTag);
if (remarksText.trim()) {
prefix.push(transformRemarks(remarksText));
}
reflection.comment.removeTags("@remarks");
}
}
// Assemble prefix and prepend to comment summary
if (prefix.length > 0) {
if (!reflection.comment) {
reflection.comment = new Comment();
}
const prefixText = ["---", ...prefix].join("<br>");
const existingSummary = reflection.comment.summary || [];
if (existingSummary.length > 0) {
reflection.comment.summary = [
...existingSummary,
{ kind: "text", text: "<br>" + prefixText },
];
} else {
reflection.comment.summary = [{ kind: "text", text: prefixText }];
}
}
}
});
}

View File

@@ -5,6 +5,7 @@ const eajsMarkdownConfig = {
plugin: [
"typedoc-plugin-mdn-links",
"typedoc-plugin-markdown",
"./typedoc-plugin-props-doc-transform.js",
"./typedoc-plugin-markdown-prepare-for-embedding.js",
],
entryPoints: [
@@ -32,7 +33,7 @@ const eajsMarkdownConfig = {
typeAliasPropertiesFormat: "table",
typeDeclarationFormat: "table",
tableColumnSettings: {
hideDefaults: false,
hideDefaults: true,
hideInherited: true,
hideModifiers: true,
hideOverrides: true,

View File

@@ -2,7 +2,6 @@
"Build step to install Python dependencies (sqlglot) for GraalVM Python."
(:require
[clojure.java.io :as io]
[clojure.string :as str]
[environ.core :as env]
[metabuild-common.core :as u]))
@@ -14,14 +13,11 @@
(def ^:private sqlglot-version
"Pinned sqlglot version for reproducible builds.
Read from .sqlglot-version file (single source of truth for both dev and build)."
(->> (io/file u/project-root-directory "resources/python-sources/.sqlglot-version")
Read from pyproject.toml (single source of truth for both dev and build)."
(->> (io/file u/project-root-directory "resources/python-sources/pyproject.toml")
slurp
;; handle # comments in the version file
str/split-lines
(remove #(str/starts-with? % "#"))
(str/join "\n")
str/trim))
(re-find #"\"sqlglot==([^\"]+)\"")
second))
(defn build-python-deps!
"Install sqlglot to resources/python-sources for bundling into uberjar.
@@ -36,7 +32,7 @@
(u/step "Install Python dependencies (sqlglot)"
(u/create-directory-unless-exists! python-sources-dir)
(u/sh {:dir u/project-root-directory}
"uv" "pip" "install" (str "sqlglot==" sqlglot-version)
"uv" "pip" "install" "-r" (u/filename python-sources-dir "pyproject.toml")
"--target" python-sources-dir
"--no-compile")
(u/announce "sqlglot %s installed to %s" sqlglot-version python-sources-dir))))

View File

@@ -14,7 +14,7 @@ fi
# Generate the current OpenAPI spec from Malli schemas using write-openapi-spec-to-file!
# This creates $OPENAPI_SPEC directly
# Using the generate-openapi-spec command from metabase.cmd.core
bun run generate-openapi
bun run openapi:generate
ret=$?
if [[ $ret -ne 0 ]]; then
@@ -82,7 +82,7 @@ if ! git diff --exit-code "$OPENAPI_SPEC" >/dev/null 2>&1; then
echo ""
echo "To fix this:"
echo " 1. Pull the latest changes from master: git pull origin master"
echo " 2. Run 'bun run generate-openapi' to update $OPENAPI_SPEC"
echo " 2. Run 'bun run openapi:generate' to update $OPENAPI_SPEC"
echo " 3. Commit the updated openapi.json file"
echo ""
echo "Differences:"

634
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,7 @@
io.github.eerohele/pp {#_#_:git/tag "2024-11-13.77" ; super fast pretty-printing library
:git/sha "ffc4edc482c057cd9412c62f018a41e9140c618b"
:git/url "https://github.com/eerohele/pp"}
io.github.metabase/macaw {:mvn/version "0.2.33"} ; Parse native SQL queries
io.github.metabase/macaw {:mvn/version "0.2.36"} ; Parse native SQL queries
io.netty/netty-codec-http {:mvn/version "4.2.9.Final"} ; override of transitive dep from aws s3
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} ; prometheus jvm collector
junegunn/grouper {:mvn/version "0.1.1"} ; Batch processing helper
@@ -617,6 +617,9 @@
:implicit-dependencies
;; too many false positives for now
:unused-ret-vals
;; hard to clear on for lib.util.match macros, and disabling the linter just there
;; doesn't seem to work
:unused-meta-on-macro
;; Kondo lints this for us anyway, and this isn't as easy to configure.
:wrong-arity
;; Kondo lints this for us anyway, and this isn't as easy to configure.

113
dev/src/dev/api/preview.clj Normal file
View File

@@ -0,0 +1,113 @@
(ns dev.api.preview
"Dev-only endpoints for previewing rendered pulse cards and dashboards.
These were previously at `/api/pulse/preview_*` but have been moved here
since they are only useful for development and testing."
(:require
[hiccup.core :refer [html]]
[hiccup.page :refer [html5]]
[metabase.api.common :as api]
[metabase.api.macros :as api.macros]
[metabase.api.routes.common]
[metabase.channel.render.core :as channel.render]
[metabase.channel.urls :as urls]
[metabase.query-processor :as qp]
[metabase.query-processor.middleware.permissions :as qp.perms]
[metabase.util.malli.schema :as ms])
(:import
(java.io ByteArrayInputStream)))
(set! *warn-on-reflection* true)
(defn- pulse-card-query-results
{:arglists '([card])}
[{query :dataset_query, card-id :id}]
(binding [qp.perms/*card-id* card-id]
(qp/process-query
(qp/userland-query
(assoc query
:middleware {:process-viz-settings? true
:js-int-to-string? false})
{:executed-by api/*current-user-id*
:context :pulse
:card-id card-id}))))
(api.macros/defendpoint :get "/preview-card/:id"
"Get HTML rendering of a Card with `id`."
[{:keys [id]} :- [:map
[:id ms/PositiveInt]]]
(let [card (api/read-check :model/Card id)
result (pulse-card-query-results card)]
{:status 200
:body (html5
[:html
[:body {:style "margin: 0;"}
(channel.render/render-pulse-card-for-display (channel.render/defaulted-timezone card)
card
result
{:channel.render/include-title? true, :channel.render/include-buttons? true})]])}))
(api.macros/defendpoint :get "/preview-dashboard/:id"
"Get HTML rendering of a Dashboard with `id`.
This endpoint relies on a custom middleware defined in `metabase.channel.render.core/style-tag-nonce-middleware` to
allow the style tag to render properly, given our Content Security Policy setup."
[{:keys [id]} :- [:map
[:id ms/PositiveInt]]]
(api/read-check :model/Dashboard id)
{:status 200
:headers {"Content-Type" "text/html"}
:body (channel.render/style-tag-from-inline-styles
(html5
[:head
[:meta {:charset "utf-8"}]
[:link {:nonce "%NONCE%" ;; this will be str/replaced by 'style-tag-nonce-middleware
:rel "stylesheet"
:href "https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap"}]]
[:body [:h2 (format "Backend Artifacts Preview for Dashboard %s" id)]
(channel.render/render-dashboard-to-html id)]))})
(api.macros/defendpoint :get "/preview-card-info/:id"
"Get JSON object containing HTML rendering of a Card with `id` and other information."
[{:keys [id]} :- [:map
[:id ms/PositiveInt]]]
(let [card (api/read-check :model/Card id)
result (pulse-card-query-results card)
data (:data result)
card-type (channel.render/detect-pulse-chart-type card nil data)
card-html (html (channel.render/render-pulse-card-for-display (channel.render/defaulted-timezone card)
card
result
{:channel.render/include-title? true}))]
{:id id
:pulse_card_type card-type
:pulse_card_html card-html
:pulse_card_name (:name card)
:pulse_card_url (urls/card-url (:id card))
:row_count (:row_count result)
:col_count (count (:cols (:data result)))}))
(def ^:private preview-card-width 400)
(api.macros/defendpoint :get "/preview-card-png/:id"
"Get PNG rendering of a Card with `id`. Optionally specify `width` as a query parameter."
[{:keys [id]} :- [:map
[:id ms/PositiveInt]]
{:keys [width]} :- [:map
[:width {:optional true} [:maybe ms/PositiveInt]]]]
(let [card (api/read-check :model/Card id)
result (pulse-card-query-results card)
width (or width preview-card-width)
ba (channel.render/render-pulse-card-to-png (channel.render/defaulted-timezone card)
card
result
width
{:channel.render/include-title? true})]
{:status 200, :headers {"Content-Type" "image/png"}, :body (ByteArrayInputStream. ba)}))
(def ^:private ^{:arglists '([handler])} style-nonce-middleware
(metabase.api.routes.common/wrap-middleware-for-open-api-spec-generation
(partial channel.render/style-tag-nonce-middleware "/dev/preview/preview-dashboard")))
(def ^{:arglists '([request respond raise])} routes
"`/dev/preview` endpoints."
(api.macros/ns-handler *ns* style-nonce-middleware))

View File

@@ -1,12 +1,15 @@
(ns dev.api.routes
(:require [dev.api.prototype]
(:require [dev.api.preview]
[dev.api.prototype]
[metabase.api.util.handlers :as handlers]))
(comment
dev.api.preview/keep-me
dev.api.prototype/keep-me)
(def ^:private dev-routes-map
{"/prototype" 'dev.api.prototype})
{"/preview" 'dev.api.preview
"/prototype" 'dev.api.prototype})
(def ^{:arglists '([request respond raise])} routes
;; This map will be merged at the top level, so /dev/prototype is available.

View File

@@ -7,6 +7,8 @@
;; run a query with debugging enabled
(binding [metabase.query-processor.debug/*debug* true]
(metabase.query-processor/process-query query))"
{:clj-kondo/config '{:linters
{:discouraged-var {metabase.lib.util.match/replace {:level :off}}}}}
(:require
[clojure.java.io :as io]
[clojure.string :as str]
@@ -123,25 +125,25 @@
(symbol (format "#_\"%s.%s\"" field-name table-name)))))
(field-id->name-form [field-id]
(list 'do (add-name-to-field-id field-id) field-id))]
(lib.util.match/replace form
(lib.util.match/replace-lite form
[:field (id :guard pos-int?) opts]
[:field id (add-name-to-field-id id) (cond-> opts
(pos-int? (:source-field opts))
(update :source-field field-id->name-form))]
(m :guard (every-pred map? (comp pos-int? :source-table)))
(m :guard (and (map? m) (pos-int? (:source-table m))))
(add-names* (update m :source-table add-table-id-name))
(m :guard (every-pred map? (comp pos-int? :metabase.query-processor.util.add-alias-info/source-table)))
(m :guard (and (map? m) (pos-int? (:metabase.query-processor.util.add-alias-info/source-table m))))
(add-names* (update m :metabase.query-processor.util.add-alias-info/source-table add-table-id-name))
(m :guard (every-pred map? (comp pos-int? :fk-field-id)))
(m :guard (and (map? m) (pos-int? (:fk-field-id m))))
(-> m
(update :fk-field-id field-id->name-form)
add-names*)
;; don't recursively replace the `do` lists above, other we'll get vectors.
(_ :guard (every-pred list? #(= (first %) 'do)))
(l :guard (and (seq? l) (= (first l) 'do)))
&match)))
x)
->sorted-mbql-query-map))
@@ -343,10 +345,10 @@
e)))))
(defn- no-$ [x]
(lib.util.match/replace x [::$ & args] (into [::no-$] args)))
(lib.util.match/replace-lite x [::$ & args] (into [::no-$] args)))
(defn- symbolize [form]
(lib.util.match/replace form
(lib.util.match/replace-lite form
[::-> x y]
(symbol (format "%s->%s" (symbolize x) (str/replace (symbolize y) #"^\$" "")))

View File

@@ -0,0 +1,157 @@
(ns dev.debug-qp.analyze-user-query
"Given access to e.g. a Cloud instance with a querying issue, this script can use a login token borrowed from your
browser to make API calls to the instance, and assemble a local mock `MetadataProvider` which contains the tables,
fields and cards necessary to run QP preprocessing and compilation locally against the same metadata the user
instance is seeing.
Of course the queries can't be executed without a connection to the user's DWH, which is usually impossible."
(:require
[clj-http.client :as http]
[medley.core :as m]
[metabase.lib.js.metadata :as lib.js.metadata]
[metabase.lib.metadata :as lib.metadata]
[metabase.query-processor.compile :as qp.compile]
[metabase.query-processor.preprocess :as qp.preprocess]
[metabase.util.json :as json]
[metabase.util.malli :as mu]))
;; XXX: INSTRUCTIONS:
;; - Log into the instance, open DevTools, and navigate to the problem question.
;; - Open the Network tab, find a `/api/card/1234` or similar request, right click and "Copy as cURL".
;; - Paste that somewhere, probably into your shell to test that the `curl` call is working.
;; - Grab its `cookie` and edit it into `my-ctx` below. (It's the `-b` arg to curl.)
;; - Edit the `instance-url` of `my-ctx` below likewise.
;; - Edit the `database-id` of `my-ctx` below to match the problem database on the user instance.
;; - That can be seen in eg. `/api/card` requests and similar.
;; - Now you can run the analysis functions below, like compile-card.
;; - See the examples at the bottom of this file.
(def ^:private my-ctx
"Contains instance details, for passing to the various functions below."
{:instance-url "https://acustomer.metabaseapp.com" ; No trailing slash.
:cookie-from-browser "COPY ME FROM YOUR curl COMMAND"
:database-id 123})
(defn- mk-headers [{:keys [cookie-from-browser instance-url] :as _ctx}]
{"accept" "application/json"
"accept-language" "en-US,en;q=0.9"
"cache-control" "no-cache"
"content-type" "application/json"
"cookie" cookie-from-browser
"pragma" "no-cache"
"priority" "u=1, i"
"referer" instance-url
"sec-ch-ua" "\"Google Chrome\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\""
"sec-ch-ua-mobile" "?0"
"sec-ch-ua-platform" "\"macOS\""
"sec-fetch-dest" "empty"
"sec-fetch-mode" "cors"
"sec-fetch-site" "same-origin"
"user-agent" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"})
(defn- instance-fetch [{:keys [instance-url] :as ctx} path]
(let [headers (mk-headers ctx)
request {:method :get
:url (str instance-url path)
:accept :json
:content-type :json
:throw-exceptions false
:headers headers}
response (-> (http/request request)
(select-keys [:body :headers :status])
(update :body json/decode))]
(if (<= 200 (:status response) 299)
(:body response)
(throw (ex-info "Non-200 response" response)))))
(defn- qm-index [objs]
(m/index-by (comp str #(get % "id")) objs))
(defn- qm->metadata [raw]
;; /query_metadata requests return a map in a different shape from what lib.js.metadata expects.
;; We get {"databases" [{...}], "tables" [{..., "fields": [{...}]}]} but we need
;; {"databases" {"12" {...}}
;; "tables" {"333" {...}}
;; "fields" {"4343" {...}}
;; "questions" {"12" {...}}}
(let [fields (for [table (get raw "tables")
:when (number? (get table "id"))
field (get table "fields")
:when (number? (get field "id"))]
field)]
{"databases" (qm-index (get raw "databases"))
"tables" (qm-index (get raw "tables"))
"fields" (qm-index fields)}))
(defn- recursive-card-metadata
"Helper for [[metadata-for]]. Prefer calling that function instead."
[ctx main-card-id]
(loop [metadata {}
cards-seen #{main-card-id}
[card-id & cards] [main-card-id]]
(if-not card-id
metadata
(let [card (instance-fetch ctx (str "/api/card/" card-id))
raw (instance-fetch ctx (str "/api/card/" card-id "/query_metadata"))
nested-cards (->> (get raw "tables")
(map #(get % "id"))
(filter string?)
(map (comp Long/parseLong #(subs % 6)))
(remove cards-seen))]
(recur (m/deep-merge metadata
{"questions" {(str card-id) card}}
(qm->metadata raw))
(into cards-seen nested-cards)
(concat cards nested-cards))))))
(defn- table-metadata
"Helper for [[metadata-for]]. Prefer calling that function instead."
[ctx table-id]
(qm->metadata {"tables" [(-> (instance-fetch ctx (str "/api/table/" table-id "/query_metadata"))
(dissoc "db"))]}))
;; =============================================== External API ===================================================
(defn metadata-for
"Given a map like `{:card [123 456], :table [789]}` returns a `MetadataProvider` with metadata for all the tables,
cards and fields transitively required by the specified roots.
Generally you only need a single `{:card [123]}` for the problem card you're investigating.
Occasionally a broken card might reference fields by ID which are not really part of their query - then they're
missing from the `/query_metadata` on that card and will be missed by this function unless you explicitly add
`{:table [789]}` to the map of roots.
Returns a complete `MetadataProvider` ready to use in a QP call."
[{:keys [database-id] :as ctx} kind->ids]
(->> (for [[kind ids] kind->ids
id ids]
(case kind
:card (recursive-card-metadata ctx id)
:table (table-metadata ctx id)))
(apply m/deep-merge)
(lib.js.metadata/metadata-provider database-id)))
(defn preprocess-card
"Given a `MetadataProvider` built by [[metadata-for]] and a card ID, tries to preprocess that card."
[metadata-provider card-id]
(mu/disable-enforcement
(-> (lib.metadata/card metadata-provider card-id)
:dataset-query
qp.preprocess/preprocess)))
(defn compile-card
"Given a `MetadataProvider` built by [[metadata-for]] and a card ID, tries to compile that card to e.g. SQL."
[metadata-provider card-id]
(mu/disable-enforcement
(-> (lib.metadata/card metadata-provider card-id)
:dataset-query
qp.compile/compile)))
(comment
;; Example calls
(let [card-id 456
mp (metadata-for my-ctx {:card [card-id]})]
(compile-card mp card-id))
(let [card-id 456
mp (metadata-for my-ctx {:card [card-id]})]
(preprocess-card mp card-id)))

View File

@@ -7,11 +7,15 @@
Configuration:
- LINEAR_API_KEY env var must be set
- Call (set-config! {:team-id \"...\" :project-id \"...\"}) before creating issues"
(:refer-clojure :exclude [run!])
(:require
[cheshire.core :as json]
[clj-http.client :as http]
[clojure.java.io :as io]
[clojure.java.shell :as shell]
[clojure.string :as str]))
[clojure.set :as set]
[clojure.string :as str]
[dev.coverage :as coverage]))
(set! *warn-on-reflection* true)
@@ -20,14 +24,21 @@
(defonce ^:private config (atom {}))
(defn set-config!
"Set configuration for Linear API calls.
"Set configuration for mutation testing.
Required keys:
- :team-id — Linear team ID
- :project-id — Linear project ID (can be set after creating a project)"
- :project-id — Linear project ID (can be set after creating a project)
Optional keys:
- :base-branch — git branch to create feature branches from (default: \"master\")
- :project-id — Linear project ID to add issues to (skips project creation in run!)"
[m]
(swap! config merge m))
(defn- base-branch []
(get @config :base-branch "master"))
(defn- api-key []
(or (System/getenv "LINEAR_API_KEY")
(throw (ex-info "LINEAR_API_KEY environment variable is not set" {}))))
@@ -215,14 +226,14 @@
"Generate a branch name for mutation testing a function.
Example: mutation-testing-lib-order-by-orderable-columns"
[target-ns fn-name]
(let [short-ns (last (clojure.string/split (str target-ns) #"\."))]
(let [short-ns (last (str/split (str target-ns) #"\."))]
(str "mutation-testing-lib-" short-ns "-" fn-name)))
(defn create-branch!
"Create and checkout a new branch from master for mutation testing a function."
"Create and checkout a new branch from the base branch for mutation testing a function."
[target-ns fn-name]
(let [branch (branch-name target-ns fn-name)]
(sh "git" "checkout" "master")
(sh "git" "checkout" (base-branch))
(sh "git" "pull")
(sh "git" "checkout" "-b" branch)
branch))
@@ -255,10 +266,11 @@
(let [title (pr-title target-ns fn-name)
body (pr-description opts)
result (sh "gh" "pr" "create" "--draft"
"--base" (base-branch)
"--title" title
"--body" body
"--label" "no-backport")]
(clojure.string/trim (:out result))))
(str/trim (:out result))))
(defn add-pr-comment!
"Add a general comment to a PR. `pr-url-or-number` can be a PR URL or number."
@@ -308,12 +320,17 @@
"-f" "start_side=RIGHT"]))]
(apply sh args)))
(defn return-to-master!
"Checkout master branch."
(defn return-to-base!
"Checkout the configured base branch."
[]
(sh "git" "checkout" "master")
(sh "git" "checkout" (base-branch))
nil)
(defn return-to-master!
"Checkout the configured base branch. Deprecated: use return-to-base! instead."
[]
(return-to-base!))
;;; --- Report helpers ---
(defn report-stats
@@ -409,6 +426,358 @@
(create-issue! {:title (linear-issue-title target-ns fn-name)
:description (linear-issue-description target-ns fn-name)}))
;;; --- Orchestration ---
(defn parse-namespace
"Parse a namespace symbol into all derived paths and names.
Auto-detects .cljc vs .clj by checking which file exists."
[target-ns-sym]
(let [target-ns (str target-ns-sym)
test-ns (str target-ns "-test")
short-name (last (str/split target-ns #"\."))
ns-path (-> target-ns
(str/replace "." "/")
(str/replace "-" "_"))
test-path-base (-> test-ns
(str/replace "." "/")
(str/replace "-" "_"))
source-ext (cond
(.exists (io/file (str "src/" ns-path ".cljc"))) ".cljc"
(.exists (io/file (str "src/" ns-path ".clj"))) ".clj"
:else ".cljc")
test-ext (cond
(.exists (io/file (str "test/" test-path-base ".cljc"))) ".cljc"
(.exists (io/file (str "test/" test-path-base ".clj"))) ".clj"
:else ".cljc")]
{:target-ns (symbol target-ns)
:test-ns (symbol test-ns)
:short-name short-name
:source-path (str "src/" ns-path source-ext)
:test-path (str "test/" test-path-base test-ext)
:report-path (str "mutation-testing-report." target-ns ".before.md")}))
(defn group-functions
"Group functions from coverage results for batch processing.
Takes the output of coverage/test-namespace.
Logic:
- Public functions with surviving mutations each become a group
- Private functions get assigned to the public function with the most test overlap
- Functions with zero surviving mutations are skipped
- Uncovered or unassignable private functions get their own group"
[coverage-results]
(let [with-survivors (into {} (filter (fn [[_ v]] (seq (:survived v))) coverage-results))
public? (fn [fn-sym]
(let [v (find-var fn-sym)]
(and v (not (:private (meta v))))))
public-fns (into {} (filter (fn [[k _]] (public? k)) with-survivors))
private-fns (into {} (remove (fn [[k _]] (public? k)) with-survivors))
assignments (into {}
(for [[priv-fn priv-data] private-fns
:let [priv-tests (set (:tests priv-data))
best (when (seq priv-tests)
(->> public-fns
(map (fn [[pub-fn pub-data]]
[pub-fn (count (set/intersection
priv-tests
(set (:tests pub-data))))]))
(filter (fn [[_ n]] (pos? n)))
(sort-by second >)
first))]
:when best]
[priv-fn (first best)]))
unassigned-private (into {} (remove (fn [[k _]] (contains? assignments k)) private-fns))
public-groups (for [[pub-fn pub-data] public-fns
:let [attached (keep (fn [[priv-fn assigned-to]]
(when (= assigned-to pub-fn) priv-fn))
assignments)
all-fn-names (vec (cons pub-fn attached))
all-mutations (vec (concat (:survived pub-data)
(mapcat #(:survived (get coverage-results %))
attached)))
all-tests (apply set/union
(keep #(:tests (get coverage-results %))
all-fn-names))]]
{:primary-fn pub-fn
:fn-names all-fn-names
:mutations all-mutations
:tests (or all-tests #{})})
private-groups (for [[priv-fn priv-data] unassigned-private]
{:primary-fn priv-fn
:fn-names [priv-fn]
:mutations (vec (:survived priv-data))
:tests (or (:tests priv-data) #{})})]
(vec (concat public-groups private-groups))))
(defn build-test-prompt
"Build a prompt for Claude to write tests that kill surviving mutations."
[{:keys [target-ns test-ns source-path test-path fn-names mutations]}]
(let [fn-sources (str/join "\n\n"
(for [fn-name fn-names
:let [fn-info (coverage/find-function-source fn-name)
source (when fn-info (coverage/read-function-from-file fn-info))]
:when source]
(str ";;; " fn-name "\n" source)))
mutation-list (str/join "\n\n"
(map-indexed
(fn [i {:keys [description mutation]}]
(str "### Mutation " (inc i) ": " description "\n```clojure\n" mutation "\n```"))
mutations))
test-content (slurp test-path)]
(str "You are writing mutation tests for functions in `" target-ns "`.\n"
"\n"
"## Source Code of Functions Under Test\n"
"\n"
"```clojure\n"
fn-sources
"\n```\n"
"\n"
"## Existing Test File (" test-path ")\n"
"\n"
"```clojure\n"
test-content
"\n```\n"
"\n"
"## Surviving Mutations to Kill\n"
"\n"
"Each mutation below represents a change to the source code that no existing test catches.\n"
"Write the simplest tests that would fail if these mutations were applied.\n"
"\n"
mutation-list
"\n"
"\n"
"## Instructions\n"
"\n"
"- Write the simplest tests that kill these surviving mutations\n"
"- Follow the patterns in the existing test file (same helpers, same assertion style)\n"
"- Never call private functions directly — test through public API\n"
"- Insert tests near related existing tests, not at the end of the file\n"
"- Use the Edit tool to modify the test file at: " test-path "\n"
"- After writing tests, verify they compile by running: clj-nrepl-eval -p $(clj-nrepl-eval --discover-ports | head -1) \"(require '" test-ns " :reload)\"\n"
"- Each test should verify one meaningful behavior — don't over-engineer\n")))
(defn invoke-claude!
"Shell out to claude CLI to write tests. Returns the output string."
[prompt]
(let [result (shell/sh "claude" "-p"
"--allowedTools" "Edit,Read,Bash(clj-nrepl-eval*)"
:in prompt)]
(when-not (zero? (:exit result))
(throw (ex-info "Claude invocation failed" {:exit (:exit result)
:err (:err result)})))
(:out result)))
(defn- all-test-names
"Get all test var names from a test namespace."
[test-ns-sym]
(require test-ns-sym :reload)
(let [ns-obj (find-ns test-ns-sym)]
(->> (ns-interns ns-obj)
vals
(filter #(:test (meta %)))
(map #(symbol (str test-ns-sym) (name (.sym ^clojure.lang.Var %))))
set)))
(defn- count-tests
"Count test vars in a namespace."
[test-ns-sym]
(count (filter #(:test (meta %)) (vals (ns-interns (find-ns test-ns-sym))))))
(defn verify-and-retry!
"Verify mutations are killed. Retry with Claude if needed.
Returns {:status :success|:partial, :killed [...], :survived [...]}."
[{:keys [fn-names test-ns test-path max-retries]
:or {max-retries 2}}]
(loop [retries-left max-retries]
(let [test-names (all-test-names test-ns)
fn-results (into {}
(for [fn-name fn-names
:let [result (coverage/test-mutations fn-name test-names)]
:when result]
[fn-name result]))
all-killed (vec (mapcat :killed (vals fn-results)))
all-survived (vec (mapcat :survived (vals fn-results)))]
(if (empty? all-survived)
{:status :success :killed all-killed :survived []}
(if (pos? retries-left)
(do
(println " " (count all-survived) "mutations still survive, retrying..."
"(" retries-left "retries left)")
(let [retry-prompt (str "Some mutations still survive after your previous tests. "
"Please write additional tests to kill them.\n\n"
"## Still-Surviving Mutations\n\n"
(str/join "\n\n"
(for [{:keys [description mutation]} all-survived]
(str "### " description "\n```clojure\n" mutation "\n```")))
"\n\n## Instructions\n"
"- The test file is at: " test-path "\n"
"- Use the Edit tool to add more tests\n"
"- Focus specifically on the mutations listed above\n"
"- Do NOT duplicate existing tests\n")]
(invoke-claude! retry-prompt))
(recur (dec retries-left)))
{:status :partial :killed all-killed :survived all-survived})))))
(defn process-group!
"Process one group of functions: branch → tests → verify → commit → PR.
Returns {:pr-url ... :issue ... :killed ... :not-killed ...}."
[session group]
(let [{:keys [target-ns test-ns test-path]} session
{:keys [primary-fn fn-names mutations]} group
primary-fn-name (name primary-fn)]
;; 1. Create branch
(println " Creating branch...")
(create-branch! (str target-ns) primary-fn-name)
;; 2. Count tests before
(require test-ns :reload)
(let [tests-before (count-tests test-ns)]
;; 3. Build prompt and invoke Claude
(println " Invoking Claude to write tests...")
(let [prompt (build-test-prompt {:target-ns target-ns
:test-ns test-ns
:source-path (:source-path session)
:test-path test-path
:fn-names fn-names
:mutations mutations})]
(invoke-claude! prompt))
;; 4. Verify and retry
(println " Verifying mutations killed...")
(let [verify-result (verify-and-retry! {:fn-names fn-names
:test-ns test-ns
:test-path test-path
:max-retries 2})
killed (:killed verify-result)
survived (:survived verify-result)
tests-after (count-tests test-ns)
tests-added (- tests-after tests-before)]
;; 5. Commit and push
(println " Committing and pushing...")
(commit-and-push! (str target-ns) primary-fn-name [test-path])
;; 6. Create Linear issue
(println " Creating Linear issue...")
(let [issue (create-issue-for-function! (str target-ns) primary-fn-name)
;; 7. Create draft PR
_ (println " Creating draft PR...")
pr-url (create-draft-pr!
{:target-ns (str target-ns)
:fn-name primary-fn-name
:fn-names (mapv name fn-names)
:linear-identifier (:identifier issue)
:mutations-before (count mutations)
:tests-added tests-added
:killed (mapv :description killed)
:not-killed (mapv (fn [m] {:description (:description m)
:rationale "Could not be killed automatically"})
survived)
:suggested-changes []})]
;; 8. Return to base branch
(return-to-base!)
(println " Done! PR:" pr-url)
{:pr-url pr-url
:issue issue
:killed killed
:not-killed survived
:tests-added tests-added})))))
(defn print-summary!
"Print a summary of the mutation testing run."
[{:keys [project groups results]}]
(println "\n========================================")
(println " Mutation Testing Summary")
(println "========================================")
(println "Project:" (:name project))
(println "Groups:" (count groups))
(let [successful (filter :pr-url results)
failed (filter :error results)
total-killed (count (mapcat :killed successful))
total-unkilled (count (mapcat :not-killed successful))
total-tests (reduce + 0 (keep :tests-added successful))]
(println "PRs created:" (count successful))
(when (seq failed)
(println "Errors:" (count failed)))
(println "Tests added:" total-tests)
(println "Mutations killed:" total-killed)
(println "Mutations not killed:" total-unkilled)
(println)
(when (seq successful)
(println "PRs:")
(doseq [r successful]
(println " " (:pr-url r))))
(when (seq failed)
(println "\nFailed:")
(doseq [r failed]
(println " " (:primary-fn r) "-" (:error r))))
(println "========================================")))
(defn run!
"Main entry point. Runs the full mutation testing workflow for a namespace.
Prerequisites:
- nREPL running
- LINEAR_API_KEY env var set
- gh CLI authenticated
- team-id configured via (set-config! {:team-id \"...\"})
Options (optional second arg):
- :base-branch — git branch to create feature branches from (default: from config, or \"master\")
- :project-id — existing Linear project ID to use (skips creating a new project)"
([target-ns-sym] (run! target-ns-sym {}))
([target-ns-sym opts]
(when-let [bb (:base-branch opts)]
(set-config! {:base-branch bb}))
(when-let [pid (:project-id opts)]
(set-config! {:project-id pid}))
(let [parsed (parse-namespace target-ns-sym)
{:keys [target-ns test-ns report-path]} parsed]
;; 1. Generate report
(println "Generating mutation testing report for" (str target-ns) "...")
(coverage/generate-report target-ns [test-ns] report-path)
(println "Report written to" report-path)
;; 2. Create or reuse Linear project
(let [project (if (:project-id @config)
(do (println "Using existing Linear project:" (:project-id @config))
{:project-id (:project-id @config)
:name (str "Existing project " (:project-id @config))})
(do (println "Creating Linear project...")
(let [p (create-project-for-namespace! (str target-ns) report-path)]
(println "Created project:" (:name p))
p)))]
;; 3. Run coverage and group functions
(println "Running coverage analysis and grouping...")
(let [coverage-results (coverage/test-namespace target-ns [test-ns])
groups (group-functions coverage-results)]
(println "Found" (count groups) "function groups to process")
;; 4. Process each group
(let [results (vec
(for [group groups]
(do
(println "\n--- Processing:" (:primary-fn group)
"(" (count (:mutations group)) "surviving mutations) ---")
(try
(process-group! parsed group)
(catch Exception e
(println "ERROR:" (.getMessage e))
(try (return-to-base!) (catch Exception _))
{:error (.getMessage e)
:primary-fn (:primary-fn group)})))))]
;; 5. Print summary
(print-summary! {:project project
:groups groups
:results results})
results))))))
(comment
;; Setup:
;; 1. Set LINEAR_API_KEY env var before starting the REPL

View File

@@ -87,6 +87,7 @@ Metabase's reference documentation.
- [The SQL editor](./questions/native-editor/writing-sql.md)
- [SQL parameters](./questions/native-editor/sql-parameters.md)
- [Table variables](./questions/native-editor/table-variables.md)
- [Referencing models and saved questions](./questions/native-editor/referencing-saved-questions-in-queries.md)
- [Snippets](./questions/native-editor/snippets.md)
- [Snippet folder permissions](./permissions/snippets.md)
@@ -150,6 +151,7 @@ Metabase's reference documentation.
- [AI overview](./ai/start.md)
- [Metabot](./ai/metabot.md)
- [Setting up Metabot](./ai/settings.md)
- [SQL generation](./ai/sql-generation.md)
### Exploration and organization

View File

@@ -28,7 +28,7 @@ If you're an admin who just wants to edit data in your tables, check out [Editab
For actions to work, you'll first need to do the following two things:
1. **Enable model actions for the database connection**. To enable actions for a database connection, admins should click on the **gear** icon in the upper right and navigate to **Admin settings** > **Databases**, then click on the database you want to create actions for. On the right side of the connection settings form, toggle the **Model actions** option. For actions to work, the database user account (the account you're using to connect to the database) must have [write permissions](../databases/users-roles-privileges.md#privileges-to-enable-actions-and-editable-table-data). And for now, actions are only supported on PostgreSQL and MySQL databases.
1. **Enable model actions for the database connection**. To enable actions for a database connection, admins should click on the **grid** icon in the upper right and navigate to **Admin** > **Databases**, then click on the database you want to create actions for. On the right side of the connection settings form, toggle the **Model actions** option. For actions to work, the database user account (the account you're using to connect to the database) must have [write permissions](../databases/users-roles-privileges.md#privileges-to-enable-actions-and-editable-table-data). And for now, actions are only supported on PostgreSQL and MySQL databases.
2. **Create at least one model from that database.** Actions are associated with models, so you'll need to have created (or have access to) at least one model before you can start creating actions.
## Who can use actions

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -124,7 +124,6 @@ Open it from **+ New → AI exploration**, then ask a question or pick one of th
When answering questions in AI exploration, Metabot searches a limited set of content instead of your entire Metabase. Admins can set a **Collection for natural language querying** to scope AI exploration to a specific collection and its subcollections. You can still refer to content outside that scope by explicitly @-mentioning it in your prompt. See [Metabot AI settings](../ai/settings.md) for more details.
## Giving feedback on Metabot responses
When you hover over Metabot's responses, you'll see options to give feedback.

View File

@@ -7,7 +7,7 @@ summary: Configure Metabot settings, including which collections Metabot can acc
> Metabot is only available as an add-on on Metabase Cloud.
_Settings > Admin settings > AI_
_Admin > AI_
This page covers admin settings for Metabase's AI assistant, [Metabot](./metabot.md).
@@ -28,7 +28,7 @@ For now, Metabot is only available for Metabase Cloud. Before you can set up Met
5. Read through the [terms of service](https://www.metabase.com/license/hosting) and click **Add Metabot AI**.
Once you've added Metabot AI in the Metabase store, you can log into your Metabase and configure it in _Admin settings > AI_.
Once you've added Metabot AI in the Metabase store, you can log into your Metabase and configure it in _Admin > AI_.
## Verified content
@@ -105,7 +105,7 @@ In other words, to restrict what data Metabot can see for each person, simply ap
## Viewing Metabot usage
You can see how many Metabot requests people have made this month by going to **Admin settings > Settings > License**.
You can see how many Metabot requests people have made this month by going to **Admin > Settings > License**.
If you aren't logged into the [Metabase Store](../cloud/accounts-and-billing.md).(, you'll need to log in to the store before you can view the usage. Once logged in to the store, go back to your Metabase and view the license page.

55
docs/ai/sql-generation.md Normal file
View File

@@ -0,0 +1,55 @@
---
title: SQL generation on self-hosted Metabases
summary: Bring your own API key to generate SQL from natural language in your self-hosted Metabase.
---
# SQL generation on self-hosted Metabases
> One-shot SQL generation with your own API key is only available on self-hosted Metabases. For Metabase Cloud, check out [Metabot](./metabot.md).
Bring your own API key to generate SQL from natural language in the [native editor](../questions/native-editor/writing-sql.md).
## Setting up one-shot SQL generation
To enable SQL generation on a self-hosted Metabase:
1. Go to **Admin settings > AI**.
2. Enter your **Anthropic API key**.
3. Select the AI model you'd like to use for SQL generation. (Opus models typically give the best results.)
![SQL generation Admin](./images/sql-generation-admin.png)
You'll need an API key from [Anthropic](https://www.anthropic.com/learn/build-with-claude). Once you've entered the key, anyone in your Metabase can use SQL generation in the native editor.
Currently, only Anthropic is supported.
## Generate SQL from natural language
![Generate SQL from a natural language prompt](./images/sql-generation.png)
To generate SQL from a natural language prompt:
1. Open the [native editor](../questions/native-editor/writing-sql.md).
2. Press **Cmd+Shift+I** (Mac) or **Ctrl+Shift+I** (Windows).
3. Enter one or more tables.
4. Type a prompt describing the query you want to write.
5. Click **Generate** to create the SQL.
6. Review the generated SQL before running it.
You can also edit existing SQL. Place your cursor in the query, trigger the prompt, and describe the change you want to make (like "add a filter for orders from this year"). Metabase will suggest edits that you can accept or reject. You must select at least one table for both new queries and edits.
![Accept or reject](./images/accept-or-reject.png)
Each prompt is independent — the AI doesn't retain context from previous generations.
Like with all generative AI, always double-check the output.
## Further reading
- [The native/SQL editor](../questions/native-editor/writing-sql.md)
- [Metabot](./metabot.md)

View File

@@ -14,6 +14,10 @@ Metabot is an AI assistant that helps you explore and analyze your data.
Admins can configure Metabot to focus on specific collections and manage example prompts.
## [SQL generation](./sql-generation.md)
Use your own API key to generate SQL from natural language on self-hosted Metabases.
## [Agent API](./agent-api.md)
A REST API for building headless, agentic BI applications on top of Metabase's semantic layer.

File diff suppressed because it is too large Load Diff

View File

@@ -91,8 +91,8 @@ Just like uploaded CSV data, the data from your Google Sheets counts toward your
To check how much storage you're using:
1. Log in to your Metabase.
2. Click on the **gear** icon in the upper right.
3. Select **Admin settings**.
2. Click the **grid** icon in the upper right.
3. Select **Admin**.
4. In the **Settings** tab, click on **License and billing**.
5. Check:
- Currently stored rows

View File

@@ -56,7 +56,7 @@ Generally, annual billing is a great deal; it's cheaper across the board. The on
## What counts as a user account?
A user account is any account which has been added to your Metabase instance (manually or via SSO) that has not been deactivated. You can view a list of user accounts in your **Admin settings** -> **People** list. Any user account which has been [deactivated](../people-and-groups/managing.md#deactivating-an-account) doesn't count toward your number of user accounts. That is to say: if an account exists, but has not been deactivated, that account will count toward your bill, _even if no one signs in and uses that account_. If an account is deactivated, that account is charged for the number of days the account was available for use during that billing period, including the day it was deactivated (since it was available for use for part of the day up until it was deactivated).
A user account is any account which has been added to your Metabase instance (manually or via SSO) that has not been deactivated. You can view a list of user accounts in your **Admin** -> **People** list. Any user account which has been [deactivated](../people-and-groups/managing.md#deactivating-an-account) doesn't count toward your number of user accounts. That is to say: if an account exists, but has not been deactivated, that account will count toward your bill, _even if no one signs in and uses that account_. If an account is deactivated, that account is charged for the number of days the account was available for use during that billing period, including the day it was deactivated (since it was available for use for part of the day up until it was deactivated).
## How billing works with embedding

View File

@@ -23,7 +23,7 @@ To migrate, you'll need to [contact our success team](https://www.metabase.com/h
The major version of your self-hosted Metabase should match the version of your Metabase Cloud instance. For example, if your Metabase Cloud instance is on version 52, then your self-hosted instance should be on version 52 as well.
You can find the version of your Metabase by clicking on the **Gear** icon in the top right of your Metabase and clicking on **About Metabase**. For Metabase Cloud, you can also see the version of your instance in the [Metabase Store](https://store.metabase.com).
You can find the version of your Metabase by clicking the **grid icon** in the top right, selecting **Help**, then clicking **About Metabase**. For Metabase Cloud, you can also see the version of your instance in the [Metabase Store](https://store.metabase.com).
### Use PostgreSQL for your application database

View File

@@ -32,7 +32,7 @@ You'll kick off the migration process in your self-hosted Metabase, and finish u
### Initiate your migration
In your Metabase, go to **Admin settings** > **Settings** > **Cloud**.
In your Metabase, go to **Admin** > **Settings** > **Cloud**.
In the **Migrate to Metabase Cloud** section, click on the **Get started** button.

View File

@@ -21,7 +21,7 @@ If you're looking for date, time, number, or currency formatting, see [Formattin
Hit cmd/ctrl + k to bring up the command palette and search for "Branding" and click on Settings -> Branding.
You can also click on the **gear** icon at the bottom of the navigation sidebar and click through **Admin settings** > **Settings** > **Appearance**.
You can also click the **grid** icon at the bottom of the navigation sidebar and click through **Admin** > **Settings** > **Appearance**.
Appearance settings are split across different tabs:
@@ -123,7 +123,7 @@ This setting affects all links in the product experience that point to Metabase.
## Help link in the settings menu
The Settings menu (the "gear" menu in the upper right of your Metabase) includes a **Help** option that links to a [Metabase help page](https://www.metabase.com/help/) by default. You can change this menu Help item by selecting one of the following options:
The Settings menu (the "grid" menu in the upper right of your Metabase) includes a **Help** option that links to a [Metabase help page](https://www.metabase.com/help/) by default. You can change this menu Help item by selecting one of the following options:
- Link to [Metabase help](https://www.metabase.com/help/) (default)
- Hide it (the Settings menu won't display the Help option at all).

View File

@@ -74,10 +74,21 @@ If you select "Don't cache results" for a question, dashboard, or database, Meta
{% include plans-blockquote.html feature="Refresh cache automatically" %}
> **Refreshing the cache automatically doesn't apply when [row and column security](../permissions/row-and-column-security.md) or [connection impersonation](../permissions/impersonation.md) permissions are in effect.** In those permissions setups, Metabase ignores the automatically generated cache and runs a fresh query (which it will then cache). So _caching_ still works for row and column security and impersonation, it's just that this preemptive, automatic caching doesn't apply in these cases, as those permissions setups filter data specific to each viewer.
If you turn on "Refresh cache automatically" for a question or dashboard, Metabase will rerun the query/queries as soon as the cache is invalidated by whichever caching policy you've set.
If you turn on refresh cache automatically for a question or dashboard, Metabase will rerun the query/queries as soon as the cache is invalidated by whichever caching policy you've set. Normally, Metabase only refreshes the cache when someone views the item _after_ its cache has expired. So, normally, when the cache has expired, the next person to view the question will be stuck waiting for the query to run and refresh the cache. But by automatically refreshing results to update the cache, the loading times will always be as fast as possible, and people will always get valid, cached results.
Normally, Metabase only refreshes the cache when someone views the item _after_ its cache has expired. Which means that the first person to view a question after the cache has expired will be stuck waiting for the query to run and refresh the cache.
If you turn on "Refresh cache automatically", Metabase will refresh the cache as soon as it expires, so the loading times will always be from a valid cache.
### Some permissions setups are incompatible with automatic cache refreshing
Automatic cache refresh is NOT available when certain permission setups are in effect:
- [Row and column security](../permissions/row-and-column-security.md)
- [Connection impersonation](../permissions/impersonation.md)
- [Database routing](../permissions/database-routing.md)
Caching will still work for queries that people run, but Metabase won't automatically refresh the cache, as these setups create many different queries, each returning data specific to each user's permissions.
### How Metabase handles parameter values when automatically refreshing the cache
When automatically refreshing results, Metabase will apply the default parameter values (if any). Metabase will also cache results of up to ten of the most frequently applied parameter values that were applied _during the last caching period_ (as defined by your caching policy). If your question or dashboard has multiple parameters, Metabase will cache up to ten of the most frequently applied _combinations_ of parameter values.
@@ -101,7 +112,7 @@ _\* Denotes [Pro](https://www.metabase.com/product/pro) and [Enterprise](https:/
### Default caching policy
To set a default caching policy for your Metabase: Hit Cmd/Ctrl + k to bring up the command palette and search for **Performance**. Or, click through **Gear** settings icon > **Admin settings** > **Performance** > **Database caching**.
To set a default caching policy for your Metabase: Hit Cmd/Ctrl + k to bring up the command palette and search for **Performance**. Or, click through **grid** icon > **Admin** > **Performance** > **Database caching**.
Click on the button next to **Default policy**, and select a [cache invalidation policy](#cache-invalidation-policies).
@@ -157,7 +168,7 @@ A question policy overrides a dashboard policy, which overrides a database polic
To clear the cache and refresh the results:
- **Questions and dashboards**: Visit the item and click through the **Info > Caching policy > Clear cache** (the "Clear cache" button is at the bottom of the sidebar).
- **Database**: Click the **Gear** icon and click through **Admin settings** > **Performance** > **Database caching**. Select your database and click the **Clear cache** button (at the bottom of the page).
- **Database**: Click the **grid** icon and click through **Admin** > **Performance** > **Database caching**. Select your database and click the **Clear cache** button (at the bottom of the page).
## Caching location

View File

@@ -214,6 +214,9 @@ config:
notification-system-event-thread-pool-size: 5
notification-temp-file-size-max-bytes: 10485760
notification-thread-pool-size: 3
oidc-allowed-networks: allow-all
oidc-providers: []
oidc-user-provisioning-enabled: true
persisted-model-refresh-cron-schedule: 0 0 0/6 * * ? *
persisted-models-enabled: false
premium-embedding-token: null
@@ -281,6 +284,7 @@ config:
smtp-override-enabled: false
source-address-header: X-Forwarded-For
sql-jdbc-fetch-size: 500
sql-tools-parser-backend: sqlglot
ssh-heartbeat-interval-sec: 180
start-of-week: sunday
subscription-allowed-domains: null

View File

@@ -12,7 +12,7 @@ By default, Metabase uses OpenStreetMaps for map visualizations, but there are a
To find the map settings:
1. At the top right of the screen, click **gear** icon > **Admin settings** > **Settings**.
1. At the top right of the screen, click **grid** icon > **Admin** > **Settings**.
2. Select `Maps` from the navigation on the left.
![Map Settings](images/MapSettings.png)
@@ -41,7 +41,7 @@ Your GeoJSON file should:
To add a custom map:
1. Go to **Admin Settings > Maps > Custom Maps**.
1. Click the **grid icon**, then go to **Admin** > **Settings** > **Maps** > **Custom Maps**.
2. Click **Add a Map**.
3. Enter a name for your map.
4. Provide the URL to your GeoJSON file.

View File

@@ -13,7 +13,7 @@ Both admins and people with [settings access](../permissions/application.md#sett
## Email on Metabase Cloud
_Admin settings > Settings > Email_
_Admin > Settings > Email_
On Metabase Cloud, you can use the default SMTP server, or set up a custom server.
@@ -32,7 +32,7 @@ When Metabase manages the SMTP server, you can't change the from address. If you
{% include plans-blockquote.html feature="Custom SMTP server on Metabase Cloud" %}
_Admin settings > Settings > Email > Set up a custom SMTP server_
_Admin > Settings > Email > Set up a custom SMTP server_
By default, Metabase Cloud manages an SMTP server for you. But if you want to use your own SMTP server, you can bring your own.
@@ -66,7 +66,7 @@ You can edit these settings at any time. You can also toggle between this custom
## Configuring your email account
_Admin settings > Settings > Email_
_Admin > Settings > Email_
For Metabase to send messages to people, you'll need to set up an email account to send emails via **SMTP** (simple mail transfer protocol). SMTP is an email standard that, when combined with SSL/TLS, provides security protection for emails.

View File

@@ -1306,6 +1306,34 @@ If Metabase stops sending notifications like alerts, it may be because long-runn
queries are clogging the notification queue. You may be able to unclog the queue by
increasing the size of the thread pool dedicated to notifications.
### `MB_OIDC_ALLOWED_NETWORKS`
- Type: keyword
- Default: `allow-all`
- [Configuration file name](./config-file.md): `oidc-allowed-networks`
What networks are OIDC requests allowed to? Possible values: 'allow-all' (default), 'allow-private', or 'external-only'.
### `MB_OIDC_PROVIDERS`
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
- Type: json
- Default: `[]`
- [Configuration file name](./config-file.md): `oidc-providers`
JSON containing OIDC provider configurations.
### `MB_OIDC_USER_PROVISIONING_ENABLED`
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
- Type: boolean
- Default: `true`
- [Configuration file name](./config-file.md): `oidc-user-provisioning-enabled`
When a user logs in via OIDC, create a Metabase account for them automatically if they don't have one.
### `MB_PERSISTED_MODEL_REFRESH_CRON_SCHEDULE`
- Type: string
@@ -1938,6 +1966,13 @@ Identify the source of HTTP requests by this header's value, instead of its remo
Fetch size for result sets. We want to ensure that the jdbc ResultSet objects are not realizing the entire results
in memory.
### `MB_SQL_TOOLS_PARSER_BACKEND`
- Type: keyword
- Default: `sqlglot`
Parser backend of `sql-tools` module.
### `MB_SSH_HEARTBEAT_INTERVAL_SEC`
- Type: integer
@@ -2140,6 +2175,8 @@ Default: `null`
A JDBC-style connection URI that can be used instead of most of `MB_DB_*` like [MB_DB_HOST](#mb_db_host). Also used when certain Connection String parameters are required for the connection. The connection type requirement is the same as [MB_DB_TYPE](#mb_db_type).
Note that the `currentSchema` JDBC parameter has no effect. [The schema used for PostgreSQL application databases must be `public`](https://github.com/metabase/metabase/issues/37836).
Examples:
```

View File

@@ -13,7 +13,7 @@ On Pro and Enterprise plans, you can customize the font Metabase uses (the defau
## Included fonts
To change your Metabase font, click on the **gear** icon in the upper right of the screen and select **Admin settings** > **Settings** > **Appearance**. Under **Font**, select from a list of included fonts.
To change your Metabase font, click the **grid** icon in the upper right of the screen and select **Admin** > **Settings** > **Appearance**. Under **Font**, select from a list of included fonts.
- [Custom font](#custom-fonts)
- [Lato](https://fonts.google.com/specimen/Lato)
@@ -40,7 +40,7 @@ Comic Sans is currently not included.
## Custom fonts
To set a custom font, click on the **gear** icon in the upper right of the screen and select **Admin settings** > **Settings** > **Appearance**.
To set a custom font, click the **grid** icon in the upper right of the screen and select **Admin** > **Settings** > **Appearance**.
For **Font**, select **Custom...** and enter URLs to your font files.
@@ -93,7 +93,7 @@ For latin text, we'd copy the relevant src URLs for each style, in this case:
- For 400: [https://fonts.gstatic.com/s/robotomono/v21/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2](https://fonts.gstatic.com/s/robotomono/v21/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2)
- For 700: [https://fonts.gstatic.com/s/robotomono/v21/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2](https://fonts.gstatic.com/s/robotomono/v21/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2)
And paste those URLS in the relevant input fields in **Admin settings** > **Settings** > **Appearance** > **Font** section where it says "Tell us where to find the file for each font weight. You don’t need to include all of them, but it’ll look better if you do."
And paste those URLS in the relevant input fields in **Admin** > **Settings** > **Appearance** > **Font** section where it says "Tell us where to find the file for each font weight. You don’t need to include all of them, but it’ll look better if you do."
## Hosting fonts on GitHub

View File

@@ -8,8 +8,8 @@ redirect_from:
Admins can update the localization settings for the instance:
1. Click on the **gear** icon in the upper right.
2. Click **Admin settings**.
1. Click the **grid** icon in the upper right.
2. Click **Admin**.
3. In the **Settings** tab, click on **Localization** in the left sidebar.
These localization settings allow you to set global language and formatting defaults for dates, times, numbers, and currencies.

View File

@@ -11,7 +11,7 @@ Metabase logs quite a bit of information by default. Metabase uses [Log4j 2](htt
## View and download Metabase logs
You can find Metabase logs in **Admin settings** > **Tools** > **Logs**. You can filter the logs by keywords (for example, "sync") and download them as a text file.
You can find Metabase logs in **Admin** > **Tools** > **Logs**. You can filter the logs by keywords (for example, "sync") and download them as a text file.
If you're running self-hosted Metabase, you'll also be able see the logs in the terminal.
@@ -25,7 +25,7 @@ Metabase uses [log4j](https://logging.apache.org/log4j/2.x/)for logging configur
### Temporary override logging configuration
To temporarily adjust the logging configuration, go to **Admin settings** > **Tools** > **Logs** and click on **Customize log levels**.
To temporarily adjust the logging configuration, go to **Admin** > **Tools** > **Logs** and click on **Customize log levels**.
You can select from log level presets for common troubleshooting tasks (for example, troubleshooting sync issues), or provide your own configuration as a JSON. For example, here's an override configuration that increases logging for troubleshooting linked filters:

View File

@@ -8,7 +8,7 @@ redirect_from:
This section contains settings for your whole instance, like its URL, the reporting timezone, and toggles for disabling or enabling some of Metabase's optional features.
You can configure these settings from **Settings** > **Admin Settings** > **General**.
You can configure these settings by clicking the **grid icon** in the upper right, then going to **Admin** > **Settings** > **General**.
## Site name
@@ -44,11 +44,11 @@ This option turns determines whether or not you allow [anonymous data about your
## Friendly table and field names
By default, Metabase attempts to make field and table names more readable by changing things like `somehorriblename` to `Some Horrible Name`. This does not work well for languages other than English, or for fields that have lots of abbreviations or codes in them. If you'd like to turn this setting off, you can do so from the Admin Panel under **Settings** > **Admin settings** > **General**.
By default, Metabase attempts to make field and table names more readable by changing things like `somehorriblename` to `Some Horrible Name`. This does not work well for languages other than English, or for fields that have lots of abbreviations or codes in them. If you'd like to turn this setting off, you can do so from the Admin Panel under **Admin** **> Settings** > **General**.
If you re-enable this setting, Metabase will run a [scan](../databases/sync-scan.md#how-database-scans-work) against your database to review your table and column names again.
To manually label field or table names in Metabase, check out the [Table Metadata](../data-modeling/metadata-editing.md) section in your admin settings. Metadata in the Table Metadata can be further curated in [models](../data-modeling/models.md).
To manually label field or table names in Metabase, check out the [Table Metadata](../data-modeling/metadata-editing.md) section in your admin panel. Metadata in the Table Metadata can be further curated in [models](../data-modeling/models.md).
## Enable X-rays

View File

@@ -14,7 +14,7 @@ Both admins and people with [settings access](../permissions/application.md#sett
For Metabase to post to your Slack channels, you’ll need to create a Slack App and make it available to Metabase.
From any Metabase page, go to **Admin settings** > **Settings** > **Notification channels** > **Slack**.
From any Metabase page, go to **Admin** > **Settings** > **Notification channels** > **Slack**.
Click on **Open Slack Apps**. Metabase will open a new browser tab and send you over to the Slack website to create the Slack app.

View File

@@ -15,7 +15,7 @@ For now, webhooks are only available for [alerts](../questions/alerts.md); you c
Before you can send an alert to a webhook, an admin will need to create a webhook to target.
Admins and people with [settings access](../permissions/application.md#settings-access) can create a webhook by clicking the **gear icon** > **Admin settings** > **Notification channels**. In **Webhooks for alerts**, click **+ Add another**. Metabase will show you a short form to fill out:
Admins and people with [settings access](../permissions/application.md#settings-access) can create a webhook by clicking the **grid icon** > **Admin** > **Notification channels**. In **Webhooks for alerts**, click **+ Add another**. Metabase will show you a short form to fill out:
- **Webhook URL**. Where you want Metabase to send the results of an alert. Must be a valid URL.
- **Give it a name**. Name the webhook so that people can pick the right hook when setting up an alert on a question.

View File

@@ -1,5 +1,5 @@
---
title: Editable tables
title: Editable tables
summary: Admins can create, update, and delete data in tables right in Metabase.
---
@@ -23,8 +23,8 @@ Currently, editable tables are available for the following databases:
To set up table editing, admins can:
1. Click the **gear** icon in the top right.
2. Select **Admin settings**.
1. Click the **grid** icon in the top right.
2. Select **Admin**.
3. Click **Databases**.
4. Select your database.
5. Toggle on **Editable table data**.

View File

@@ -15,7 +15,7 @@ People all around the world use Metabase, and everyone has different preferences
## Field formatting
_Admin settings > Table Metadata > Database > Table > Field > Formatting_
_Admin > Table Metadata > Database > Table > Field > Formatting_
You can override the global defaults for a specific field by going to the `Table Metadata` section of the Admin Panel. Select the database and table of the field in question, then click scroll down to the **Formatting**.
@@ -25,7 +25,7 @@ The options you'll see here will depend on the field's data type and it's [seman
## Text formatting options
_Admin settings > Table Metadata > Database > Table > Field > Formatting_
_Admin > Table Metadata > Database > Table > Field > Formatting_
Options depend on the [semantic type](./semantic-types.md) you select for the field.
@@ -55,7 +55,7 @@ https://www.google.com/search?q=askew
## Dates and times
_Admin settings > Table Metadata > Database > Table > Field > Formatting_
_Admin > Table Metadata > Database > Table > Field > Formatting_
Options depend on the [semantic type](./semantic-types.md) you select for the field.
@@ -99,7 +99,7 @@ Choose between 12-hour and 24-hour time format:
## Numbers
_Admin settings > Table Metadata > Database > Table > Field > Formatting_
_Admin > Table Metadata > Database > Table > Field > Formatting_
Options depend on the [semantic type](./semantic-types.md) you select for the field.

View File

@@ -27,7 +27,7 @@ And here are the values as seen in the table:
This unfolding allows you to filter for values found in the original JSON object.
Metabase will prefix the unfolded column names with the name of the original column that contained the JSON. You can change the column names in **Admin settings** > [Table metadata](metadata-editing.md), or by creating a [model](./models.md) and editing the column metadata.
Metabase will prefix the unfolded column names with the name of the original column that contained the JSON. You can change the column names in **Admin** > [Table metadata](metadata-editing.md), or by creating a [model](./models.md) and editing the column metadata.
## Toggling JSON unfolding for a database
@@ -35,8 +35,8 @@ If you notice a hit to performance from this JSON unfolding, we recommend turnin
To turn off JSON unfolding for a database:
1. Click on the **Gear** in the upper right.
2. Select **Admin settings**
1. Click the **grid** icon in the upper right.
2. Select **Admin**
3. Visit the **Databases** tab.
4. Select the relevant database.
5. Click **Show advanced options**.
@@ -49,8 +49,8 @@ To turn off JSON unfolding for a database:
If performance degrades, or you'd rather keep the JSON contained in the original column, you can turn off unfolding for individual fields in their settings.
1. Click on the **Gear** in the upper right.
2. Select **Admin settings**.
1. Click the **grid** icon in the upper right.
2. Select **Admin**.
3. Visit the **Table metadata** tab.
4. Select the database that contains the field you want to update.
5. Select the table that contains the field.
@@ -67,9 +67,9 @@ For example, if you upload a CSV with JSON in it, you might need to update the d
## Databases that support JSON unfolding
- [BigQuery](../databases/connections/postgresql.md): automatically enabled, applies to `STRUCT` types only.
If your data is stored in the [STRUCT data type](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type) in BigQuery, you can query the table's nested fields. This is enabled by default. However, Metabase won't unfold JSON stored in BigQuery as the `JSON` type. This is because in BigQuery, nested fields are _part of the table definition itself_, so when Metabase syncs with your BigQuery database, it'll be able to get metadata about any of your tables, including tables with nested fields. Querying nested fields, however, doesn't extend to arrays (REPEATED (STRUCT)) in BigQuery.
- [Druid (JDBC)](../databases/connections/druid.md)
- [MongoDB](../databases/connections/mysql.md): automatically enabled for all nested fields.
- [MySQL](../databases/connections/mysql.md)

View File

@@ -6,7 +6,7 @@ redirect_from:
# Table metadata admin settings
_Admin settings > Table metadata_
_Admin > Table metadata_
![Table metadata settings](./images/table-metadata-settings.png)
@@ -18,7 +18,7 @@ Admins can grant access to these metadata settings to other groups. See [table m
## Table settings
_Admin settings > Table metadata > Database > Table_
_Admin > Table metadata > Database > Table_
You can search for tables, or use the tree navigation to find each of your connected databases and their tables.
@@ -65,7 +65,7 @@ To hide **all of the tables in a database** (say, if you've migrated to a new da
## Field settings
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
Fields are also known as Columns (see the [difference between fields and columns](https://www.metabase.com/learn/grow-your-data-skills/data-fundamentals/database-basics#columns-vs-fields)).
@@ -91,13 +91,13 @@ Click the preview button to see sample data from that field.
## Scan values for a specific field
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
To scan or discard field values for a specific field, click on the **Field values** button. Metabase uses these values to populate dropdown menus in filter widgets. (Values aren't dropped from your database.)
## Field data
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
### Field name
@@ -147,7 +147,7 @@ If Metabase doesn't support the casting option you need, you can [create a SQL q
## Field metadata
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
### Semantic type
@@ -159,7 +159,7 @@ See [semantic types](../data-modeling/semantic-types.md).
## Field behavior
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
### Field visibility
@@ -208,7 +208,7 @@ Select "Custom mapping" from the dropdown menu. For each value in the column, en
## Field formatting
_Admin settings > Table Metadata > Database > Table > Field_
_Admin > Table Metadata > Database > Table > Field_
See [Field formatting](./formatting.md#field-formatting).

View File

@@ -32,7 +32,7 @@ To persist models for faster loading, you'll need to turn on model persistence f
### Turn on model persistence for your Metabase
To turn on model persistence for your Metabase, go to **Admin settings** > **Performance** > **Model persistence**.
To turn on model persistence for your Metabase, go to **Admin** > **Performance** > **Model persistence**.
You can set models to refresh based on one of the default frequencies (every 1 hour, 2 hours, etc.), or select the **Custom** option to use [cron syntax](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html) to specify your own update frequency.
@@ -46,7 +46,7 @@ If someone [changes the query definition of a model](./models.md#edit-a-models-q
Once you've turned on model persistence for your Metabase, you'll need to set it up for each specific database, as Metabase will need to create a schema in your data warehouse to store the persisted models.
1. Go to **Admin settings** > **Databases** > [your database] > **Turn model persistence on**. If the credentials you've given Metabase to connect to your database are permissive, Metabase should do all the work for you: Metabase will check if the schema already exists, or otherwise attempt to create it. If the connection's credentials _lack_ the necessary permissions to create the schema in your database, you'll need to create the schema in the database yourself.
1. Go to **Admin** > **Databases** > [your database] > **Turn model persistence on**. If the credentials you've given Metabase to connect to your database are permissive, Metabase should do all the work for you: Metabase will check if the schema already exists, or otherwise attempt to create it. If the connection's credentials _lack_ the necessary permissions to create the schema in your database, you'll need to create the schema in the database yourself.
2. To manually create the schema in your data warehouse, click on the **info icon** to get the schema name.
@@ -68,7 +68,7 @@ To refresh a model's results, go to the model and click on the three-dot menu (*
## View model persistence logs
You can view the logs for model persistence by clicking on the **gear** icon in the upper right and selecting **Admin settings** > **Tools** > **Model caching logs**. See [Admin tools](../usage-and-performance-tools/tools.md).
You can view the logs for model persistence by clicking on the **grid** icon in the upper right and selecting **Admin** > **Tools** > **Model caching logs**. See [Admin tools](../usage-and-performance-tools/tools.md).
## Difference between persisted models and caching

View File

@@ -113,7 +113,7 @@ There are some limitations to this indexing:
- The indexed field must be a text/string type.
- The record containing the field must have an integer entity key.
- To keep your search speedy, Metabase will only index 5000 unique values from that field, so this option isn't the best choice to turn on for tables with a ton of records.
- To keep your search speedy, Metabase will only index 25,000 unique values from that field, so this option isn't the best choice to turn on for tables with a ton of records.
## Edit a model's query

View File

@@ -8,8 +8,8 @@ Metabase allows admins to create segments so people can quickly and easily refer
To manage segments:
1. Click the **gear** icon in the upper right.
2. Select **Admin setting**.
1. Click the **grid** icon in the upper right.
2. Select **Admin**.
3. Go to the **Table Metadata** tab.
4. Click **Segments** at the bottom left.

View File

@@ -95,7 +95,7 @@ You can think of semantic types as adding meaning and context to a field to comm
## Editing data and semantic types
Admins, and people with [permission to manage table metadata](../permissions/data.md#manage-table-metadata-permissions), can cast data types and edit semantic types in the Admin settings' Table Metadata tab.
Admins, and people with [permission to manage table metadata](../permissions/data.md#manage-table-metadata-permissions), can cast data types and edit semantic types in the Admin's Table Metadata tab.
### Cast data types

View File

@@ -101,7 +101,7 @@ Segments are saved filters on tables. You can use segments to create an official
People will see segments as options in the Filter block of the [query builder](../questions/query-builder/editor.md).
For now, in addition to Data Studio, segments can also be managed through **Admin settings > Table Metadata**, see [Segments in table metadata](../data-modeling/segments.md).
For now, in addition to Data Studio, segments can also be managed through **Admin > Table Metadata**, see [Segments in table metadata](../data-modeling/segments.md).
To see all segments on a table, select the table in **Data Structure** and switch to **Segments** tab.

View File

@@ -95,7 +95,7 @@ docker run -d \
### Metabase environment variables
These settings can also be configured in the Metabase UI at **Admin Settings** > **Settings** > **Python Runner** (`/admin/settings/python-runner`). Note that environment variables take precedence over UI settings.
These settings can also be configured in the Metabase UI at **Admin** > **Settings** > **Python Runner** (`/admin/settings/python-runner`). Note that environment variables take precedence over UI settings.
| Variable | Description |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |

View File

@@ -45,6 +45,44 @@ Currently, you can't convert between different transform types (like converting
6. Optionally, assign tags to your transforms. Tags are used by [jobs](jobs-and-runs.md) to run transforms on schedule.
## Variables in SQL transforms
SQL transforms support [variables](../../questions/native-editor/sql-parameters.md) (`{% raw %}{{my_variable}} {% endraw %}`), which are only useful when combined with [snippets](../../questions/native-editor/snippets.md).
For (a really simple) example, let's say you want to count rows per week across several tables. You could create a snippet called "rows per week" containing the full query with a `{% raw %}{{table}}{% endraw %}` variable:
```sql
{% raw %}
SELECT
date_trunc('week', created_at) AS week,
count(*) AS row_count
FROM {{table}}
GROUP BY week
ORDER BY week
{% endraw %}
```
That way you can use the snippet to create multiple transforms, each sourced from different tables. Each transform's entire SQL is just:
```sql
{% raw %}{{snippet: rows per week}}{% endraw %}
```
In each transform's variable panel, set the default value of `table` to the target table (e.g., `orders`, `returns`, `subscriptions`). See [table variables](../../questions/native-editor/table-variables.md).
If you ever need to change the query (say, to switch from weekly to daily granularity), you update the snippet once and every transform picks up the change on its next run.
### Transform variable must be optional, or have a default value
Parameters in transforms must either:
- Wrap the variable in optional blocks (`[[ ]]`).
- Supply a default value.
The reason transform variables must have a default value (or be optional) is that transforms run on a schedule, so there's no way to pass a value to the variable when the job runs the transform.
The incremental `{%raw%}[[WHERE id > {{checkpoint}}]]{% endraw %}` pattern shown in [Incremental query transforms](#incremental-query-transforms) is an example of this an optional variable in practice. See also [optional variables](../../questions/native-editor/optional-variables.md).
## Run a query transform
See [Run a transform](transforms-overview.md#run-a-transform). You'll see logs for a transform run on the transform's page.

View File

@@ -59,7 +59,7 @@ _Data Studio > Transforms_
You can see all your Metabase's transforms:
1. Make sure you have [appropriate permissions to see transforms](#permissions-for-transforms).
2. Click on the **grid** icon on top right and go to **Data Studio**.
2. Click the **grid** icon on top right and go to **Data Studio**.
3. In the left sidebar, select **Transforms**.
## Create a transform
@@ -71,7 +71,7 @@ If you're using remote sync, you won't be able to create transforms if your inst
To create a transform:
1. Make sure you have [appropriate permissions for creating transforms](#permissions-for-transforms).
2. Click on the **grid** icon on top right and go to **Data Studio**.
2. Click the **grid** icon on top right and go to **Data Studio**.
3. In the left sidebar, select **Transforms**.
4. Click **+ New** and select a source for your transform.
@@ -85,6 +85,8 @@ To create a transform:
See [query-based transforms](query-transforms.md) and [Python transforms](python-transforms.md) for more information. You can reference target tables of other transforms when writing your transform.
If you're writing a SQL transform, variables _must_ be wrapped in optional blocks (`[[ ]]`), or given a default value. See [variables in SQL transforms](query-transforms.md#variables-in-sql-transforms) for more details.
If you have the Metabot AI add-on, you can [use Metabot](#use-metabot-to-generate-code-for-transforms) to generate code for your transform.
6. Click **Save** in the top right corner and fill out the transform information:
@@ -191,13 +193,13 @@ Incremental transforms work differently for query-based transforms and Python tr
## Versioning transforms
_Admin settings > General > Remote sync_
_Admin > General > Remote sync_
You can check your transforms into git with [Remote Sync](../../installation-and-operation/remote-sync.md). If you enable transform sync, Metabase will serialize transforms as YAML files and push them to your specified GitHub repo branch.
To enable git sync of transforms:
1. Go to Admin settings by click on the **grid** icon in top right and select **Admin**.
1. Go to Admin settings by click the **grid** icon in top right and select **Admin**.
2. On the **General** tab, pick **Remote sync** in the left sidebar.
3. Follow the steps to [Set up Remote Sync](../../installation-and-operation/remote-sync.md), and toggle "Transforms" sync on.

View File

@@ -14,7 +14,7 @@ Connect Metabase to your data sources.
## Adding a database connection
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
Fill out the fields for that database, and click **Save changes** at the bottom.
@@ -65,11 +65,11 @@ See [Syncing and scanning](./sync-scan.md).
**Caution: Deleting a database is irreversible! All saved questions and dashboard cards based on the database will be deleted as well!**
Go to **Admin settings** > **Databases** > your database and click **Remove this database**.
Go to **Admin** > **Databases** > your database and click **Remove this database**.
## Restoring the Sample Database
If you've deleted the Metabase [Sample Database](https://www.metabase.com/glossary/sample-database), go to **Admin settings** > **Databases** and click **Bring the Sample Database back**.
If you've deleted the Metabase [Sample Database](https://www.metabase.com/glossary/sample-database), go to **Admin** > **Databases** and click **Bring the Sample Database back**.
## Troubleshooting

View File

@@ -4,7 +4,7 @@ title: Amazon Athena
# Amazon Athena
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Connection and sync
@@ -209,6 +209,12 @@ If Metabase also needs to create tables, you'll need additional AWS Glue permiss
}
```
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features available for Athena.

View File

@@ -6,7 +6,7 @@ redirect_from:
# Google BigQuery
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Prerequisites
@@ -161,6 +161,12 @@ FROM [my_dataset.my_table]
If you're having trouble with your BigQuery connection, you can check out this [troubleshooting guide](../../troubleshooting-guide/bigquery-drive.md) that covers BigQuery issues, [this one](../../troubleshooting-guide/db-connection.md) on data warehouse connections, or visit [Metabase's discussion forum](https://discourse.metabase.com/search?q=bigquery) to see if someone has encountered and resolved a similar issue.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features available for BigQuery.

View File

@@ -5,7 +5,7 @@ description: Learn how to connect Metabase to your ClickHouse database, includin
# ClickHouse
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
You can edit these settings at any time. Just remember to save your changes.
@@ -101,6 +101,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features for ClickHouse.

View File

@@ -4,7 +4,7 @@ title: Databricks
# Databricks
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**. Then select **Databricks**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**. Then select **Databricks**.
You can edit these settings at any time. Just remember to save your changes.
@@ -101,6 +101,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features available for Databricks.

View File

@@ -6,7 +6,7 @@ title: Druid
> Prefer the Druid JDBC connection.
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Edit connection details
@@ -64,6 +64,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features available for Druid.

View File

@@ -6,7 +6,7 @@ title: MariaDB
> This page covers connecting to MariaDB as a _data warehouse_. For using MariaDB as Metabase's _application database_, see [Configuring the Metabase application database](../../installation-and-operation/configuring-application-database.md).
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
MariaDB shares a driver with MySQL, so select the **MySQL** driver.
@@ -101,6 +101,12 @@ JSON schema inference doesn't work with MariaDB, due to implementation differenc
If your password contains characters that aren't UTF-8, then you might need to add an additional variable to the connection string `passwordCharacterEncoding=<your_encoding_here>`. This ensures that MariaDB understands the special characters in the password during authentication.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Database routing
With database routing, an admin can build a question once using one database, and the question will run its query against a different database with the same schema depending on who is viewing the question.

View File

@@ -6,7 +6,7 @@ redirect_from:
# MongoDB
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Supported versions
@@ -130,6 +130,12 @@ Instead, Metabase gets a sample of the fields in a collection by scanning a samp
If you're not seeing all of the fields show up for a collection in Metabase, one workaround is to include all possible keys in the first document of the collection, and give those keys null values. That way, Metabase will be able to recognize the correct schema for the entire collection.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Database routing
With database routing, an admin can build a question once using one database, and the question will run its query against a different database with the same schema depending on who is viewing the question.

View File

@@ -8,7 +8,7 @@ redirect_from:
> This page covers connecting to MySQL as a _data warehouse_. For using MySQL as Metabase's _application database_, see [Configuring the Metabase application database](../../installation-and-operation/configuring-application-database.md).
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Supported versions
@@ -187,6 +187,12 @@ mysql:
If your password contains characters that aren't UTF-8, then you might need to add an additional variable to the connection string `passwordCharacterEncoding=<your_encoding_here>`. This ensures that MySQL understands the special characters in the password during authentication.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
Choose whether to enable features related to [Metabase models](../../data-modeling/models.md). These features will often require that the database user account, the one you use to connect to your database, has both read and write privileges.

View File

@@ -6,7 +6,7 @@ redirect_from:
# Oracle
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Supported versions
@@ -131,6 +131,12 @@ For example, if you're running Metabase from a directory called `/app/`, you sho
The process for adding plugins when running via Docker is similar, but you'll need to mount the `plugins` directory. Refer to instructions [here](../../installation-and-operation/running-metabase-on-docker.md#adding-external-dependencies-or-plugins) for more details.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Danger zone
See [Danger zone](../danger-zone.md).

View File

@@ -8,7 +8,7 @@ redirect_from:
> This page covers connecting to PostgreSQL as a _data warehouse_. For using PostgreSQL as Metabase's _application database_, see [Configuring the Metabase application database](../../installation-and-operation/configuring-application-database.md).
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
Fill out the fields for that database, and click **Save changes** at the bottom.
@@ -186,6 +186,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
Choose whether to enable features related to [Metabase models](../../data-modeling/models.md). These features will often require that the database user account, the one you use to connect to your database, has both read and write privileges.

View File

@@ -4,7 +4,7 @@ title: Presto
# Presto
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
Fill out the fields for that database, and click **Save changes** at the bottom.
@@ -94,6 +94,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Database routing
With database routing, an admin can build a question once using one database (data catalog), and the question will run its query against a different data catalog with the same schema depending on who is viewing the question.

View File

@@ -7,7 +7,7 @@ redirect_from:
# Amazon Redshift
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Connection and sync
@@ -98,6 +98,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
Choose whether to enable features related to Metabase models. These will often require a write connection.

View File

@@ -6,7 +6,7 @@ redirect_from:
# Snowflake
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
You can edit these settings at any time. Just remember to save your changes.
@@ -14,17 +14,17 @@ You can edit these settings at any time. Just remember to save your changes.
Paste a connection string here to pre-fill the remaining fields below.
## Display name
### Display name
The display name for the database in the Metabase interface.
## Account or hostname
### Account or hostname
You have the option of using an account name or hostname. Your hostname is your database's IP address or its domain name (e.g., database.example.com).
If your URL includes your database's region, you can connect using an account name, otherwise connect using a hostname.
### AWS
#### AWS
Connect with an account name.
@@ -37,7 +37,7 @@ You'd enter `az12345.ca-central-1.aws` as the account name in Metabase.
Not all regions require the cloud platform identifier. If you're in `us-west-2`, you would enter `az12345` as the account name. For the requirements per region, see [the official Snowflake's documentation](https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#non-vps-account-locator-formats-by-cloud-platform-and-region).
### app.snowflake.com
#### app.snowflake.com
Connect with a host name.
@@ -47,31 +47,31 @@ You can get your host name by going to Admin > Accounts. The Account name will b
Learn more about [Account Identifiers in Snowflake](https://docs.snowflake.com/en/user-guide/admin-account-identifier).
## Username
### Username
The database username for the account that you want to use to connect to your database. You can set up multiple connections to the same database using different user accounts to connect to the same database, each with different sets of [privileges](../users-roles-privileges.md).
On your app.snowflake.com account page, you can find Users and roles under Admin > Users & Roles.
## Password
### Password
The password for the username that you use to connect to the database.
## RSA private key (PEM)
### RSA private key (PEM)
Not required. You have the option of using a **Local file path** or an **Uploaded file path**. If you're on Metabase Cloud, you'll need to select **Uploaded file path** and upload your certificate.
## Warehouse
### Warehouse
Snowflake warehouse. If the user lacks a default warehouse, you'll need to enter the warehouse to connect to.
On app.snowflake.com, you can find warehouses under Admin > Warehouses.
## Database name (case sensitive)
### Database name (case sensitive)
The name of the database you want to connect to in Snowflake. On app.snowflake.com you can find databases under Data > Databases.
## Schemas (optional)
### Schemas (optional)
You can specify which schemas you want to sync and scan. If no schema is passed, then all schema available to that user and role will be listed as folders in Metabase.
@@ -96,7 +96,7 @@ Let's say you have three schemas: FOO, BAR, and BAZ.
Note that only the `*` wildcard is supported; you can't use other special characters or regexes.
## Role (optional)
### Role (optional)
> **WARNING:** Metabase gets _all the permissions_ combined from _every_ role you grant the user in Snowflake. Setting the 'Role' here just adds the role to the connection string to tell Snowflake which role to default to – it **won't restrict** access on its own. So **be careful which roles you assign to the connection user**: if down the line you grant that user broader roles in Snowflake, Metabase will automatically gets those new permissions, potentially showing data you didn't mean for it to see. If instead you want Metabase to issue `SET ROLE` commands before querying anything, check out [connection impersonation](../../permissions/impersonation.md).
@@ -107,25 +107,25 @@ Specify a role to override the database user's default role. For example, if the
You can enter `ANALYTICS` in the Role field to ensure that the `METABASE` user connects to Snowflake using the `ANALYTICS` role by default.
## Use an SSH tunnel
### Use an SSH tunnel
See our [guide to SSH tunneling](../ssh-tunnel.md).
## Additional JDBC connection string options
### Additional JDBC connection string options
Some databases allow you to append options to the connection string that Metabase uses to connect to your database.
## Re-run queries for simple explorations
### Re-run queries for simple explorations
Turn this option **OFF** if people want to click **Run** (the play button) before applying any [Summarize](../../questions/query-builder/summarizing-and-grouping.md) or filter selections.
By default, Metabase will execute a query as soon as you choose an grouping option from the **Summarize** menu or a filter condition from the [drill-through menu](https://www.metabase.com/learn/metabase-basics/querying-and-dashboards/questions/drill-through). If your database is slow, you may want to disable re-running to avoid loading data on each click.
## Choose when syncs and scans happen
### Choose when syncs and scans happen
See [syncs and scans](../sync-scan.md#choose-when-syncs-and-scans-happen).
### Periodically refingerprint tables
#### Periodically refingerprint tables
> Periodic refingerprinting will increase the load on your database.
@@ -133,6 +133,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features available for Snowflake.

View File

@@ -4,7 +4,7 @@ title: SparkSQL
# SparkSQL
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Settings
@@ -77,6 +77,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Danger zone
See [Danger zone](../danger-zone.md).

View File

@@ -4,7 +4,7 @@ title: SQL Server
# SQL Server
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
## Supported versions
@@ -97,6 +97,12 @@ A fingerprinting query examines the first 10,000 rows from each column and uses
To connect to Azure SQL, you'll need to set the port to 1433.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Database routing
With database routing, an admin can build a question once using one database, and the question will run its query against a different database with the same schema depending on who is viewing the question.

View File

@@ -6,7 +6,7 @@ title: SQLite
> SQLite isn't available for [Metabase Cloud](https://www.metabase.com/cloud/).
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
Fill out the fields for that database, and click **Save changes** at the bottom.
@@ -61,6 +61,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Database routing
With database routing, an admin can build a question once using one database, and the question will run its query against a different database with the same schema depending on who is viewing the question.

View File

@@ -7,7 +7,7 @@ description: Learn how to connect Metabase to your Starburst or Trino database,
> This driver also works for connections to a Trino database.
To add a database connection, click on the **gear** icon in the top right, and navigate to **Admin settings** > **Databases** > **Add a database**.
To add a database connection, click the **grid** icon in the top right, and navigate to **Admin** > **Databases** > **Add a database**.
You can edit these settings at any time. Just remember to save your changes.
@@ -91,6 +91,12 @@ Turn this option **ON** to scan a sample of values every time Metabase runs a [s
A fingerprinting query examines the first 10,000 rows from each column and uses that data to guesstimate how many unique values each column has, what the minimum and maximum values are for numeric and timestamp columns, and so on. If you leave this option **OFF**, Metabase will only fingerprint your columns once during setup.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features for Starburst.

View File

@@ -41,6 +41,12 @@ For example, if you're running Metabase from a directory called `/app/`, you sho
The process for adding plugins when running via Docker is similar, but you'll need to mount the `plugins` directory. Refer to instructions [here](../../installation-and-operation/running-metabase-on-docker.md#adding-external-dependencies-or-plugins) for more details.
## Writeable connection
{% include plans-blockquote.html feature="Writeable connection" is_plural=true%}
Set up an additional connection used for write operations. See [Writeable connections](../writeable-connection.md).
## Model features
There aren't (yet) any model features for Vertica.

View File

@@ -11,8 +11,8 @@ But since of course you're making [backups of your Metabase application database
To access the Danger Zone section for a database connection:
1. Click on the **gear** icon in the top right of Metabase.
2. Go to **Admin settings** > **Databases**.
1. Click the **grid** icon in the top right of Metabase.
2. Go to **Admin** > **Databases**.
3. Find the database you want to modify and click on it.
4. Scroll down to the bottom of the database's settings page to find the "Danger Zone" section.

View File

@@ -33,7 +33,7 @@ Once you set the `MB_ENCRYPTION_SECRET_KEY` value, Metabase will automatically e
## Encrypting an existing connection
If you added databases before setting the `MB_ENCRYPTION_SECRET_KEY` value, you can encrypt the connection details by going to each one of those databases in **Admin settings** > **Databases** and clicking on the **Save** button. Existing databases with unencrypted details will continue to work normally.
If you added databases before setting the `MB_ENCRYPTION_SECRET_KEY` value, you can encrypt the connection details by going to each one of those databases in **Admin** > **Databases** and clicking on the **Save** button. Existing databases with unencrypted details will continue to work normally.
## Rotating an encryption key

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -15,16 +15,16 @@ Metabase periodically runs different types of queries on your data warehouse to
When Metabase first connects to your database, it performs a [sync](#how-database-syncs-work) to determine the metadata of the columns in your tables and automatically assign each column a [semantic type](../data-modeling/semantic-types.md).
You can follow the progress of these queries from **Gear icon** >**Admin settings** > **Tools** > **Tasks** and filtering by the various sync tasks.
You can follow the progress of these queries from **grid icon** >**Admin** > **Tools** > **Tasks** and filtering by the various sync tasks.
Once the queries are done running, you can view and edit the synced metadata from **Admin settings** > **Table Metadata**. For more info, see [editing metadata](../data-modeling/metadata-editing.md).
Once the queries are done running, you can view and edit the synced metadata from **Admin** > **Table Metadata**. For more info, see [editing metadata](../data-modeling/metadata-editing.md).
## Choose when syncs and scans happen
By default, Metabase does a lightweight hourly sync and an intensive daily scan of field values. If you have a large database, you might want to choose when syncs and scans happen.
1. Click on the **Gear icon**.
2. Select **Admin settings**.
1. Click on the **grid icon**.
2. Select **Admin**.
3. Go to **Databases**.
4. Select your database.
5. In the **Connection and sync** section, click on **Edit connection details**.
@@ -54,21 +54,21 @@ Regardless of which option you pick, if you [set a field to use a dropdown list
## Manually syncing tables and columns
1. Go to **Admin settings** > **Databases** > your database.
1. Go to **Admin** > **Databases** > your database.
2. Click **Sync database schema**.
## Manually scanning column values
To scan values from all the columns in a table:
1. Go to **Admin settings** > **Table Metadata** > your database.
1. Go to **Admin** > **Table Metadata** > your database.
2. Select the table that you want to bring up to date with your database.
3. Click the **gear icon** at the top of the page.
4. Click **Re-scan this table**.
To scan values from a specific column:
1. Go to **Admin settings** > **Table Metadata** > your database.
1. Go to **Admin** > **Table Metadata** > your database.
2. Select the table.
3. Find the column you want to bring up to date with your database.
4. Click the **gear icon** in the panel for that column.
@@ -78,7 +78,7 @@ To scan values from a specific column:
To clear the [scanned field values for a table](#syncing-and-scanning-databases):
1. Go to **Admin settings** > **Table Metadata**.
1. Go to **Admin** > **Table Metadata**.
2. Select the database and table.
3. Click the **gear icon** in the upper right.
4. Click **Discard cached field values**.
@@ -89,7 +89,7 @@ You can also tell Metabase to forget the cached values for individual fields by
To prevent Metabase from running syncs and scans against a specific table, change the [table visibility](../data-modeling/metadata-editing.md#table-visibility) to **Hidden**:
1. Go to **Admin settings** > **Table Metadata** > your database.
1. Go to **Admin** > **Table Metadata** > your database.
2. Hover over the table name in the sidebar.
3. Click the **eye** icon.

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