Compare commits

...

245 Commits

Author SHA1 Message Date
Romeo Van Snick
8c72149945 Rename docker-compose.yml to compose.yml for better compatibility with python-runner repo 2025-09-29 21:08:08 +10:00
Romeo Van Snick
350e69e4e9 Add RUN_MODE to python-runner 2025-09-29 21:08:08 +10:00
Romeo Van Snick
ad9e3af2cd Disable limits in local dev 2025-09-29 21:08:08 +10:00
Romeo Van Snick
9bc2f603e2 Remove python runner dir in CI 2025-09-29 21:08:08 +10:00
Romeo Van Snick
b9e1f5774d Set up the python runner bucket 2025-09-29 21:08:07 +10:00
Romeo Van Snick
0e93fb8dbc Add missing setting 2025-09-29 21:08:07 +10:00
Romeo Van Snick
92e9305d20 Import python runner in Cypress docker compose file 2025-09-29 21:08:07 +10:00
Romeo Van Snick
df4a0869b5 Add docker-compose file for python-runner 2025-09-29 21:08:07 +10:00
Kamil Mielnik
581c1e7a38 WRK-871 Details view: JSON formatting missing in details view (#63892)
* Format JSON in new detail view

* Use mantine props

* Use Code

* Use CodeEditor
2025-09-29 10:31:59 +00:00
Romeo Van Snick
17a61a81c8 Add missing import (#64171) 2025-09-29 11:17:26 +02:00
Romeo Van Snick
1d699a5fae Use the entities display name if possible (#64076) 2025-09-29 13:32:11 +10:00
Ngoc Khuat
277d1fdaff Skip sync transforms temp table (#64037) 2025-09-28 15:00:43 +00:00
Oleksandr Yakushev
82b3ba1fcc [perf] Optimize metabase.lib.field.resolution/merge-metadata (#62425) 2025-09-28 13:37:56 +03:00
Dan Stone
dd7845cc83 Improve python error feedback capability (#64053)
* Add i18n / formatting to existing log calls

* Cleanup log conveyance to failure :message

* Improve error feedback capability

- Ensure exception messages do not leak to user logs by default 'Something went wrong'
- Exceptions can carry a `transform-message` key to provide a custom failure message (with i18n)
- Defined messaging for:
   - Error during table copy
   - Error during execution
   - Error during writeback

I would like to follow up with additional context in the form of message log entries for further progress / context of any failure.

Logs can be correlated to metabase logs for stack traces or further debugging. Python output remains presented as-is (traceback etc).
2025-09-27 15:31:57 +00:00
Edward Paget
7e7ed1ac9f fix(scim): support patch requests without a path (#64130)
adheres to the standard and accomodates how we can get these requests
from Okta.
2025-09-26 16:18:45 -05:00
Jeff Bruemmer
19387d8d29 add transforms option (#64128) 2025-09-26 16:45:46 -04:00
Alex Yarosh
7ac8feb49b docs: embedding hub changes (#64012)
* embedding hub changes

* more edits

* Apply suggestions from code review

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

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-26 20:04:08 +00:00
Alex Yarosh
4447e2c6bc expand embedding comparison table (#64071) 2025-09-26 15:47:01 -04:00
Oleksandr Yakushev
8434b23764 [match] Prevent match-lite from extracting common result that depends on uncommon bindings (#64073)
* [match] Prevent match-lite from extracting common result that depends on uncommon bindings

* [match] Fail if match-lite's :guard is provided with incorrect predicate syntax

Also fix its treatment of named functions passed as predicates.
2025-09-26 22:39:26 +03:00
Jeff Bruemmer
be72407fc8 docs - RLS notes (#64111) 2025-09-26 19:34:07 +00:00
John Swanson
e530911305 Correctly render empty series in static viz (#63987)
* Correctly render empty series in static viz

[UXW-1754: Row chart with an empty series fails to send on static viz](https://linear.app/metabase/issue/UXW-1754/row-chart-with-an-empty-series-fails-to-send-on-static-viz)

`getColumnValueFormatter` and `getColumnValueStaticFormatter` had
different behaviors for empty strings, so this worked on the frontend
but failed for static visualizations.

* cleanup

---------

Co-authored-by: Aleksandr Lesnenko <alxnddr@gmail.com>
2025-09-26 11:49:59 -07:00
Noah Moss
ca9ba3390d make u/profile log lines look nicer (#64114) 2025-09-26 18:01:53 +00:00
Oleksandr Solovyov
a947f3cb94 [advanced-config] uppercased letter in users email should not fail (#64109)
fixes #62894
2025-09-26 17:20:39 +00:00
Dan Stone
33de954fcf Temporarily disable python transforms tests for BQ/Snowflake (#64118) 2025-09-26 17:15:34 +00:00
Kamil Mielnik
db79c64d3d DSN-73 Verified badge icon should be correct size and nearer text in model and saved question titles (#63871)
* Fix verified badge styling

* Misalign icons :see-no-evil:

* Remove min width on dashboard caption input

* Fix overflow
2025-09-26 16:14:41 +00:00
Jakub Chodorowicz
3e646a69f3 Move dev instances upsell down on embedding settings pages (#64099) 2025-09-26 17:57:27 +02:00
Sébastien
d6c15aab01 Closes EMB-853 renamed legends get ignored by translation (#64023)
* Closes EMB-853 renmaed legends get ignored by translation

* feedback

* if -> ts-pattern
2025-09-26 16:04:57 +02:00
Jeff Bruemmer
34c224b5f2 docs - verified content (#64067) 2025-09-26 13:28:33 +00:00
Sébastien
568443a99c Closes EMB-407 transparent background download pdf (#63836)
* Closes EMB-407 transparent background download pdf

* e2e

* cleanup
2025-09-26 15:18:24 +02:00
github-automation-metabase
5fe1efe15b Update generated docs (master) (#64078)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-26 06:46:56 -04:00
Alexander Polyankin
894176cf26 Add missing transform filters (#63998)
* Params

* Params

* Params

* Params

* Params

* Params

* Rename param

* Transform params

* Transform params

* Transform params

* Transform params

* Transform params

* Transform params

* Transform params

* Transform params

* fixes

* add a test

* add a test

* add a test

* add a test

* add a test

* Fix field filters for type/TextLike

* Fix polling
2025-09-26 06:39:11 -04:00
lbrdnk
c28f66c961 Add guards to metabot and semantic search job implementations (#64031)
* Fix arity

* Add guards for job implementations

* Adjust test
2025-09-26 10:36:00 +02:00
Kamil Mielnik
85e6e7f779 DSN-74 People page should use tabs instead of radios for active/deactivated control (#63817)
* Inline AdminPaneLayout

* Do not show invite button on deactivated tab

* Simplify

* Use modern button

* Add tabs in People page

* Move mb

* Fix typo

* Make people search input full width

* Move tabs to the top
2025-09-26 15:19:24 +07:00
Kamil Mielnik
d44a2cab66 DSN-67 We could improve the "Scanning for Filter Values" setting (#63887)
* Update scanning schedule form to use new UI components

* Update tests
2025-09-26 15:18:35 +07:00
Brad Anderson
e349faa741 Fix: #60843 Prevents dashboard from being saved when clicking "Discard changes" (#63899)
* UXW-333 Prevents `updateDashboardAndCards` from being called when discarding changes (still called when clicking "Save changes")

* UXW-333 Adds e2e test
2025-09-25 17:40:54 -04:00
Aleksandr Lesnenko
817af7b71d pass lat and lon fields to the tile endpoints as query params (#63533)
* for fetching tiles encode field refs as safe base64

* clojure implementation

* update public api

* pass lat and lon fields in query params

* specs

* fix specs

* update preview endpoints and tests

* clean up previous url safe base64 implementation

* fix specs
2025-09-25 21:11:26 +00:00
Jeff Bruemmer
e8cd58b376 Revise contributor instructions in PR template (#64059)
Updated warning section for first-time contributors.
2025-09-25 20:07:31 +00:00
Brad Anderson
62d7beec2b Fix: #63463 Loosens condition that forces saving to dashboard (#63855)
* UXW-709 When drilling into a dashboard card that had a filter applied, the resulting query can now be saved outside of the original dashboard. "New Question"/"New SQL query" via dashboard edit screen behavior is unaffected (still forces you to save to that dashboard).

* UXW-709 Updates unit tests
2025-09-25 15:55:45 -04:00
Brad Anderson
c2ec56beed UXW-172 Fix is-neg condition (#63905) 2025-09-25 15:54:48 -04:00
Nathan Voxland
5274867997 Log system signals for better debugging (#63780) 2025-09-25 13:56:48 -05:00
Anderson
b5a9d7457f Have a separate email content for user joining through setup invitation (#63778)
* Allow redirect url from password reset page

* Add await when inviting user so that errors will be displayed

* Allow for different email content when inviting user from setup / Set returnUrl param

* Code refinements

* Add backend tests

* Add frontend tests

* Run formatter

* handle email content when invitor's first name is empty/unset
2025-09-25 15:36:08 -03:00
Radoš Milićev
12f4387864 Fix links in CONTRIBUTING.md (#63653) 2025-09-25 14:31:54 -04:00
Jakub Chodorowicz
6b5e3bd646 Prg 154 allow running dev env on a custom port (#63875)
* Allow running dev on custom MB_FRONTEND_DEV_PORT

Signed-off-by: Jakub Chodorowicz <jakub@chodorowicz.com>

* Unify port env var naming between JS and Clojure

* Add dev server port info

* Update devenv docs

Co-authored-by: Sébastien <sebastien@metabase.com>

* update docs

---------

Signed-off-by: Jakub Chodorowicz <jakub@chodorowicz.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2025-09-25 20:18:33 +02:00
José H.
b7c4063dd9 [docs] Add SPARQL driver in community drivers list (#61994) 2025-09-25 13:57:36 -04:00
Anton
49fcc4a40f Fixed misspell in timezones.md (#64041) 2025-09-25 13:52:24 -04:00
Denis Berezin
7fb62b782b Document Layout - add charts side-to-side (#63101)
* Add working table with 2 columns

* Table layout and dnd polishing

* ResizeNode

* Refactor to use grid container

* Add Card node title ellipsification

* cleanup, add min height

* Fix FlexContainer to work with ResizeNode

* Fix empty ResizeNode after card dnd

* Document layout no dnd kit (#63363)

* using tiptap drag and drop

* Fix some issues with dnd

* Add support for resize node, fix styles

* Remove comments

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>

* Fixes after merge comments

* Fix issues with dnd logic

* Fix issue where rearranging columns within a flexContainer didn't respect which side of a cardEmbed we dropped; it always inserted dragged column to the left.

* Fix issue where moving a cardEmbed out of a flexContainer and placing it right after doesn't wrap the card in a resizer

* Fix issues with dropzones and card id

* Fix issue with column resize handlers

* Fix issues with cards previews on dnd

* Feat: UXW-717 Reconcile comments with document layout (#63593)

* UXW-717 Add comment option to cardEmbed menu
* UXW-717 Wires up comment option in cardEmbed menu
* UXW-717 Stubs out CommentsMenu reorganization so logic can be reused inside cardEmbeds (button and menu item)
* UXW-717 Design tweaks per design feedback (and fixes ts error)
* UXW-717 Updates hover state of cardEmbed ellipsis menu trigger per design feedback

* Fix issues with funnel chart and resizer

* Fix issues with column rearrange

* Fix adding third column position

* UXW-739 Adds ResizeNode wrapper to charts added by metabot

* UXW-749 [1/2] Fix: Cutting/copying a chart from a group then pasting it (cmd+v) results in a chart without a resizeNode

* UXW-749 [2/2] Fix: Copying and pasting from one chart to another doesn't preserve the structure correctly

* Refactor dnd logic - consolidate all use cases in HandleEditorDrop

* remove redundant message and add_message icons

* actually remove redundant message icons

* replace references to old icons with new icons

* Fix issue with dropping a card into existing flexContainer

* Fix issue with moving a card from one flexContainer into another

* UXW-748 - Fix broken document when typing with a card selected

* Handle dropping cards onto paragraphs (#63754)

* Handle dropping cards onto paragraphs

* PR Feedback

* cleanup flex containers

---------

Co-authored-by: Denis Berezin <denis.berezin@metabase.com>

* small feedbacks

* breaking up document drop handler

* UXW-689 - add basic e2e tests

* type fix

* loki diff

* Add more e2e tests

* Fix typecheck

* happy types

* passing document.cy.spec

* Fix e2e tests

* e2e test for card resizing

* flex container column resize tests

* Add node deletion e2e tests

* Fix review issues

* Try to fix typecheck

* Refactor useCommentsButtonProps to a react component

* final PR feedback

* fix removing a card via menu

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
Co-authored-by: Brad Anderson <brad@metabase.com>
Co-authored-by: Maz Ameli <maz@metabase.com>
Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2025-09-25 21:08:49 +04:00
Phoomparin Mano
f393237eb9 Adjust link and action button layout for modular embedding cards (#63876) 2025-09-25 23:00:29 +07:00
Alexander Polyankin
48376d3f63 Fix field filters for type/TextLike (#64045) 2025-09-25 15:47:16 +00:00
github-automation-metabase
a67c48a036 Update generated docs (master) (#64017)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-25 10:29:52 -04:00
Alex Yarosh
8c3d77473a docs: renaming sheets columns (#64011) 2025-09-25 10:29:07 -04:00
Kamil Mielnik
9f029dbb2f WRK-900 [Bug] Detail View: related records are messed up in tables with no PK (#63977)
* Add repro for WRK-900

* Hide relationships when there's no PK
2025-09-25 21:07:38 +07:00
Kamil Mielnik
1adaaae66e WRK-899 Detail view: keep fields used as titles & sub-titles in the regular list of fields (#63889)
* Include header columns in the body of detail view

* Update tests
2025-09-25 21:07:07 +07:00
Oleksandr Solovyov
21956d6ab7 [serdes] NativeQuerySnippet collection_id should not prevent export (#63870)
Previously `extract-query "NativeQuerySnippet"` would use standard method for exporting entities, which checks for
collection_id in entity and prevents its export if it's outside requested collections.

While that makes sense for Cards (and we do escape analysis for those), snippets live in their own separate collection
namespace and this logic is not relevant to them. So now we export either those in requested collections _or_
dependencies of cards that are being exported.
2025-09-25 17:03:23 +03:00
Sébastien
fd94a0b0cb feat(sdk-embed): adds onVisualizationChange to InteractiveDashboard and InteractiveQuestion (#63969)
* Closes EMB-152 add onVisualizationChange callback to SDK embedding components

* .

* .

* .

* fixed schema

* height

* .
2025-09-25 15:59:28 +02:00
Nicola Mometto
7b6fbd6374 Python Transforms (#63700)
* cherry pick driver code (#63628)

* cherry pick driver code

* update changelog

* add type->database-type unit tests

* kondo

* create/insert table

* insert-from-jsonl test

* missing rename-tables for snowflake

* extend rename-tables-test to more drivers

* line breaks

* add driver python transform feature

* handle review comments

* kondo

* feat: replace string->val with a more generic insert-col->val

* remove non transactional impls

* chore: docsstrings

* TODO

* extra test

* fix extra test

* test all base-types

* fix sqls

* setup ci properly (#63693)

* S3 transport layer for python-runner (#63697)

* Add s3 dependencies (#63706)

* port prometheus/instrumentation (#63631)

* port prometheus metrics

* port instrumentation

* instrument existing transform paths

* wider buckets

* remove nil handling

* warn on nil bytes/rows

* add stage-type mapping

* typo

* split CI into separate jobs (#63746)

* Add python_runner.clj (#63747)

* Add python runner

* Kondo

* Traceback fix

* Remove dead code

* start the runner by default

* Cancellation not used yet

* Remove unused keys from fields-metadata

* cherry pick driver fixes

* start for postgres and mysql as well

* clickhouse fixes

* relax for snowflake

* typo

* exclude transform-python-test from appdb

* exclude transforms-python-test as well

* try exclude-tag here too

---------

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>

* execute.clj (#63755)

* execute.clj

* with drivers test and execute test

* with wait tables

* missing utils

* /s/open/start

* fix failing tests due to getting the wrong source and target

* kondo

* move cancel-python-code-http-call to python-runner

* Dan's feedback

* we have 2 implementstation of cancel

* and 2 db-routing-enabled?

---------

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>

* Transforms python APIs (#63762)

* transforms python api

* new tests pass locally

* clean up the db id utils

* move to util

* fix: missing refer

* chore: missing tests/kondo

* kondo

* more kondo

* fix: make function resilient

* eh, validate on old

* on new

* Add python transform support for jobs (#63764)

* Add python transforms job support

* Fix whitespace in when

* Avoid churn due to new local

* Add feature test

* Add ignore for logs (there might be away to avoid this)

* Use new target-database-id util

* Remove duplicated fn

---------

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
Co-authored-by: Dan Stone <wotbrew@gmail.com>

* Move transform ordering tests to the python module (#63827)

* Add transform ordering tests to the python module

- Consistent with api tests for python transforms being in the python
module
- In practice transforms module 'knows' about python, so this is
organizational only
- Copied test helpers for now, not sure if functions strong enough to be
shared

* Check :transforms/python

* Proj python/table name len (#63842)

* feat: ensure temp-table-name respects max length

* add unit test

* add limit for missing drivers

* depend on it

* fix: use db instead of db-id

* Proj ptyhon/table swap (#63878)

* add rename feature, rename-table! multimethod

* add table swap with atomic or rename or fallback

* changelog

* drop requirement

* ensure we're testing replace flow

* wait for completion, table already exists

* wip

* add rename-table-test

* fix for snowflake

* fix: support bigints (#63898)

* fix: support bigints

* merge meta

* Frontend for Python Transforms (#63699)

* cherry pick driver code (#63628)

* cherry pick driver code

* update changelog

* add type->database-type unit tests

* kondo

* create/insert table

* insert-from-jsonl test

* missing rename-tables for snowflake

* extend rename-tables-test to more drivers

* line breaks

* add driver python transform feature

* handle review comments

* kondo

* feat: replace string->val with a more generic insert-col->val

* remove non transactional impls

* chore: docsstrings

* TODO

* extra test

* fix extra test

* test all base-types

* fix sqls

* setup ci properly (#63693)

* feat: add Python transform frontend pages

* Integerate with FE

* Fix wrong source field name used for showing script in transform view

* Revert "Fix wrong source field name used for showing script in transform view"

This reverts commit c7dfb541f3.

* Fix edit button not doing anything for python scripts

Closes QUE-2300

* call api proxy instead of internal call, also have settings page for transform

* python transforms now properly being scheduled

* FE select table when creating python transform

* multiple tables

* ensure tables are unique in tables selector

* source alignments

* run test

* dead code

* Better default schema, better docstring

* fixex test run

* add cancel button

* better endpoint name

* add cancel button to test python endpoint

* working button

* Auto-update Python transform function signature when table aliases change

- Automatically updates def transform(...) signature when aliases are modified
- Updates docstring Args section with aligned descriptions and table names
- Adds reset button to restore default slugified aliases
- Validates alias input to only allow valid Python identifiers
- Handles multi-line signatures for many parameters (>80 chars)
- Shows visual distinction between custom and auto-generated aliases
- Fixes code editor layout to prevent overlap with Run button

* feat: settings page with s3 configs

* feat: split settings

* remove placeholders

* eh, rename to appease the munging

* Logs are viewable inline during execution of python transforms

* Some minor fixes around docstring rewriting

* solve more fe lints

* fix type errors

* more type check fixes - including some smelly optional override stuff

* fix delete transform + table

* fix: don't remove indentation before """

* chore: allow empty schemas

* Critical fix: docstring indentation

* Separate file / s3 lifetimes / writes / reads from http calls (#63294)

- python-runner no longer simulates a single http api that does
everything (incl s3 interaction)
- Separates file lifetimes (shared storage) from execution
- Easier to see how to avoid reading into strings (e.g. for piping into
tmp files, follow up)

Earlier on we had a service that managed much of the I/O, metabase now
coordinates it so it makes little sense pretending there is still
an overall http api.
# Conflicts:
#	enterprise/backend/src/metabase_enterprise/transforms/api.clj
#	enterprise/backend/src/metabase_enterprise/transforms/execute.clj
#	enterprise/backend/src/metabase_enterprise/transforms/python_runner.clj
#	enterprise/backend/test/metabase_enterprise/transforms/python_runner_test.clj

* Add first-class PythonTransformSource type

# Conflicts:
#	enterprise/frontend/src/metabase-enterprise/transforms/components/QueryEditor/EditorBody/EditorBody.tsx

* Lay python editor out in two columns

* Make python editor resizable

* Move parseCSV to utils

* Split off results into separate component

* Add empty and loading state components

* Split up ExecutionResult component

* Move python execution to separate hook

* Remove color in editor gutter

* Increase indentUnit for python

* Allow extension to be passed to CodeEditor

* Add pandas completions

* Use DataGrid for displaying results

* Python library APIs (#63378)

Co-authored-by: Romeo Van Snick <romeo@romeovansnick.be>
# Conflicts:
#	enterprise/backend/src/metabase_enterprise/api/routes.clj
#	enterprise/backend/src/metabase_enterprise/transforms/python_runner.clj
#	enterprise/backend/test/metabase_enterprise/transforms/python_runner_test.clj
#	enterprise/frontend/src/metabase-enterprise/api/tags.ts
#	resources/migrations/057_update_migrations.yaml

* transforms python module (#63503)

# Conflicts:
#	enterprise/backend/src/metabase_enterprise/transforms/api.clj
#	enterprise/backend/src/metabase_enterprise/transforms/execute.clj
#	enterprise/backend/src/metabase_enterprise/transforms/jobs.clj
#	enterprise/backend/src/metabase_enterprise/transforms/util.clj
#	enterprise/backend/src/metabase_enterprise/transforms_python/python_runner.clj
#	enterprise/backend/test/metabase_enterprise/transforms_python/api_test.clj
#	enterprise/backend/test/metabase_enterprise/transforms_python/e2e_test.clj
#	enterprise/backend/test/metabase_enterprise/transforms_python/python_runner_test.clj

* Use normal title case

* Handle json-line output

* Remove unused props

* Rename PythonEditor to PythonQueryEditor

* Move script rewriter to utils

* Flatten Python transform page

* Rename shared to common

* Remove dead code

* Reduce specialization in Transform editor

* Remove unused props

* Move PythonQueryEditor to top-level components

* Nest Python editor components in PythonTransformEditor

* Restructure Python transform editor

* Split up component more

* Add helpers for python editor

* Disable default schema

* Add test ids

* Fix import path

* Reuse transform save modal

* Fix parameter

* Add e2e tests for python common lib

* Move python transform api to general transforms folder

* Move type to metabase-api/types

* Disable debugger for python transforms for now, allow to open it with query param

* Disable debugger hotkey for python transforms

* Add syntax highlighting to Python script on Transform page

* Remove cancelation code for now

# Conflicts:
#	enterprise/frontend/src/metabase-enterprise/transforms/components/RunButton/RunButton.tsx
#	enterprise/frontend/src/metabase-enterprise/transforms/pages/TransformPage/RunSection/RunSection.tsx

* Add more generic RunInfo

* Remove unused LogOutput

* Set up python runner in CI (#63566)

* Check out with submodules in CI

* Include python-runner in docker compose for tests

* Set up python runner in CI

* Fix import

* Update python-runner
# Conflicts:
#	python-runner

* Fix font sizes

* Fix table layout

* Revert "Remove unused LogOutput"

This reverts commit 90c0704344.

* Add LogOutput back to runsection

* Remove duplicate schema field

* Use css instead of styled components

* Fix LogOutput styling

* Add transforms/python feature

* Do not allow picking unsupported databases

* Use slugify from metabase/lib/formatting

* Add type for transform alias map

* Fix some type imports

* Refactor PythonDataPicker

# Conflicts:
#	enterprise/frontend/src/metabase-enterprise/transforms/components/PythonTransformEditor/PythonDataPicker/PythonDataPicker.tsx

* Further refactors

* Use data selector for database picker

* Use data picker to pick tables

* Remove unused var

* Improve tests for python transform

* Fix duplicate imports

* Use DatabaseId in python transform

* Remove source/draft ambiguity in types

* Move library types to metabase-types/api

* Remove api transforms

* Remove vibe-comment

* Remove unused component

* Validate source more

* Move TableInput to separate file

* Remove TableOption type

* Simplify props

* Move AliasInput to separate file

* Remove unused props

* Add border to empty results

* Fix import

* Remove extension

* Move draft back to new python transform page

* Fix new python transform page nesting

* Simpify new transform page structure

* Add analytics to history revision and data reference (#63587)

* Transform run cancellation (#62387)

* Add cancellation to transform api

* Use effect to set polling state

* Add cancellation button to run button

* Set up cancellation on transform page

* Use confirmation modal to cancel transform

* Add run cancel button to run list

* Make api endpoint take transform id instead of run id.

* Fix cancelation api

* Add canceled state to TransformRunStatus

* Add canceled state to transform page

* Add polling to RunListPage

* Remove comment in code

* Set status to canceling when you click cancel

* Add canceling status

* Poll when the transform is canceling

* Add copy for runs that are being canceled

* Manually track fetching status for running and canceling

* Handle canceling status in utils

* Avoid wrapping cancel button in run list

* Add test for canceling run on transform page

* Add e2e for canceling test from runs page

* Fix prop type

* Change copy to Canceling

* Add custom color to canceled state

* Remove mock code

* Increase cancelation tiemout

* Fix copy in text

* Fix snowplow event

* Remove void specifier and close modal in handler directyle

* Add optimistic update for run cancelation

* Avoid manually tracking cancelation status

* Avoid useEffect when changing isPolling

* Add in missing helper

* Remove another useEffect

* Add placeholder run when starting

---------

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>

* Fix command z not working in connection string input (#63689)

- Stop preventing default event handling onPaste.
- Improve Cypress paste emulation to be compatible with React way of handling paste events.

* Transforms prepare for python (#63698)

* pull out transform run utils to prepare for python transforms

* fix: check running-run on transform-id, not run-id

* chore: put on a new line

* Fix #63629: Filter on cards causes an unnecessary horizontal scroll bar for public/static embedding (#63650)

* Closes EMB-50 tabs dimmed out on iOS (#63690)

* S3 transport layer for python-runner (#63697)

* Add s3 dependencies (#63706)

* port prometheus/instrumentation (#63631)

* port prometheus metrics

* port instrumentation

* instrument existing transform paths

* wider buckets

* remove nil handling

* warn on nil bytes/rows

* add stage-type mapping

* typo

* Implement data picker according to designs

* Further adjust to design file

* Fix some e2e tests

* Clone python runner when running CI

* Avoid starting the container in e2e tests twice

* Hide python runner admin route when user does not have the feature for it

* Remove unused variables

* Move Python runner amdin page to transforms plugin

* Do not show Python library link when the user does not have python transforms support

* Hide Python transforms features when the user does not have transforms-python token

* Use useHasTokenFeature hook where possible

* Add transforms-python feature

* Fix e2e tests

* Do not include python-runner in test docker-compose yet

* Tag tests for python transforms with @transforms-python

* Add helper to update Python library

* Add check for python output

* Fix Revert on python library when it does not exist

* Fix e2e test helper

* Add python runner settings in CI

* WIP

* Translate pandas definitions

* Add AnsiLogs component

* Use AnsiLogs in Logs components

* Use AnsiLogs in LogOutput component

* Remove token feature gate in enterprise folder

* Return alias instead of throwing

* Check type in isPythonTransform assertion

* Use isResourceNotFoundError

* Make transforms-python it's own plugin

* /s/localhost/localstack

* Add test for editing Python transform

* Verify that running the transform with the library works

* Add missing transforms-python feature to mocks

* Simplify source layout

* Change setting type of python runner token

* Fix tests

* Use ConcreteTableIds in python transforms only

* Deduplicate codemirror helpers

* Ignore errors in non-parseable JSON lines

* Use useLocation hook to enable debugger

* Use useErrorMessage helper

* Move SourceSection to transforms-python plugin

* Fix codemirror helpers

* Remove undefined color

* Fix types

* Fix codemirror helpers more

* Move transform ordering tests to the python module (#63827)

* Add transform ordering tests to the python module

- Consistent with api tests for python transforms being in the python
module
- In practice transforms module 'knows' about python, so this is
organizational only
- Copied test helpers for now, not sure if functions strong enough to be
shared

* Check :transforms/python

* Proj python/table name len (#63842)

* feat: ensure temp-table-name respects max length

* add unit test

* add limit for missing drivers

* depend on it

* fix: use db instead of db-id

* Proj ptyhon/table swap (#63878)

* add rename feature, rename-table! multimethod

* add table swap with atomic or rename or fallback

* changelog

* drop requirement

* ensure we're testing replace flow

* wait for completion, table already exists

* wip

* add rename-table-test

* fix for snowflake

* Extract sourceDatabaseId helper

* Fix e2e helpers

* Fix error in table picker

* Add missing test-id

* Fix transform tests

* Validate python source

* Fix issues after merge

* Fix tag test

* Fix other tag test

---------

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Nicolo-Maria <nicolo.mezzopera@metabase.com>
Co-authored-by: Dan Stone <wotbrew@gmail.com>
Co-authored-by: Chris Truter <chris@metabase.com>
Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Eric Normand <ericwnormand@gmail.com>
Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Jakub Chodorowicz <jakub.chodorowicz@metabase.com>
Co-authored-by: Brad Anderson <brad@metabase.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>

* Proj python fix tests (#63970)

* add premium feature

* fix tests that didn't run previously

* exclude from vertica

* remove internal id from mongo table-rows

* fix: require transforms/python db feature for python transforms

* missing in stats

* fix for mongo

* whitespaces to appease the linter

* more more whitespace for the linter gods

* disambiguate for the e2e tests

* Some small type fixes for Python transforms  (#64021)

* Remove duplicate helper

* Add updateEnterpriseSetting helper for type-correctness

* Add missing python runner settings

* Use correct setting name

---------

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Dan Stone <wotbrew@gmail.com>
Co-authored-by: Romeo Van Snick <romeo@romeovansnick.be>
Co-authored-by: Nicolo-Maria <nicolo.mezzopera@metabase.com>
Co-authored-by: Chris Truter <chris@metabase.com>
Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Eric Normand <ericwnormand@gmail.com>
Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Jakub Chodorowicz <jakub.chodorowicz@metabase.com>
Co-authored-by: Brad Anderson <brad@metabase.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2025-09-25 13:03:38 +00:00
Romeo Van Snick
eeecd9a10a Fix native preview button in transforms (#63865)
* Allow passing language as a plugin to CodeEditor

* Pull out NativeQueryPreview

* Render NativeQueryPreview in the native transform editor

* Relax prop types

* Add e2e test for query preview in transforms

* Fix e2e tests

* Pass query instead of question

* Use CopyButton instead of bespoke component

* Fix unit test
2025-09-25 20:42:46 +10:00
Roman Abdulmanov
685424df74 fixed colors (#64029) 2025-09-25 10:06:54 +00:00
Phoomparin Mano
c4af9c415b Double scrollbars are showing up on metabase-browser when always show scrollbar is set (#63971) 2025-09-25 16:07:56 +07:00
Mahatthana (Kelvin) Nomsawadi
ca66414190 Reset colors when passing undefined to theme (#63957)
* Reset colors when passing undefined to theme

* Add tests
2025-09-25 10:07:43 +01:00
Tyler Plude
855e846d0a Add result ID to search click event tracking (#64009)
* Add result ID to search click event tracking

* include entity_id in SearchEventSchema type
2025-09-24 22:13:03 -07:00
Alexander Polyankin
270086315f Add last run status info to jobs (#63994) 2025-09-24 20:51:02 -04:00
Alexander Polyankin
16ea0dc141 Rename DatePicker intervals to "past" and "future" (#63993) 2025-09-24 20:49:52 -04:00
Timothy S. Dean
94b9737f0b Handle trailing spaces appropriately in snippet template tags (#63582)
Attempting to use a snippet template-tag with a trailing space in the snippet name (e.g., `{{snippet: foobar }}`) caused an exception to be thrown when running the native query (missing required parameter).

The regex used for identifying snippet tags will now strip trailing spaces appropriately.
2025-09-24 23:05:27 +00:00
Nathan Voxland
4448933543 Add MB_WEBPACK_PORT env variable config (#63355) 2025-09-24 17:15:19 -05:00
Alex Yarosh
a9e0afd420 interactive embedding notes (#64007) 2025-09-24 17:18:31 -04:00
Cam Saul
d018b4a1bc Use Lib for query->source-card; fix busted tests and busted X-Ray code (#63921)
* Misc Lib improvements

* Restore deleted function... for now

* Test fix 🔧

* Mark more stuff in Legacy MBQL utils as deprecated

* Use Lib for query->source-card

* Update modules config

* Update tests 🔧

* Fix the X-Rays

* Druid test fix 🔧

* Appease Kondo
2025-09-24 13:47:57 -07:00
lbrdnk
92011484de Disable fks sync on CH (#63976)
* Disable fks sync

* Describe implementation with warning

* Kondo
2025-09-24 22:34:03 +02:00
Alex Yarosh
c10fd5cd5b docs: db routing with static embedding (#63999)
* docs: db routing with static embedding

Clarified limitations of database routing in Metabase.

* Update docs/permissions/database-routing.md

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

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-24 20:22:35 +00:00
Oleksandr Yakushev
564556d706 [perf] Suggest drop-in replacements for clojure.core functions from metabase.util.performance (#62971)
* [perf] Suggest drop-in replacements for clojure.core functions from metabase.util.performance

* Fix walk for nil

* [perf] Make metabase.util.performance/reduce compatible with non-iterables (like strings)
2025-09-24 20:08:22 +00:00
Riley Thompson
5e55ecd75c Add regression test for clickhouse jdbc driver (#63906)
* add regression test for clickhouse

* revert to 0.8.4

* fix

* fix tests
2025-09-24 13:50:03 -06:00
Ngoc Khuat
0a33f82baf Fix static viz ignore viz-settings order (#63980) 2025-09-24 19:28:38 +00:00
github-automation-metabase
7ecdc2f757 Update generated docs (master) (#63947)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-24 13:34:01 -04:00
Phoomparin Mano
0bf1ac867f Replace interactive embedding card in embed modal with embed flow (#63942) 2025-09-25 00:14:36 +07:00
Cam Saul
cbdc23a7bf Fix sorting on aggregate columns (#63940)
* Fix sorting on aggregate columns

* cljfmt
2025-09-24 10:55:23 -06:00
Edward Paget
c59e9de090 fix: check email in update before validing for sso (#63915)
Changes to any user attribute for sso users were failing preventing them
from being to set their user locale. This was due to us not checking if
`email` is present in the body of the update request before failing a
`check-403` call if the user has an sso source.
2025-09-24 11:10:58 -05:00
Uladzimir Havenchyk
9668ebe98c chore: upgrade typescript to 5.9 (#61494) 2025-09-24 18:45:28 +03:00
Sébastien
3c2d46151b Closes EMB-851 static embed pin map (#63897)
* Closes EMB-851 static embed pin map

* .

* .

* Update frontend/src/metabase/visualizations/lib/map.ts

Co-authored-by: Phoomparin Mano <poom@metabase.com>

* Update frontend/src/metabase/visualizations/lib/map.ts

Co-authored-by: Phoomparin Mano <poom@metabase.com>

---------

Co-authored-by: Phoomparin Mano <poom@metabase.com>
2025-09-24 17:42:40 +02:00
Alexander Polyankin
1cacd1f510 Constraint available relative directions in the DatePicker for transforms (#63893) 2025-09-24 11:36:39 -04:00
Ryan Laurie
a8074ca04b Thinner + Darker Borders (#63675)
* fix button borders

* update-border-color

* dont mix the colors

* update loki

* update again

* Update Loki Snapshots

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2025-09-24 15:31:25 +00:00
Oleksandr Solovyov
28014961c3 add index on report_card.type (#63493) 2025-09-24 15:19:51 +00:00
Tyler Plude
ec5b20af0c [METABOT] Fix bug in Metabot search deduplication (#63943)
* [METABOT] Fix bug in Metabot search deduplication

* rm debug defs
2025-09-24 08:01:06 -07:00
dpsutton
ab68e21cf7 revert nett-common and netty-buffer bumps (#63967)
rather than 4.2.4.Final, they will now be "io.netty/netty-buffer
4.1.118.Final" which puts it the same as that in 3.5.0.

The issue is that both the bigquery-cloud-sdk driver and athena driver
get put onto the classpath and it depends on which order the drivers are
added for resolving which version of this netty buffer dep you get.

```clojure
user=> (metabase.classloader.core/the-classloader)
user=> (enumeration-seq (.. (Thread/currentThread) getContextClassLoader (getResources "io/netty/buffer/ByteBufAllocator.class")))
(#object[java.net.URL 0x3bb34fe "jar:file:/private/tmp/fixed-athena/plugins/bigquery-cloud-sdk.metabase-driver.jar!/io/netty/buffer/ByteBufAllocator.class"]
 #object[java.net.URL 0x4ac1867d "jar:file:/private/tmp/fixed-athena/plugins/athena.metabase-driver.jar!/io/netty/buffer/ByteBufAllocator.class"])
```

One annoying bit to keep track of this is that bigquery uses a regular
jar that specifies its dependencies, so you can you regular tooling:

```
clj -X:deps tree :aliases '[:drivers]' > /tmp/deps.with.netty.downgrade
```

And then see those deps:

```
  . metabase/bigquery-cloud-sdk /Users/dan/projects/work/metabase/modules/drivers/bigquery-cloud-sdk
    . com.google.cloud/google-cloud-bigquery 2.54.2
      . com.google.cloud/google-cloud-core 2.60.0
      . ... [lots omitted]
    . org.apache.arrow/arrow-memory-netty 17.0.0
      X org.apache.arrow/arrow-memory-core 17.0.0 :older-version
      . org.apache.arrow/arrow-memory-netty-buffer-patch 17.0.0
        X org.apache.arrow/arrow-memory-core 17.0.0 :older-version
        . io.netty/netty-buffer 4.1.110.Final
```

This version was created without any pinning of netty. Checking the
athena version is a bit more tricky. We use a fat jar. But you can
download the source from amazon as a jar with deps:

```
athena-jdbc-3.5.0-lean-jar-and-separate-dependencies-jars
```

https://docs.aws.amazon.com/athena/latest/ug/jdbc-v3-driver-previous-versions.html

This zip includes `runtime-dependencies` which includes
`netty-buffer-4.1.jar`

The pom inside of this jar specifies

```xml
  <parent>
    <groupId>io.netty</groupId>
    <artifactId>netty-parent</artifactId>
    <version>4.1.118.Final</version>
  </parent>
```

And `META-INF/maven/io.netty/netty-buffer/pom.properties` specifies

```
artifactId=netty-buffer
groupId=io.netty
version=4.1.118.Final
```

So i am confident now that with the pinning in bigquery and this version
here that regardless of class resolution from
"jar:file:/private/tmp/fixed-athena/plugins/bigquery-cloud-sdk.metabase-driver.jar!/io/netty/buffer/ByteBufAllocator.class"
or
"jar:file:/private/tmp/fixed-athena/plugins/athena.metabase-driver.jar!/io/netty/buffer/ByteBufAllocator.class"
you will get the same class and athena shall work again.
2025-09-24 09:48:38 -05:00
dpsutton
821c0f624b ensure type/* for base-type if otherwise not known (#63930)
* ensure type/* for base-type if otherwise not known

Using the follow query that returns pg arrays:

```sql
SELECT *
FROM (
  VALUES
    (1, ARRAY['https://example.com', 'https://example.org']),
    (2, ARRAY['https://test.com']),
    (3, ARRAY['https://foo.com', 'https://bar.com', 'https://baz.com'])
) AS adhoc_table (id, urls);
```

In 55 this returned a nil base-type

```clojure
user=> (metabase.query-processor.metadata/legacy-result-metadata
         {:database 2
          :type :native,
          :native {:template-tags {},
                   :query "SELECT *\nFROM (\n  VALUES\n    (1, ARRAY['https://example.com', 'https://example.org']),\n    (2, ARRAY['https://test.com']),\n    (3, ARRAY['https://foo.com', 'https://bar.com', 'https://baz.com'])\n) AS adhoc_table (id, urls);"}}
         1)
[{:display_name "id",
  :source :native,
  :field_ref [:field "id" {:base-type :type/Integer}],
  :ident "native[]__id",
  :name "id",
  :database_type "int4",
  :base_type :type/Integer,
  :semantic_type :type/PK}
 {:display_name "urls",
  :source :native,
  :field_ref [:field "urls" {:base-type nil}],
  :ident "native[]__urls",
  :name "urls",
  :database_type "_text",
  :base_type nil}]
```

In master, this just fails. If you remove the type checking in places,
it will eventually return similar.

This base-type is computed in sql-jdbc/metadata

```clojure
(mu/defn query-result-metadata :- [:sequential driver-api/schema.metadata.column]
  "Default implementation of [[metabase.driver/query-result-metadata]] for JDBC-based drivers. Gets metadata without
  actually running a query."
  ([driver :- :keyword
    query  :- :map]
   (driver-api/with-qp-setup [query query]
     (let [database               (driver-api/database (driver-api/metadata-provider))
           {:keys [query params]} (driver-api/compile query)]
       (query-result-metadata driver database query params))))

  ([driver      :- :keyword
    database    :- driver-api/schema.metadata.database
    ^String sql :- :string
    params      :- [:maybe [:sequential :any]]]
   (sql-jdbc.execute/do-with-connection-with-options
    driver
    database
    nil
    (fn [^java.sql.Connection conn]
      (with-open [stmt (sql-jdbc.execute/prepared-statement driver conn sql params)]
        (let [rsmeta (.getMetaData stmt)]
          (mapv (fn [i]
                  (let [database-type (.getColumnTypeName rsmeta i)]
                    {:lib/type      :metadata/column
                     :name          (.getColumnLabel rsmeta i)
                     :database-type database-type
                     :base-type     (or (sql-jdbc.sync.interface/database-type->base-type driver (keyword database-type))
                                        :type/*)}))
                (range 1 (inc (.getColumnCount rsmeta))))))))))
```

Before this change, there was no `or` and
`sql-jdbc.sync.interface/database-type->base-type` would just return
nil.

```clojure
user=> (metabase.driver.sql-jdbc.sync/database-type->base-type :postgres (keyword "_text"))
nil
```

Unfortunately this is a multimethod so we can't easily update it in one
spot to fall back to type/*. Each implementation must take care to do
this. A few do (clickhouse does) but a few dont (snowflake does not). So
we update this call site to ensure we have something from the
multimethod or add our own type/*

Why did this start happening now?
> I think it's because I added :metadata/model-metadata to :metabase.lib.schema.info/info at some point

Query info stashes the model metadata which is
```clojure
[:metadata/model-metadata {:optional true} [:maybe [:sequential ::lib.schema.metadata/lib-or-legacy-column]]]
```

We had been throwing errors asynchronously when trying to save metadata,
but now we have on-thread type mismatch for model metadata, which is
what is in the ticket.

* add test to ensure we can handle existing base_type nil's

this got annoying because legacy metadata from the app-db comes from a
different location. I was adding a card schema migration but this is
better here.

The previous commit makes sure we don't put out more bad data, but this
commit here ensures that we can update the card.
2025-09-24 09:37:11 -05:00
Vamsi Peri
7423aaf572 Remove sessions from sync metadata registry (#63949)
Remove sessions table from sync metadata registry
2025-09-24 14:34:51 +00:00
Phoomparin Mano
03f68f0a2c Add embed flow to the command palette (#63944) 2025-09-24 21:22:55 +07:00
Romeo Van Snick
e8f125c19b Add error info to transform job page (#63623)
* Move cron info up into cron section

* Move schedule explanation logic to utils

* Allow ReactNode as explain message

* Render explain message in cron component

* Add status to transform job page

* Add e2e tests for job error message

* Add assertion to lookup

* Add extract shared logic for run status

* Add missing test-ids

* Select correct section
2025-09-25 00:20:23 +10:00
Romeo Van Snick
21c5271879 Fix failing transform tags test (#63964)
* Do not expect tag to be in popover when it's already selected

* Invalidate specific tags when deleting a tag

* Fix other tag test
2025-09-25 00:19:57 +10:00
Jeff Bruemmer
45d701fd6b docs - updates on documents, comments, and Metabot (#63922) 2025-09-24 14:06:31 +00:00
Uladzimir Havenchyk
7c1152e9d0 chore: fix type usage of parameters in sdk (#63918)
* chore: fix type usage for parameters in sdk

* make types plane
2025-09-24 16:21:54 +03:00
Kamil Mielnik
ab20852102 DSN-70 Custom maps section in Admin should have a nicer empty state for its table (#63874)
* Add empty state to custom maps component

* Add unit test
2025-09-24 11:04:24 +00:00
Phoomparin Mano
137bc61a42 Show toggle for enabling saves for question embeds in embed flow (#63908) 2025-09-24 16:35:02 +07:00
Kamil Mielnik
f567aeece1 Do not render tabs in ParameterSidebar if there's only 1 tab (#63815) 2025-09-24 16:21:16 +07:00
Mahatthana (Kelvin) Nomsawadi
e60e38682b Don't send new user login for SDK or Embedded Analytics JS (#63883)
* Don't send new user login for SDK or Embedded Analytics JS

* Update tests

* Stop using hard-coded string to detect modular embedding headers

* Conform to spacing rule
2025-09-24 08:59:23 +01:00
Timofey Kachalov
84efc79be4 refactor(sdk-bundle): Adjust SDK compatibility warning messages and banners (#63919) 2025-09-24 11:58:32 +04:00
Kamil Mielnik
8de8d13eb6 Update copy (#63823) 2025-09-24 14:55:11 +07:00
Kamil Mielnik
a52d58a3e2 Make info panel headings consistent (color, line-height, margin-bottom) (#63882) 2025-09-24 14:54:27 +07:00
Kamil Mielnik
f34cb3d5c3 Use Tab component in TagEditorSidebar (#63829) 2025-09-24 14:52:49 +07:00
Kamil Mielnik
d8571ccabe Update background color of Field items in TableSection (#63831) 2025-09-24 14:52:15 +07:00
Kamil Mielnik
19f279610b Remove forceActiveStyle prop from ActionButton (#63869) 2025-09-24 14:51:51 +07:00
Riley Thompson
5647233576 Fix starbust query cancellation with isClosed proxy implementations (#63857)
* add isClosed impls to proxys

* use mt/id in test

* test all drivers

* check if statements are supported, refactor statement-supported? multimethod to drive feature

* add namespace to statements feature
2025-09-24 05:10:13 +00:00
Riley Thompson
14481bcf2f Clean up transforms test schemas (#63406)
* clean up transforms test schemas

* use destroy-dataset! for bigquery
2025-09-24 05:08:55 +00:00
Mahatthana (Kelvin) Nomsawadi
bbccbb4842 fix(embed-js): Only report analytics when not in the preview (#63812)
* Only report analytics when not in the preview

This would avoid confusing preview usage with the actual usage

* Ensure analytics would be report even if iframe isn't ready

* Add E2E test

* Make the test pass

* Stop relying on redux store to fetch `site-url`

* Clarify test with comment

* Add tests

* Fix linter

I was naming a file wrong, so it doesn't match unit test spec. This rule
apparently doesn't take tests into account, which is great.
2025-09-24 06:06:45 +01:00
Cam Saul
4b99060f46 Do not recurse forever when resolving bad :field refs for expressions (#63926) 2025-09-23 17:57:27 -07:00
Cam Saul
3903af487f Misc Lib improvements to support wider Lib usage in the backend (#63911)
* Misc Lib improvements

* Restore deleted function... for now

* Test fix 🔧
2025-09-24 00:14:30 +00:00
Phoomparin Mano
0fb061b839 Minor visual improvements for embedding hub (#63813) 2025-09-24 06:14:51 +07:00
Tyler Plude
ab9e7bbcb8 [BOT-292] Metabot with Search 2.0 (#62067)
* Add search endpoint for Metabot and adjusted metadata

* More tests and better split out of logic into separate files

* dedupe search terms and description if they happen to be the same

* fix bad merge conflict

* Answer blending sql querying via search tables (#62415)

* Only put tables used in SQL into context
* Change format of injected database content by reusing existing models
* Create separate function for new schema enchancement
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>

* Add table reference for implicitly joinable fields in context injection (#62753)

* Update Metabot profile IDs (#62864)

* [Answer blending] Updates to the metabot admin configs (#62551)

* remove ability to configure the selected collection for default metabot

* add verified toggle

* add migrations for metabot collection configuration changes

* add metabot PUT endpoint, rip out metabot entities endpoints and usages, convert FE over to new FE, fix tests

* remove metabot_entity table, model, usage and refactor code to use updated metabot and metabot_prompts schemas

* enforce verification and regenerate prompts on verification + collection id change

* fix bad references

* fix bad regexp for fetch-mock collections by id endpoints breaking unit tests

* fix kondo

* fix + increase tests for enterprise/backend/test/metabase_enterprise/metabot_v3/tools/api_test.clj

* fix migration boolean default value syntax

* maybe fix be tests

* fix some tests

* enforce verified filter on the search endpoint

* factor in verified check into find-metric

* hide verification setting behind token feature check, fix migration lint, do not migrate collection id for default metabot, fix kondo errors

* fix core tests

* fix kondo

* fix most of the failing serialization tests

* fix migration lints

* fix one test file

* maybe fix migration failures

* move test resource file

* prevent default metabot from getting a collection id configured

* yolo

* add cascade delete to entity drop table migration

* increase unit tests for verification config UI

* remove extra add column

* fix migration... i think for real this time

* for real for real fix migrations

* no really

* tests fixes and add preventing verification toggle if the instance does not have the content_verification feature

* fix failing feature tests in ci

* fix one more

* fix cljfmt issue

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>

* [Metabot] Quick slash command + dynamic profile ui (#62909)

* quick slash command + dyanmic profile ui

* add back code i did not mean to remove

* fix failing test

* Revert "fix failing test"

This reverts commit b1c7e9efb9.

* [BOT-284] BE for notebook query tool table support (#62710)

* init

* moved query-table endpoint to query-datasource; add support for both model and table ids

* unit test fixes

* use existing card-query and table-query utils

* adding unit tests

* [Answer blending] Respect collection id in search (#63150)

* remove find metric tool

* adjustments to search endpoint

* add tests

* fix failing test in ci

* add way to override the metabot id

* add way to override metabot request id

* fix whitespace lint

* missing paren...

* fix failing test

* New BE `/search` endpoint for ai-service `SearchTool` (#63154)

* new metabot search tool BE endpoint

* remove debug prints

* parallel searches; incomplete effort to return more fields for results

* Simplify result transforming & response schemas

* unit tests

* kondo fixes; pr feedback

* fallback search model list

* remove references to MetabotEntity model which got re-added incorrectly in rebase

* Make internal metabot use verified content by default (#63529)

Update linter for usage of task in metabot-v3

Move suggested-prompts to separate namespace

Add collection condition only when current-user-id

Add startup task

migration

Update tests

Verified cards only

Test

Docstrings

Limit number of entities

Review feedback

* [METABOT] Default use_verified_content to false; tweak prompts (#63843)

* default use_verified_content false; limit amount of prompts

* reworked unit test to not depend on ai-service or appdb contents

* PR feedback

* remove mock of get metrics and models in favor of mt/with-empty-h2-app-db

* Small refactors & simplifications to metabot PUT endpoint and suggested
prompts job

* small simplification, use m/distinct-by for deduping tables

* rm deprecated search_data_sources

* Cleanup and prioritize verified content (#63877)

* Remove now redundant content verification guard

* Prioritize verified content

* Redundant merge

* Test

* ensure field IDs in user_is_viewing context are snake_cased

* Use proper source column alias as name for the model / table fields (#63826)

* Use proper source column alias as name for the model / table fields

* Add name as fallback

* test fix

* switch to using :lib/desired-column-alias, revert test fix

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>
Co-authored-by: Noah Moss <noahbmoss@gmail.com>

* Fix some tests: consistent use of snake_case, and a :lib/source-column-alias fallback for model field :name

* fix more field_id snake_case issues

* fix another snake case issue - revert result_columns back to result-columns

---------

Co-authored-by: Thomas Schmidt <thomas.heinz.schmidt@gmail.com>
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
Co-authored-by: Thomas Schmidt <thomas@metabase.com>
Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: lbrdnk <lbrdnk@users.noreply.github.com>
2025-09-23 14:42:29 -07:00
Alex Yarosh
a137631b7f docs: time grouping stages (#63934)
* docs: time grouping stages

* Update docs/dashboards/filters.md
2025-09-23 21:19:43 +00:00
Edward Paget
e4a04ebc05 fix: jetty stats handler tests flake (#63912)
* fix: jetty stats handler tests flake

These tests could hang indefinitely on the `(.join server)` statement
because the core.async/go block handling async requests was incorrectly
doing a blocking take `<!!`. This also updates our :dev alias to enable
core.async to print warnings when using blocking operations in `go` blocks.

* fixup! oncomplete dispatch in go block

* fixup! using alts!! in go block
2025-09-23 16:17:41 -05:00
Stas Gavrylov
432287f763 update comments email template (#63626)
* WRK-893: Update API to store comment html content

* WRK-891: Add HTML content sending in comments. Add proper SmartLink serialization to HTML.

* WRK-891: Update comment email template to display comment content

* WRK-891: Fixes for comment email template. Add space insertion after smartLink

* trigger comment notification by id

* put old api behavior back and cleanup comment notification context

* whitespace

* fix tests

* Fix changeset author

* WRK-891: Fix smart links serialization. Add parent comment display in email

* Add style payload for comments email template

* fix types

* 🙄

* test email changes

* test email again

* make email formatting looking perfect

* cleanup

* require html content to be provided when posting a comment

* fix e2e test

* fix another e2e test

* pass 0 as tiptap placeholder when no label available

---------

Co-authored-by: Oleksandr Solovyov <o@solovyov.net>
Co-authored-by: Uladzimir Havenchyk <uladzimir@metabase.com>
Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
2025-09-23 21:16:18 +00:00
Oreon Lothamer
d8be52f651 use vertica-ce from ecr (#63609)
* use vertica-ce from ecr

* use CI_IAM_ROLE for vertica ECR

* Adjust test

---------

Co-authored-by: lbrdnk <lbrdnk@users.noreply.github.com>
2025-09-23 11:09:32 -10:00
Nathan Voxland
6516199e3f Improve FieldValues inactive check (#63847) 2025-09-23 21:04:09 +00:00
Thomas Schmidt
1bba35237e Auto set template tags (#63691) 2025-09-23 19:19:34 +00:00
Alex Yarosh
4337a1baf3 docs: scim <> saml interaction (#63925) 2025-09-23 15:07:38 -04:00
Brad Anderson
23cb8b11c0 Fix: #50011 Hides "Here are some explorations of" when there are no explorations (#63839) 2025-09-23 14:07:33 -04:00
Cam Saul
c863f41c2a Improved mu/defn and mu/fn macros: only evaluate schemas once (#63737)
* Mega Malli fn improvements

* Remove commented out code
2025-09-23 10:44:03 -07:00
Jakub Chodorowicz
34e7a0e9f1 Adjust DB form page width and form fields spacing (#63818)
* Adjust DB form page width and form fields spacing

* Extract layout field settings to one file

* Use lg spacing between form fields

* Conditionally add labelProps property

* Fix typo

* Refactor deprecated default export

* Simplify responsive max width

* Simplify DB page responsive styles

* Add card background to add db form
2025-09-23 18:25:56 +02:00
dpsutton
454a05e3c6 Downgrade athena (#63894)
having issues with classpath. I _think_ that athena is finding the
bigquery provided version of a netty class and that is failing. but it's
a bit difficult to follow.

downgrading now until we can take time to understand and sort it.
2025-09-23 10:53:34 -05:00
Ryan Laurie
1ed050b03d Make Loki work more consistently (#63859)
* fix loki?

* update loki scripts

* use ci

* remove double build

* update scripts

* update documentation
2025-09-23 15:16:54 +00:00
Robert Kosara
9041dbb329 docs: Remove the +New option for creating new models (#63861)
* Remove the +New option for creating new models

* Bring back the from-scratch approach

Based on Alex's requested change, but also realized that you can use the command palette

* Move note about models getting pinned back into the new model section

* Format UI terms using bold instead of italics
2025-09-23 08:08:29 -07:00
lbrdnk
63cc5c3137 Semantic search token usage tracking (#63591)
* Pass type of embedding request

* Initial migration

* Add remaining opts args

* Add SemanticSearchTokenTracking module

* Connect token tracking to ai-service impl

* Add test for token tracking writes.

* Remove prompt_tokens including migration

* Add usage trimmer job

* Trimmer test

* Activate trimmer job

* test

* Record tokens for openai

* Update test

* Use total-tokens

* Comment

* linter

* Add index

* Exclude from copy
2025-09-23 16:50:55 +02:00
Phoomparin Mano
7551f766dd Hide non-customer-facing embedding hub settings (#63838) 2025-09-23 21:45:31 +07:00
John Swanson
f4b675b3f2 Serialize transforms (#63658)
Serialization for transforms
2025-09-23 07:34:17 -07:00
github-automation-metabase
123e0cef51 Update generated docs (master) (#63864)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-23 09:17:55 -04:00
Fraser
eed3bb1e19 Make timing steps continue-on-error (#63881) 2025-09-23 14:11:01 +02:00
Cam Saul
dad9b5c709 Don't spend minutes trying to generate descriptions for massive schemas (#63739)
* Don't spend minutes trying to generate descriptions for massive schemas

* Cljs test fix 🔧
2025-09-23 14:38:07 +03:00
Nicolò Pretto
166e20c944 fix typo in WhatsNewNotification: 'dimiss' -> 'dismiss' 2025-09-23 09:51:15 +00:00
Jakub Chodorowicz
e4d0318213 Migrate last direct usage of settings store url, to useStoreUrl (#63771) 2025-09-23 08:56:04 +02:00
Alexander Polyankin
3913c6e850 Fix pills input padding (#63840) 2025-09-23 02:03:55 +00:00
Fraser
f6384ac6f3 Automatically update e2e timings (#61950) 2025-09-23 10:19:09 +12:00
Maz Ameli
bd6d5e527b Replace usage of info_filled icon with info icon (#63734)
* replace usage of info_filled icon with info icon

* Update Loki Snapshots

* drop unused info_filled icon

* Update Loki Snapshots

* update stories

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Aleksandr Lesnenko <alxnddr@gmail.com>
2025-09-22 21:10:42 +00:00
Maz Ameli
4dac7d9dcf remove dropshadow from glossary container (#63730)
Co-authored-by: Stas Gavrylov <stas@metabase.com>
2025-09-22 21:04:20 +00:00
Nemanja Glumac
5bda80e53f Add simple event tracking for drill-thrus and other "click actions" (#63792) 2025-09-22 22:56:04 +02:00
Sébastien
07a34dc556 Removed loki flaky test (#63824) 2025-09-22 13:15:17 -07:00
Alex Yarosh
3a4e2a311c docs: send only attachements (#63846)
* docs: send only attachements

* Apply suggestions from code review

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

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-22 20:04:43 +00:00
Jeff Bruemmer
f0427f0a34 docs - add glossary and image (#63844) 2025-09-22 16:00:04 -04:00
Jeff Bruemmer
ebcdcf1ce9 Remove .json format from subscription options (#63849) 2025-09-22 15:59:09 -04:00
Nick Fitzpatrick
20afc08468 Hiros document desires (#63035)
* starting document copying

* Document copy from collection page

* remove card node
2025-09-22 16:53:00 -03:00
Alexander Polyankin
c60b139cf4 Fix removing all tags from a transform job (#63837) 2025-09-22 14:24:19 -04:00
Alexander Polyankin
cb7e5a0437 Transforms schedule component (#63767) 2025-09-22 18:04:33 +00:00
Anderson
1e4e4bcae5 Update user invitation email design (#63682)
* Update user invitation email design

* Add alt attr to img element

* Update system event clj test
2025-09-22 14:32:20 -03:00
Phoomparin Mano
f65c0fb194 Add completion checks for creating models in embedding hub (#63661)
* update test and implementation

* update models query to make more sense

* flake fix: try adding timeouts to Done

* remove cy.wait from tests

* wait for model creation

* modify create model tests
2025-09-22 16:33:12 +00:00
Nathan Voxland
fa187a027d Fix crash during reindexing of analytics on upgrade (#63770)
* Don't force reindex on serialization load to be async

* Allow reindex to be controlled when calling load

* Keep load reindex sync for now

* Make reindex async again
2025-09-22 10:30:57 -05:00
Alexander Polyankin
d25a14aec8 Show an error that variables in transforms are not supported (#63759) 2025-09-22 15:30:17 +00:00
Brad Anderson
4a59711053 Fix: #63258 + UXW-570 Fix truncated dashboard notifications (#63726)
* UXW-570 Fix truncated toast messages

* UXW-570 Updates e2e test

* #63258 Fix e2e tests (breaking because toast is wider, blocks previously unobscured elements)
2025-09-22 11:18:46 -04:00
Nemanja Glumac
c33489af35 Add tracking to bookmarks (#63752) 2025-09-22 15:12:24 +00:00
Alexander Polyankin
d6c7ef8e63 Fix MLv2 error with audit v1 (#63772) 2025-09-22 10:27:41 -04:00
Nemanja Glumac
6e7aeb6d71 Add tracking to the metadata changes from admin (#63672) 2025-09-22 16:20:52 +02:00
github-automation-metabase
6db1e7e2ad Update generated docs (master) (#63810)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-22 09:37:11 -04:00
Alexander Polyankin
f901d08b1c Hide cursor in the readonly native editor (#63774) 2025-09-22 13:26:06 +00:00
Alexander Polyankin
5bc7d211ba Remove unsupported buttons from native editor in transforms (#63768) 2025-09-22 09:20:43 -04:00
Alexander Polyankin
d04ffc2925 Change the default editor height for transforms (#63773) 2025-09-22 09:20:27 -04:00
Kamil Mielnik
c471c32f3f Allow to un-resolve deleted comments (#63429) 2025-09-22 10:06:21 +03:00
Romeo Van Snick
d867a09072 Convert MBQL transforms to SQL (#63565)
* Add a controlled version of NotebookNativePreview

* Use ReactNode instead of JSX.Element

* Add a button to toggle native sql

* Add sql preview to transform query editor

* Pull out resizehandle component

* Add e2e test for converting an MBQL transform to a native transform

* Move native query toggle out of header

* Move Native editor sidebar to separate component

* Fix overflow in native sidebar

* Fix copy on button

* Move connected NotebookNativePreview to query_builder

* Remove canConvertToNative prop

* Rename prop

* Move styles to css
2025-09-22 15:42:54 +10:00
github-actions[bot]
090fbc2d5a [chore] Update E2E test timings with recent data (#63327) 2025-09-22 14:27:07 +12:00
Fraser
33fe7b3afe Delete skipped expression popover e2e tests (#63732) 2025-09-22 12:08:46 +12:00
Phoomparin Mano
ea7eeeb15e Update related settings copy and alignment on embedding hub (#63660) 2025-09-20 09:46:30 +07:00
Phoomparin Mano
b693b137f6 Replace the language selection step on the setup flow with the language selector (#63584) 2025-09-20 08:55:34 +07:00
Phoomparin Mano
1a6044bbb3 Use blue shadow on embedding hub card to highlight next step (#63655) 2025-09-20 08:55:24 +07:00
Phoomparin Mano
e5706a4def Adjust the embedding homepage copy and add upsell for OSS (#63646) 2025-09-20 08:55:09 +07:00
Nicolò Maria Mezzopera
0014bbc775 fix(ci): properly account for ci-skip condition in jobs that specify IF condition (#63785) 2025-09-19 22:10:56 +00:00
Alex Yarosh
b96b8fbead docs: fix include in editable tables (#63783) 2025-09-19 17:55:54 -04:00
Phoomparin Mano
7e09b87447 Update embedding hub card copy for embed in your code step (#63656) 2025-09-20 04:46:49 +07:00
Brad Anderson
102025c69c UXW-753 Fix issues with Detail visualization columns getting out of sync (#63769) 2025-09-19 17:12:14 -04:00
Alex Yarosh
80b31c4b1e docs: progress bar (#63779)
* progress bar

* header

* Optimized 3 images, saved 190.8 KB (53.7% smaller)

* link

* Apply suggestions from code review

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

* feedback

* links again?..

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-19 16:59:49 -04:00
Alex Yarosh
d4adbb7ef0 docs: snippets (#63761)
* snippets

* Optimized 5 images, saved 209.2 KB (50.7% smaller)

* link

* sql -> native

* Apply suggestions from code review

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

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

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

* rawwwwww

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-19 16:50:12 -04:00
Jeff Bruemmer
04192e385d docs - model list view (#63727) 2025-09-19 20:48:25 +00:00
Uladzimir Havenchyk
18b9ae17c7 rework comments sidebar (#63636)
don't use Sidesheet and use sidebar on the same level as Metabot sidebar, so both sidebars can be opened side by side
2025-09-19 19:48:46 +00:00
Alexander Polyankin
b69218fe00 Transforms copy changes (#63760) 2025-09-19 15:03:48 -04:00
Alexander Polyankin
61a8141a44 Remove selected tags (#63714) 2025-09-19 18:40:19 +00:00
Cam Saul
2ed9e3e1d4 Delete dead FE code (#63740)
* Delete dead FE code

* Update enterprise/frontend/src/metabase-enterprise/audit_app/lib/cards/queries.js

---------

Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
2025-09-19 10:18:13 -07:00
Abed Habli
a1a9e71867 docs: renaming Debian references (#63763)
* Renaming debian

* redirect to the doc
2025-09-19 17:52:17 +02:00
Edward Paget
0ed177ddb1 fix: remove commented out card pivot test (#63701) 2025-09-19 15:41:59 +00:00
Mahatthana (Kelvin) Nomsawadi
814dd5c800 Fix static embedding dashboard calling GET: /api/dashboard unnecessarily (#63753) 2025-09-19 21:32:19 +07:00
Oleksandr Solovyov
8285e38f9c cleanup v3/v2/ (#63634) 2025-09-19 17:25:58 +03:00
github-automation-metabase
8ad7ee944d Update generated docs (master) (#63736)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-19 09:07:22 -04:00
Phoomparin Mano
975002a6f7 Revert the cloud setup flow's layout to match self-hosted setup flow (#63577) 2025-09-19 18:34:03 +07:00
Phoomparin Mano
3fddbec54e feat(embed-js): better error message when useExistingUserSession fails (#63125) 2025-09-19 18:13:18 +07:00
Sébastien
c3461ab646 Closes EMB-816 misleading text for number of rows (#63561)
* Closes EMB-816 misleading text for number of rows

* updated e2e tests

* marked a test as flaky until we figure out what is up with it

* addressed legit comment

* Update Loki Snapshots

* loki

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2025-09-19 12:31:22 +02:00
Phoomparin Mano
b5878d9bf5 Show the embedding hub checklist in embedding homepage (#63596) 2025-09-19 16:47:51 +07:00
Oleksandr Solovyov
6206b6f204 document comments: enable user mentions (#63426) 2025-09-19 09:42:06 +03:00
Mahatthana (Kelvin) Nomsawadi
4ef96bd73a feat(embed-js): Add analytics to Embedded Analytics JS (#63580)
* Add Embedded Analytics JS usage snowplow schema

* Add analytics

* Clarify code with comments

* Fix analytics logic + fix typing

* Add unit tests

* Make E2E embedded analytics JS util supports multiple elements

* Fix code not working in Cypress for some reason

* Add E2E test suite

* Fix import order

* Remove extra line

* Fix E2E from util API change

* Update snowplow/iglu-client-embedded/schemas/com.metabase/embedded_analytics_js/jsonschema/1-0-0

Co-authored-by: Phoomparin Mano <poom@metabase.com>

* Update e2e/test/scenarios/embedding/sdk-iframe-embedding/sdk-iframe-embedding.cy.spec.ts

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Update e2e/support/helpers/e2e-embedding-iframe-sdk-helpers.ts

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Fix E2E by updating a function API

* Pass snowplow schema lint

---------

Co-authored-by: Phoomparin Mano <poom@metabase.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-09-19 06:46:35 +01:00
Romeo Van Snick
b336da1517 Allow going back to snippet root collection (#63549)
* Allow going back to root collection from top-level snippet collection

* Add e2e test for snippet collections
2025-09-19 12:23:34 +10:00
Alexander Polyankin
c98dca560d Unskip transform tests (#63733) 2025-09-18 21:50:52 -04:00
github-actions[bot]
e370820778 Fix: Weird menu state shown when there are no questions in recents (#62985)
* Fix: Weird menu state shown when there are no questions in recents

Fixes #62728

🤖 Generated with [Claude Code](https://claude.ai/code)

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

* fix the robot's mess

---------

Co-authored-by: Claude Code[bot] <noreply@anthropic.com>
Co-authored-by: Ryan Laurie <iethree@gmail.com>
2025-09-18 23:06:45 +00:00
github-actions[bot]
69405a7dea Update translations 2025-09-16 (#63546)
* Update translation sources 2025-09-16

* Update translations

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-18 23:06:31 +00:00
Fraser
c0ca9e0493 Only run auto-backport when a backport label is added (#63606)
* Only run auto-backport when a backport label is added

* Assign backported PR to original PR creator
2025-09-18 16:57:42 -06:00
Fraser
ced8d84def Delete out of datte skipped caching test (#63680) 2025-09-19 10:39:24 +12:00
Fraser
8c8279e570 Delete unimplemented FKs in nested queries e2e test (#63684) 2025-09-19 10:39:00 +12:00
Fraser
6afe00b9f1 Fix custom column isNull e2e test (#63685) 2025-09-19 10:38:45 +12:00
Fraser
6e8cca3da7 Unskip join source table e2e tests (#63678) 2025-09-19 10:38:12 +12:00
Maz Ameli
a1582d07de Fix palette preview in Branding section (#63677)
* add tabs

* move reset button and remove needless tableheader

* fix palette preview chart

* change tab text

* cleanup

---------

Co-authored-by: Ryan Laurie <iethree@gmail.com>
2025-09-18 21:19:11 +00:00
Ryan Laurie
40387cc586 Update ActionButton to typescript (#63454)
* update ActionButton to typescript

* address review comments

* replace ref
2025-09-18 21:14:07 +00:00
Alexander Polyankin
911da7f851 Make transform list pages wider (#63710) 2025-09-18 20:49:56 +00:00
Jeff Bruemmer
4deefb87b3 docs - basic table editing (#63601) 2025-09-18 16:36:23 -04:00
Timofey Kachalov
c56432c2f7 Properly run tiles-query as admin user for static endpoints if JWT token is valid (#63692) 2025-09-19 00:11:33 +04:00
Timofey Kachalov
f9e9a20d94 fix(sdk): Properly load Tiles from MB instance (#63676) 2025-09-19 00:07:31 +04:00
Alex Yarosh
5d68e7363f docs: remove references to date coercion (#63716) 2025-09-18 15:58:58 -04:00
Stas Gavrylov
0efa8eac5b Data Reference – Glossary (#63576)
* feat: add glossary UI

* fix: line-height

* Styling

* Show edit button only on hover

* Make the term required

* Delete modal

* Actions menu

* Fix popovers opened for multiple terms

* Autofocus

* glossary api

* serialization

* support moving from h2 to cloud

* WRK-890: Replaces mocked glossary API with proper integration

* WRK-889: Refactor Glossary editing and overall styling

* WRK-889: Fix layout margin

* WRK-889: Update Glossary styling

* WRK-890: Address design comments. Refactor GlossaryTable into a separate component

* WRK-890: Add unit tests for GlossaryTable component

* WRK-890: Add e2e tests for Glossary

* WRK-890: Cleanup css, fix linter

* add boat empty state

* add explanatory text

* text and font weight tweaks

* tweak placeholder text

* improve delete confirm modal

* WRK-889: Update Glossary styling, implement hotkeys handling

* WRK-890: Add optimistic updates for Glossary editing

* WRK-890: Fix unit tests

* WRK-889: Adjust line height for glossary items

* WRK-889: More styling adjustments for GlossaryRowEditor

* WRK-890: Fix e2e tests

* fix merge results

* WRK-890: Address PR comments

* serialization fixes

---------

Co-authored-by: Konstantin Darutkin <spalt08@mail.ru>
Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
Co-authored-by: Oleksandr Solovyov <o@solovyov.net>
Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
Co-authored-by: Maz Ameli <maz@metabase.com>
2025-09-18 22:38:56 +03:00
Oleksandr Yakushev
6e3dfdb62b Upgrade c3p0 to 0.11.2 (#63213) 2025-09-18 21:47:35 +03:00
lbrdnk
e915bf2d40 Add guards to semantic search jobs (#63694) 2025-09-18 18:57:14 +02:00
Cam Saul
118613ab89 Binning middleware needs to update :lib/original-binning as well (#63673)
* Binning middleware needs to update `:lib/original-binning` as well

* Test fixes 🔧

* cljfmt
2025-09-18 09:37:29 -07:00
Alexander Polyankin
4f764b82ef Do not autopivot transform query results (#63669) 2025-09-18 16:21:09 +00:00
Alexander Polyankin
ce5a554d4d Add transform count to the job list (#63647) 2025-09-18 16:16:51 +00:00
github-automation-metabase
eb5100abc6 Update generated docs (master) (#63679)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-18 11:44:29 -04:00
Sébastien
64630276e3 Closes EMB-50 tabs dimmed out on iOS (#63690) 2025-09-18 15:57:35 +02:00
Brad Anderson
6f660a3090 Fix #63629: Filter on cards causes an unnecessary horizontal scroll bar for public/static embedding (#63650) 2025-09-18 09:51:31 -04:00
Nicola Mometto
eab4bf076d Transforms prepare for python (#63698)
* pull out transform run utils to prepare for python transforms

* fix: check running-run on transform-id, not run-id

* chore: put on a new line
2025-09-18 13:46:24 +00:00
Jakub Chodorowicz
2be60d80c3 Fix command z not working in connection string input (#63689)
- Stop preventing default event handling onPaste.
- Improve Cypress paste emulation to be compatible with React way of handling paste events.
2025-09-18 15:20:03 +02:00
Romeo Van Snick
3038880e46 Transform run cancellation (#62387)
* Add cancellation to transform api

* Use effect to set polling state

* Add cancellation button to run button

* Set up cancellation on transform page

* Use confirmation modal to cancel transform

* Add run cancel button to run list

* Make api endpoint take transform id instead of run id.

* Fix cancelation api

* Add canceled state to TransformRunStatus

* Add canceled state to transform page

* Add polling to RunListPage

* Remove comment in code

* Set status to canceling when you click cancel

* Add canceling status

* Poll when the transform is canceling

* Add copy for runs that are being canceled

* Manually track fetching status for running and canceling

* Handle canceling status in utils

* Avoid wrapping cancel button in run list

* Add test for canceling run on transform page

* Add e2e for canceling test from runs page

* Fix prop type

* Change copy to Canceling

* Add custom color to canceled state

* Remove mock code

* Increase cancelation tiemout

* Fix copy in text

* Fix snowplow event

* Remove void specifier and close modal in handler directyle

* Add optimistic update for run cancelation

* Avoid manually tracking cancelation status

* Avoid useEffect when changing isPolling

* Add in missing helper

* Remove another useEffect

* Add placeholder run when starting

---------

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2025-09-18 10:34:50 +00:00
Nemanja Glumac
b2b04e9206 Add analytics to history revision and data reference (#63587) 2025-09-18 09:44:49 +00:00
Alexander Polyankin
ef8111537e Update copy in UpdateTargetModal.tsx (#63667) 2025-09-18 08:11:11 +00:00
Nemanja Glumac
7bce3ff38a Add analytics to Metabot events (#63415)
* Add Metabot events

* Fix tooltip label

* Add new event for metabot chat controlled from the native editor
2025-09-18 08:44:19 +02:00
Cam Saul
066d3a233d Pure Lib QP: remove code to wrap legacy middleware (#63645)
* cumulative-aggregations-middleware => Lib

* Ensure MBQL 5

* Fix NOCOMMIT

* Fix indentation

* Pure-Lib QP :party:

* Remove some more unused legacy MBQL utils
2025-09-18 00:13:20 +00:00
Fraser
095ad3c39c Tag latest flaky tests (#63617) 2025-09-18 11:54:13 +12:00
Fraser
d50561227f Fix skipped chart drill-through e2e tests (#63548) 2025-09-18 11:18:02 +12:00
Ryan Laurie
63f9d193a9 Use Iframe for changelog (#62943)
* use iframe for changelog

* add query params and tabs

* always show update info

it's useful even if you're on the latest version

* remove metabase as configurable host

* always allow changelog/releases in iframes

* fix tests

* fix tests

* update tests

* use root metabase domains

* fix test

---------

Co-authored-by: John Swanson <john.d.swanson@gmail.com>
2025-09-17 14:40:52 -06:00
Alexander Polyankin
0a18c13feb Skip failing tests (#63663) 2025-09-17 18:57:52 +00:00
Nemanja Glumac
a58c63a74f Add analytics to x-rays (#63498) 2025-09-17 19:55:51 +02:00
Alexander Polyankin
96f1ef42a2 Disable transform editing buttons when running (#63642) 2025-09-17 16:05:51 +00:00
Jakub Chodorowicz
fd073fb8b1 PRG-130 Send parse connection string parsing events only on blur (#63558)
* Send parse connection string parsing events only on blur

* Use synchronous last parsing result for monitoring

* Improve test assertions for connection string events

* Make sure we don' track the same parsing event again

* Refactor ts-match to simple if conditions

* Make lastStatus have the same success value depending on non undefined values

* Improve the snowplow Cypress test setup for connection string
2025-09-17 16:03:04 +00:00
Alexander Polyankin
a7971b43b1 Fix table layout issues in transform pages (#63540) 2025-09-17 11:49:22 -04:00
Cam Saul
0d1b4e253a cumulative-aggregations-middleware => Lib (#63592)
* cumulative-aggregations-middleware => Lib

* Ensure MBQL 5

* Fix NOCOMMIT
2025-09-17 15:47:13 +00:00
Sébastien
9986021635 Closes EMB-303 search bar in embedding no longer awfully long (#63632) 2025-09-17 17:09:28 +02:00
Anderson
ae422678b2 [Admin settings] Show help documentation to connect DB in a side panel (#63420)
* [Admin settings] Show help documentation to connect DB in a side panel

* Add unit tests

* Add more tests / tweaks

* Add e2e tests

* Make sure help content is not rendered if not available

* Self-review tweaks

* Use useDocsUrl in DatabaseHelpSidePanel.tsx

* Use aside element for the side panel

* Use await findBy when possible

* Use JSDoc / Remove unnecessary comment

* Add some style to blockquote

* Add test file for useHelpLink

* Fix failing test

* Add analytics event
2025-09-17 11:59:05 -03:00
Cam Saul
8ecc5c6718 add-rows-truncated middleware => Lib (#63616) 2025-09-17 14:52:21 +00:00
Cam Saul
dec95aabe7 resolve-joins middleware => Lib, remove add-source-metadata (#63608) 2025-09-17 14:50:07 +00:00
Ngoc Khuat
98e29f6c39 [Transform python] python lib apis and settings (#63568) 2025-09-17 14:23:11 +00:00
lbrdnk
b110c02981 Fix empty db url for metric collector job (#63633) 2025-09-17 14:15:20 +00:00
github-automation-metabase
a5056280e8 Update generated docs (master) (#63611)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-17 10:10:53 -04:00
Alexander Polyankin
33f5b24a0a Fix flaky "Change target" behavior for transforms (#63526) 2025-09-17 09:15:43 -04:00
Uladzimir Havenchyk
323e6f3823 chore: drop unused files (#63500) 2025-09-17 13:29:30 +03:00
Timofey Kachalov
b5b9bad486 Add the better handling of the missing enterprise token for Sample Apps e2e (#63573) 2025-09-17 11:51:20 +04:00
Thomas Schmidt
7f4cb813c2 Adjust legacy sql generation button to toggle Metabot chat (#63554)
* Adjust legacy sql generation button to toggle Metabot chat

* Change placeholder text
2025-09-17 07:01:08 +00:00
Oleksandr Solovyov
068f460124 [metabot] tool_calls should be a vector (#63579)
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
2025-09-17 09:09:28 +03:00
Oleksandr Solovyov
50e10ee9c2 enable metabot stats and prevent recursive token checks (#63574) 2025-09-17 09:09:06 +03:00
Cam Saul
4d54129a72 viz-settings middleware => Lib (#63597) 2025-09-16 22:32:46 -07:00
Cam Saul
3d2789c2f1 Fix #63571 (#63595) 2025-09-16 20:31:19 -07:00
Ryan Laurie
2e4c39904e Handle when tags are not defined (#63588)
Co-authored-by: Fraser <fraser@metabase.com>
2025-09-17 00:29:57 +00:00
Alexander Polyankin
0911bbe3b3 Simplify state management and fix the flicker with the transform run button (#63586)r
* Fix optimistic updates with transforms

* Fix optimistic updates with transforms
2025-09-16 18:49:37 -04:00
Luis Paolini
e08969edd9 Document password handling for special characters (#63590)
* Document password handling for special characters

* Add section on special character passwords in MariaDB

Added information about handling passwords with special characters in MariaDB connection strings.

* Apply suggestions from code review

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Update docs/databases/connections/mysql.md

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-09-16 19:22:14 -03:00
Aleksandr Lesnenko
b5942d4f36 respect decimals setting when compact formatting is enabled (#63457) 2025-09-16 17:54:07 -04:00
Alex Yarosh
914d56b2c0 docs: transforms draft (#63594)
* skeleton

* transforms

* privileges

* Apply suggestions from code review

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

* edits

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2025-09-16 21:53:21 +00:00
Anton Kulyk
035be556eb Improve the look and feel of charts in dashboards (#62610)
* Fix vertical legend position for cartesian charts

* Set dashcard title font size to 14px

* Decrease legend dot size

* Decrease value labels density for cartesian charts

* Use normal font weight for axis names, labels, etc

* Make y axis split lines solid vs dashed

* Show scalar card title in dashcard header

* Always show pie chart total

* Hide axis names on small dashcards

* Update screenshots

* Fix tests

* Fix number cards font size

* Fix hiding cartesian chart axis

* Fix font styles in other legend components

* Fix e2e tests

* Hide cartesian chart axes independently

* Use different legend style only in dashboards

* Apply min spacing to labels per series

* Fix legend dot size in edit mode

* Update label sizes for funnels and sankey charts

* Use bold font for data labels

* Update screenshots

* prettier

* Update Loki Snapshots

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2025-09-16 21:09:38 +00:00
Cam Saul
1919a6cd62 Trivial middleware, qp.compile, qp.execute => Lib (#63419)
* Trivial preprocessing middleware => Lib

* Handle already-native MBQL 5 queries

* substitute-persisted-query => Lib

* A bunch of post-processing middleware is actually MBQL-5 ready

* Fixx 🔧

* Test fixes 🔧

* More fixes 🔧

* Fix more stuff

* Wow even more test fixes =(

* (Hopefully) final round of test fixes

* Kondo

* More test fixes 🔧

* Fixes 🔧
2025-09-16 22:08:09 +03:00
Jeff Bruemmer
1a16244f86 docs - note on images (#63583) 2025-09-16 13:54:25 -04:00
1673 changed files with 601888 additions and 503804 deletions

View File

@@ -173,6 +173,7 @@
metabase.driver.sql-jdbc.execute/execute-prepared-statement!
metabase.notification.models/create-notification!
metabase.pulse.send/send-pulse!
metabase-enterprise.transforms-python.python-runner-test/execute!
;; Safe in tests, where we typically use a private, temporary index per thread.
metabase.search.core/reindex!
metabase.util/run-count!
@@ -418,6 +419,7 @@
metabase-enterprise.transforms.api-test/with-transform-tags clojure.core/let
metabase-enterprise.transforms.api-test/with-worker-runs clojure.core/let
metabase-enterprise.transforms.test-util/with-transform-cleanup! clojure.core/let
metabase-enterprise.transforms.execute/with-transform-lifecycle clojure.core/let
metabase.actions.test-util/with-actions clojure.core/let
metabase.api.common/let-404 clojure.core/let
metabase.app-db.custom-migrations.util/with-temp-scheduler! clojure.core/fn
@@ -519,6 +521,7 @@
metabase-enterprise.cache.config-test/with-temp-persist-models hooks.common/with-seven-bindings
metabase-enterprise.semantic-search.db.connection/with-migrate-tx hooks.common/let-one-with-optional-value
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
metabase-enterprise.transforms-python.drivers-test/with-test-table hooks.common/with-two-bindings
metabase.api.common.internal-test/with-jetty-server hooks.common/with-one-binding
metabase.api.macros/defendpoint hooks.metabase.api.macros/defendpoint
metabase.app-db.schema-migrations-test.impl/test-migrations hooks.metabase.app-db.schema-migrations-test.impl/test-migrations
@@ -832,7 +835,16 @@
performance-sensitive
{:linters
{:discouraged-var
{clojure.core/update-keys {:message "Use metabase.util.performance/update-keys"}}}}
{clojure.core/every? {:message "Use metabase.util.performance/every?"}
clojure.core/mapv {:message "Use metabase.util.performance/mapv"}
clojure.core/run! {:message "Use metabase.util.performance/run!"}
clojure.core/select-keys {:message "Use metabase.util.performance/select-keys"}
clojure.core/some {:message "Use metabase.util.performance/some"}
clojure.core/update-keys {:message "Use metabase.util.performance/update-keys"}
clojure.walk/keywordize-keys {:message "Use metabase.util.performance/keywordize-keys"}
clojure.walk/postwalk {:message "Use metabase.util.performance/postwalk"}
clojure.walk/prewalk {:message "Use metabase.util.performance/prewalk"}
clojure.walk/walk {:message "Use metabase.util.performance/walk"}}}}
qp-and-driver-source-namespaces
{:linters

View File

@@ -502,6 +502,7 @@
metabase.embedding.init
metabase.embedding.jwt
metabase.embedding.settings
metabase.embedding.util
metabase.embedding.validation}
:uses #{analytics
api
@@ -516,6 +517,7 @@
public-sharing
queries
query-processor
request
server
settings
tiles
@@ -547,6 +549,13 @@
settings
util}}
glossary
{:team "Workflows"
:api #{metabase.glossary.api}
:uses #{api
models
util}}
indexed-entities
{:team "UX West"
:api #{metabase.indexed-entities.api
@@ -885,6 +894,19 @@
enterprise/advanced-permissions
enterprise/sandbox}}
python-runner
{:team "Workflows"
:api #{metabase.python-runner.api
metabase.python-runner.core
metabase.python-runner.init}
:uses #{api
driver
models
query-processor
request
util
enterprise/transforms}}
queries
{:team "Querying"
:api #{metabase.queries.api
@@ -930,6 +952,7 @@
:uses #{api
legacy-mbql
lib
lib-be
models
permissions
query-processor
@@ -1012,6 +1035,7 @@
metabase.request.schema}
:uses #{api
config
embedding
permissions ; FIXME
session
settings
@@ -1217,6 +1241,7 @@
events
lib
models
premium-features
query-processor
settings
task
@@ -1462,6 +1487,7 @@
driver
legacy-mbql
lib
lib-be
models
queries
query-permissions
@@ -1617,6 +1643,7 @@
:api #{metabase-enterprise.comments.api
metabase-enterprise.comments.init}
:uses #{api
channel
events
models
request
@@ -1721,7 +1748,6 @@
settings
util}}
enterprise/embedding-hub
{:team "Embedding"
:api #{metabase-enterprise.embedding-hub.api}
@@ -1805,10 +1831,12 @@
pulse
query-processor
request
search
server
settings
sync
system
task
users
util
warehouse-schema
@@ -1969,8 +1997,11 @@
{:team "Querying"
:api #{metabase-enterprise.transforms.api
metabase-enterprise.transforms.core
metabase-enterprise.transforms.init}
:uses #{api
metabase-enterprise.transforms.init
metabase-enterprise.transforms.instrumentation
metabase-enterprise.transforms.util}
:uses #{analytics
api
app-db
config
driver
@@ -1986,7 +2017,25 @@
sync
task
task-history
util}}
upload
util
enterprise/transforms-python}}
enterprise/transforms-python
{:team "Querying"
:api #{metabase-enterprise.transforms-python.api
metabase-enterprise.transforms-python.execute
metabase-enterprise.transforms-python.init
metabase-enterprise.transforms-python.settings}
:uses #{api
app-db
config
driver
models
query-processor
settings
util
enterprise/transforms}}
enterprise/upload-management
{:team "UX West"

View File

@@ -13,7 +13,10 @@
toucan2.core/insert-returning-pk!
toucan2.core/insert-returning-pks!
clojure.core.async/<!!
clojure.core.async/<!
clojure.core.async/>!!
clojure.core.async/>!
clojure.core.async/alts!
clojure.core.async/alts!!
clojure.core.async/close!
clojure.core.async/poll!

View File

@@ -3,7 +3,7 @@
> **Warning**
>
> If that is your first contribution to Metabase, please sign the [Contributor License Agreement](https://docs.google.com/a/metabase.com/forms/d/1oV38o7b9ONFSwuzwmERRMi9SYrhYeOrkbmNaq9pOJ_E/viewform) (unless it's a tiny documentation change). Also, if you're attempting to fix a translation issue, please submit your changes to our [Crowdin project](https://crowdin.com/project/metabase-i18n) instead of opening a PR.
> If that is your first contribution to Metabase, please sign the [Contributor License Agreement](https://docs.google.com/a/metabase.com/forms/d/1oV38o7b9ONFSwuzwmERRMi9SYrhYeOrkbmNaq9pOJ_E/viewform). Also, if you're attempting to fix a translation issue, please submit your changes to our [Crowdin project](https://crowdin.com/project/metabase-i18n) instead of opening a PR.
Closes https://github.com/metabase/metabase/issues/[issue_number]

View File

@@ -74,7 +74,7 @@ runs:
git push -u origin ${BACKPORT_BRANCH}
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${GITHUB_ACTOR}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "${PR_BODY}")
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${{ github.event.pull_request.user.login }}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "${PR_BODY}")
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT

172
.github/scripts/average-e2e-timings.js vendored Normal file
View File

@@ -0,0 +1,172 @@
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const collectAndAverageTimings = async ({
github,
context,
daysBack = 7,
existingTimingsPath = "e2e/support/timings.json",
}) => {
try {
const artifacts = await findMergedTimingArtifacts({
github,
context,
daysBack,
});
if (artifacts.length === 0) {
throw new Error("No timing artifacts found in the specified time range");
}
const timingData = await downloadAndExtractArtifacts(
artifacts,
token,
github,
context,
);
if (timingData.length === 0) {
throw new Error("No valid timing data extracted from artifacts");
}
const averagedTimings = averageTimings(timingData);
averagedTimings.durations.sort((a, b) => a.spec.localeCompare(b.spec));
fs.writeFileSync(
existingTimingsPath,
JSON.stringify(averagedTimings, null, 2),
);
console.log(`✅ Updated ${existingTimingsPath} with new timing data`);
return { artifactCount: timingData.length };
} catch (error) {
console.error("❌ Error in collectAndAverageTimings:", error.message);
throw error;
}
};
const findMergedTimingArtifacts = async ({ github, context, daysBack }) => {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysBack);
console.log(
`Searching for merged timing artifacts created after ${cutoffDate.toISOString()}`,
);
const artifacts = [];
let page = 1;
while (true) {
try {
const { data } = await github.rest.actions.listArtifactsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
name: "merged-e2e-timings",
per_page: 100,
page,
});
const validArtifacts = data.artifacts
.filter((a) => new Date(a.created_at) > cutoffDate && !a.expired)
.map(({ name, created_at, id }) => ({ name, created_at, id }));
artifacts.push(...validArtifacts);
if (
data.artifacts.some((a) => new Date(a.created_at) <= cutoffDate) ||
data.artifacts.length < 100
) {
break;
}
page++;
} catch (error) {
console.log(
`Error fetching artifacts page ${page - 1}: ${error.message}`,
);
break;
}
}
console.log(`Found ${artifacts.length} merged timing artifacts total`);
return artifacts;
};
const downloadAndExtractArtifacts = async (artifacts, github, context) => {
const tempDir = "timing-artifacts-temp";
fs.mkdirSync(tempDir, { recursive: true });
const timingData = [];
for (const artifact of artifacts) {
try {
console.log(`Downloading artifact: ${artifact.name}`);
const zipPath = path.join(tempDir, `${artifact.name}.zip`);
const extractPath = path.join(tempDir, artifact.name);
const { data } = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: "zip",
});
fs.writeFileSync(zipPath, Buffer.from(data));
fs.mkdirSync(extractPath, { recursive: true });
execSync(`unzip -q "${zipPath}" -d "${extractPath}"`);
const timingFilePath = path.join(extractPath, "runTimings.json");
if (fs.existsSync(timingFilePath)) {
const content = JSON.parse(fs.readFileSync(timingFilePath, "utf8"));
if (content.durations?.length) {
timingData.push(content);
console.log(
`✅ Extracted timing data from ${artifact.name} (${content.durations.length} specs)`,
);
} else {
console.log(`⚠ Invalid timing data structure in ${artifact.name}`);
}
} else {
console.log(`⚠ No timing data found in ${artifact.name}`);
}
fs.rmSync(extractPath, { recursive: true });
} catch (error) {
console.log(`❌ Error processing ${artifact.name}: ${error.message}`);
}
}
fs.rmSync(tempDir, { recursive: true });
console.log(
`Successfully extracted timing data from ${timingData.length}/${artifacts.length} artifacts`,
);
return timingData;
};
const averageTimings = (timingDataArray) => {
if (!timingDataArray.length) {
throw new Error("No timing data to process");
}
const specTimings = timingDataArray
.flatMap((data) => data.durations)
.filter((item) => item.spec && typeof item.duration === "number")
.reduce((acc, { spec, duration }) => {
(acc[spec] = acc[spec] || []).push(duration);
return acc;
}, {});
const durations = Object.entries(specTimings).map(([spec, durations]) => ({
spec,
duration: Math.round(
durations.reduce((sum, d) => sum + d, 0) / durations.length,
),
}));
return { durations };
};
module.exports = { collectAndAverageTimings, averageTimings };

View File

@@ -0,0 +1,132 @@
const { averageTimings } = require("./average-e2e-timings");
describe("averageTimings", () => {
it("should throw error when given empty array", () => {
expect(() => averageTimings([])).toThrow("No timing data to process");
});
it("should work for n=1", () => {
const input = [
{
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
},
];
const result = averageTimings(input);
expect(result.durations).toHaveLength(2);
expect(result.durations).toContainEqual({
spec: "test1.cy.js",
duration: 1000,
});
expect(result.durations).toContainEqual({
spec: "test2.cy.js",
duration: 2000,
});
});
it("should calculate average for multiple specs", () => {
const input = [
{
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
},
{
durations: [
{ spec: "test1.cy.js", duration: 2000 },
{ spec: "test2.cy.js", duration: 4000 },
],
},
];
const result = averageTimings(input);
expect(result.durations).toHaveLength(2);
expect(result.durations).toContainEqual({
spec: "test1.cy.js",
duration: 1500,
});
expect(result.durations).toContainEqual({
spec: "test2.cy.js",
duration: 3000,
});
});
it("should round averages to nearest integer", () => {
const input = [
{
durations: [{ spec: "test1.cy.js", duration: 1001 }],
},
{
durations: [{ spec: "test1.cy.js", duration: 1002 }],
},
];
const result = averageTimings(input);
expect(result.durations[0].duration).toBe(1002);
});
it("should ignore invalid entries", () => {
const input = [
{
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: "invalid" },
{ duration: 2000 }, // missing spec
{ spec: "test3.cy.js", duration: 3000 },
],
},
];
const result = averageTimings(input);
expect(result.durations).toHaveLength(2);
expect(result.durations).toContainEqual({
spec: "test1.cy.js",
duration: 1000,
});
expect(result.durations).toContainEqual({
spec: "test3.cy.js",
duration: 3000,
});
});
it("should handle merging different specs", () => {
const input = [
{
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
},
{
durations: [
{ spec: "test1.cy.js", duration: 3000 },
{ spec: "test3.cy.js", duration: 1000 },
],
},
];
const result = averageTimings(input);
expect(result.durations).toHaveLength(3);
expect(result.durations).toContainEqual({
spec: "test1.cy.js",
duration: 2000,
});
expect(result.durations).toContainEqual({
spec: "test2.cy.js",
duration: 2000,
});
expect(result.durations).toContainEqual({
spec: "test3.cy.js",
duration: 1000,
});
});
});

View File

@@ -0,0 +1,39 @@
const extractChangedTimings = ({ oldTimings, newTimings }) => {
const oldTimingMap =
oldTimings.durations?.reduce((map, item) => {
if (item.spec) {
map[item.spec] = item.duration;
}
return map;
}, {}) || {};
const changedDurations = newTimings.durations
.filter((item) => {
if (!item || !item.spec || typeof item.duration !== "number") {
return false;
}
const newSpec = convertPathFormat(item.spec);
const oldDuration = oldTimingMap[newSpec] || oldTimingMap[item.spec];
// Include if it's a new spec or the duration has changed
return oldDuration === undefined || oldDuration !== item.duration;
})
.map((item) => ({
spec: convertPathFormat(item.spec),
duration: item.duration,
}));
return {
changedTimings: { durations: changedDurations },
hasChanges: changedDurations.length > 0,
};
};
// Convert cypress-split path format to timings.json path format
// from "e2e/test/scenarios/..." to "../test/scenarios/..."
function convertPathFormat(specPath) {
return specPath.startsWith("e2e/test/")
? specPath.replace("e2e/test/", "../test/")
: specPath;
}
module.exports = { extractChangedTimings };

View File

@@ -0,0 +1,195 @@
const { extractChangedTimings } = require("./extract-changed-timings");
describe("extractChangedTimings", () => {
it("should return empty durations when no changes", () => {
const oldTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
expect(result.changedTimings.durations).toHaveLength(0);
expect(result.hasChanges).toBe(false);
});
it("should detect new specs", () => {
const oldTimings = {
durations: [{ spec: "test1.cy.js", duration: 1000 }],
};
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
expect(result.changedTimings.durations).toHaveLength(1);
expect(result.changedTimings.durations[0]).toEqual({
spec: "test2.cy.js",
duration: 2000,
});
expect(result.hasChanges).toBe(true);
});
it("should detect duration changes", () => {
const oldTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2200 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
expect(result.changedTimings.durations).toHaveLength(1);
expect(result.changedTimings.durations[0]).toEqual({
spec: "test2.cy.js",
duration: 2200,
});
expect(result.hasChanges).toBe(true);
});
it("should handle missing old timings", () => {
const oldTimings = { durations: [] };
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
// All timings should be considered "changed" since old timings is empty
expect(result.changedTimings.durations).toHaveLength(2);
expect(result.changedTimings.durations).toContainEqual({
spec: "test1.cy.js",
duration: 1000,
});
expect(result.changedTimings.durations).toContainEqual({
spec: "test2.cy.js",
duration: 2000,
});
expect(result.hasChanges).toBe(true);
});
it("should handle invalid timing entries", () => {
const oldTimings = {
durations: [{ spec: "test1.cy.js", duration: 1000 }],
};
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: "invalid" },
{ duration: 2000 }, // missing spec
{ spec: "test3.cy.js", duration: 3000 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
// Only test3.cy.js should be included (new spec with valid data)
expect(result.changedTimings.durations).toHaveLength(1);
expect(result.changedTimings.durations[0]).toEqual({
spec: "test3.cy.js",
duration: 3000,
});
expect(result.hasChanges).toBe(true);
});
it("should handle timing data with different specs", () => {
const oldTimings = {
durations: [
{ spec: "test1.cy.js", duration: 1000 },
{ spec: "test2.cy.js", duration: 2000 },
],
};
const newTimings = {
durations: [
{ spec: "test1.cy.js", duration: 3000 },
{ spec: "test3.cy.js", duration: 1000 },
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
expect(result.changedTimings.durations).toHaveLength(2);
expect(result.changedTimings.durations).toContainEqual({
spec: "test1.cy.js",
duration: 3000,
});
expect(result.changedTimings.durations).toContainEqual({
spec: "test3.cy.js",
duration: 1000,
});
expect(result.hasChanges).toBe(true);
});
it("should convert cypress-split paths to relative paths", () => {
const oldTimings = {
durations: [
{
spec: "../test/scenarios/dashboard/dashboard.cy.spec.js",
duration: 1000,
},
{
spec: "../test/scenarios/admin/databases.cy.spec.js",
duration: 2000,
},
],
};
const newTimings = {
durations: [
{
spec: "e2e/test/scenarios/dashboard/dashboard.cy.spec.js",
duration: 1200,
},
{
spec: "e2e/test/scenarios/admin/databases.cy.spec.js",
duration: 2000,
},
{
spec: "e2e/test/scenarios/collections/collections.cy.spec.js",
duration: 1500,
},
],
};
const result = extractChangedTimings({ oldTimings, newTimings });
expect(result.changedTimings.durations).toHaveLength(2);
expect(result.changedTimings.durations).toContainEqual({
spec: "../test/scenarios/dashboard/dashboard.cy.spec.js",
duration: 1200,
});
expect(result.changedTimings.durations).toContainEqual({
spec: "../test/scenarios/collections/collections.cy.spec.js",
duration: 1500,
});
expect(result.hasChanges).toBe(true);
});
});

30
.github/scripts/setup-python-runner.sh vendored Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
set -e
# Parse command line arguments
SKIP_START=false
while [[ $# -gt 0 ]]; do
case $1 in
--skip-start)
SKIP_START=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--skip-start]"
exit 1
;;
esac
done
echo "Cloning python-runner repository..."
rm -r python-runner
git clone --depth 1 https://${METABASE_AUTOMATION_USER_TOKEN}@github.com/metabase/python-runner-container.git python-runner
if [ "$SKIP_START" = false ]; then
echo "Starting python-runner..."
cd python-runner
make run-ci
cd ..
fi

View File

@@ -35,21 +35,21 @@ jobs:
build-static-viz: false
test-args: >-
:only '"enterprise/backend/test"'
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 1)
build-static-viz: true
test-args: >-
:only '"test"'
:partition/total 2
:partition/index 0
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 2)
build-static-viz: true
test-args: >-
:only '"test"'
:partition/total 2
:partition/index 1
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
services:
mysql:
@@ -147,21 +147,21 @@ jobs:
build-static-viz: false
test-args: >-
:only '"enterprise/backend/test"'
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 1)
build-static-viz: true
test-args: >-
:only '"test"'
:partition/total 2
:partition/index 0
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 2)
build-static-viz: true
test-args: >-
:only '"test"'
:partition/total 2
:partition/index 1
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
services:
mysql:
@@ -259,11 +259,13 @@ jobs:
build-static-viz: false
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
exclude-tag: ':mb/transforms-python-test'
- name: Enterprise Tests
build-static-viz: false
test-args: >-
:only '"enterprise/backend/test"'
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 1)
build-static-viz: true
test-args: >-
@@ -271,14 +273,14 @@ jobs:
:exclude-tags [:mb/driver-tests]
:partition/total 2
:partition/index 0
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
- name: EE App DB Tests (Part 2)
build-static-viz: true
test-args: >-
:only '"test"'
:partition/total 2
:partition/index 1
exclude-tag: ':mb/driver-tests'
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
env:
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests

View File

@@ -6,11 +6,19 @@ on:
jobs:
create-backport:
if: | # the PR must be merged and have a backport label
if:
| # the PR must have a backport label when it's merged, or be labeled with a backport label after merge
github.event.pull_request.merged == true && (
contains(github.event.pull_request.labels.*.name, 'backport') ||
contains(github.event.pull_request.labels.*.name, 'double-backport') ||
contains(github.event.pull_request.labels.*.name, 'triple-backport')
github.event.action == 'closed' && (
contains(github.event.pull_request.labels.*.name, 'backport') ||
contains(github.event.pull_request.labels.*.name, 'double-backport') ||
contains(github.event.pull_request.labels.*.name, 'triple-backport')
) ||
github.event.action == 'labeled' && (
github.event.label.name == 'backport' ||
github.event.label.name == 'double-backport' ||
github.event.label.name == 'triple-backport'
)
)
runs-on: ubuntu-latest
steps:

View File

@@ -689,25 +689,57 @@ jobs:
junit-name: 'be-tests-starburst-ee'
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
# TODO(rileythomp, 2025-08-20): Re-enable vertica tests once the docker image is updated
# be-tests-vertica-ee:
# if: ${{ false && inputs.driver == 'Vertica' }}
# runs-on: ubuntu-22.04
# timeout-minutes: 90
# env:
# CI: 'true'
# DRIVERS: vertica
# services:
# vertica:
# image: vertica/vertica-ce:12.0.2-0
# ports:
# - "5433:5433"
# steps:
# - uses: actions/checkout@v4
# - name: Make plugins directory
# run: mkdir plugins
# - name: Test Vertica driver
# uses: ./.github/actions/test-driver
# with:
# junit-name: 'be-tests-vertica-ee'
# test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
login-to-ecr:
if: inputs.driver == 'Vertica'
runs-on: ubuntu-22.04
timeout-minutes: 15
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Get ECR password
id: login-ecr
run: |
PASSWORD=$(aws ecr get-login-password --region us-east-1)
REGISTRY="$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com"
echo "docker_username=AWS" >> $GITHUB_OUTPUT
echo "docker_password=$PASSWORD" >> $GITHUB_OUTPUT
echo "registry=$REGISTRY" >> $GITHUB_OUTPUT
outputs:
registry: ${{ steps.login-ecr.outputs.registry }}
docker_username: ${{ steps.login-ecr.outputs.docker_username }}
docker_password: ${{ steps.login-ecr.outputs.docker_password }}
be-tests-vertica-ee:
if: inputs.driver == 'Vertica'
runs-on: ubuntu-22.04
needs: login-to-ecr
timeout-minutes: 90
env:
CI: 'true'
DRIVERS: vertica
services:
vertica:
image: ${{ needs.login-to-ecr.outputs.registry }}/vertica-ce:25.3.0-0
ports:
- "5433:5433"
credentials:
username: ${{ needs.login-to-ecr.outputs.docker_username }}
password: ${{ needs.login-to-ecr.outputs.docker_password }}
steps:
- uses: actions/checkout@v4
- name: Make plugins directory
run: mkdir plugins
- name: Test Vertica driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-vertica-ee'
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"

View File

@@ -90,6 +90,7 @@ jobs:
junit-name: 'be-tests-athena-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -106,6 +107,19 @@ jobs:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
# DS 2025-09-26 temporarily disabled while we diagnose flakes
# - name: Transforms Python Tests
# test-args: >-
# :only-tags [:mb/transforms-python-test]
# needs-python-runner: true
env:
CI: 'true'
DRIVERS: bigquery-cloud-sdk
@@ -116,15 +130,19 @@ jobs:
MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING }}
name: BigQuery
name: BigQuery ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test BigQuery Cloud SDK driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-bigquery-cloud-sdk-ee'
test-args: >-
:only-tags [:mb/driver-tests]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -141,6 +159,18 @@ jobs:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Transforms Python Tests
test-args: >-
:only-tags [:mb/transforms-python-test]
needs-python-runner: true
env:
CI: 'true'
DRIVERS: clickhouse
@@ -148,9 +178,14 @@ jobs:
MB_CLICKHOUSE_TEST_PORT: 8123
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
name: Clickhouse
name: Clickhouse ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Add ClickHouse TLS instance to /etc/hosts
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
@@ -170,8 +205,7 @@ jobs:
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-clickhouse-ee'
test-args: >-
:only-tags [:mb/driver-tests]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -208,6 +242,7 @@ jobs:
junit-name: 'be-tests-druid-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -244,6 +279,7 @@ jobs:
junit-name: 'be-tests-druid-jdbc-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -280,6 +316,7 @@ jobs:
junit-name: 'be-tests-databricks-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -317,6 +354,7 @@ jobs:
junit-name: 'be-tests-databricks-multi-catalog-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -338,12 +376,17 @@ jobs:
name: H2
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test H2 driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-h2'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -388,6 +431,11 @@ jobs:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Transforms Python Tests
test-args: >-
:only-tags [:mb/transforms-python-test]
needs-python-runner: true
services:
mysql:
@@ -442,6 +490,11 @@ jobs:
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
#if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test ${{ matrix.version.name }}
uses: ./.github/actions/test-driver
with:
@@ -476,6 +529,15 @@ jobs:
- name: MongoDB Latest
junit-name: be-tests-mongo-latest-ee
image: mongo:latest
job:
- name: Driver Tests
test-args: >-
:exclude-tags '[:mongo-sharded-cluster-tests :mb/transforms-python-test]'
:only-tags [:mb/driver-tests]
- name: Transforms Python Tests
test-args: >-
:only-tags [:mb/transforms-python-test]
needs-python-runner: true
env:
CI: 'true'
DRIVERS: mongo
@@ -489,16 +551,19 @@ jobs:
env:
MONGO_INITDB_ROOT_USERNAME: metabase
MONGO_INITDB_ROOT_PASSWORD: metasample123
name: ${{ matrix.version.name }}
name: ${{ matrix.version.name }} ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test ${{ matrix.version.name }}
uses: ./.github/actions/test-driver
with:
junit-name: ${{ matrix.version.junit-name }}
test-args: >-
:exclude-tags [:mongo-sharded-cluster-tests]
:only-tags [:mb/driver-tests]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -562,7 +627,7 @@ jobs:
with:
junit-name: ${{ matrix.version.junit-name }}
test-args: >-
:exclude-tags [:mongo-sharded-cluster-tests]
:exclude-tags '[:mongo-sharded-cluster-tests :mb/transforms-python-test]'
:only-tags [:mb/driver-tests]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
@@ -670,6 +735,7 @@ jobs:
junit-name: ${{ matrix.version.junit-name }}
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -704,6 +770,11 @@ jobs:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Transforms Python Tests
test-args: >-
:only-tags [:mb/transforms-python-test]
needs-python-runner: true
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
env:
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
@@ -745,6 +816,11 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
#if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test ${{ matrix.version.name }} (${{ matrix.job.name }})
uses: ./.github/actions/test-driver
with:
@@ -821,6 +897,7 @@ jobs:
junit-name: 'be-tests-presto-jdbc-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -850,8 +927,14 @@ jobs:
skip: false
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/upload-tests]
:exclude-tags '[:mb/upload-tests :mb/transforms-python-test]'
:fail-fast? true
- name: Redshift Transforms Python Tests
skip: false
test-args: >-
:only-tags [:mb/transforms-python-test]
:fail-fast? true
needs-python-runner: true
env:
CI: 'true'
@@ -864,6 +947,11 @@ jobs:
name: ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test ${{ matrix.job.name }}
if: ${{ !matrix.job.skip }}
uses: ./.github/actions/test-driver
@@ -887,6 +975,19 @@ jobs:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
# DS 2025-09-26 temporarily disabled while we diagnose flakes
# - name: Transforms Python Tests
# test-args: >-
# :only-tags [:mb/transforms-python-test]
# needs-python-runner: true
env:
CI: 'true'
DRIVERS: snowflake
@@ -905,15 +1006,19 @@ jobs:
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
name: Snowflake
name: Snowflake ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test Snowflake driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-snowflake-ee'
test-args: >-
:only-tags [:mb/driver-tests]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -948,6 +1053,7 @@ jobs:
junit-name: 'be-tests-sparksql-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -977,6 +1083,7 @@ jobs:
junit-name: 'be-tests-sqlite-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -1004,6 +1111,15 @@ jobs:
- name: SQL Server 2022
junit-name: be-tests-sqlserver-2022-ee
image: mcr.microsoft.com/mssql/server:2022-latest
job:
- name: Driver Tests
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Transforms Python Tests
test-args: >-
:only-tags [:mb/transforms-python-test]
needs-python-runner: true
env:
CI: 'true'
DRIVERS: sqlserver
@@ -1019,15 +1135,19 @@ jobs:
ACCEPT_EULA: Y
SA_PASSWORD: 'P@ssw0rd'
MSSQL_MEMORY_LIMIT_MB: 1024
name: ${{ matrix.version.name }}
name: ${{ matrix.version.name }} ${{ matrix.job.name }}
steps:
- uses: actions/checkout@v4
- name: Setup python-runner
if: ${{ matrix.job.needs-python-runner == true }}
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Test ${{ matrix.version.name }}
uses: ./.github/actions/test-driver
with:
junit-name: ${{ matrix.version.junit-name }}
test-args: >-
:only-tags [:mb/driver-tests]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -1079,42 +1199,75 @@ jobs:
# aws-region: ${{ vars.AWS_REGION }}
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
# TODO(rileythomp, 2025-08-20): Re-enable vertica tests once the docker image is updated
# be-tests-vertica-ee:
# needs: determine-driver-skips
# if: ${{ false && !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
# runs-on: ubuntu-22.04
# timeout-minutes: 60
# env:
# CI: 'true'
# DRIVERS: vertica
# services:
# vertica:
# image: vertica/vertica-ce:12.0.2-0
# ports:
# - "5433:5433"
# name: Vertica
# steps:
# - uses: actions/checkout@v4
# - name: Make plugins directory
# run: mkdir plugins
# - name: Test Vertica driver
# uses: ./.github/actions/test-driver
# with:
# junit-name: 'be-tests-vertica-ee'
# test-args: >-
# :only-tags [:mb/driver-tests]
# - name: Upload Test Results
# uses: ./.github/actions/upload-test-results
# if: always()
# with:
# input-path: ./target/junit/
# output-name: ${{ github.job }}
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
# aws-region: ${{ vars.AWS_REGION }}
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
login-to-ecr:
needs: determine-driver-skips
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
timeout-minutes: 15
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Get ECR password
id: login-ecr
run: |
PASSWORD=$(aws ecr get-login-password --region us-east-1)
REGISTRY="$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com"
echo "docker_username=AWS" >> $GITHUB_OUTPUT
echo "docker_password=$PASSWORD" >> $GITHUB_OUTPUT
echo "registry=$REGISTRY" >> $GITHUB_OUTPUT
outputs:
registry: ${{ steps.login-ecr.outputs.registry }}
docker_username: ${{ steps.login-ecr.outputs.docker_username }}
docker_password: ${{ steps.login-ecr.outputs.docker_password }}
be-tests-vertica-ee:
needs: [determine-driver-skips, login-to-ecr]
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
runs-on: ubuntu-22.04
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: vertica
services:
vertica:
image: ${{ needs.login-to-ecr.outputs.registry }}/vertica-ce:25.3.0-0
ports:
- "5433:5433"
credentials:
username: ${{ needs.login-to-ecr.outputs.docker_username }}
password: ${{ needs.login-to-ecr.outputs.docker_password }}
name: Vertica
steps:
- uses: actions/checkout@v4
- name: Make plugins directory
run: mkdir plugins
- name: Test Vertica driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-vertica-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
with:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
drivers-tests-result:
needs:

View File

@@ -60,6 +60,14 @@ jobs:
CYPRESS_IS_EMBEDDING_SDK: "${{ inputs.name == 'embedding-sdk' }}"
steps:
- uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Setup python-runner
env:
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: ./.github/scripts/setup-python-runner.sh
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
@@ -123,7 +131,7 @@ jobs:
SPLIT_INDEX: ${{ inputs.split_index }}
SPLIT_FILE: "./e2e/support/timings.json"
SPLIT_OUTPUT_FILE: "newTimes.json"
SPLIT_TIME_THRESHOLD: 5.0
SPLIT_TIME_THRESHOLD: 0.01 # 1% threshold for changes
# do not write cypress-split summary to github
SPLIT_SUMMARY: false
SPEC_PATH: ${{ env.specs || inputs.specs }}
@@ -143,7 +151,7 @@ jobs:
run: |
echo "Running tests with specs: $SPEC_PATH"
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="${{inputs.tags}}+-@skip",grepOmitFiltered=true \
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}",grepOmitFiltered=true \
--spec "$SPEC_PATH" \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
@@ -177,6 +185,51 @@ jobs:
name: failed-tests-${{ inputs.name }}-${{ inputs.edition }}
path: ./cypress/test-results
- name: Extract chunk-specific changed timings
id: extract-timings
if: inputs.split_index >= 0 && github.ref == 'refs/heads/master'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const oldTimingsPath = "./e2e/support/timings.json";
const newTimingsPath = "./e2e/support/newTimes.json";
if (!fs.existsSync(newTimingsPath)) {
throw new Error(`newTimes.json not found - cypress-split may not have generated it`);
}
if (!fs.existsSync(oldTimingsPath)) {
throw new Error(`Original timings file ${oldTimingsPath} not found`);
}
const oldTimings = fs.existsSync(oldTimingsPath)
? JSON.parse(fs.readFileSync(oldTimingsPath, "utf8"))
: { durations: [] };
const newTimings = fs.existsSync(newTimingsPath)
? JSON.parse(fs.readFileSync(newTimingsPath, "utf8"))
: { durations: [] };
const { extractChangedTimings } = require('${{ github.workspace }}/.github/scripts/extract-changed-timings.js');
const result = extractChangedTimings({ oldTimings, newTimings });
if (result.hasChanges) {
core.setOutput('has-changes', 'true');
fs.writeFileSync('chunkTimings.json', JSON.stringify(result.changedTimings, null, 2));
console.log(`Extracted ${Object.keys(result.changedTimings).length} changed timings for chunk index ${{ inputs.split_index }}`);
}
- name: Upload Timing Data
uses: actions/upload-artifact@v4
if: steps.extract-timings.outcome == 'success' && steps.extract-timings.outputs.has-changes == 'true'
with:
name: timing-data-${{ inputs.name }}-${{ inputs.split_index }}
path: chunkTimings.json
retention-days: 1 # gets merged immediately by parent workflow
- name: Publish Summary
if: failure()
uses: actions/github-script@v7

View File

@@ -87,6 +87,67 @@ jobs:
name: Flaky
tags: "@flaky"
collect-timings:
name: Collect E2E Timings
runs-on: ubuntu-22.04
timeout-minutes: 10
needs: e2e-tests
if: always() && !cancelled() && github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
.github
e2e/support
sparse-checkout-cone-mode: false
- name: Download all timing artifacts
uses: actions/download-artifact@v4
with:
pattern: timing-data-*
path: timing-files
merge-multiple: false
continue-on-error: true
- name: Merge split timings
id: merge
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const chunkFiles = glob.sync('timing-files/*/chunkTimings.json');
const specs = new Map();
chunkFiles.forEach(file => {
const { durations = [] } = JSON.parse(fs.readFileSync(file, 'utf8'));
durations.forEach(({ spec, duration }) => specs.set(spec, duration));
});
if (specs.size === 0) {
throw new Error('No timing data found - no specs had timing changes');
}
const outputPath = 'e2e/support/runTimings.json';
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
const runTimings = {
durations: [...specs].map(([spec, duration]) => ({ spec, duration }))
};
fs.writeFileSync(outputPath, JSON.stringify(runTimings, null, 2));
console.log(`Merged timing data for ${specs.size} specs`);
- name: Upload merged timing files
if: steps.merge.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: merged-e2e-timings
path: e2e/support/runTimings.json
retention-days: 7
e2e-tests-result:
needs:
- e2e-tests

View File

@@ -26,26 +26,8 @@ jobs:
- name: Compile CLJS
run: yarn build-pure:cljs
- name: Build Storybook
# We won't be able to use Storybook play's `userEvent` with production build
# see https://github.com/storybookjs/storybook/issues/19758
run: NODE_ENV=development yarn build-storybook
- name: Serve Storybook
run: yarn http-server storybook-static -p 6006 &
shell: bash
- name: Wait for Storybook to become available
run: |
echo "Waiting for Storybook to be served..."
while ! curl --silent --fail http://localhost:6006; do
sleep 5
echo "Retrying..."
done
echo "Storybook is being served!"
- name: Update Loki Snapshots
run: yarn test-visual:loki || yarn test-visual:loki-approve-diff && yarn test-visual:loki-prune
run: yarn test-visual:loki:ci || yarn test-visual:loki-approve-diff && yarn test-visual:loki-prune
- name: Check for changes
id: check-changes

View File

@@ -53,14 +53,8 @@ jobs:
- name: Compile CLJS
run: NODE_ENV=development yarn build-pure:cljs
- name: Build Storybook
# We won't be able to use Storybook play's `userEvent` with production build
# see https://github.com/storybookjs/storybook/issues/19758
run: NODE_ENV=development yarn build-storybook
- name: Run Loki Visual Tests
run: |
yarn loki --requireReference --reactUri file:./storybook-static --chromeFlags='--headless --disable-gpu' --verboseRenderer
run: yarn test-visual:loki:ci
- name: Generate Visual Report on Failure
if: failure()

View File

@@ -8,6 +8,7 @@ on:
pull_request:
types: [opened, synchronize, reopened]
merge_group:
workflow_dispatch:
jobs:
files-changed:
@@ -74,7 +75,7 @@ jobs:
secrets: inherit
backend-tests:
if: ${{ !cancelled() }}
if: ${{ !cancelled() && needs.files-changed.result == 'success' }}
needs: [files-changed, static-viz-files-changed]
uses: ./.github/workflows/backend.yml
secrets: inherit
@@ -112,7 +113,7 @@ jobs:
e2e-tests:
needs: [files-changed, uberjar]
if: always() && !cancelled()
if: always() && !cancelled() && needs.files-changed.result == 'success'
uses: ./.github/workflows/e2e-tests.yml
secrets: inherit
with:

View File

@@ -0,0 +1,92 @@
name: Update E2E Timings
permissions:
contents: write
pull-requests: write
on:
workflow_dispatch:
inputs:
days_back:
description: "Number of days to look back for timing artifacts"
type: number
default: 7
required: false
schedule:
# Run weekly on Sundays at 6 AM UTC
- cron: "0 6 * * 0"
jobs:
collect-and-average-timings:
runs-on: ubuntu-22.04
timeout-minutes: 30
env:
DAYS_BACK: ${{ inputs.days_back || 7 }}
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Collect and average timing data
id: collect-timings
uses: actions/github-script@v7
with:
script: |
const { collectAndAverageTimings } = require('${{ github.workspace }}/.github/scripts/average-e2e-timings.js');
const result = await collectAndAverageTimings({
github,
context,
token: '${{ secrets.GITHUB_TOKEN }}',
daysBack: ${{ env.DAYS_BACK }},
existingTimingsPath: 'e2e/support/timings.json'
});
core.setOutput('artifact-count', result.artifactCount);
- name: Set up git configuration
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
BRANCH_NAME="update-e2e-timings-$(date +%Y%m%d-%H%M%S)"
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
echo "Branch name will be: $BRANCH_NAME"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ env.BRANCH_NAME }}
base: master
commit-message: |
Update E2E test timings with averaged data
Collected timing data from ${{ env.DAYS_BACK }} 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 <actions@github.com>
committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
title: "[chore] Update E2E test timings with recent data"
body: |
## Summary
This PR updates the E2E test timing data with averaged values from recent test runs.
## Details
- Collected data from the last **${{ env.DAYS_BACK }} days** of test runs
- Averaged timing data from **${{ steps.collect-timings.outputs.artifact-count }}** test runs
## Generated automatically
🤖 This PR was created automatically by the `update-e2e-timings` workflow.
The timing data helps cypress-split optimize test distribution across parallel jobs.
Co-Authored-By: GitHub Actions <actions@github.com>
labels: |
.CI & Tests
add-paths: e2e/support/timings.json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

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