Compare commits

...

333 Commits

Author SHA1 Message Date
Cal Herries
14a34eea89 Add test for scheduled sync tasks 2024-04-23 18:41:54 +01:00
Cal Herries
6303690433 Add missing permissions 2024-04-23 15:41:30 +01:00
Cal Herries
2d603393ef Switch to use the EDN file 2024-04-23 12:17:01 +01:00
Cal Herries
0430591332 Add data_permissions for sample DB 2024-04-23 12:03:03 +01:00
Nemanja Glumac
435f9acbad Re-wire the Groups entity to use RTK Query under the hood (#41480)
* Delete unused endpoint from `PermissionsApi`

* Add group tag helpers

* Define RTK Query 5 main endpoints for `permissionApi` (group)

* Re-wire 5 main api methods in groups entity to work with RTK Query

* Re-wire `clearMember` action to work with RTK Query

* Add `Groups.api.update`

* Update types

* Fix membership failing to update after `CREATE_MEMBERSHIP`

* Make sure `members` is not undefined

* Fix cache invalidation
2024-04-19 16:28:49 +02:00
Noah Moss
79583b9892 [Feature branch] Split data access and query builder access (#41581)
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
Co-authored-by: John Swanson <john.swanson@metabase.com>
Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
2024-04-19 10:13:38 -04:00
Kamil Mielnik
e6fa8f9058 Fix Dispatch type (#41631)
* Fix Dispatch type

* Fix typing

* Fix types
2024-04-19 11:38:53 +00:00
Mahatthana (Kelvin) Nomsawadi
855b32452b Add prefix to CSS custom properties (#41574)
* Migrate --border-*

* Migrate *breadcrumb*

* Migrate --color-bg-black

* Migrate --color-bg-dark

* Migrate --color-bg-light

* Migrate --color-bg-medium

* Migrate --color-bg-white

* Migrate --color-border

* Migrate --color-brand*

* Migrate --color-error

* Migrate --color-focus

* Migrate --color-shadow

* Migrate --color-success

* Migrate --color-text-dark

* Migrate --color-text-light

* Migrate --color-text-medium

* Migrate --color-text-white

* Migrate --color-white

* Migrate --default-border-radius

* Migrate --default-button-border-radius

* Migrate --default-font-family

* Migrate --default-font-size

* Migrate --default-header-margin

* Migrate --gap-1

* Migrate --icon-width

* Migrate --input-border-color

* Migrate --input-border-radius

* Migrate --margin-*

* Migrate --muted-color

* Migrate --padding-*

* Migrate --page-header-padding

* Migrate --subtitle-color

* Migrate --title-color

* Reset to master

* Remove unused --color-text-default

* Remove unused --color*

* Remove unused CSS custom properties

* Prefix only necessary CSS custom properties
2024-04-19 12:18:49 +01:00
Oisin Coveney
8e4632208a Convert EmbeddingContext to Sdk Store slice (#41585) 2024-04-19 13:59:35 +03:00
Uladzimir Havenchyk
5b9522cfc9 Add a way to update filter type and operator from parameter sidebar (#41555) 2024-04-19 12:01:45 +03:00
Nemanja Glumac
2ab84940ed Introduce task api using RTK Query and use it to replace Tasks entity (#41452)
* Throw for unused Tasks endpoints

* RTK Query `taskApi`

* Wire tasks entity list and get endpoints to use RTK Query

* Fix typo

* Expose `taskApi`

* Rework `TaskModal` to use RTK Query api

* Throw on `Tasks.api.get`

* Rework `TaskApp` to use RTK Query api

* Delete `Tasks` entity

... which never should've been an entity in the first place.

* Delete unused `TaskApi` entry

* Fix next pagination page not being properly disabled

* Do not export `isLastPage` function

* Convert TasksApp to TS

* Use Mantine Tooltip

* Remove stray comment

* Add empty lines for better legibility

* Update the response type

* Define `task_details` type more strictly

* Use the existing `DatabaseId` type

* Provide tags for `list` and `get` endpoints

* Address code review

* Handle error and loading in the `TasksApp`

* Handle error and loading in the `TaskModal`

* Extract the page size into a variable

* Index databases without mutation

* Refactor TaskModal
2024-04-19 00:54:04 +00:00
John Swanson
370671017c Don't say we'll cache archived models (#41329)
When we actually run the job to cache models, we select "refreshable
models", which selects `PersistedInfo` where:

- the `state` is something refreshable, and

- the question marked for caching is actually a model, and

- the question hasn't been archived.

However, when we selected `PersistedInfo`s to display to the user, we
didn't apply any conditions. Instead, we included PersistedInfo where
the question was not a model, or where the question had been archived.

To prevent confusion, let's hide these.

Fixes https://github.com/metabase/metabase/issues/41318
2024-04-18 13:57:34 -07:00
Ryan Laurie
427fbc1c82 Fix invisible user items in search filters (#41604) 2024-04-18 20:20:02 +00:00
Raphael Krut-Landau
96f9d6c14e [Cache] Add "Invalidate cache now" button (#40972) 2024-04-18 18:26:57 +00:00
Denis Berezin
198ebcf5d1 Fix Embedded dashboard parameters parse runtime error (#41545)
* Fix parameters parse

* Add e2e test

* Add e2e test
2024-04-18 17:15:56 +00:00
Jeff Bruemmer
9797a74c21 add redshift to list of dbs that support uploads (#41594) 2024-04-18 13:01:26 -04:00
Cal Herries
091c4a68d8 Fix null characters in JSON breaking CardRevisionAddType for postgres (#41584) 2024-04-18 16:59:34 +00:00
Raphael Krut-Landau
470df0bf7f [Cache] Schedule strategy follow up (#41420)
* Put Schedule in two columns
* Simplify timezone display
* Support 24-hour format
* Fix bugs
2024-04-18 12:03:02 -04:00
Kamil Mielnik
6bf0ada3c3 Bump dompurify in yarn.lock (#41583) 2024-04-18 15:38:33 +00:00
Ryan Laurie
784c26ac7a Question + Model Picker (#40207)
* WIP questionpicker

* use to replace dashcard content

* much better stuff

* question picker

* values source card modal

* create action form

* awesome hacky fix for crashing model detail page

* use entity picker in sandboxing modal

* deprecate ye olde question picker

* keep old question picker in add-card dashboard sidebar

* better last level check

* update types

* make stuff better

* fix rebase conflicts

* better initial value handling

* update e2e tests

* sweet sweet unit tests

* fix sandbox tests

* fix tests

* type fix

* fixes

* search style improvements

* remove unused prop

* safer path detection

* delayed loading spinner

* change confirm default

* set default collection for placeholder card

* address review comments

* update unit tests

* update e2e tests

* fix re-rebase

* add some happy little comments
2024-04-18 15:25:05 +00:00
Braden Shepherdson
8ff9805d9d [MBQL lib] Allow more filtering options for string PKs (#41477)
The filters for _numeric_ PK and FK filters are slightly different than a general numeric column. That's fine for numeric keys, but with string keys that same set of numeric filters was being returned, and then the ones for strings were removed. That left only `Is` and `Is Not` for a string PK or FK.

This PR allows more flexibility in filtering by string PKs and FKs, by checking the base type of the column and using the right set of filters for the type of key.

Closes #40665.
2024-04-18 08:46:01 -04:00
Mahatthana (Kelvin) Nomsawadi
14f1e19a91 Bump dompurify (#41564) 2024-04-18 13:33:37 +01:00
Nemanja Glumac
ee80dea28a Introduce pagination to RTK list endpoints (#41538)
* Add the pagination type

* Add the pagination helper

* Update types

* Update `user` API

* Update `collection` API

* Update `search` API

* Update `UserListQuery` type

* Explicitly define default pagination inputs

* Remove the pagination helper

* Update and fix types

`limit` and `offset` are actually nullable.

* Separate types (again)

* Fix `PaginationResponse` type
2024-04-18 12:21:16 +00:00
Chris Truter
d521d628ca Increase timeout for with-dynamic-redefs test (#41516) 2024-04-18 12:30:48 +02:00
Alexander Solovyov
fcca920e95 migrate caching settings over to the new cache_config table (#41462) 2024-04-18 13:03:09 +03:00
Lena
e9f1142287 Link the process in the CODEOWNERS file (#41507) 2024-04-18 11:36:48 +02:00
Phoomparin Mano
e21201cb4b Links to metabase URLs should open in the same window in link cards (#41508)
* external link cards should apply the correct target attribute

* add unit tests for absolute and relative question links in link cards
2024-04-18 09:32:17 +00:00
Chris Truter
4a3cdd0e51 Test that delete CSV argument is passed through (#41562) 2024-04-18 09:27:45 +00:00
Cal Herries
8e3ac99a85 Exclude sample content and "Metabase Analytics" content from usage stats (clone) (#41522) 2024-04-18 09:18:59 +00:00
Nicolò Pretto
c288e4d9f3 Feedback for the Embedding Homepage (#40746)
* FeedbackModal for the Embedding Homepage

* add source and change to correct endpoint

* fix tests, adding await and fixing endpoint

* use toEqual to also check for the source

* addresses PR feedback

* button becomes Send when email is written

* make 'comments' optional, trim inputs and refactor tests

* product-feedback endpoint (#40974)

* mvp of the proxy endpoint for the product feedback

* cleanup and fixes

* email :string -> ms/NonBlankString

* revert my solution

* Dan diff file

* makes fields other than source optional

* don't use default value in dev, to not spam the prod endpoint

* change from setting to function for product-feedback-url, as it's not meant to be changed by end users

* changed endpoint as per https://metaboat.slack.com/archives/C010ZSXQY87/p1712918951362979?thread_ts=1704910781.153589&cid=C010ZSXQY87

* fix wrong dismiss reason sent for 'I'm not interested right now'

* Update frontend/src/metabase/home/components/EmbedHomepage/EmbedHomepage.tsx

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* renamed field: feedback -> comment

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2024-04-18 10:04:41 +02:00
Mahatthana (Kelvin) Nomsawadi
80ada89391 Scope html styles and CSS custom properties (#41527) 2024-04-18 09:01:14 +01:00
Cal Herries
f02b79a621 Add curate permissions for "All Users" for example collection (#41512) 2024-04-18 07:28:57 +00:00
Ngoc Khuat
d1a7cec69c Count Field usage (#39671) 2024-04-18 03:45:36 +00:00
Kamil Mielnik
dee6226ea9 Migrate dashboards to RTK (#41311)
* Add provideDashboardListTags and provideDashboardTags

* Keep the utils sorted alphabetically

* Include collection tags in dashboard tags

* Add getDashboard

* Migrate basic CRUD dashboard endpoints to RTK

* Migrate remaining dashboard endpoints to RTK

* Fix save dashboard action

* Remove unused favorite/unfavorite dashboard endpoints

* Fix copy dashboard action

* Move types to types file

* Type dashboard API requests

* Fix get

* Fix typo

* Pass body

* Add a comment

* Support noEvent option to fix a failing test
2024-04-17 22:40:24 +00:00
Uladzimir Havenchyk
152ca0e500 Fix formatting of pre-release.yml (#41497) 2024-04-18 00:41:32 +03:00
Noah Moss
39cf70c334 Revert "[Feature branch] Split data access and query builder access (#39209)" (#41552)
This reverts commit e0e4ade04e.
2024-04-17 16:11:22 -05:00
Braden Shepherdson
20efd0e6f8 [MBQL lib] Github auto nag on metabase.lib.js changes (#41251)
Add a Github workflow to nag on a PR that changes `metabase.lib.js` without updating `docs/developers-guide/mbql-library-changelog.md` as well.

You can set the `.metabase-lib/no-change` label to override for a change that edits the namespace without materially changing the API.
2024-04-17 16:38:17 -04:00
Alexander Solovyov
75f669b227 [cache] log query hash during calculation of eligibility (#41530) 2024-04-17 23:10:32 +03:00
lbrdnk
6f14f039aa Cancel statement on completion of execute-reducible-query (#41470)
* Cancel statement on completion of execute-reducible-query

* Address review remarks
2024-04-17 20:53:42 +02:00
Chris Truter
83cbaae024 EE endpoint for deleting CSV uploads (#41293) 2024-04-17 18:35:59 +00:00
Noah Moss
e0e4ade04e [Feature branch] Split data access and query builder access (#39209)
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
Co-authored-by: John Swanson <john.swanson@metabase.com>
Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
2024-04-17 14:32:17 -04:00
Romeo Van Snick
42ce333d51 Fix broken test in FilterPicker (#41523) 2024-04-17 17:59:31 +02:00
Cal Herries
09e16f6fc8 Use fk-metadata instead of table-fk-metadata everywhere (#41375) 2024-04-17 15:36:05 +00:00
Nicolò Pretto
cb6075a569 fix: border appearing on night theme if bordered=false (#41514) 2024-04-17 16:06:43 +02:00
Mahatthana (Kelvin) Nomsawadi
a2cdc1345d Migrate float.modules.css (#41510)
* Migrate .floatLeft, .floatRight

* Remove unused components
2024-04-17 13:48:10 +00:00
Braden Shepherdson
0bde1fc9eb [MBQL lib] Column Extract from URL, email requires regex support (#41484)
Not all drivers support regular expressions. Don't return the
Column Extractions for URL -> (sub)domain and email -> domain unless the
database can support the regular expression matches.

Part of the follow-up for Extract Column epic #38964.
2024-04-17 12:59:00 +00:00
Uladzimir Havenchyk
320307f765 Remove not flaky tests from flaky group (#41496) 2024-04-17 15:26:11 +03:00
Mahatthana (Kelvin) Nomsawadi
81db2adb17 Migrate .clearfix (#41509) 2024-04-17 13:21:16 +01:00
Ngoc Khuat
d2905f89e2 Revert "Add BEC team codeowner (#41500)" (#41505)
This reverts commit 9b0e2817ca.
2024-04-17 09:38:22 +00:00
Ngoc Khuat
9b0e2817ca Add BEC team codeowner (#41500) 2024-04-17 15:29:36 +07:00
Nicolò Pretto
2b126f5ddb add unit tests for #41440 (#41465) 2024-04-17 09:05:53 +02:00
Oisin Coveney
d1e8e441f6 Convert EmbedFrame.module.css to CSS modules (#41367) 2024-04-17 09:16:46 +03:00
Denis Berezin
caac0cb646 Embedding SDK - Fix table header styles calculation (#41449)
* Fix table styles calculation

* Review fixes

* Fix unit tests
2024-04-17 00:55:09 +03:00
Braden Shepherdson
8c2c556ad3 Use cached Malli validators/explainers; make :fn schemas cacheable (#39947)
This improves our Malli performance substantially! Many of our code paths were calling vanilla `malli.core/validate` or `malli.core/explain`; these redo the (possibly expensive) compilation of the schema into a validator or explainer for every call.

We should use the caching versions in `metabase.util.malli.registry` everywhere, and our runtime performance will be much improved.

However when I started using these cached versions, I found that the memory use was growing out of control. Eventually I tracked this down to `:fn` schemas. Functions are only comparable by pointer equality, so they make poor cache keys. `(fn ...)` or `comp` calls in a schema on a `mu/defn` function **get re-created for every call of the `mu/defn`'d function**! That's a big time sink recompiling the schemas if we're *not* caching, and a huge memory sink if we *are* caching!

This PR pulls every such schema I could find out into a `def` so it uses the same closure and is cacheable. I'd like to automate that in some part of the Malli pipeline, or maybe a linter rule, but I haven't found a good way to do it yet.

Part of #39946.
2024-04-16 13:16:24 -04:00
Nemanja Glumac
093308d23d Re-wire the Users entity to use RTK Query under the hood (#41312)
* Define the types for the `create` user request

* First draft for the create user API using RTK Query

* Export RTK user API

* Define explicit `create` api for the user entity

* Define `updatePassword` mutation

* Wire users entity `resetPasswordManual` to use RTK under the hood

* Use `updatePassword` mutation

* Wire users list calls

* Remove unused `update_password` from services

* Fix the dispatch used to list users

* Dispatch the action type and the payload

* Wire `deactivateUser` to work with RTK

* Wire `reactivateUser` to work with RTK

* Delete unused entries from `UserApi`

* Convert the reducer to use `switch` statement

* Remove unused user `send_invite` endpoint from FE

* Wire `resetPasswordEmail` in users entity to work with RTK

* Provide cache invalidation for the `userApi`

* Add `provideUserListTags` helper

* Pass `email` as a sole argument

* Throw on `get` a user

This endpoint hasn't been used in a long itme.
Removed in https://github.com/metabase/metabase/pull/41334/files#diff-241e0afa11f03a0942eb322aa47695e58496379b0b6d42e6e59ecf6a20fe623cL448

* Use RTK Query to `updateUser`

* Fix wrong syntax

* Use RTK Query to `getUser`

It was used by `.load` and `fetch` after all.

* Export `useUpdateUserMutation`

* Refactor `getRecipientsList`

* Fix response type for the `getUser`

* Pass user id as a sole argument instead of in an object

* Fix users entity `get` method

* Fix the user update password form

* Fix unit tests

* Fix deactivate and reactivate query invocations

See e949812.

* Adjust types according to the feedback

* Order utils alphabetically

* Throw on `delete`

* Revert "Fix unit tests"

This reverts commit 2c329e8364.

* Fix FE unit test mocks

* Rename imports to use plural for consistency

* Fix one last remaining FE unit test
2024-04-16 15:48:05 +00:00
Nemanja Glumac
da9125f8b0 Fix double await syntax in a FE unit test (#41435) 2024-04-16 16:18:48 +02:00
Romeo Van Snick
176f631782 Add most used functions to suggestions when they open (#41267)
* Add list of most popular functions and aggregations

* Render 'All functions' link on expression dropdown

* Suggest popular functions when no input is given

* Do not return focus to search bar

* Default to not matching any substring

* Open suggestions when the expression editor opens

* Group expressions by group

* Render group titles in dropdown

* Match the expression suggestions height to the design

* Add tests for popular functions

* Convert tests to it-style

* Only return supported popular functions

* Limit most used functions group to 5 items

* Make the suggestion footer keyboard-accessible

* Make a different group for popular aggregations

* Remove padding on suggestion list

* Resize list

* Add test for popular functions in suggest

* Move showMetabaseLinks into args

* Remove unneeded optional chain

* Clarify popup issue in ACE hack

* Use correct target for links

* Reference correct method in comment

* Split up popular expressions

* Remove Set around popular functions/aggregations

* Do not allow interacting with suggestion header

* Do not use _ prefix for variable

* Add verb in test

* Remove unused async in test

* Test if functions are in document

* Move tests for suggestWithFooter to separate file

* Use correct target in link

* Fix typo in key

* Allow picking suggestion when textbox is empty

* Remove unnecessary cast

* Avoid double scroll bars

* Only show link to all functions when editor is empty

* Split up popular functions between expressions and filters

* Fix type of popular variable

* Use the same heuristic for footer as for popular expressions

* Close suggestions when the editor is not focused

* Add missing open prop in test

* Simplify test

* Add selector for expression editor widget

* Fix e2e tests

Fix e2e tests

* Avoid non-interactive clicks from closing the dropdown

* Use custom expression helpers in broken test

* Fix test

* Remove should exist check for selector
2024-04-16 16:16:14 +02:00
Chris Truter
d95e990284 Add test for how uploads sanitize similar columns (#41412) 2024-04-16 14:05:53 +02:00
Uladzimir Havenchyk
03e9e1c63f [e2e] Unify dashboard filter editing (#41443) 2024-04-16 10:38:51 +00:00
Alexander Solovyov
801445b1e1 global setting to enable/disable native sql parsing (#41315) 2024-04-16 13:21:49 +03:00
Ngoc Khuat
10f892ff25 missing test case for update scan field values schedule (#41459) 2024-04-16 09:36:08 +00:00
Oisin Coveney
e5a0255909 Refactor and convert DataSelector.module.css to CSS modules (#41362) 2024-04-16 11:01:15 +03:00
Nicolò Pretto
c393df2a72 revert dashcard border to put back transparency for transparent embed… (#41432) 2024-04-16 09:02:11 +02:00
Phoomparin Mano
585f49ddb8 add heypoom to the embedding team in team.json (#41458) 2024-04-16 13:42:19 +07:00
Oisin Coveney
19420a2b49 Convert rounded.module.css to CSS modules (#41357) 2024-04-16 09:26:08 +03:00
Ngoc Khuat
9b8df36dde Migration to remove scan field values triggers for DBs that turn it off (#41348) 2024-04-16 04:52:58 +00:00
Braden Shepherdson
6f5b24c6ed [MBQL lib] Add "Combine columns" drill-thru, lib/as-returned, API changelog (#41060)
The drill-thru is mostly a stub to put an entry in the context menu. Most of the logic for this is built on the FE using the existing functions for consuming and creating expressions.

See [thread](https://metaboat.slack.com/archives/C06P22KS4JH/p1712264174056569) for a lot more context.

This also adds `docs/developer-guide/mbql-library-changelog.md` and documents adding this function to the library as the first change in the API. I'll follow up in another PR with Github automation to expect the changelog to be updated whenever `src/metabase/lib/js.cljs` changes.

Fixes #39978.
2024-04-15 17:21:29 -04:00
Jesse Devaney
7eb6bf61ce Fix progress bar display error in query builder (#41330)
* fix progress bar in query builder

* add spec to verify regression fix
2024-04-15 17:14:32 -04:00
Jeff Bruemmer
793e5130d1 docs - note on model caching (#41446)
* note on model caching

* update link

* blockquote
2024-04-15 13:36:40 -07:00
Jeff Bruemmer
263534f965 docs - note on internal metabase account (#41207) 2024-04-15 13:20:15 -07:00
Chris Truter
b829cb7d6d Fix reflection getting Liquibase URL (#41411) 2024-04-15 16:35:36 +00:00
Tim Macdonald
6e0f5059ae Move QueryField creation to the card's after-update (#41421) 2024-04-15 17:32:55 +01:00
Alex Yarosh
e9256fda39 [docs] Static embedding description (#41282)
* static embedding

* Update docs/embedding/start.md

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

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2024-04-15 10:19:27 -06:00
Denis Berezin
7d19cb3d68 Convert TableInteractive.module.css (#41294) 2024-04-15 18:20:01 +03:00
Ngoc Khuat
616cfb1adb Fix unable to disable scan field values (#41170) 2024-04-15 14:20:42 +00:00
Cal Herries
e0e95863e5 Fix resources/sample-content.edn (no such file or directory) error running jar (#41408) 2024-04-15 16:51:41 +03:00
Chris Truter
af442cbf6c Fix infinite loop attack for bouncycastle (#41406)
See: https://security.snyk.io/vuln/SNYK-JAVA-ORGBOUNCYCASTLE-6612984
2024-04-15 11:52:10 +00:00
Nicolò Pretto
d952d779a0 fix header being rendered (with its border) on dashboard embedded with titled=false and only one tab (#41399)
* fix header being rendered (with its border) on dashboard embedded with titled=false and only one tab

* embedframe-header -> embed-frame-header
2024-04-15 11:35:13 +00:00
Uladzimir Havenchyk
d7e839dad5 [Parameters] Add "Disconnect from cards" button (#41380) 2024-04-15 11:14:47 +00:00
Chris Truter
0660f2a88c Release migration locks during shutdown (#41089) 2024-04-15 10:32:08 +00:00
dependabot[bot]
bf68fbd480 Bump mysql2 from 3.6.3 to 3.9.4 (#41398)
Bumps [mysql2](https://github.com/sidorares/node-mysql2) from 3.6.3 to 3.9.4.
- [Release notes](https://github.com/sidorares/node-mysql2/releases)
- [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md)
- [Commits](https://github.com/sidorares/node-mysql2/compare/v3.6.3...v3.9.4)

---
updated-dependencies:
- dependency-name: mysql2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2024-04-15 10:17:35 +00:00
Oisin Coveney
688ef2ce21 Convert transitions.module.css to CSS modules (#41361) 2024-04-15 13:10:49 +03:00
Oisin Coveney
6a5a27bf0e Convert shadow.module.css to CSS modules (#41360) 2024-04-15 13:10:30 +03:00
Oisin Coveney
803c79c4f9 Convert legend.module.css to CSS modules (#41355) 2024-04-15 11:10:07 +03:00
Oisin Coveney
22525bad0e Convert width.module.css to CSS modules (#41359) 2024-04-15 11:09:50 +03:00
Cal Herries
de043add81 Log errors fetching sync metadata (#41376) 2024-04-15 10:07:10 +03:00
Raphael Krut-Landau
8006c2c68e Add Schedule strategy to Admin > Performance (#41230)
Adds a Schedule strategy to the Admin / Performance page

![image](https://github.com/metabase/metabase/assets/130925/d78ff74d-df1c-477a-b157-a0c3f21a861b)

[Loom](https://www.loom.com/share/5623cd5d772e417ba2e58c29ae1b8c14?sid=9844f0db-5bb1-4d35-af9b-206d263f5516)

As discussed with @luizarakaki, rather than modify the legacy `SchedulePicker` to work with this use case, I made a new version of it - a `Schedule` component - that uses Mantine and a better (though imperfect) approach to localization. For now, the dropdowns are not vertically aligned in this Schedule picker - something I can try to remedy later if that is important.

Tests for the Schedule component will be added in a follow-up PR.
2024-04-12 23:13:03 -04:00
adam-james
f826a28fc2 Bugfix 37914 wrong filters UI in embedding (#41342)
* Keep Filter Values for enabled Parameters in Embedding

Fixes: #37914

Previously, we removed locked parameters entirely. This is to prevent leaking potentially sensitive values.

However, for the situation where the same field backs 1 locked or disabled paramter and 1 enabled parameter, we do
still want to send the paramater values, because the enabled parameter implies that the values are permissible to see
in the embed.

So, this change will still remove parameters based on their 'disabled/locked/enabled' status, but will NOT remove the
linked field ids if they're also being used by 'enabled' parameters.

This results in the backend correctly sending necessary parameter values to the embed, where the frontend can then
render the appropriate UI instead of falling back to just text filters.

* test the case where a locked and enabled param share same field

* Address feedback. Added comment to explain classify fn
2024-04-12 21:49:50 +00:00
Denis Berezin
bec3d589db Add interactive question component with drill down (#41106)
* Add interactive question component

* Remove excess click actions for embedding

* Disable non-working drills

* Cleaner components api

* Clean up

* Review fixes

* Fix lint error

* Review fixes

---------

Co-authored-by: Oisin Coveney <oisin@metabase.com>
2024-04-12 22:41:32 +03:00
Uladzimir Havenchyk
ade34797d8 Render parameter type and operator in parameter sidebar (#41218) 2024-04-12 22:27:12 +03:00
lbrdnk
5d8cc872fa Exclude implicitly joinable columns from visible-columns in fk-columns-to (#41274)
* Exclude implicitly joinable columns from visible-columns in fk-columns-to

* Add test

* Update description
2024-04-12 20:59:01 +02:00
Cam Saul
b93e99dcd6 [P1 BUG FIX] Cumulative aggregations with multiple breakouts should reset counts when values of breakouts other than change (#41340)
* Fix add-alias-info matching for nominal field literal refs

* Minor cleanup

* Minimize diff

* A bit more cleanup

* Cumulative aggregations w/ multiple breakouts should reset counts (#2862)

* Update dox

* Do the nesting FIRST before adding alias info.

* Remove unused
2024-04-12 18:48:03 +00:00
Cal Herries
bdb34a3309 Sample content (#40753) 2024-04-12 19:21:54 +03:00
Nick Fitzpatrick
e57313a1f4 No implicit any on callbacks (#41314)
* no implicit any on callbacks

* cleaning up use of any

* small adjustments

* PR feedback and unit test adjustment

* typing click handler
2024-04-12 12:58:33 -03:00
Cal Herries
423a02fa45 Log sync errors in CI to debug redshift flakes (#41022) 2024-04-12 15:27:32 +00:00
Cal Herries
92181ab153 Add INFO log for each table being fingerprinted (#41365) 2024-04-12 17:50:41 +03:00
Cal Herries
bcf8ddb1e2 Mitigate redshift flakes by not dropping tables in upload tests (#41366) 2024-04-12 17:49:04 +03:00
Romeo Van Snick
b355cc240c Expression editor suggestion improvements (#41304)
* Add help text icon in expression editor suggestions (#41092)

* Add helptext to expression suggestions

* Deduplicate getDatabase calls

* Allow customising popover width

* Split out help text content component

* Render an info icon with the help text

* Remove duplicated defaults for popover

* Explicitly wait for suggestion items in test

* Remove commented out code

* Use React imports directly

* Rename e to evt

* Inline getHelpText when creating suggestions

* Avoid overlapping content

* Remove unnecessary Fragment

* Avoid selecting item when clicking link or button inside element

* Allow shifting popover

* Stop Popover mouse events for bubbling

* Use onMouseDown instead of onMouseDownCapture to avoid clicks inside popover from triggering select

* Add help text to all suggestions that are know mbql functions

* Expression suggestions clean up (#41236)

* Move ExpressionEditorSuggestions to tsx

* Convert ExpressionEditorSuggestions to functional component

* Simplify suggestion match component

* Move ExpressionEditorSuggestions a separate folder

* Remove default export

* Use Popover instead of Tippy for suggestions list

* Fix suggestions tests

* Remove data-ignore-outside-clicks

* Add default for suggestions

* Fix e2e test

* Move helptext wrapper to ExpressionEditorTextfield

* Switch to metabase/ui for Button

* Use arrow function
2024-04-12 16:39:59 +02:00
Cal Herries
1c5e14884d Correct sync error handling in redshift tests (#41363) 2024-04-12 17:07:40 +03:00
Oisin Coveney
2051da0be7 Convert Formula component to TS (#41349) 2024-04-12 16:42:28 +03:00
Nicolò Pretto
baf69d50f1 embed-homepage ms2: connect example dashboard (#41180)
* connect the example dashboard id setting to the embedding homepage (#41138)

* connect the example dashboard id setting to the embedding homepage

* fix types

* Update frontend/src/metabase/home/components/EmbedHomepage/StaticTabContent.tsx

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

---------

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

* use isNotNull

---------

Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
2024-04-12 13:04:45 +00:00
Cam Saul
dd40ac5f74 Fix add-alias-info matching for nominal field literal refs (#41326)
* Fix add-alias-info matching for nominal field literal refs

* Minor cleanup

* Minimize diff

* A bit more cleanup
2024-04-12 14:20:49 +02:00
Oisin Coveney
85f486c2ea Convert AccountHeader to TS (#41181)
Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
2024-04-12 10:36:35 +03:00
Nemanja Glumac
450ab94d9b Remove unused keys from UserApi (#41334) 2024-04-12 09:01:50 +02:00
Raphael Krut-Landau
639aa29c4a In Admin > Performance, display minimum query duration in seconds (#40970) 2024-04-11 21:35:36 +00:00
Cam Saul
bb97466c43 Convert auto-bucket-datetimes middleware to MLv2 (#41157)
* Convert `auto-bucket-datetimes` middleware to MLv2

* New Kondo rule time.

* Test & lint fixes 🔧

* Fix typos

* Apply suggestions from code review

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

---------

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2024-04-11 18:24:44 +00:00
Mahatthana (Kelvin) Nomsawadi
9e88fb35fd Migrate bordered.module.css (#41307)
* Migrate .bordered

* Migrate .borderBottom, .borderTop

* Fix typo

* Remove unused classes

* Migrate .borderRight

* Migrate .borderLeft

* Migrate .borderLight

* Migrate .borderError

* Migrate .borderSuccess

* Migrate .borderBrand

* Migrate .borderTransparent

* Migrate .borderless

* Migrate .borderMedium

* Review: Add missing CS import
2024-04-11 18:38:47 +01:00
Nick Fitzpatrick
29ef29e99a 40895 entity path in palette (#40985)
* collection and db breadcrumbs

* compact breadcrumbs

* adding navigation keybindings

* recent_views and search changes to always include collection or db name

* removing breadcrumbs, display parent name

* removing unneeded components

* e2e and FE unit tests

* waitFor

* e2e tests for keybindings

* help

* be tests passing

* PR feedback and type adjustments

* unit test adjustment
2024-04-11 17:14:35 +00:00
Jeff Bruemmer
f1ca96647c docs - drop note on sandboxes (#41220)
* drop note on sandboxes

* model caching

* reinstate model caching sandbox warning
2024-04-11 13:24:17 -03:00
Chris Truter
8441ffc233 Don't return nil schema, and test more table fields (#41169)
* Test created_at and schema fields
* Coerce nil schema to empty string
2024-04-11 16:06:33 +02:00
Oisin Coveney
9f5e92e431 Add JWT auth flow to Embedding SDK code (#40902)
Co-authored-by: Nicolò Pretto <info@npretto.com>
2024-04-11 17:04:45 +03:00
Alexander Polyankin
bd9c63c55d Migrate alerts to RTK (#41190)
* Replace AlertApi

* Replace AlertApi

* Replace AlertApi

* Fix bugs

* Fix bugs

* Fix typo

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2024-04-11 12:49:18 +00:00
dependabot[bot]
4b97c42702 Bump tar from 6.1.11 to 6.2.1 (#41306)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.11 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.11...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2024-04-11 12:23:09 +00:00
Uladzimir Havenchyk
926e572588 Render correct parameter sidebar tab content after switching parameter (#41144) 2024-04-11 10:26:43 +00:00
Cal Herries
4dcc6bc9fa Re-enable Redshift uploads and make uploads tests faster (#41088) 2024-04-11 09:47:14 +00:00
Uladzimir Havenchyk
85e2e8cd6a Rework parameters sidebar to mantine (#41130) 2024-04-11 09:37:23 +00:00
Oisin Coveney
2c21fa99a0 Convert colors.module.css to CSS Modules (#40366) 2024-04-11 12:14:36 +03:00
Nemanja Glumac
9d19c4b69c Preserve the original question name in a "save question" modal (#41197)
* Keep the original question name in the save modal where possible

Resolves #41196

* Update tests

* Append "- Modified" to a suggested question name

* Preserve the original question description

* Address reviews - adjust the translation string

* Add E2E reproduction

* Remove extra space

* Improve assertions
2024-04-11 09:43:19 +02:00
Nicolò Pretto
99d15b0416 add env var to enable css modules classnames linting (#41173)
* .eslintrc -> .eslintrc.js

* This might work?

* add env for linting css modules class names

* avoid extending postcss-modules/recommended, specify just the used rule instead

* add missing . on comment

* Remove unused .detailName references

* Fix ManageApiKeys and GuiQueryEditor

* Fix SettingText and DashboardControls

* fix missing textUnspaced class

* remove usave of S.headerButton, that's actually never used because we never pass headerLink to ReferenceHeader

* Remove flexNoShrink

---------

Co-authored-by: Oisin Coveney <oisin@metabase.com>
2024-04-11 08:54:22 +02:00
Kamil Mielnik
32a328a054 Update offset on EntityPickerModal (#41183) 2024-04-11 06:15:42 +00:00
Raphael Krut-Landau
d839a4d4e6 Localize 'How often should we check for results?' (#41254) 2024-04-10 20:20:37 +00:00
adam-james
9cdf2340a8 Fix embed download endpoint (#41264)
* Fix embed download endpoint

We have added format_rows as a query parameter. In most endpoints this just works fine.

Here, however, the problem is that we use query parameters to pass parameter values. We run some validation over these
provided parameter values (eg. don't allow a user to over ride a 'locked' parameter). Since we have these 2 different
use cases for query parameters, the validation was failing on :format_rows, which isn't expected to exist at all.

Here, I dissoc the :format_rows key prior to this validation step, so normal validation can continue.

* Add test that uses this card embedding endpoint
2024-04-10 17:30:10 +00:00
Alexander Solovyov
3a220a796c Migration to backfill QueryField for native cards (#41188) 2024-04-10 17:01:19 +00:00
dpsutton
2a8e19b222 Nested queries have limits which can defeat their purpose (#41051)
* Nested queries have limits which can defeat their purpose

Consider a query

```sql
select count(*) from {{#199}}
```

This query should return the number of distinct rows in the query
defined by 199. But it's actually limited by the excel limit of
1048575. And that's because when the value of `{{#199}}` is expanded it
has that limit applied as normal.

```clojure
qp=> (let [card-id 199] ;; use a valid card id for you
       (-> {:database 1,
            :type :native,
            :native {:query "select count(*) from {{ref}}"
                     :template-tags {:ref {:card-id card-id
                                           :type :card
                                           :name "ref"
                                           :display-name "ref"}}}
            :middleware {:disable-max-results? true}}
           qp.compile/compile
           :query
           (metabase.db.query/format-sql )
           println))
select
  count(*)
from
  (
    SELECT
      "PUBLIC"."ORDERS"."ID" AS "ID",
      "PUBLIC"."ORDERS"."TOTAL" AS "TOTAL"
    FROM
      "PUBLIC"."ORDERS"
    LIMIT
      1048575
  )
```

But we can suppress this limit when substituting a query inside yielding

```sql
select
  count(*)
from
  (
    SELECT
      "PUBLIC"."ORDERS"."ID" AS "ID",
      "PUBLIC"."ORDERS"."TOTAL" AS "TOTAL"
    FROM
      "PUBLIC"."ORDERS"
  )
```

And this is proper because we want to limit the _outer_ query, not
internal queries.

* Remove limit from test expectation

* stupid trailing space

* another subquery test

* Use helper function to disable limit middleware
2024-04-10 10:17:22 -05:00
Cal Herries
51142efc8b Fix snowflake's delete-old-datasets! for tests (#41249) 2024-04-10 15:08:00 +00:00
dpsutton
b1e835415c Fix #39138 again (#40936)
* Fix #39138 again

Fixes #40934

Got unfixed somehow in #40578. seems like some file renames caught it off guard

But the long and short is that its possible for a persisted model to end
up in the state "refreshing" (possible an instance restart during
refreshing). The refreshing job doesn't look for these so they become
effectively invisible.

Since the job to refresh them will only run one at a time cluster wide,
any jobs that are in the "refreshing" state when the refresher begins to
refresh are necessarilly stuck (no one else could currently be
refreshing them). So we can just add them to the queue of models to
refresh.

```clojure
(jobs/defjob ^{org.quartz.DisallowConcurrentExecution true ;; <----
               :doc "Refresh persisted tables job"}
  PersistenceRefresh [job-context]
  (refresh-job-fn! job-context))
```

* Ensure ee/oss pathways are taken in tests

Annoying little footgun here. CI does not run with an ee token, so in
order to ensure that test pathway goes through ee version of a
defenterprise we _must_ use the `mt/with-premium-features
{:cache-granular-controls}`,  but we also want to ensure that it goes
through the oss version.

So two options, a `doseq` on both features (empty set and the feature
that triggers this). But want a test in the enterprise folder as well to
ensure.

The real trickiness comes from running tests at a repl and CLI. My REPL
always has an ee token in it. My command line always lacks that as
well. So want to be explicit about the token features in effect at test
time.

That's why I'm essentially duplicating tests (ee in ee folder, oss in
regular pathway)
2024-04-10 09:44:48 -05:00
Noah Moss
514f9e0575 Remove -Xmx4g option from :cloverage alias (#41253) 2024-04-10 14:15:32 +00:00
Tim Macdonald
34954c8d64 [ParseSQL] Support queries with {[variable}}s and {{field filter}}s in them (#41193)
* Support parsing {{variable}}s and such in queries

[Fixes #39954]

[Fixes #39953]
2024-04-10 13:46:29 +00:00
Nick Fitzpatrick
eb8109de09 increase activation distance for dashboard parameter dragging (#41123)
* increase activation distance for dashboard parameter dragging

* updating drag sensitivity for other places

* adjusting e2e tests
2024-04-10 10:28:51 -03:00
Cal Herries
cfb86ef85e Faster driver/describe-database for redshift (#41068) 2024-04-10 13:28:23 +00:00
Cal Herries
59aa80bd9a Comment out flaky rollback test for MySQL (#41244) 2024-04-10 13:05:16 +00:00
Nick Fitzpatrick
830b8faf9b Upgrading react-dnd and react-draggable (#41212) 2024-04-10 09:16:44 -03:00
Mahatthana (Kelvin) Nomsawadi
3c0f8181ac Migrate base.module.css (#41246)
* Migrate .pointerEventsNone

* Remove unused global class

* Migrate .faded, .fadeInHover

* Migrate .circle

* Remove unreachable code

* Remove unused props
2024-04-10 11:57:16 +00:00
Oisin Coveney
489edea4ad Convert PreviewPane to TS (#40961) 2024-04-10 13:14:06 +03:00
Denis Berezin
fc2ff55743 Restore tabs display for embedded dashboard without title (#41204)
* Restore tabs display for embedded dashboard without title

* Add unit test

* Remove empty file

---------

Co-authored-by: Mahatthana Nomsawadi <mahatthana.n@gmail.com>
2024-04-10 08:35:20 +00:00
Mahatthana (Kelvin) Nomsawadi
f3d161382d Upgraded xlsx module (#41231) 2024-04-10 07:11:54 +00:00
Cal Herries
4c00bc4982 Mitigate some redshift flakes (#41191) 2024-04-10 09:48:34 +03:00
Cam Saul
82dd157aca <datetime expression> = <date literal string> should do something sane (#36068)
* H2 compilation improvements: avoid unneeded casting

* Legacy MBQL schema improvements

* Add additional test for wrap-value-literals

* Simpler version of the fixes from #36068

* Test and lint fixes 🔧
2024-04-09 22:59:04 +00:00
Cam Saul
f901b5ae5f Improved pivot QP: use MLv2 and avoid recursive preprocessing (performance improvement) (#40141)
* Pivot QP use MLv2

* Wow

* Test fix 🔧

* Fixes

* Actions should use strings for column names (fix :update-row and :create-row normalization)

* MLv2 schema should check against keys for the other query type

* Ok, have I fixed things?

* More fixes 🔧

* Fix indentation

* Another round of test fixes. 🔧

* Hopefully the last few test fixes 🔧

* We need to test normalization for queries that have keyword keys as well.

* Fix Cljs i18n namespaces

* Sort namespaces

* Only test against H2

* Test fixes 🔧

* Register MBQL clause schemas and test fixes 🔧

* Test fixes and PR feedback

* Test fix

* Remove the normalization tests

* Test fixes 🔧

* Fix kondo

* PR feedback

* Resurrect remove-all-breakouts

* Test fixes 🔧
2024-04-09 22:32:02 +00:00
Raphael Krut-Landau
91486ae41c Use DelayedLoadingAndErrorWrapper component in Admin > Performance (#41103) 2024-04-09 22:25:26 +00:00
Cam Saul
63910c89d6 Bump backend tests -Xmx from 2 GB to 12 GB (#41213) 2024-04-09 21:28:45 +00:00
Jeff Bruemmer
184cb81275 typo (#41205) 2024-04-09 11:01:51 -07:00
Braden Shepherdson
15b90a0281 [MBQL lib] Don't enforce expression types when converting from legacy (#41147)
These type checks are useful when constructing clauses with full MLv2
metadata available, but when converting from legacy the `:field` refs
have only `:base-type`.

If a column has its metadata overridden to eg. coerce a string or
integer into a date, its `:base-type` will remain `:type/Integer` or
`:type/Text` and the `:effective-type` will be `:type/Date` etc.

This change skips the type check while converting from legacy, and
while checking `can-run`. Eventually this should be replaced with
tracking `:effective-type` on refs or using real metdata, instead of
relying on the `:base-type` only.

Fixes #41122.
2024-04-09 17:42:27 +00:00
Romeo Van Snick
1eacf8e674 Be more specific about which sections need a seperator (#41137) 2024-04-09 18:43:12 +02:00
Cal Herries
db6013d82a Fix coercing multiple columns from int to float in H2 (#41139) 2024-04-09 18:30:42 +03:00
Mahatthana (Kelvin) Nomsawadi
fef3ba6e11 Migrate components/modal.module.css (#41091)
* Remove unused CSS custom properties

* Migrate .ModalContainer

* Migrate .Modal

* Migrate .ModalBackdrop

* Migrate Modal transition classes

* Migrate modal sizes

* Fix tests

* Add missing classes from migration

* Address review: use `data-testid`

* Remove unused classes

* Revert the util that selects Mantine modal
2024-04-09 15:29:25 +01:00
Abed Habli
4aaf880a46 Fix exporting-results list.md (#41167)
I have edited the list of "Download results."
2024-04-09 07:06:07 -07:00
Alexander Polyankin
2500b207d3 Fix tags for self-referencing entities (#41129) 2024-04-09 13:10:52 +00:00
Nemanja Glumac
71a3226267 Improve the fetching logic in a notebook native preview (#41141)
* Improve the fetching logic in a notebook native preview

* Rely on `isFetching` always

* Show a disabled convert button when there's no query
2024-04-09 12:30:01 +00:00
Nemanja Glumac
a160d8b7ae Convert ui actions to TypeScript (#41080)
* Convert `ui` actions to TypeScript

* Make sure fetched user is not `null`
2024-04-09 11:35:31 +00:00
Tim Macdonald
e1d3c48e90 Bump Macaw (now with sanitization) (#41166) 2024-04-09 10:49:15 +00:00
Nemanja Glumac
f016befaef Rename native query preview sidebar component (#41082) 2024-04-09 10:37:54 +02:00
Alexander Solovyov
1504b86572 serialize result metadata for any card (#41131)
We need to serialize it for any card, since when a normal question depends on a native question - their result_metadata won't be updated when opening a dashboard, which leads to front-end not understanding how to connect the parameters from a dashboard to a card, and errors during queries.
2024-04-09 09:50:10 +03:00
adam-james
2d98cd74b7 Bugfix 38839 manually set axis position (#41143)
* Group axes with the correct map shape.

Prior to this, any time axes were all explicitly set :left or :right by the user, the `group-axes-at-once` function
would incorrectly return the map in the following form:

`{:left ["a" "b" "c"]}` which is incorrect. It must instead look like this:

`{"a" :left "b" :left "c" :left}`

This fixes the map shape and permits the user to explicitly set all axes to the left or to the right.

* Add a test to confirm the axis settings are respected and do render
2024-04-08 23:24:54 +00:00
Cam Saul
a871940545 More Metric => LegacyMetric renaming (#40418)
* Wow

* Test fix 🔧

* Fixes

* Actions should use strings for column names (fix :update-row and :create-row normalization)

* MLv2 schema should check against keys for the other query type

* Ok, have I fixed things?

* More fixes 🔧

* Fix indentation

* Another round of test fixes. 🔧

* Hopefully the last few test fixes 🔧

* We need to test normalization for queries that have keyword keys as well.

* Fix Cljs i18n namespaces

* Sort namespaces

* Only test against H2

* Rename `metabase.mbql` to `metabase.legacy-mbql`

* Fix Kondo warnings

* Test fixes 🔧

* Register MBQL clause schemas and test fixes 🔧

* Test fixes and PR feedback

* Test fix

* Remove the normalization tests

* Test fixes 🔧

* Fix kondo

* Fix import

* Another fix 🔧

* Merge

* FIXES

* Add another missing REQUIRE

* More Metric => LegacyMetric renaming

* metabase.models.metric-important-field => metabase.models.legacy-metric-important-field

* Even more renaming

* More renaming

* Even more fixes.

* More renaming

* Wow, even more renaming!!
2024-04-08 22:32:06 +00:00
Cam Saul
a6291809a6 Use window functions for cumulative sum and cumulative count [BigQuery] (#40982)
* Use window functions for SQL drivers for cumulative count and sum

* H2/Snowflake/Redshift implementation

* Test fixes 🔧

* Use SELECT expression positions as the default window function implementation

* Some test fixes.

* Fix bugs in add-alias-info

* MongoDB fixes

* Another pass

* BigQuery should use positions?

* BigQuery almost works 100% now.

* MLv2 fix conversion behavior for expressions with type info

* Add transformations for nesting cumulative aggregations in expressions

* Minor tweaks

* MLv2 should escape aliases using driver/escape-alias (dynamically bind this)

* Kondo fix

* Lint fixes 🔧

* More test fixes and revert test changes

* Update some tests to use correct case for nominal Field literal refs

* Wow! BigQuery is fixed! Remove upgrade-field-literals middleware

* Disable tests for the failing drivers, we can fix in follow-on PRs.

* Enable BigQuery :window-functions

* Extra info

* Add GH issue numbers

* Update more GH issue numbers

* Fix SQL Server!!!! wooo

* I THOUGHT WE WERE DISABLING SPARK SQL!!!!!!!!!!1

* Cleanup to get things ready for review.

* Snowflake + Vertica shouldn't use output column numbers for ORDER BY in OVER

* Code cleanup

* Snowflake + Vertica shouldn't use output column numbers for ORDER BY in OVER

* Code cleanup

* Test fixes 🔧

* Much simpler BigQuery implementation

* Add GH issue context

* Remove commented-out code

* Remove unused add-stage-at-index

* Documentation

* Test fix? 🔧

* Test fix 🔧
2024-04-08 21:41:53 +00:00
Cam Saul
6c515648da Legacy MBQL documentation (#40657)
* Legacy MBQL documentation

* Do not commit dox

* Subject the legacy MBQL stuff to the same stricter linting rules as MLv2

* PR feedback
2024-04-08 21:03:15 +00:00
Jeff Bruemmer
e9f0cb0e8a docs - notes on events and timelines (#41102) 2024-04-08 13:03:31 -07:00
Nemanja Glumac
dd8e0728b6 Preserve notebook native preview user preferences (#41064)
* Add new user-local setting `notebook-native-preview-preferences`

* Update user settings on FE

* Use two distinct setting entries

* Update FE types

* More explicit `notebook-native-preview-sidebar-width` definition

* Add selectors

* Initialize QB uiControls based on the BE settings

* Rename QB store entry

`isNativePreviewSidebarOpen` -> `isShowingNotebookNativePreview`

* Rename QB store entry

`nativePreviewSidebarWidth` -> `notebookNativePreviewSidebarWidth`

* Add Redux actions

* Dispatch user settings on top of Redux store

* Fix test title typo

* Add E2E test to make sure sidebar is resizable

* Expand the E2E test to cover persisting settings across sessions

* Be more explicit that we're resizing along x axis

* Store window breakpoint as a variable

* Calculate handle's left position more explicitly
2024-04-08 20:40:56 +02:00
Alexander Solovyov
8c46de0168 cache invalidation test was over-specific (#41132) 2024-04-08 17:02:24 +00:00
Oisin Coveney
d93af458dc Fix bad spacing on notebook action buttons (#41134) 2024-04-08 19:29:59 +03:00
Ryan Laurie
9be788d110 Make Collection Picker child-aware (#40655)
* make collectionpicker child-aware

* update unit tests

* add another unit test

* move test some place more sensible

* fix rebase
2024-04-08 16:23:31 +00:00
Cal Herries
89dc03f2c7 Refactor: move fingerprinting and classifier code from sync directory (#39436) 2024-04-08 15:37:34 +00:00
Alexander Polyankin
28689319f6 Allow to change RHS table in each join (#40891) 2024-04-08 15:32:58 +00:00
Romeo Van Snick
177a70ef78 Quickfix heigth on row picker (#41063)
* Make the height of the rows consistent when there is no info icon

* Limit the amount of connected tables shown

* Use Array.from({ length })

Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>

* Use a regex to find connected tables in test

---------

Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
2024-04-08 16:16:09 +02:00
Romeo Van Snick
15e5ca6c86 Move search to toplevel in accordionlist (#40008)
* Make canSelectSection a function

* Use a custom type for actions in the accordionlist

* Add the globalSearch prop to AccordionList

* Improve AccordionList tests

* Enable globalSearch in the AccordionPicker on the FilterColumn and QueryColumn picker

* Assume alwaysVisible for action sections

* Add state for empty search results

* Fix test case

* Allow passing globalSearch to Select

* Set globalSearch in semantic type picker

* Handle search when a section does not have items

* Rename variable to be more descriptive

* Add borders to headings in select

* Add border to back button too
2024-04-08 15:59:03 +02:00
Braden Shepherdson
40c4d7f46e [MBQL lib] suggestedJoinConditions accepts a join position for edits (#41035)
This optional fourth argument will disregard all joins at `position` and
later when suggesting conditions. This is useful when editing a join's
RHS, which wipes out the condition. We don't want to generate a
suggested condition that's based on keys from the RHS of the join we
just edited, or from later joins.

Fixes #40916. Part of #40890.
2024-04-08 13:45:29 +00:00
Braden Shepherdson
29f9a88f00 [API] Ensure the :schema is returned as an empty string, not nil (#41052)
Fixes #41023.
2024-04-08 13:31:05 +00:00
Alexander Solovyov
846425172d cache invalidation (#40774)
New endpoint accepts entity and entity ids in form of `database=1&dashboard=2&question=3`. If you don't supply
`&include=overrides`, then it tries to find configs directly referencing supplied entities and updates their
`invalidated_at`. If you supply `&include=overrides`, all the referenced cards are
updated (`report_card.cache_invalidated_at`).

Cache strategies then select the maximum `invalidated_at` in their logic. EE-only.

resolves #40548
2024-04-08 15:00:22 +03:00
Chris Truter
c53ac13ec5 Remove unused driver argument passed around (#41100) 2024-04-08 13:46:17 +02:00
Chris Truter
17ce608985 Add endpoint to list uploaded tables (#41117) 2024-04-08 13:45:40 +02:00
Nick Fitzpatrick
2596b73ebc React Testing Library V12 (#40981)
* update deps

* adjusting tests to work with new RTL

* removing testing-library/no-unnecessary-act:off
2024-04-08 07:44:28 -03:00
Alexander Polyankin
2834f048a3 Migrate timeline and timeline events to RTK (#41020) 2024-04-08 13:12:44 +03:00
Alexander Polyankin
840f2eca2a Migrate search to RTK (#41040) 2024-04-08 13:11:58 +03:00
Nemanja Glumac
f85e17245a Make notebook native preview sidebar resizable (#40984)
* Display sql preview on sizes less than 1280px

* Handle min notebook width using Mantine responsitve style prop

* Display the resizable sidebar on sizes >= 1280px

* Set sidebar width on resize

* Add custom handle

* Simplify window resizing with `useWindowSize` hook

* Use "white" from the theme colors

* Improve sidebar semantics with `aside`

* Define the `NativeQueryPreviewSidebar` as `aside`

* Use Mantine style props instead of CSS module

* Run the E2E test again using regular browser

We removed the nested media query that ReplayIO doesn't support.
Now this test can run using Replay Chromium as all other tests do.

* Add some breathing room between the scrollbar and the resizable sidebar
2024-04-08 11:19:09 +02:00
Oisin Coveney
3c97894b93 Convert PublicError and PublicNotFound to TS (#40956) 2024-04-08 11:54:09 +03:00
Tim Macdonald
15709fa8eb Remove lingering Joda Time references (#40958)
[Fixes #13922]
2024-04-08 08:28:01 +00:00
Alexander Solovyov
6a8f24ccdb make qp.middleware.cache-test/min-ttl-test pass consistently (#41008) 2024-04-08 10:20:13 +03:00
Oisin Coveney
44b576c96c Convert spacing.module.css to CSS Modules (#40518) 2024-04-08 10:18:11 +03:00
Noah Moss
707ad07238 Ignore internal user when initializing from config file (#41097) 2024-04-06 12:03:44 -04:00
Cam Saul
f0cb822bc7 MLv2 normalization: add :lib/uuid automatically to options maps if missing (#40466)
* MLv2 normalization: add :lib/uuid automatically to options maps if it's missing

* Remove unneeded normalization code in QP

* Fix 🔧

* Ignore invalid metadata providers when passed in from the REST API
2024-04-06 02:01:02 +00:00
Nicolò Pretto
9402bd68b9 embed-homepage - ms1 PR (#40382)
* create ui for the full embedding homepage, without any logic (#40251)

* wip ui for the full embedding homepage

* adds missing link + fix keys

* remove done TODO

* Update frontend/src/metabase/home/components/EmbedHomepage/InteractiveTabContent.tsx

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* destructure props in the function arguments

* rename customerFacingAnalyticsDocsUrl to analyticsDocsUrl

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* add new settings for embedding homepage (#40455)

* remove old minimal version

* adds new embedding homepage settings and sets them at the end of the setup

* comment e2e test

* add tests for embedding homepage flags

* more tests

* remove unit tests related to old minimal version

* fixes flags descriptions

* :keyword for embedding-homepage

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* replace reloadSettings and refreshSettingsList with initializeSettings

* `value => value === true` -> `Boolean`

* toMatchObject -> toEqual for more strict tests

* remove useless undefined check for settings, the component is conditionally rendered based on a setting so it means they're loaded anyway

* fix: use :hidden as we changed the type to :keyword

* specifically check for the `embedding` feature in the tokenFeatures object instead of checking if any feature is active

* remove double \n

Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>

* connect settings to the embed homepage and show it (#40528)

* fix nested a warnings in browser console

* connect settings to the embed homepage and show it

* fix stories

* remove localStorage.clear() as we're not using it in the logic anymore

* dismiss without feedback for the embedding homepage (#40587)

* feat: add dismiss functionality to the embed homepage

* update setup e2e to check for the dismissal of embedding homepage

* use List from metabase/ui (#40638)

* set embedding-secret-key when embedding is enabled (#40813)

* set embedding-secret-key when embedding is enabled

* use "named" setters and getters as suggested by Noah

* Fix tests for ms1 after updating on master (#41069)

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
2024-04-05 23:25:47 +03:00
Cam Saul
9d112756ff Use window functions for cumulative sum and cumulative count (#40752)
* Use window functions for SQL drivers for cumulative count and sum

* H2/Snowflake/Redshift implementation

* Test fixes 🔧

* Use SELECT expression positions as the default window function implementation

* Some test fixes.

* Fix bugs in add-alias-info

* MongoDB fixes

* Another pass

* BigQuery should use positions?

* Disable tests for the failing drivers, we can fix in follow-on PRs.

* Extra info

* Add GH issue numbers

* Update more GH issue numbers

* Fix SQL Server!!!! wooo

* I THOUGHT WE WERE DISABLING SPARK SQL!!!!!!!!!!1

* Cleanup to get things ready for review.

* Snowflake + Vertica shouldn't use output column numbers for ORDER BY in OVER

* Code cleanup

* Test fix? 🔧
2024-04-05 13:03:24 -07:00
Uladzimir Havenchyk
15bc33492b Fix flaky object-detail e2e test (#41096) 2024-04-05 19:04:28 +00:00
Cam Saul
697ac34dae Do not i18n the logs (#41067) 2024-04-05 18:53:27 +00:00
Romeo Van Snick
a91f3d8636 No error message on preview api error (#40996)
* Correctly pass an error string to Vizualization

* Fix error layout for preview vizualization

* Do not animate preview vizualization height

* Add reproduction for #40724

* Fix test description

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

* Rename coerceError to getErrorMessage

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2024-04-05 11:42:10 -04:00
Alexander Polyankin
56469a29db Fix loading metadata for models (#41074) 2024-04-05 18:30:21 +03:00
Chris Truter
c8bbe47189 Tighten metabase.upload interface (#41075) 2024-04-05 15:18:40 +00:00
Alexander Polyankin
30f3acd49a Fix question schema notebook (#41078) 2024-04-05 13:35:26 +00:00
Mahatthana (Kelvin) Nomsawadi
7649bba353 Migrate list.module.css (#41030)
* Moved a CSS rule, so we could only migrate 1 CSS file

* Migrate .ListSectionHeader

* Migrate .ListItem

* Migrate .ListItemArrow

* Migrate .ListSectionHeaderCursor

* Migrate .ListSectionToggleAble

* Migrate .ListSectionTitle

* Migrate .ListSectionExpanded

* Migrate .ListSectionIcon

* Migrate .ListItemCursor

* Migrate .ListItemDisabled

* Migrate .ListItemSelected

* Migrate .ListItemTitle

* Migrate .ListItemDescription

* Migrate a CSS class used in tests

* Fix tests from migrating global CSS classes
2024-04-05 13:49:32 +01:00
Mahatthana (Kelvin) Nomsawadi
7c344da791 Migrate LineAreaBarChart.module.css (#41018)
* Migrate .LineAreaBarChart

* Migrated .LineAreaBarChartRenderer

* Migrate .mute-*

* Fix tests that rely on a static class

* Fix wrong DOM query selector syntax

* Address review: Remove CSS comment noise
2024-04-05 13:49:19 +01:00
Jeff Bruemmer
398f85181c docs - dash card notes (#41057) 2024-04-05 08:41:26 -04:00
Cal Herries
b9464c30e9 Simple sync exceptions (#41066) 2024-04-05 15:16:23 +03:00
Mahatthana (Kelvin) Nomsawadi
1c42af3740 Fix flaky test (#41073) 2024-04-05 13:05:53 +01:00
Jeff Bruemmer
16ab6a833a remove redirect (#41049) 2024-04-05 07:54:38 -04:00
dependabot[bot]
f7f864d6a1 Bump follow-redirects from 1.15.4 to 1.15.6 in /release (#40612)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2024-04-05 11:55:25 +03:00
Braden Shepherdson
199e069c4d [MLv2] First pass of docs improvements in metabase.lib.js (#40679)
Goal is to properly document the interface between MLv2 and the FE. In
particular I want to call out the health and fate of each function. Some
of them only exist to support legacy compatibility, for example, and
that should be documented to discourage new usage and make clear the
conditions for removing the functions.
2024-04-04 22:09:09 +00:00
Romeo Van Snick
c4b3e79200 Make info icons less obtrusive (#40865)
* Hide info icons when no description exists

* Move defaults for InfoIcon to separate file

* Add open delay to popover

* Remove inverted background from type label

* Fix margin on semantic type label

* Make semantic type not be bold

* Fix unit tests for hidden info icons

* Rename file to constants

* Remove unused showIfEmpty prop altogether

* Fix the height of the expression suggestions that don't have an info icon

* Fix padding on filter modal

* Fix the height on dataref
2024-04-04 20:10:24 +00:00
Aleksandr Lesnenko
8fe94af974 asynchronously load libraries for png and pdf exports (#41036) 2024-04-04 15:31:27 -03:00
Jeff Bruemmer
f7b7bda730 docs - update public sharing (#41044) 2024-04-04 14:09:18 -04:00
Denis Berezin
1f93ba5c6c Add types generation for embedding SDK package (#40975)
* Add manual imports fixup

* Fixes

* Fixes

* Review fixes

* Review fixes
2024-04-04 16:56:03 +00:00
lbrdnk
ee6f4ac163 Enable not emptyable arguments for is-empty and not-empty clauses (#40957)
* Enable not emptyable arguments for is-empty and not-empty clauses

* Add review remarks

* Shorten dataset name so it is usable with some drivers

* Disable test for Athena

* Handle possibly missing base type

* Update comment

* Missing base type field is treated as not emptyable

* Update desugar middleware

Now middleware adds base type to fields with integer id that are missing that first. This ensures correct function of is-empty expansion.

* Update desugar middleware

Cleanup base types necessary for desugaring is-empty

* Update desugar middleware cleanup

* Update test

* Adjust tests

* Remove now redundant e2e test

* Update comment
2024-04-04 18:51:10 +02:00
Nicolò Pretto
7d16a878b0 Revert "fix: padding not working on <Modal> component" (#41029)
This reverts commit 88a36a6a6d.
2024-04-04 16:12:27 +00:00
Kamil Mielnik
7603ad8a9d Repro #23103 - Wrong number of distinct values in a native model (#41027)
* Repro #23103 - Wrong number of distinct values in a native model

* Fix flake
2024-04-04 16:05:28 +00:00
Jeff Bruemmer
4879d7d614 docs - mention of raw unformatted results (#41033) 2024-04-04 11:53:28 -04:00
Braden Shepherdson
4bad50337f [MLv2] Handle nested "Zoom In" drills on binned columns (#40965)
The first time you zoom in on a bin in a binned query, it works fine.

For example, Orders COUNT by SUBTOTAL auto-binned has a bin for 40-60.
If you zoom in on that, it correctly shows the range 40 to 60 broken
down into smaller bins which are 2.5 units wide. If you then try to
zoom in on one of these smaller bins, say 50-52.5, there are two
problems:

1. The filters from the first zoom (`>= 40` and `< 60`) are still
   there, and so are the new ones (`>= 50` and `< 52.5`).
2. The new filter is actually wrong; it uses the big width (20)
   from the original bins, not the width of the nested bins (2.5),
   so the new filters are `>= 50` and `< 70`.

This PR fixes both issues, so nested bins (1) remove the old filters,
and (2) use the correct width for the nested bins.
2024-04-04 11:03:06 -04:00
Alexander Solovyov
c059d8d585 do not print stacktrace when client closes connection too early (#40834) 2024-04-04 14:57:27 +03:00
Chris Truter
321e2d0d55 Testing parity for appending to and replacing uploads (#40899) 2024-04-04 11:50:46 +00:00
Chris Truter
862daf338a Break out upload.types namespace (#40741) 2024-04-04 10:30:28 +00:00
Mahatthana (Kelvin) Nomsawadi
2d281d2c84 Fix flaky test caused by unmounted HoverCard (#40966)
* Fix flaky test caused by unmounted HoverCard

* Remove unused test util
2024-04-04 10:18:39 +01:00
Nicolò Pretto
88a36a6a6d fix: padding not working on <Modal> component (#40748) 2024-04-04 11:04:02 +02:00
Oisin Coveney
4e7d6f4584 Convert cursor.module.css to CSS modules (#40522) 2024-04-04 11:42:06 +03:00
Alexander Polyankin
a5759d7681 Fix question_id in question schema 1-0-3 (#41002) 2024-04-04 09:23:07 +03:00
Braden Shepherdson
cb506458ee [QP] Correctly find joined fields in the previous stage (#40933)
This logic was trying to match `[name join-alias]` pairs previously, but
that won't work since the later stage field doesn't have a `:join-alias`
on it. This falls back to matching on only the ID or name, but only if
it's unambiguous.

Fixes #40252.
2024-04-03 22:01:43 +00:00
Alex Yarosh
7a4b2fb07e [docs] some format fixes (#40938)
* fix indent in referencing models

* remove syntax highlighting for now
2024-04-03 15:22:10 -06:00
John Swanson
cce183e7ce Don't throw when sent non-int limit/offset (#40821)
This middleware shouldn't throw exceptions when `limit` or `offset`
query parametesr aren't integers.
2024-04-03 13:30:10 -05:00
Lena
8386526374 Make Noah the codeowner for permissions et al (#40919)
* Make Noah the codeowner for permissions et al

et al = `integrations` (OSS google auth, LDAP and Slack integrations) + `enterprise` (audit, advanced SSO, advanced config stuff).  `enterprise` also has LLM which doesn't really have a codeowner now, but it also doesn't need one.

* Correct the paths and the scopes
2024-04-03 19:10:55 +02:00
Raphael Krut-Landau
c1f4bff840 Basic cache admin UI (#39234)
Adds:
* Performance tab 
* Data caching settings tab
* Two-panel strategy editor
* "Don't cache" strategy
* "TTL" strategy
* "Duration" strategy
* "Use default" option for databases
* OSS page
* Unit tests

[Loom](https://www.loom.com/share/b1b173a52be9401588ffb63f718e052a?sid=bed4bdf5-f967-4d93-af55-9b96b6b52730)
2024-04-03 12:04:23 -04:00
Nemanja Glumac
9a8e0a088e Add analytics events for the native code preview in a notebook (#40962)
* Bump the question schema and update it with the new events

* Enable snowplow tracking on the frontend

* Add E2E test for tracking snowplow events related to native preview sidebar

* Fix question id type in the schema
2024-04-03 17:59:54 +02:00
Mahatthana (Kelvin) Nomsawadi
a583764b74 Fix embedding (#40841)
* Fix overflow static dashboard

* Fix `frame` event's height for interactive dashboards

* Fix E2E tests

* Fix test names to match the correct GitHub issue numbers

* Use `data-element-id` for consistency

* Make the test selector more consistent
2024-04-03 16:32:50 +01:00
Ngoc Khuat
d84764c51e comment out flaky caching test (#40963) 2024-04-03 15:28:53 +00:00
Ngoc Khuat
7866f7e20b Fix unable to search field values from partitioned tables (#40767) 2024-04-03 15:27:25 +00:00
Lena
359a8dacc6 Clarify that backport/no-backport is an internal thing (#40954)
* Clarify that backport/no-backport is an internal thing

A few times per month, external contributors ask about the notion doc they can't access and about the labels they are not expected to set. This is not the experience we should give to people who not only take their time to contribute to metabase, but also actually read the PR template.

(slightly duplicated condition in the beginning and the end of the paragraph — "For those employed [...] If you're not employed [...]" — is on purpose)

* Add linebreaks in PR template

Co-authored-by: Tim Macdonald <tim@metabase.com>

---------

Co-authored-by: Tim Macdonald <tim@metabase.com>
2024-04-03 16:12:57 +02:00
Nick Fitzpatrick
9c477a75f3 Command Palette UI adjustments (#40650)
* Command Palette UI adjustments

* move search to end of results list

* ellipsis long command palette items, min icon size and showing debounced text

* setting fixed width. adjusting admin result icons and text

* fixing e2e test
2024-04-03 10:21:28 -03:00
Alexander Polyankin
1235d9a8ee Deprecate MLv1 metadata (#40947) 2024-04-03 16:11:26 +03:00
Jeff Bruemmer
7e899cff28 add 49.3 to list of releases (#40943) 2024-04-03 08:55:05 -04:00
Uladzimir Havenchyk
54ed754436 Fix row id mapping in object detail modal displaying (#40874)
* Map row id on object detail id

* Add a fallback

* Add e2e test

* Add a limit

* Provide a fix for #34070

* Simplify test
2024-04-03 12:32:30 +00:00
Nemanja Glumac
2cf943207c Use MLv2 to determine whether a query can be previewed (#40609)
* Use MLv2 to determine whether a query can be previewed

* Add E2E repro for #40608

* Expand E2E repro

* Fix test

* Make sure the step is active and visible before offering to preview its query

* Expand E2E test

* Address review comment - use `getNotebookStep` helper
2024-04-03 11:09:53 +00:00
Jeff Bruemmer
9521828422 docs - formatting exports (#40921) 2024-04-03 06:53:21 -04:00
Uladzimir Havenchyk
d12c549a1f Do not put js code into page title (#40901)
* Do not put js code into page title

* Typecheck

* Add e2e test

* Update e2e/test/scenarios/question/document-title.cy.spec.js

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

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2024-04-03 10:48:39 +00:00
Mahatthana (Kelvin) Nomsawadi
af54379afa [MS2] 1. Analytics (#40681)
* Remove old config + fix typo

* Add new analytics for 4 new illustration settings
2024-04-03 11:21:55 +01:00
Cal Herries
e5f9d2c412 Create the internal user in a migration (#40907) 2024-04-03 10:11:50 +00:00
Mahatthana (Kelvin) Nomsawadi
f94ec2cce8 Migrate dashboard.module.css (#40918)
* Migrate .DashEditing

* Migrate .CardTitle

* Migrate .PinMap

* Migrate .PinMapUpdateButtonDisabled

* Migrate .BrandColorResizeHandle

* Migrate .VisualizationSlowSpinner

* Migrate .AddSeriesModal

* Migrate 2 more classes
2024-04-03 10:07:17 +01:00
Mahatthana (Kelvin) Nomsawadi
1299f251a1 Remove unused CSS files (#40908)
* Fix default export

* Remove unused classes

* Remove unused components
2024-04-03 10:06:40 +01:00
Mahatthana (Kelvin) Nomsawadi
715174d314 [M1] Metabase illustration settings (#40219)
* Add login page illustration (#39299)

* Move Metabot setting to a new place

* Add setting for login page illustration

* fix UI styling

* Fix not able to upload the same file twice

* Update the admin > appearance > conceal metabase copy

* Prepare the component for tests

* Add some unit tests

* Prevent settings not saved sometimes

After setting the value to/from `custom` one of the setting could be
failed to save, I suspect this happens because the race condition from
trying to save the setting values relatively at the same time.

* Make unit tests easier to read

* Add all unit test test cases

* Add basic E2E test

* Show fallback icon for broken images

* Make use of the proper component

* Update the comment to affect the reality

* Add an error message when file size is over 2MB

* Show an error message when the file size > 2MB

* Add the error message tests

* Preserve the preview image aspect-ratio

* Detect corrupted images

* Fix failing unit tests

* Address feedback: Type

* Feedback: Create meaningful variables

* Feedback: Make selector API more sensible

* Prevent race condition

Makes sure we save the settings completely before making other actions
that also change settings otherwise, there could be race conditions.

* Feedback: tighten up a type

* Feedback: Fix inconsistency callback calls

* 2. Add the rest 3 illustration settings (#40487)

* Add the rest 3 illustration settings

* Don't make the X icon feel lonely

* Fix illustration widget saving

* Fix type error

* 3. Update the select UX to match new design (#40491)

* Update the select UX to match new design

* Move info dot next to file upload button

* Simplify IllustrationWidget API

* Update setting title copy

* Clean up test comments

* Feedback: Clarify `any` type

* Feedback: Use shorthand object creation

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

* Feedback: Use global CSS glass instead of inlining values

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

* Feedback: Remove unnecessary curly brackets in JSX

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

---------

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

* 4. Connect illustrations to new setting values (#40139)

* Wire landing page illustration to the setting value

* Wire the unsubscribe page to the setting value

* Prepare no rows error to be wired to the setting value

* Wire no question results illustration to the setting value

* Wire no search results illustration to setting value

* Fix error from fixing conflict

* Fix required unnecsseary prop on Image

* Use Image component

* Link dashboard card no results image to the setting value

* Move the no data results to more a generic directory

* Link collection picker no results image to the setting value

* Make illustrations more accessible

* Fix failed tests

* Test unscribe page illustration + test no illustration

* Add landing page tests

* Simplify Cypress selectors

* Refactor: Rename settings to match the new requirements

No question data -> No Data
No search results -> No object

These changes are made so, these 2 settings are generic enough to
include other illustrations later on.

* Add no data illustration test

* Add no object illustration tests

* Fix unsubscribe page background style

* Fix not able to build static viz

* 5. Follow-up on milestone 1 (#40710)

* Update custom background illustration scale behavior

* Remove the need to put an SVG file in static folder

* Fix test from rebasing off master with @testing-library/user-event upgrade

---------

Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
2024-04-03 09:07:54 +01:00
dpsutton
12ea8d42b5 Creator sentiment emails iterate (#40922)
* Send creator emails every week

mod the email by 52 instead of by 12 and use the current week as an
anchor

```clojure
user=> (require '[java-time.api :as t])
nil
user=> (let [every-day (take 1000 (t/iterate t/plus (t/local-date 2024 1 1) (t/days 1)))
             f         (fn [d]
                         (let [wf (java.time.temporal.WeekFields/of (java.util.Locale/getDefault))]
                           (.get d (.weekOfWeekBasedYear wf))))]
         (sort (keys (frequencies (map f every-day)))))
(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 40 41 42 43 44 45 46 47 48 49 50 51 52)
```

```clojure
user=> (metabase.util.cron/cron-string->schedule-map "0 0 2 ? * 7")
{:schedule_minute 0,
 :schedule_day "sat",
 :schedule_frame nil,
 :schedule_hour 2,
 :schedule_type "weekly"}
```

* fix typo in creator sentiment email, clean up a bit

typo: had `:verison` instead of `version`.

And while I'm in there, make the payload all in one file and have the
email namespace json->bytes->b64-encode so the data is all in one place
and the setting that allows it to be present or not is co-located.
2024-04-02 14:34:56 -05:00
Nick Fitzpatrick
32c0bf9e06 command palette trigger in app bar search (#40747)
* command palette trigger in app bar search

* remove tabbing to the trigger button
2024-04-02 14:48:26 -03:00
Nemanja Glumac
e36669d9c6 SQL in a sidebar - Milestone 2: Visual tweaks (#40694)
* Move all notebook containers styles to one place

* Make NotebookStep wide again

* Tweak sidebar width and breakpoints

* Remove top margin from NotebookSteps

* Stop line wrapping in an ace editor

* Uniform width between step header and step body

* Remove stray styles

* Scroll content independently

* Use named export

* Use Mantine button for "Visualize"

* Convert notebook step width to rem

* Adjust the position of the step close button

* Adjust notebook step max-width to 1200px

* Remove the `cx` where not needed

* Use HTML 5 `aside` element for the sidebar

* Use Mantine responsive styles

* Fix E2E test
2024-04-02 17:42:57 +00:00
Denis Berezin
9526eaa937 CI for Embedding SDK (#40629) 2024-04-02 17:38:47 +00:00
Maz Ameli
be3a46b854 fix instance analytics typo (#36596) 2024-04-02 10:27:07 -07:00
Chris Truter
8db8b43059 Fix bumping minor version past 10 (#40859) 2024-04-02 19:02:19 +02:00
Ryan Laurie
5b19999dfc Use RTK Query instead of entity framework for DatabaseCandidates (#40403)
* use RTK Query instead of entity framework for DatabaseCandidates

* use skipToken to tighten up API request types
2024-04-02 16:49:46 +00:00
Alexander Polyankin
69353015bb Replace Questions.loadList with RTK (#40896) 2024-04-02 18:46:23 +03:00
Uladzimir Havenchyk
a4fb40e496 Add repro test for a model that uses another model and both have custom column with the same name (#40851)
* Add repro test for a model that uses another model and both have custom column with the same name

* Skip test

* Update e2e/test/scenarios/models/reproductions/39150-model-with-same-custom-column-name.cy.spec.js

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

* Update e2e/test/scenarios/models/reproductions/39150-model-with-same-custom-column-name.cy.spec.js

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

* Convert to ts and add question

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2024-04-02 15:40:21 +00:00
Alexander Polyankin
c27b0a35f4 Migrate metrics entity to RTK (#40900) 2024-04-02 18:36:40 +03:00
Chris Truter
61f7c7f243 Make generated_columns optional for csvupload events (#40880) 2024-04-02 16:55:32 +02:00
Jeff Bruemmer
250058711c docs - notification perms (#40692) 2024-04-02 10:48:11 -04:00
Nemanja Glumac
03aaf47d77 Add breakpoints to the Mantine theme overrides (#40903) 2024-04-02 14:36:32 +00:00
Mahatthana (Kelvin) Nomsawadi
4ac74859b0 Migrate frontend/src/metabase/components/Popover/Popover.module.css (#40730)
* Migrated .PopoverContainer, .PopoverBody

* Make global CSS more prominent

* Migrate 2 classes

* Remove unused class

* Migrate .PopoverBodyWithArrow

* Remove unused class

* Migrate .PopoverBodyMarginBottom

* Remove unused class

* Migrate .PopoverFooter

* Remove unused class

* Preserve classes order for easier review

* Fix unit tests
2024-04-02 14:30:31 +01:00
Alexander Polyankin
28640cfd93 Show query execution time in the QB header for MBQL queries (#40889) 2024-04-02 12:30:42 +00:00
Uladzimir Havenchyk
27fbc96829 Add repro for "Dashboard filters do not work with custom columns on saved questions used in models" (#40784) 2024-04-02 14:45:53 +03:00
Uladzimir Havenchyk
ee28134c9b Add a repro for 'GUI model joining native ones break with column not found error' (#40772)
* Add a repro for 'GUI model joining native ones break with column not found error'

* Rename test

* Add await

* Skip test
2024-04-02 14:44:50 +03:00
Alexander Polyankin
de322a10aa Migrate schema entity to RTK (#40785) 2024-04-02 11:41:56 +00:00
Leo Paillier
d9d8c688e3 Fixes code snippet typo with extra closing parenthsis in case expression (#40873) 2024-04-02 07:07:30 -04:00
Jeff Bruemmer
f52d2f09f5 fix typo (#40858) 2024-04-02 07:06:57 -04:00
Oisin Coveney
215c0bc197 Convert BrowserCrumbs to TS (#40847) 2024-04-02 13:10:46 +03:00
Ryan Laurie
80e690534a Update Translations Strings for 49.3 (#40780)
* put new strings in the pot

* update some more strings

* add new translated strings
2024-04-01 13:20:14 -06:00
Jeff Bruemmer
dacca2446d update releases (#40862) 2024-04-01 15:14:25 -04:00
Jeff Bruemmer
53884aebf5 remove doc updates (#40028) 2024-04-01 18:57:37 +00:00
Romeo Van Snick
49926dbabf Handle null value for field in CategoryFingerprint (#40861) 2024-04-01 18:23:47 +00:00
Roman Abdulmanov
cfca498700 Team json update (#40855) 2024-04-01 19:19:00 +03:00
Anton Kulyk
69b63e89ea Update query results download tooltip copy (#40849) 2024-04-01 17:13:33 +01:00
Anton Kulyk
45b2f147e8 Fix x-ray dashboards run some queries twice on load (#40844)
* Fix parameter values comparison in x-rays

* Skip rerun when `parameterValues` are not initialized

* Remove not used code
2024-04-01 17:07:28 +01:00
Uladzimir Havenchyk
48863d24c3 Remove flakiness from ListPicker spec (#40832) 2024-04-01 17:55:16 +03:00
Uladzimir Havenchyk
6d146ab82d Do not update Pie chart state unnecessary (#40840) 2024-04-01 17:43:34 +03:00
Ryan Laurie
8a9a0a162b Properly Handle Other Users' Personal Subcollections in CollectionPicker (#40822) 2024-04-01 14:04:36 +00:00
Ryan Laurie
d68187e1fc Allow CollectionMover to display API errors (#40700)
* HELP

* Help delivered

* allow entityPicker to display api errors

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2024-04-01 08:00:44 -06:00
Oisin Coveney
6655faa047 Convert hide.modules.css to CSS modules (#40574) 2024-04-01 11:14:30 +03:00
Alex Yarosh
6ec4282d39 [docs] creating a HAR file (#40734)
* add link

* create HAR

* formatting

* periods

* more formatting

* feedback
2024-03-29 13:00:29 -06:00
Raphael Krut-Landau
34d35b84ca Fix model card overflow bug (#40736) 2024-03-29 18:29:10 +00:00
Anton Kulyk
bd33c86a8d Don't rerun dashcard queries when switching tabs without parameter changes (#40783)
* Add E2E test

* Move `getDatasetQueryParams` to `data-fetching.js`

* Use `null` as a parameter value when missing

* Fix missing default `datasetQuery`
2024-03-29 17:19:52 +00:00
Alexander Polyankin
97ba53dfe6 Fix adding a filter for a UUID PK column (#40777) 2024-03-29 16:31:46 +00:00
Ryan Laurie
9423ca83c0 fix timing test flake (#40786) 2024-03-29 10:58:25 -05:00
Alexander Polyankin
7f147bdf15 Migrate database and some field endpoints to RTK (#40701) 2024-03-29 13:33:19 +00:00
Nicolò Pretto
f8d3566137 enableMouseEvents on EmbedModal to allow selecting with the mouse on … (#40771) 2024-03-29 12:10:16 +00:00
Alexander Polyankin
b3e900b24b Allow saving native questions with required parameters without defaults (#40739) 2024-03-29 14:04:32 +03:00
Nick Fitzpatrick
589a665eb4 Fix for userEvents 14 on Master (#40761)
* Nick is sad

* tests passing
2024-03-28 22:53:48 +00:00
John Swanson
c451e286bc Fix :dataset_query (#40652)
... among other things.

The order of arguments was wrong here, causing the default
implementation of `post-process-collection-childre` to be called. The
implementation for `:card` ends up removing the `:dataset_query`
entirely, which is what was intended.
2024-03-28 15:43:35 -07:00
John Swanson
9961c354af Add location and effective_location to search (#40687)
For collections retrieved from the Search API, attach `location` and
`effective_location` so the frontend knows... where they're located.
2024-03-28 15:43:21 -07:00
dpsutton
e0d2407749 Throttle reset_password endpoint (#40760) 2024-03-28 17:30:28 -05:00
adam-james
23bda114fa Cards with Results Should Show up in Subscriptions (#40743)
* Cards with Results Should Show up in Subscriptions

Fixes: #40726

This PR fixes a bug where setting 'Hide thie card if there are no results' in a Dashcard's viz-settings would hide the
card even when there were results.

* adjust test to show that the setting is respected

Before, the setting would always hide the card. Now, the card is only hidden when there are no results. This test sets
up 4 cards: 2 are normal, 2 have no results, but 1 has the setting to hide empty. THerefore, we should expect to see 3 results.
2024-03-28 21:19:26 +00:00
Sloan Sparger
3522a5ccf2 Upgrade @testing-library/user-event to v14 (#40269)
* upgrades @testing-library/user-event to v14, updates test to accomidate events always returning promises and other breaking API changes

* Fixing up broken suites. Half way

* rest of tests

* green plz

* Fix new entries of userEvent usage

* unit test adjustments

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
Co-authored-by: Uladzimir Havenchyk <uladzimir.dev@gmail.com>
2024-03-28 18:24:23 +00:00
adam-james
347c5ef47c Make format export optional (#40606)
* Make Formatting on Exports Optional

Formatting on exports can be turned off via a middleware key:

`:format-export?` which will control if the 'app style' formatting will be applied to the exported results.
`:format-export? false` will return the file as in previous Metabase versions.

* format_export's default value provided in the QP middleware

Should still default to formatted for v. 49, as the formatting is intended as a feature. This default value should be
provided in a central place, so is added to the middleware and is therefore not necessary for the API to provide a default.

* weird indentation

* Add format_export param to dashboard dashcard query endpoint

* Add format_export option to embedded dashboard downloads endpoint

* Move default true value to the streaming-results-writer impls

* Default true but for real this time

* Add format_export to the public Question endpoint

* Add test to card api

* Dashboard exports endpoint test

* Add test to dashboard embeds

Test still fails and I don't know why yet

* the dangers of println debugging....

Well, I was passing a string false the whole time. Turns out the problem was me

* Public Card downloads endpoint now has format_export and test

* export_format also works for adhoc queries using api/dataset

* Let defendpoint do the boolean parsing

* Embed endpoint should now work too.

* Unify format-rows and format-export middlewares

* Sneaky endpoint!

* In xlsx, always parse temporal strings so that viz-settings formatting is applied

* Snuck in an incomplete change by accident. removed it.

* Can now specify format/no format on subscriptions

* Fix and move migration

* default format rows to true in attachments

* Ok, I think that should actually work for subscriptions.

* Change endpoints to use format_rows instead of confusing format_export

* format_rows everywhere

* Fix more test failures

* redef'd function had wrong signature

* Make format export optional (FE part) (#40643)

* Refactor download URL builder code

Need to handle query `params` and request `body` separately

* Export `useHover` from `metabase/ui`

* Add unformatted export for dashboard cards

* Add unformatted export for saved questions

* Fix POST body encoding

* Add unformatted export for ad-hoc questions

* Ensure `format_export` is always false for Excel

* Add unformatted export to public questions

* Add unformatted export to embedded questions and dashboards

* Fix behavior for PNG format

* Add tests for `QueryDownloadPopover`

* Fix e2e downloads helper (allow URL query params)

* Rework alt key handling

* Add e2e test

* Switch to `format_rows` instead of `format_export`

* Add unformatted export option to subscriptions

* Fix `isHoldingAltKey` initial state

* Rework e2e test to use Total column

* Fix initial checkbox state for subscriptions

* Use "Alt" in tooltip label for Windows/Linux

* Show the subscriptions toggle only for CSV attachments

---------

Co-authored-by: adam-james <21064735+adam-james-v@users.noreply.github.com>

* Address review feedback

---------

Co-authored-by: Anton Kulyk <kuliks.anton@gmail.com>
2024-03-28 17:59:21 +00:00
Jeff Bruemmer
6bf8b21cdf docs - update info collection page (#40197) 2024-03-28 13:08:13 -04:00
Alex Yarosh
036de06748 fix typo (#40744) 2024-03-28 10:43:58 -06:00
Oisin Coveney
dac5a001cd Fix wrong icon (#40738) 2024-03-28 18:21:05 +02:00
Kamil Mielnik
907a416009 Fix some warnings in JS console (#40727)
* Fix warning about passing a ref to a function component
- this didn't work and fortunately was not used (anymore) at all

* Move comment where it belongs

* Fix warning that functions cannot be react children
2024-03-28 15:38:40 +00:00
Alexander Polyankin
0b27a96fb0 Do not sort suggested columns, metrics, segments by name (#40636) 2024-03-28 16:24:06 +02:00
Oisin Coveney
3fbfeddf3d Convert text.module.css to CSS modules (#40582)
Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2024-03-28 16:15:00 +02:00
Oisin Coveney
153e039685 Convert scroll.module.css to CSS modules (#40621) 2024-03-28 16:11:38 +02:00
dpsutton
bcfe9e44a1 clear errors in success pathway (#40672)
if a previous run had an error, it will remain present but the state
would be "indexed". We should just clear that error.
2024-03-28 09:02:31 -05:00
Oisin Coveney
06c664ae79 Convert layout.module.css to CSS modules (#40561)
Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2024-03-28 14:32:26 +02:00
Oleg Gromov
bb04cccad2 Native query default parameter values: ParameterValuePicker for list values (#39852) 2024-03-28 11:31:45 +00:00
Alexander Polyankin
ce4af92c59 Trim clipboard data in filter widgets (#40718) 2024-03-28 10:55:32 +00:00
Tim Macdonald
df1366e128 ParseSQL: select * (#40637)
* Parse wildcards in native queries

Add query_field.direct_reference

* Do away with old Model refs in Card model test

* Do not show stale cards with a select *
2024-03-28 09:54:05 +00:00
Mahatthana (Kelvin) Nomsawadi
bee426d232 Update dependencies (#40713) 2024-03-28 09:21:06 +00:00
Chris Truter
0e0f5bb92d Strip tsv file extension (#40702) 2024-03-28 08:59:54 +00:00
Anton Kulyk
7efd8d3627 Fix dashcards from other tabs displayed after leaving editing mode (#40696) 2024-03-28 00:07:49 +00:00
Chris Truter
e63d82ef3e Accept TSV files for upload (#40566) 2024-03-27 22:18:42 +02:00
Sloan Sparger
c6c8ec0d3c Correct issue where EE only LDAP user provisioning feature is breaking OSS instances (#40685) 2024-03-27 14:39:00 -05:00
Ryan Laurie
70ec6d8615 Handle invalid file drop (#40690) 2024-03-27 13:35:55 -06:00
Raphael Krut-Landau
b426fe7eee Remove database prompt banner (#40648) 2024-03-27 13:51:46 -04:00
Ryan Laurie
a016ecca40 Collection moving with EntityPicker (#39869)
* wip - collection moving

fix subcollection detection

make stuff nice

update e2e tests

prettier

new e2e coverage

fix unit tests

fix stacked modals

cleanup

less diff

sneaky-ass modal wrappers

fix some e2e tests

* rebase rejigger

* fix tricky e2e flake
2024-03-27 10:22:04 -06:00
Uladzimir Havenchyk
0c9e3d55f9 Upgrade node to 18.20 (#40661)
* Upgrade node to 18.20

* Trigger CI

* Try 20 again

* Revert "Try 20 again"

This reverts commit 8f4b39e7a6.

* Revert change

* Add nvmrc to the frontend_sources list
2024-03-27 16:00:59 +00:00
John Swanson
c6dd2765b0 Allow group managers to view all people (#40619)
Fixes #40328

[Our documentation](https://www.metabase.com/docs/latest/people-and-groups/managing#group-managers) states that:

> Group managers can:
>
> - View all people in the Admin settings > People tab.

This fixes enforcement to be aligned with the documentation. This behavior makes sense, because as the docs also state, Group Managers should be allowed to *add* people to the group they manage, which they can only do if they can see those people!

Initially, I also removed a faulty test, which was described as:
```
;; Non-segmented users are allowed to ask for a list of all of the users in the Metabase instance. Pulse email lists
;; are an example usage of this. Segmented users should not have that ability. Instead they should only see
;; themselves. This test checks that GET /api/user for a segmented user only returns themselves
```

but actually failed to test this in a relevant way (because it was testing the `GET /api/user` endpoint rather than the `GET /api/user/recipients` endpoint). The test had continued to pass only because the user was the only member of the group they managed.

I initially thought this behavior wasn't desired, but as it turns out, it *is* in fact documented behavior to disallow sandboxed users from seeing any email suggestions. Investigating, I found that a bug was allowing sandboxed users to see all users on the `/api/user/recipients` endpoint if the user-visibility setting was set to `:all`.

Thus, the second commit fixes the bug and re-adds the (fixed) test. A sandboxed user now only sees their own user when hitting `/api/user/recipients`.
2024-03-27 15:40:27 +00:00
Oisin Coveney
9a0fdd429a Convert hover.module.css to CSS modules (#40575) 2024-03-27 17:08:41 +02:00
Uladzimir Havenchyk
16e5553ed6 Use a released version of source-map-js (#40667) 2024-03-27 14:14:01 +00:00
Noah Moss
7d2e51946a Custom migration to delete old truncate-audit-log quartz job (#40649) 2024-03-27 10:06:30 -04:00
Kamil Mielnik
244a19da32 Deprecate entity hooks (#40666)
* Deprecate entity hooks still in use

* Remove unused entity hooks

* Move entity framework hooks to a separate directory
2024-03-27 20:45:06 +07:00
Anton Kulyk
a6684ac137 Fix filters disappeared from dashboard PDFs (#40664) 2024-03-27 13:05:43 +00:00
Oisin Coveney
bf2bb2421a Convert headings.module.css to CSS modules (#40570) 2024-03-27 12:59:33 +02:00
Nemanja Glumac
cc1f9f038f Trigger a query limit update on blur while in notebook mode (#40630)
* Trigger the query limit change on blur

* Use named export

* Add a unit test

* Fix E2E tests

* Fix unit tests

* Additional E2E test fix

* Address review comments
2024-03-27 08:09:50 +00:00
Raphael Krut-Landau
a43261f2c6 Restore Models in Browse data (#40633)
* Revert "Remove Models from Browse data (#40108)"

This reverts commit 45a2e8689f.

* Make rtl friendly

* Add 'no description' for blank-description models

* Fix vertical position of collection header toggle

* Move FixedSizeIcon into metabase/ui and increase height of empty state

* Move FixedSizeIcon back into BrowseModels.styled.tsx

* Localize 'No description'

* Add comment

* Fix type issue
2024-03-26 23:43:40 -04:00
Ryan Laurie
5fb43f2663 Tag Flaky Tests (#40654) 2024-03-26 23:53:10 +00:00
Denis Berezin
98f6cd824e Embedding SDK - integration commit (#40198)
* Minimal SDK code

* Fix viz, styles

* Move SDK code to enterprise folder

* Fix files structure

* Clean-up

* Review fixes

* Review fixes

* Review fixes

* Remove elementid

* Reuse some options from main webpack config

* Actualize package.json

* Actualize package.json
2024-03-26 23:10:40 +00:00
Alexander Polyankin
65e527cdb3 Resolve crash due to missing stageIndex parameter in available-metrics (#40614) 2024-03-26 21:46:09 +00:00
Nemanja Glumac
b777f58d48 Remove readonly notebook view (#40641) 2024-03-26 21:22:25 +01:00
dpsutton
564e642f8a Raise model index limit (#40642)
* Use expression fix on both pk-ref and value-ref

The gist: inside the query expressions are referred to as [:expression
"foo"]. But as a nested query, it's just another field, and the fact
that it was an expression is irrelevant and should not leak from that
stage. So "downstream" should just be [:field "foo" base-type].

Oddly, if you just fix one field or the other, the query would be
"valid", would log an error, but no valid values would be returned.

It seems like the invalid expression reference was just dropped so we
only selected a single column and those were removed (we run `(filter
valid-tuples?)` as we only want valid key/value pairs).

So let's add a `(> (count values) 0)` which was failing previously.

* Fix indexing error

When updating the model index, it diffs the existing values vs the new
values and deletes appropriate and adds the new values. But it was using
the wrong column when updating the model index values. It used `pk_ref`
but that table uses `model_pk`. This led to the following errors on
stats:

```sql
select pk_ref, value_ref, state, error from model_index where state = 'error'

pk_ref,value_ref,state,error
"[""field"",563028,null]","[""field"",563018,null]",error,"ERROR: column ""pk_ref"" does not exist Position: 68"
"[""field"",534030,null]","[""field"",534037,null]",error,"ERROR: column ""pk_ref"" does not exist Position: 68"
"[""field"",545945,null]","[""field"",545948,null]",error,"ERROR: column ""pk_ref"" does not exist Position: 68"
```

Now those errors will be fixed and the model indexes will correctly
track the values

* Bump indexing threshold to 25,000

Had been limited to 5,000 rows of model indexing. This was a made up
number as we went into this endeavor. But it's proving to be far too
small. Bumping to 25,000

* Parititon deletions and inserts

With a higher limit need to ensure that we don't send too large of a
query.

```clojure
model-index=> (t2/insert! ModelIndexValue
                          (map (fn [id]
                                 {:name           (str (gensym "value"))
                                  :model_pk       id
                                  :model_index_id 1})
                               (range 25000)))
Execution error (PSQLException) at org.postgresql.jdbc.PgPreparedStatement/<init> (PgPreparedStatement.java:105).
PreparedStatement can have at most 65,535 parameters. Please consider using arrays, or splitting the query in several ones, or using COPY. Given query has 75,000 parameters
```

* move comment and fix test failure explanation

comment should be next to the number? check. and also need to use
`mc/explain` which returns info rather than `mc/validate` which returns
a bool:

```clojure
;; bad
model-index-test=> (let [values [[1 "foo"] [2 "foo"] ["foo" "foo"]]]
                     (-> (mc/validate [:sequential [:tuple number? string?]] values)
                         (me/humanize)))
nil
;; good
model-index-test=> (let [values [[1 "foo"] [2 "foo"] ["foo" "foo"]]]
                     (-> (mc/explain [:sequential [:tuple number? string?]] values)
                         (me/humanize)))
[nil nil [["should be a number"]]]
```
2024-03-26 15:22:12 -05:00
Cam Saul
4543a7fd9b Fix filtering on LocalDates (#40530)
* Split from #40146: only the filter changes

* Don't include format-rows changes here.

* Test fixes 🔧

* Test fix 🔧

* Misc test fixes 🔧

* Test fix 🔧

* Tweak SQL Server fix
2024-03-26 13:57:52 -06:00
Braden Shepherdson
b9f6cdb29d [MLv2] Return correct drill-thrus for single-row aggregates (#40462)
They were showing "break out by" and quick filters, when they should be
showing "break out by" and "See these Orders".

These are the `pivot` and `underlying-records` drills, internally.

Fixes #40174.
2024-03-26 12:54:16 -06:00
Ryan Laurie
49864834cd CSV Replacement Frontend (#40480)
* add UI for CSV replace

* update e2e tests

* update unit tests

* use enums everywhere + delete commented code block

* all the enums
2024-03-26 17:08:18 +00:00
Tim Macdonald
79f06ef9d0 Add the dev debug page to the docs (#40626) 2024-03-26 16:48:04 +00:00
Alexander Polyankin
4afee8dbed Use RTK beneath the database entity (#40560) 2024-03-26 16:14:18 +00:00
Ryan Laurie
b770bd58ab Collection Moving Cleanup (#40388) 2024-03-26 09:58:14 -06:00
Aleksandr Lesnenko
0b179262b4 fix normalized chart crashes when trend lines enabled but there is no insights data (#40624) 2024-03-26 12:17:21 -03:00
dependabot[bot]
826d632136 Bump express from 4.17.1 to 4.19.2 (#40618)
Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.1...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2024-03-26 14:13:22 +00:00
Nemanja Glumac
a07d90935a Show generated SQL in a side bar automatically - Milestone 1 (#40595)
* Show SQL in a notebook sidebar [PoC/task-1] (#40279)

* Display SQL in the notebook sidebar

* Remove unused modal

* Rename the component to `SQLPreviewSidebar`

* Hide the close button in a SQL sidebar

* Rename the component to `NativeQueryPreviewSidebar`

* Toggle a native query sidebar preview [PoC/task-2] (#40327)

* Use Mantine components where possible

* Rename the button to `ToggleNativeQueryPreview`

* Toggle the native query preview sidebar on and off

* Update the button tooltip based on the sidebar state

* Shorten var names

* Read the state directly from the store

* Re-enable the button that converts a query into its native counterpart [PoC/task-3] (#40362)

* Re-enable the button that converts a query into its native counterpart

* Use Mantine button

* Remove references to the modal

* Fix tests

* Use RTKQuery for API calls in the native preview sidebar [PoC/extras] (#40430)

* Use RTKQuery for API calls

* Fetch question inside the component

* Export named component

* Add type guard

* Use RTK query for the native dataset endpoint

* Improve semantics around error message

* Improve native sidebar basic styling [PoC/task-4]  (#40463)

* Refactor sidebar to use `AceEditor`

* Fix the query display condition

* Style the code as in the native query editor

* Style the toggle icon

* Make native preview usable on small screens

* Cover "Show SQL/native query in a sidebar" with E2E tests [PoC/task-5] (#40540)

* Fix query formatting for Mongo

* Add initial smoke tests

* Cover "converting a question to SQL" scenarios

* Cover "converting a question to a native query" scenarios

* Add test that covers behavior on small screens

* Adjust the viewport width to counter CI's behavior

* Revert "Adjust the viewport width to counter CI's behavior"

This reverts commit 370a76b621.

* Add E2E repro for #38181

* Add repro for #40557

* Try a workaround for the failing test in CI

* Show empty sidebar if query has no data source

* Add E2E coverage for an empty sidebar state
2024-03-26 15:02:10 +01:00
Oisin Coveney
be6c90d5ce Fix width issue in Embed Code sample (#40616) 2024-03-26 15:16:23 +02:00
Chris Truter
d8694f1a3a Bump follow-redirects from 1.15.5 to 1.15.6 (#40615)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-26 12:37:43 +00:00
Oisin Coveney
1ba18765d0 Convert link.module.css to CSS modules (#40584) 2024-03-26 14:20:58 +02:00
Nicolò Pretto
9cf856f2a9 export List from metabase/ui (#40348)
* export List from metabase/ui

* fix text color

* adds WithIcon example

* fix typos
2024-03-26 11:30:47 +01:00
Mahatthana (Kelvin) Nomsawadi
ba74c6bff3 Fix e2e util cypressWaitAll (#40572)
* Fix e2e utils

* Update e2e/support/commands/api/composite/createDashboardWithQuestions.js

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

* Remove unnecessary return

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

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2024-03-26 10:01:05 +00:00
Oisin Coveney
743f4ff9d7 Remove SaveQuestionModal.module.css and replace with general CSS modules (#40500) 2024-03-26 09:44:24 +00:00
2362 changed files with 275494 additions and 218780 deletions

View File

@@ -18,7 +18,14 @@
:use {:level :warning}
:warn-on-reflection {:level :warning}
:metabase/defsetting-must-specify-export {:level :warning}
:metabase/defsetting-must-specify-export {:level :warning}
:metabase/i-like-making-cams-eyes-bleed-with-horrifically-long-tests {:level :warning}
:metabase/mbql-query-first-arg {:level :warning}
:metabase/missing-test-expr-requires-in-cljs {:level :warning}
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning}
:metabase/validate-deftest {:level :warning}
:metabase/validate-logging {:level :warning}
:metabase/warn-about-schema= {:level :warning}
;;
;; disabled linters
@@ -34,6 +41,7 @@
;; :docstring-leading-trailing-whitespace {:level :warning}
;; :docstring-no-summary {:level :warning}
;; :shadowed-var {:level :warning}
;; :def-fn {:level :warning}
:unresolved-symbol
{:exclude
@@ -162,6 +170,15 @@
metabase-enterprise.sandbox.api.table table
metabase-enterprise.test met
metabase.analytics.stats stats
metabase.analyze.query-results qr
metabase.analyze.classifiers.category classifiers.category
metabase.analyze.classifiers.core classifiers
metabase.analyze.classifiers.name classifiers.name
metabase.analyze.classifiers.no-preview-display classifiers.no-preview-display
metabase.analyze.classifiers.text-fingerprint classifiers.text-fingerprint
metabase.analyze.fingerprint.fingerprinters fingerprinters
metabase.analyze.fingerprint.insights insights
metabase.analyze.fingerprint.schema fingerprint.schema
metabase.api.activity api.activity
metabase.api.alert api.alert
metabase.api.automagic-dashboards api.magic
@@ -262,6 +279,7 @@
metabase.legacy-mbql.schema mbql.s
metabase.legacy-mbql.util mbql.u
metabase.lib.util lib.util
metabase.lib.util.match lib.util.match
metabase.models.activity activity
metabase.models.application-permissions-revision a-perm-revision
metabase.models.bookmark bookmark
@@ -289,6 +307,7 @@
metabase.models.timeline timeline
metabase.models.timeline-event timeline-event
metabase.native-query-analyzer query-analyzer
metabase.native-query-analyzer.parameter-substitution nqa.sub
metabase.plugins.initialize plugins.init
metabase.public-settings public-settings
metabase.public-settings.premium-features premium-features
@@ -342,14 +361,7 @@
metabase.server.protocols server.protocols
metabase.shared.util shared.u
metabase.shared.util.currency currency
metabase.sync.analyze.classifiers.category classifiers.category
metabase.sync.analyze.classifiers.name classifiers.name
metabase.sync.analyze.classifiers.no-preview-display classifiers.no-preview-display
metabase.sync.analyze.classifiers.text-fingerprint classifiers.text-fingerprint
metabase.sync.analyze.fingerprint fingerprint
metabase.sync.analyze.fingerprint.fingerprinters fingerprinters
metabase.sync.analyze.fingerprint.insights insights
metabase.sync.analyze.query-results qr
metabase.sync.analyze.fingerprint sync.fingerprint
metabase.sync.field-values field-values
metabase.sync.interface i
metabase.sync.schedules sync.schedules
@@ -402,12 +414,7 @@
ring.adapter.jetty9.servlet servlet
saml20-clj.core saml
toucan.db db
toucan.models models}}
:metabase/validate-deftest {:level :warning}
:metabase/missing-test-expr-requires-in-cljs {:level :warning}
:metabase/i-like-making-cams-eyes-bleed-with-horrifically-long-tests {:level :warning}
:metabase/warn-about-schema= {:level :warning}}
toucan.models models}}}
:lint-as
{clojure.core.logic/defne clj-kondo.lint-as/def-catch-all
@@ -571,6 +578,19 @@
metabase.test/with-temp hooks.toucan2.tools.with-temp/with-temp
metabase.test/with-temp-file hooks.metabase.test.util/with-temp-file
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
metabase.util/format-color hooks.metabase.util/format-color
metabase.util.log/trace hooks.metabase.util.log/info
metabase.util.log/tracef hooks.metabase.util.log/infof
metabase.util.log/debug hooks.metabase.util.log/info
metabase.util.log/debugf hooks.metabase.util.log/infof
metabase.util.log/info hooks.metabase.util.log/info
metabase.util.log/infof hooks.metabase.util.log/infof
metabase.util.log/warn hooks.metabase.util.log/info
metabase.util.log/warnf hooks.metabase.util.log/infof
metabase.util.log/error hooks.metabase.util.log/info
metabase.util.log/errorf hooks.metabase.util.log/infof
metabase.util.log/fatal hooks.metabase.util.log/info
metabase.util.log/fatalf hooks.metabase.util.log/infof
metabase.util.malli.registry/def hooks.metabase.util.malli.registry/def}
:macroexpand
@@ -631,7 +651,7 @@
:name source-namespaces}
{:pattern "(?:(?:^metabase\\.cmd.*)|(?:^build.*)|(?:^metabuild-common.*)|(?:^release.*)|(?:^i18n.*)|(?:^lint-migrations-file$))"
:name printable-namespaces}
{:pattern "^metabase\\.lib\\.*"
{:pattern "^metabase\\.(?:(?:lib)|(?:legacy-mbql))\\.*"
:name metabase-lib}
;;
;; QP source namespaces: `metabase.query-processor.*`, `metabase-enterprise.sandbox.query-processor.*`, and
@@ -643,12 +663,10 @@
:config-in-ns
{test-namespaces
{:linters
{:inline-def {:level :off}
:missing-docstring {:level :off}
:private-call {:level :off}
:metabase/defsetting-must-specify-export {:level :off}
:hooks.metabase.test.data/mbql-query-first-arg {:level :error}
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning}}
{:inline-def {:level :off}
:missing-docstring {:level :off}
:private-call {:level :off}
:metabase/defsetting-must-specify-export {:level :off}}
:hooks
{:analyze-call
{clojure.core/defmacro hooks.clojure.core/non-thread-safe-form-should-end-with-exclamation
@@ -689,19 +707,23 @@
metabase.test/with-temp {:message "Don't use application database inside MLv2 code"}}
:discouraged-namespace
{toucan.db {:message "Don't use application database inside MLv2 code"}
toucan.models {:message "Don't use application database inside MLv2 code"}
toucan.util.test {:message "Don't use application database inside MLv2 code"}
toucan2.core {:message "Don't use application database inside MLv2 code"}}}}
{metabase.util.malli.schema {:message "Prefer metabase.lib.schema schemas in MLv2 code"}
toucan.db {:message "Don't use application database inside MLv2 code"}
toucan.models {:message "Don't use application database inside MLv2 code"}
toucan.util.test {:message "Don't use application database inside MLv2 code"}
toucan2.core {:message "Don't use application database inside MLv2 code"}}}}
qp-and-driver-source-namespaces
{:linters
{:discouraged-namespace
{toucan.db {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.models {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.util.test {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan2.core {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}
{metabase.util.malli.schema {:message "Prefer metabase.lib.schema or metabase.legacy-mbql.schema schemas in QP/driver code"}
toucan.db {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.models {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.util.test {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan2.core {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}
:discouraged-var
{metabase.db.query/query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
{medley.core/map-keys {:message "Use clojure.core/update-keys"}
medley.core/map-vals {:message "Use clojure.core/update-vals"}
metabase.db.query/query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
metabase.db.query/reducible-query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}}}}}

View File

@@ -1,6 +1,8 @@
(ns hooks.clojure.test
(:require [clj-kondo.hooks-api :as hooks]
[clojure.string :as str]))
(:require
[clj-kondo.hooks-api :as hooks]
[clojure.string :as str]
[hooks.common]))
(def ^:private disallowed-parallel-forms
"Things you should not be allowed to use inside parallel tests. Besides these, anything ending in `!` not whitelisted
@@ -106,18 +108,8 @@
clojure.core.async/to-chan!
clojure.core.async/to-chan!!
metabase.driver.sql-jdbc.execute/execute-prepared-statement!
metabase.query-processor.store/store-database!})
(defn- node->qualified-symbol [node]
(try
(when (hooks/token-node? node)
(let [sexpr (hooks/sexpr node)]
(when (symbol? sexpr)
(when-let [resolved (hooks/resolve {:name sexpr})]
(symbol (name (:ns resolved)) (name (:name resolved)))))))
;; some symbols like `*count/Integer` aren't resolvable.
(catch Exception _
nil)))
metabase.query-processor.store/store-database!
next.jdbc/execute!})
(defn- warn-about-disallowed-parallel-forms [form]
(letfn [(error! [form message]
@@ -125,7 +117,7 @@
:message message
:type :metabase/validate-deftest)))
(f [form]
(when-let [qualified-symbol (node->qualified-symbol form)]
(when-let [qualified-symbol (hooks.common/node->qualified-symbol form)]
(cond
(disallowed-parallel-forms qualified-symbol)
(error! form (format "%s is not allowed inside a ^:parallel test or test fixture" qualified-symbol))

View File

@@ -1,6 +1,7 @@
(ns hooks.common
(:require [clj-kondo.hooks-api :as hooks]
[clojure.pprint]))
(:require
[clj-kondo.hooks-api :as hooks]
[clojure.pprint]))
(defn with-macro-meta
"When introducing internal nodes (let, defn, etc) it is important to provide a meta of an existing token
@@ -369,3 +370,33 @@
arg])
body))]
{:node node*}))
(defn node->qualified-symbol [node]
(try
(when (hooks/token-node? node)
(let [sexpr (hooks/sexpr node)]
(when (symbol? sexpr)
(when-let [resolved (hooks/resolve {:name sexpr})]
(symbol (name (:ns resolved)) (name (:name resolved)))))))
;; some symbols like `*count/Integer` aren't resolvable.
(catch Exception _
nil)))
(defn format-string-specifier-count
"Number of things like `%s` in a format string, not counting newlines (`%n`) or escaped percent signs (`%%`). For
checking the number of args to something that takes a format string."
[format-string]
(count (re-seq #"(?<!%)%(?![%n])" format-string)))
(comment
;; should be 1
(format-string-specifier-count "%s %%")
;; should be 2
(format-string-specifier-count "%s %%%n%s")
;; should be 0
(format-string-specifier-count "%n%%%%")
;; should be 1
(format-string-specifier-count "%-02d"))

View File

@@ -92,6 +92,8 @@
metabot-get-prompt-templates-url
metabot-prompt-generator-token-limit
multi-setting-read-only
notebook-native-preview-shown
notebook-native-preview-sidebar-width
notification-link-base-url
num-metabot-choices
openai-api-key

View File

@@ -139,7 +139,7 @@
(when-not ((some-fn symbol? nil?) (hooks/sexpr table))
(hooks/reg-finding! (assoc (meta table)
:message "First arg to mbql-query should be either a table name symbol or nil."
:type ::mbql-query-first-arg)))
:type :metabase/mbql-query-first-arg)))
(let [result (replace-$id-special-tokens query)
;; HACK I'm not sure WHY it works but I ran into https://github.com/clj-kondo/clj-kondo/issues/1773 when
;; trying to get this working -- for some magical reason wrapping the whole thing in a `do` form seems to fix

View File

@@ -0,0 +1,17 @@
(ns hooks.metabase.util
(:require
[clj-kondo.hooks-api :as api]
[hooks.common]))
(defn format-color [{:keys [node], :as x}]
(let [[_format-color _color format-string-node & args] (:children node)]
(when (api/string-node? format-string-node)
(let [expected-arg-count (hooks.common/format-string-specifier-count (api/sexpr format-string-node))
actual-arg-count (count args)]
(when-not (= expected-arg-count actual-arg-count)
(api/reg-finding! (assoc (meta node)
:message (format "metabase.util/format-color format string expects %d arguments instead of %d."
expected-arg-count
actual-arg-count)
:type :format))))))
x)

View File

@@ -0,0 +1,157 @@
(ns hooks.metabase.util.log
(:require
[clj-kondo.hooks-api :as api]
[clojure.string :as str]
[clojure.walk :as walk]
[hooks.common]))
;;;; Checks for both log + logf
(defn- disallow-i18n [node]
(walk/postwalk
(fn [x]
(when-let [qualified-symbol (hooks.common/node->qualified-symbol x)]
(when (and (= (namespace qualified-symbol) "metabase.util.i18n")
(or (str/includes? (name qualified-symbol) "tru")
(str/includes? (name qualified-symbol) "trs")))
(api/reg-finding! (assoc (meta x)
:message "do not i18n the logs!"
:type :metabase/validate-logging)))))
node))
(defn- check-position-of-exception-arg
"Tell people to do (info e \"whatever\") instead of (info \"whatever\" e)."
[node]
(let [[f _first-arg & args] (:children node)]
(doseq [arg args]
(when (and (api/token-node? arg)
(= (api/sexpr arg) 'e))
(api/reg-finding! (assoc (meta arg)
:message (format "pass exceptions as the first arg to %s" (api/sexpr f))
:type :metabase/validate-logging))))))
(defn- common-checks [node]
(check-position-of-exception-arg node)
(disallow-i18n node))
(defn- check-for-i18n-format-specifiers
"Make sure people aren't using things like {0} in their log messages."
[node string-node]
(when (re-find #"\{\d+\}" (api/sexpr string-node))
(api/reg-finding! (assoc (meta node)
:message "this looks like an i18n format string. Don't use identifiers like {0} in logging."
:type :metabase/validate-logging))))
;;; log only
(defn- check-not-format-string
"Make sure we're not using something like log/trace with a format string."
[{[f] :children, :as node} string-node]
(when (pos? (hooks.common/format-string-specifier-count (api/sexpr string-node)))
(api/reg-finding! (assoc (meta node)
:message (format "%s used with a format string, use %s instead."
(api/sexpr f)
(str (api/sexpr f) "f"))
:type :metabase/validate-logging))))
(defn- warn-against-log+format-instead-of-logf
"Tell people to use things like infof instead of info + format."
[{[f] :children, :as node} [x :as args]]
(when (and (= (count args) 1)
(api/list-node? x))
(let [[y] (:children x)]
(when (and (api/token-node? y)
(= (hooks.common/node->qualified-symbol y) 'clojure.core/format))
(api/reg-finding! (assoc (meta node)
:message (format "Use %s instead of %s + format"
(str (api/sexpr f) "f")
(api/sexpr f))
:type :metabase/validate-logging))))))
(defn info
"Valid log, debug, etc."
[{:keys [node], :as x}]
(common-checks node)
(let [[_f & args] (:children node)
[_e & [x :as args]] (if (and (api/token-node? (first args))
(api/string-node? (second args)))
args
(cons nil args))]
(when (api/string-node? x)
(check-for-i18n-format-specifiers node x)
(check-not-format-string node x))
(warn-against-log+format-instead-of-logf node args))
x)
;;;; logf only
(defn- check-format-string-arg-count
"Make sure the number of args passed to something like tracef match the number of format string specifiers."
[{[f] :children, :as node} format-string args]
(let [expected-arg-count (hooks.common/format-string-specifier-count (api/sexpr format-string))
actual-arg-count (count args)]
(when (zero? expected-arg-count)
(api/reg-finding! (assoc (meta node)
:message (format "Don't use %s with no format string arguments, use %s instead."
(api/sexpr f)
(str/replace (str (api/sexpr f)) #"f$" ""))
:type :metabase/validate-logging)))
(when-not (= expected-arg-count actual-arg-count)
(api/reg-finding! (assoc (meta node)
:message (format "log format string expects %d arguments instead of %d."
expected-arg-count
actual-arg-count)
:type :metabase/validate-logging)))))
(defn infof
"Valid logf, debugf, etc."
[{:keys [node], :as x}]
(common-checks node)
(let [[_f & args] (:children node)
[_e format-string & args] (if (api/string-node? (first args))
(cons nil args)
args)]
;; TODO -- maybe we can error if you did not pass a format string, e.g. (log/infof 1 2) is obviously incorrect.
(when (api/string-node? format-string)
(check-for-i18n-format-specifiers node format-string)
(check-format-string-arg-count node format-string args)))
x)
(comment
;; should fail, missing a format arg
(infof
{:node (api/parse-string
(str '(log/warnf "Migration lock was acquired after %d retries.")))})
(infof
{:node (api/parse-string
(str '(log/warnf e "Migration lock was acquired after %d retries.")))})
;; should fail, too many format args
(infof
{:node (api/parse-string
(str '(log/warnf "Migration lock was acquired after %d retries." 1 2)))})
(infof
{:node (api/parse-string
(str '(log/warnf e "Migration lock was acquired after %d retries." 1 2)))})
(infof
{:node (api/parse-string
(str '(log/warnf "Migration lock was acquired after {0} retries." 1)))})
;; should fail, has format args but is warn rather than warnf
(info
{:node (api/parse-string
(str '(log/warn e "Migration lock was acquired after %d retries." 1 2)))})
;; should fail -- should not use i18n/tru or i18n/trs
(infof
{:node (api/parse-string
(str '(log/warnf (metabase.util.i18n/tru "Migration lock was acquired after {0} retries." 1))))})
(info
{:node (api/parse-string
(str '(log/warn (metabase.util.i18n/trs "Migration lock was acquired after {0} retries." 1))))}))

View File

@@ -35,6 +35,7 @@
(eval . (put 'methodical/defmulti 'clojure-doc-string-elt 2))
(eval . (put 'mi/define-simple-hydration-method 'clojure-doc-string-elt 3))
(eval . (put 'mi/define-batched-hydration-method 'clojure-doc-string-elt 3))
(eval . (put 'mr/def 'clojure-doc-string-elt 2))
(eval . (put 'mu/defn 'clojure-doc-string-elt 2))
;; Define custom indentation for functions inside metabase.
;; This list isn't complete; add more forms as we come across them.
@@ -84,6 +85,7 @@
(clojure-indent-style . always-align)
(cljr-favor-prefix-notation . nil)
(cljr-insert-newline-after-require . t)
(cljr-print-right-margin . 118)
(clojure-docstring-fill-column . 118)
(cider-preferred-build-tool . clojure-cli)
(cider-default-cljs-repl . shadow-select)

210
.eslintrc
View File

@@ -1,210 +0,0 @@
{
"rules": {
"strict": [2, "never"],
"no-undef": 2,
"no-var": 1,
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "none",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"no-empty": [1, { "allowEmptyCatch": true }],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "moment",
"message": "Moment is deprecated, please use dayjs"
},
{
"name": "moment-timezone",
"message": "Moment is deprecated, please use dayjs"
}
]
}
],
"curly": [1, "all"],
"eqeqeq": [1, "smart"],
"import/no-default-export": 2,
"import/no-named-as-default": 0,
"import/no-commonjs": 1,
"import/order": [
"error",
{
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"orderImportKind": "asc",
"caseInsensitive": false
},
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index"
],
"warnOnUnassignedImports": false
}
],
"no-console": [2, { "allow": ["warn", "error", "errorBuffer"] }],
"react/no-is-mounted": 2,
"react/prefer-es6-class": 2,
"react/display-name": 1,
"react/prop-types": 2,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/no-find-dom-node": 0,
"react/no-children-prop": 2,
"react/no-string-refs": 2,
"react/no-unescaped-entities": 2,
"react/jsx-no-target-blank": 2,
"react/jsx-key": 2,
"react/forbid-component-props": [2, { "forbid": ["sx"] }],
"react-hooks/exhaustive-deps": [
"warn",
{ "additionalHooks": "(useSyncedQueryString|useSafeAsyncFunction)" }
],
"prefer-const": [1, { "destructuring": "all" }],
"no-useless-escape": 0,
"no-only-tests/no-only-tests": [
"error",
{
"block": [
"describe",
"it",
"context",
"test",
"tape",
"fixture",
"serial",
"Feature",
"Scenario",
"Given",
"And",
"When",
"Then",
"describeWithSnowplow",
"describeEE"
]
}
],
"complexity": ["error", { "max": 54 }]
},
"globals": {
"before": true,
"cy": true,
"Cypress": true
},
"env": {
"browser": true,
"es6": true,
"commonjs": true,
"jest": true,
"jest/globals": true
},
"parser": "babel-eslint",
"plugins": ["react", "no-only-tests"],
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript"
],
"settings": {
"import/internal-regex": "^metabase/|^metabase-lib/",
"import/resolver": {
"webpack": {
"typescript": true
}
},
"import/ignore": ["\\.css$"],
"react": {
"version": "detect"
}
},
"parserOptions": {
"ecmaFeatures": {
"legacyDecorators": true
}
},
"overrides": [
{
"files": ["*.js", "*.jsx", "*.ts", "*.tsx"],
"rules": {
"no-unconditional-metabase-links-render": "error",
"no-literal-metabase-strings": "error"
}
},
{
"files": [
"*.unit.spec.*",
"frontend/src/metabase/admin/**/*",
"frontend/src/metabase/setup/**/*",
"frontend/lint/**/*",
"*.stories.*",
"e2e/**/*",
"**/tests/*",
"release/**/*"
],
"rules": {
"no-unconditional-metabase-links-render": "off",
"no-literal-metabase-strings": "off"
}
},
{
"extends": ["plugin:@typescript-eslint/recommended"],
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"prefer-rest-params": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true,
"destructuredArrayIgnorePattern": "^_"
}
],
// This was introduced in 6.0.0
"@typescript-eslint/no-unsafe-declaration-merging": "off"
}
},
{
"extends": [
"plugin:jest/recommended",
"plugin:jest-dom/recommended",
"plugin:testing-library/react"
],
"plugins": ["jest", "jest-dom", "testing-library"],
"files": [
"*.unit.spec.ts",
"*.unit.spec.tsx",
"*.unit.spec.js",
"*.unit.spec.jsx"
],
"rules": {
"jest/valid-title": ["error", { "ignoreTypeOfDescribeName": true }],
// In unit tests, you should use `act(() => {})` to invoke things causing state updates
// and rerenders to remove lengthy warnings from jest. Disabling the rule allows this.
"testing-library/no-unnecessary-act": "off"
}
}
]
}

224
.eslintrc.js Normal file
View File

@@ -0,0 +1,224 @@
/* eslint-disable import/no-commonjs */
/* eslint-disable no-undef */
// `postcss-modules` lints css modules class names, but it currently crashes
// eslint on vscode. If you use webstorm or want to run the lint for the cli, you
// can use this flag to enable it. This is set to true in CI
const shouldLintCssModules =
process.env.LINT_CSS_MODULES === "true" || process.env.CI;
module.exports = {
rules: {
strict: [2, "never"],
"no-undef": 2,
"no-var": 1,
"no-unused-vars": [
"error",
{
vars: "all",
args: "none",
varsIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
"no-empty": [1, { allowEmptyCatch: true }],
"no-restricted-imports": [
"error",
{
paths: [
{
name: "moment",
message: "Moment is deprecated, please use dayjs",
},
{
name: "moment-timezone",
message: "Moment is deprecated, please use dayjs",
},
],
},
],
curly: [1, "all"],
eqeqeq: [1, "smart"],
"import/no-default-export": 2,
"import/no-named-as-default": 0,
"import/no-commonjs": 1,
"import/order": [
"error",
{
"newlines-between": "always",
alphabetize: {
order: "asc",
orderImportKind: "asc",
caseInsensitive: false,
},
groups: [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
],
warnOnUnassignedImports: false,
},
],
"no-console": [2, { allow: ["warn", "error", "errorBuffer"] }],
"react/no-is-mounted": 2,
"react/prefer-es6-class": 2,
"react/display-name": 1,
"react/prop-types": 2,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/no-find-dom-node": 0,
"react/no-children-prop": 2,
"react/no-string-refs": 2,
"react/no-unescaped-entities": 2,
"react/jsx-no-target-blank": 2,
"react/jsx-key": 2,
"react/forbid-component-props": [2, { forbid: ["sx"] }],
"react-hooks/exhaustive-deps": [
"warn",
{ additionalHooks: "(useSyncedQueryString|useSafeAsyncFunction)" },
],
"prefer-const": [1, { destructuring: "all" }],
"no-useless-escape": 0,
"no-only-tests/no-only-tests": [
"error",
{
block: [
"describe",
"it",
"context",
"test",
"tape",
"fixture",
"serial",
"Feature",
"Scenario",
"Given",
"And",
"When",
"Then",
"describeWithSnowplow",
"describeEE",
],
},
],
complexity: ["error", { max: 54 }],
...(shouldLintCssModules
? {
"postcss-modules/no-undef-class": "error",
}
: {}),
},
globals: {
before: true,
cy: true,
Cypress: true,
},
env: {
browser: true,
es6: true,
commonjs: true,
jest: true,
"jest/globals": true,
},
parser: "babel-eslint",
plugins: ["react", "no-only-tests", "postcss-modules"],
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
],
settings: {
"import/internal-regex": "^metabase/|^metabase-lib/",
"import/resolver": {
webpack: {
typescript: true,
},
},
"import/ignore": ["\\.css$"],
react: {
version: "detect",
},
"postcss-modules": {
baseDir: "./frontend/src",
},
},
parserOptions: {
ecmaFeatures: {
legacyDecorators: true,
},
},
overrides: [
{
files: ["*.js", "*.jsx", "*.ts", "*.tsx"],
rules: {
"no-unconditional-metabase-links-render": "error",
"no-literal-metabase-strings": "error",
},
},
{
files: [
"*.unit.spec.*",
"frontend/src/metabase/admin/**/*",
"frontend/src/metabase/setup/**/*",
"frontend/lint/**/*",
"*.stories.*",
"e2e/**/*",
"**/tests/*",
"release/**/*",
],
rules: {
"no-unconditional-metabase-links-render": "off",
"no-literal-metabase-strings": "off",
},
},
{
extends: ["plugin:@typescript-eslint/recommended"],
files: ["*.ts", "*.tsx"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
rules: {
"prefer-rest-params": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
ignoreRestSiblings: true,
destructuredArrayIgnorePattern: "^_",
},
],
// This was introduced in 6.0.0
"@typescript-eslint/no-unsafe-declaration-merging": "off",
},
},
{
extends: [
"plugin:jest/recommended",
"plugin:jest-dom/recommended",
"plugin:testing-library/react",
],
plugins: ["jest", "jest-dom", "testing-library"],
files: [
"*.unit.spec.ts",
"*.unit.spec.tsx",
"*.unit.spec.js",
"*.unit.spec.jsx",
],
rules: {
"jest/valid-title": ["error", { ignoreTypeOfDescribeName: true }],
},
},
],
};

11
.github/CODEOWNERS vendored
View File

@@ -1,3 +1,14 @@
# Before changing this file, make sure you have read how we use it:
# https://www.notion.so/metabase/Pull-Request-Code-Review-Guide-374f05df889748b69d67af753f9bc9b8?pvs=4#36550c04ceab40b5a8b8bee6264b090c
# TL;DR: if @camsaul hasn't been involved you're probably doing it wrong
src/metabase @camsaul
modules @camsaul
enterprise/backend @camsaul
enterprise/backend/src/metabase_enterprise/advanced_permissions @noahmoss
enterprise/backend/src/metabase_enterprise/audit_app @noahmoss
enterprise/backend/src/metabase_enterprise/sso @noahmoss
enterprise/backend/src/metabase_enterprise/task/truncate_audit_table.clj @noahmoss
enterprise/backend/src/metabase_enterprise/internal_user.clj @noahmoss
src/metabase/**/*permissions* @noahmoss
src/metabase/integrations @noahmoss

View File

@@ -1,5 +1,9 @@
> [!IMPORTANT]
> If you are merging into master, please add either a `backport` or a `no-backport` label to this PR. You will not be able to merge until you do this step. Refer to the section [Do I need to backport this PR?](https://www.notion.so/metabase/Metabase-Branching-Strategy-6eb577d5f61142aa960a626d6bbdfeb3?pvs=4#89f80d6f17714a0198aeb66c0efd1b71) in the Metabase Branching Strategy document for more details.
> For those employed by Metabase: if you are merging into master, please add either a `backport` or a `no-backport` label
> to this PR. You will not be able to merge until you do this step. Refer to the section [Do I need to backport this
> PR?](https://www.notion.so/metabase/Metabase-Branching-Strategy-6eb577d5f61142aa960a626d6bbdfeb3?pvs=4#89f80d6f17714a0198aeb66c0efd1b71)
> in the Metabase Branching Strategy document for more details. If you're not employed by Metabase, this section does not
> apply to you, and the label will be taken care of by your reviewer.
> **Warning**
>

View File

@@ -23,10 +23,12 @@ frontend_sources: &frontend_sources
- "**/tsconfig*.json"
- "package.json"
- "babel.config.json"
- ".eslintrc"
- ".eslintrc.js"
- "postcss.config.js"
- "webpack.config.js"
- "webpack.embedding-sdk.config.js"
- "webpack.static-viz.config.js"
- ".nvmrc"
frontend_specs: &frontend_specs
- *shared_specs

3
.github/team.json vendored
View File

@@ -7,7 +7,6 @@
"ranquild",
"uladzimirdev",
"kamilmielnik",
"oleggromov",
"nemanjaglumac",
"romeovs"
]
@@ -15,7 +14,7 @@
{
"name": "Embedding",
"label": ".Team/Embedding",
"members": ["WiNloSt", "deniskaber", "oisincoveney", "npretto"]
"members": ["WiNloSt", "deniskaber", "oisincoveney", "npretto", "heypoom"]
},
{
"name": "BEC",

39
.github/workflows/embedding-sdk.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Embedding SDK
on:
push:
branches:
- "master"
- "release-**"
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
sdk-build:
if: contains(github.event.pull_request.labels.*.name, 'embedding-sdk-build')
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "cljs"
- name: Build SDK bundle
run: yarn run build-embedding-sdk
- name: Generate SDK package.json
run: yarn run embedding-sdk:generate-package "$GITHUB_SHA"
- name: Generate .npmrc for Github npm registry
uses: actions/setup-node@v4
with:
registry-url: "https://npm.pkg.github.com"
- name: Publish package
run: yarn run embedding-sdk:publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,32 @@
name: Update changelog for MBQL library changes
on:
pull_request:
types:
- opened
- reopened
- labeled
- unlabeled
- synchronize
paths:
- src/metabase/lib/js.cljs
jobs:
mbql-library-changelog-check:
runs-on: ubuntu-latest
if: ${{ !contains( github.event.pull_request.labels.*.name, '.metabase-lib/no-change' ) }}
steps:
# - uses: jesusvasquez333/verify-pr-label-action@v1.4.0
# with:
# github-token: '${{ secrets.GITHUB_TOKEN }}'
# valid-labels: '.metabase-lib/no-change'
# disable-reviews: true
- uses: actions/checkout@v4
- name: Check for updated MBQL library changelog
run: |
if [[ $(git show HEAD -- docs/developers-guide/mbql-library-changelog.md) ]]; then
echo "Library and changelog updated together - all good"
else
echo "metabase.lib.js namespace changed but docs/developers/guide/mbql-library-changelog.md did not"
echo "Update the changelog with the changes made to the library's API."
echo "If the edits did not change the API, add the `.metabase-lib/no-change` label."
exit 1
fi

View File

@@ -5,11 +5,11 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Metabase version (e.g. v0.46.3)'
description: "Metabase version (e.g. v0.46.3)"
type: string
required: true
commit:
description: 'A full-length commit SHA-1 hash'
description: "A full-length commit SHA-1 hash"
required: true
jobs:
@@ -91,21 +91,21 @@ jobs:
edition: [oss, ee]
java-version: [11, 17]
steps:
- name: Prepare JRE (Java Run-time Environment)
uses: actions/setup-java@v4
with:
java-package: jre
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
- run: java -version
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Launch uberjar (and keep it running)
run: java -jar ./metabase.jar &
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
- name: Prepare JRE (Java Run-time Environment)
uses: actions/setup-java@v4
with:
java-package: jre
java-version: ${{ matrix.java-version }}
distribution: "temurin"
- run: java -version
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Launch uberjar (and keep it running)
run: java -jar ./metabase.jar &
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
run-sanity-check:
runs-on: ubuntu-22.04
@@ -132,43 +132,43 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- name: Check out the code
uses: actions/checkout@v4
with:
ref: ${{ needs.release-artifact.outputs.commit }}
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: "temurin"
- name: Prepare Cypress environment
uses: ./.github/actions/prepare-cypress
- name: Run Snowplow micro
uses: ./.github/actions/run-snowplow-micro
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact for ${{ matrix.edition }}
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Get the version info
run: |
jar xf metabase.jar version.properties
mv version.properties resources/
- name: Run a few important Cypress tests as sanity check
run: |
mkdir -p ./target/uberjar && cp metabase.jar ./target/uberjar/metabase.jar
yarn test-cypress-run --folder onboarding
- name: Check out the code
uses: actions/checkout@v4
with:
ref: ${{ needs.release-artifact.outputs.commit }}
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: "temurin"
- name: Prepare Cypress environment
uses: ./.github/actions/prepare-cypress
- name: Run Snowplow micro
uses: ./.github/actions/run-snowplow-micro
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact for ${{ matrix.edition }}
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Get the version info
run: |
jar xf metabase.jar version.properties
mv version.properties resources/
- name: Run a few important Cypress tests as sanity check
run: |
mkdir -p ./target/uberjar && cp metabase.jar ./target/uberjar/metabase.jar
yarn test-cypress-run --folder onboarding
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-failed-tests-recording
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-failed-tests-recording
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
containerize:
needs: [release-artifact, run-sanity-check]
@@ -185,71 +185,71 @@ jobs:
outputs:
image_tag: ${{ steps.target.outputs.image_tag }}
steps:
- name: Get the version
run: |
if [[ "${{ matrix.edition }}" == "ee" ]]; then
echo "VERSION=${{ needs.release-artifact.outputs.ee_version }}" >> $GITHUB_ENV
else
echo "VERSION=${{ needs.release-artifact.outputs.oss_version }}" >> $GITHUB_ENV
fi
- name: Check out the code
uses: actions/checkout@v4
with:
ref: ${{ needs.release-artifact.outputs.commit }}
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Move the Uberjar to the context dir
run: mv metabase.jar bin/docker/.
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Build ${{ matrix.edition }} container
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64
network: host
tags: localhost:5000/local-metabase:${{ env.VERSION }}
no-cache: true
push: true
- name: Get the version
run: |
if [[ "${{ matrix.edition }}" == "ee" ]]; then
echo "VERSION=${{ needs.release-artifact.outputs.ee_version }}" >> $GITHUB_ENV
else
echo "VERSION=${{ needs.release-artifact.outputs.oss_version }}" >> $GITHUB_ENV
fi
- name: Check out the code
uses: actions/checkout@v4
with:
ref: ${{ needs.release-artifact.outputs.commit }}
- uses: actions/download-artifact@v4
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Move the Uberjar to the context dir
run: mv metabase.jar bin/docker/.
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Build ${{ matrix.edition }} container
uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64
network: host
tags: localhost:5000/local-metabase:${{ env.VERSION }}
no-cache: true
push: true
- name: Launch container
run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ env.VERSION }}
timeout-minutes: 5
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3
- name: Launch container
run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ env.VERSION }}
timeout-minutes: 5
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Retag and push container image to ${{ vars.DOCKERHUB_OWNER }} staging Docker Hub repo
id: target
run: |
# SHA-1 truncated to 7 digits should be enough
SHORT_HASH=${COMMIT:0:7}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Retag and push container image to ${{ vars.DOCKERHUB_OWNER }} staging Docker Hub repo
id: target
run: |
# SHA-1 truncated to 7 digits should be enough
SHORT_HASH=${COMMIT:0:7}
IMAGE_TAG=$VERSION-$SHORT_HASH
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
IMAGE_TAG=$VERSION-$SHORT_HASH
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
echo "Retag local image for staging"
docker tag localhost:5000/local-metabase:$VERSION $CONTAINER_IMAGE
echo "Retag local image for staging"
docker tag localhost:5000/local-metabase:$VERSION $CONTAINER_IMAGE
echo "Pushing container image $CONTAINER_IMAGE ..."
docker push $CONTAINER_IMAGE
echo "Pushing container image $CONTAINER_IMAGE ..."
docker push $CONTAINER_IMAGE
echo "Finished!"
shell: bash
env:
COMMIT: ${{ needs.release-artifact.outputs.commit }}
echo "Finished!"
shell: bash
env:
COMMIT: ${{ needs.release-artifact.outputs.commit }}
verify-docker-pull:
runs-on: ubuntu-22.04
@@ -261,22 +261,22 @@ jobs:
env:
IMAGE_TAG: ${{ needs.containerize.outputs.image_tag }}
steps:
- name: Login to Docker Hub # authenticated, to avoid being rate-throttled
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Pull the container image
run: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
echo "Pulling container image $CONTAINER_IMAGE ..."
docker pull $CONTAINER_IMAGE
echo "Successful!"
- name: Launch container
run: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
docker run --rm -dp 3000:3000 $CONTAINER_IMAGE
timeout-minutes: 5
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3
- name: Login to Docker Hub # authenticated, to avoid being rate-throttled
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Pull the container image
run: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
echo "Pulling container image $CONTAINER_IMAGE ..."
docker pull $CONTAINER_IMAGE
echo "Successful!"
- name: Launch container
run: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
docker run --rm -dp 3000:3000 $CONTAINER_IMAGE
timeout-minutes: 5
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
timeout-minutes: 3

1
.gitignore vendored
View File

@@ -51,6 +51,7 @@
/resources/frontend_client/embed.html
/resources/frontend_client/index.html
/resources/frontend_client/public.html
/resources/embedding-sdk
/resources/i18n/*.edn
/resources/instance_analytics.zip
/resources/license-backend-third-party.txt

2
.nvmrc
View File

@@ -1 +1 @@
v18.16.1
v18.20.0

View File

@@ -1,9 +1,9 @@
<style>
:root {
--default-font-family: "Lato";
--color-brand: #509ee3;
--color-brand-alpha-04: rgba(80, 158, 227, 0.04);
--color-brand-alpha-88: rgba(80, 158, 227, 0.88);
--color-focus: #cbe2f7;
--mb-default-font-family: "Lato";
--mb-color-brand: #509ee3;
--mb-color-brand-alpha-04: rgba(80, 158, 227, 0.04);
--mb-color-brand-alpha-88: rgba(80, 158, 227, 0.88);
--mb-color-focus: #cbe2f7;
}
</style>

View File

@@ -39,7 +39,7 @@
(update override :file (partial str u/project-root-directory))))
;; doesn't find the usage in fingerprinters, which is a macro emitting a defmethod. The quoting changes the
;; shape of the seq so the spec doesn't match it
[{:file "/src/metabase/sync/analyze/fingerprint/fingerprinters.clj"
[{:file "/src/metabase/analyze/fingerprint/fingerprinters.clj"
:message "Error generating fingerprint for {0}"}]))
(defn- strip-roots

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env node
/* eslint-env node */
/* eslint-disable import/no-commonjs, import/order, no-console */
const fs = require("fs");
const path = require("path");
const SDK_DIST_DIR_PATH = path.resolve("./resources/embedding-sdk/dist");
const SDK_PACKAGE_NAME = "@metabase/embedding-sdk-react";
/*
* This script replaces all custom aliases in Embedding SDK generated ".d.ts" files so that this imports could be resolved
* in the host app
* */
// this map should be synced with "tsconfig.sdk.json"
const REPLACES_MAP = {
"metabase-enterprise/": `${SDK_PACKAGE_NAME}/dist/enterprise/frontend/src/metabase-enterprise/`,
"metabase-lib/": `${SDK_PACKAGE_NAME}/dist/frontend/src/metabase-lib/`,
"metabase-lib": `${SDK_PACKAGE_NAME}/dist/frontend/src/metabase-lib`,
"metabase-shared/": `${SDK_PACKAGE_NAME}/dist/frontend/src/metabase-shared/`,
"metabase-types/": `${SDK_PACKAGE_NAME}/dist/frontend/src/metabase-types/`,
"metabase/": `${SDK_PACKAGE_NAME}/dist/frontend/src/metabase/`,
"cljs/": `${SDK_PACKAGE_NAME}/dist/target/cljs_release/`,
};
const traverseFilesTree = dir => {
try {
const results = [];
const list = fs.readdirSync(dir);
list.forEach(file => {
file = path.join(dir, file);
const stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
results.push(...traverseFilesTree(file));
} else if (file.endsWith(".d.ts")) {
// Is a ".d.ts" file
results.push(file);
}
});
return results;
} catch (error) {
console.error(`Error when walking dir ${dir}`, error);
}
};
const replaceAliasedImports = filePath => {
let fileContent = fs.readFileSync(filePath, { encoding: "utf8" });
Object.entries(REPLACES_MAP).forEach(([alias, replacement]) => {
fileContent = fileContent
.replaceAll(`from "${alias}`, `from "${replacement}`)
.replaceAll(`import("${alias}`, `import("${replacement}`)
.replace(
// replace dynamic imports using alias, with possible relative paths - "../../" and "frontend/src/"
// import("(../)*(frontend/src/)*<alias>
new RegExp(
`import\\("(\\.\\.\/)*(frontend\/src\/)*${alias.replace("/", "\\/")}`,
"gi",
),
`import("${replacement}`,
);
});
fs.writeFileSync(filePath, fileContent, { encoding: "utf-8" });
console.log(`Edited file: ${filePath}`);
};
const fixupTypesAfterCompilation = () => {
const dtsFilePaths = traverseFilesTree(SDK_DIST_DIR_PATH);
dtsFilePaths.forEach(replaceAliasedImports);
};
fixupTypesAfterCompilation();

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env node
/* eslint-env node */
/* eslint-disable import/no-commonjs, import/order, no-console */
const fs = require("fs");
const path = require("path");
const IGNORED_PACKAGES = ["react", "react-dom"];
function filterOutReactDependencies(object) {
const result = {};
Object.entries(object).forEach(([packageName, version]) => {
if (!IGNORED_PACKAGES.includes(packageName)) {
result[packageName] = version;
}
});
return result;
}
function generateSdkPackage() {
let maybeCommitHash = process.argv[2];
if (maybeCommitHash) {
// get short commit hash
maybeCommitHash = maybeCommitHash.slice(0, 7);
}
const mainPackageJson = fs.readFileSync(
path.resolve("./package.json"),
"utf-8",
);
const mainPackageJsonContent = JSON.parse(mainPackageJson);
const sdkPackageTemplateJson = fs.readFileSync(
path.resolve(
"./enterprise/frontend/src/embedding-sdk/package.template.json",
),
"utf-8",
);
const sdkPackageTemplateJsonContent = JSON.parse(sdkPackageTemplateJson);
const todayDate = new Date().toJSON().slice(0, 10).replaceAll("-", "");
const mergedContent = {
...sdkPackageTemplateJsonContent,
dependencies: filterOutReactDependencies(
mainPackageJsonContent.dependencies,
),
resolutions: filterOutReactDependencies(mainPackageJsonContent.resolutions),
version: maybeCommitHash
? `${sdkPackageTemplateJsonContent.version}-${todayDate}-${maybeCommitHash}`
: sdkPackageTemplateJsonContent.version,
};
const mergedContentString = JSON.stringify(mergedContent, null, 2);
console.log("Generated SDK package.json:");
console.log(mergedContentString);
const sdkDistDirPath = path.resolve("./resources/embedding-sdk");
if (!fs.existsSync(sdkDistDirPath)) {
fs.mkdirSync(sdkDistDirPath);
}
fs.writeFileSync(
path.resolve(path.join(sdkDistDirPath), "package.json"),
mergedContentString,
"utf-8",
);
}
generateSdkPackage();

View File

@@ -37,7 +37,7 @@
org.slf4j/slf4j-api]}
com.draines/postal {:mvn/version "2.0.5"} ; SMTP library
com.github.seancorfield/honeysql {:mvn/version "2.6.1126"} ; Honey SQL 2. SQL generation from Clojure data maps
com.github.seancorfield/next.jdbc {:mvn/version "1.3.925" } ; Talk to JDBC DBs
com.github.seancorfield/next.jdbc {:mvn/version "1.3.925"} ; Talk to JDBC DBs
com.github.steffan-westcott/clj-otel-api {:mvn/version "0.2.6"} ; Telemetry library
com.github.vertical-blank/sql-formatter {:mvn/version "2.0.4"} ; Java SQL formatting library https://github.com/vertical-blank/sql-formatter
com.google.guava/guava {:mvn/version "33.1.0-jre"} ; dep for BigQuery, Spark, and GA. Require here rather than letting different dep versions stomp on each other — see comments on #9697
@@ -76,13 +76,12 @@
io.github.eerohele/pp {:git/tag "2024-01-04.60" ; super fast pretty-printing library
:git/sha "a428751"
:git/url "https://github.com/eerohele/pp"}
io.github.metabase/macaw {:mvn/version "0.1.23"} ; Parse native SQL queries
io.github.metabase/macaw {:mvn/version "0.1.26"} ; Parse native SQL queries
;; The 2.X line of Resilience4j requires Java 17, so we cannot upgrade this dependency until that is our minimum JVM version
io.github.resilience4j/resilience4j-retry {:mvn/version "1.7.1" #_ "must be 1.7.1"} ; Support for retrying operations
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} ; prometheus jvm collector
io.prometheus/simpleclient_jetty {:mvn/version "0.16.0"} ; prometheus jetty collector
javax.servlet/servlet-api {:mvn/version "2.5"} ; used by ring's multipart-params (file upload)
joda-time/joda-time {:mvn/version "2.12.5"}
kixi/stats {:mvn/version "0.5.5" ; Various statistic measures implemented as transducers
:exclusions [org.clojure/data.avl]}
lambdaisland/uri {:mvn/version "1.19.155"} ; Used by openai-clojure
@@ -102,8 +101,7 @@
:exclusions [lambdaisland/uri]} ; OpenAI
net.i2p.crypto/eddsa {:mvn/version "0.3.0"} ; ED25519 key support (optional dependency for org.apache.sshd/sshd-core)
net.redhogs.cronparser/cron-parser-core {:mvn/version "3.5" ; describe Cron schedule in human-readable language
:exclusions [joda-time/joda-time ; exclude joda time 2.3 which has outdated timezone information
org.slf4j/slf4j-api]}
:exclusions [org.slf4j/slf4j-api]}
net.sf.cssbox/cssbox {:mvn/version "5.0.1" ; HTML / CSS rendering
:exclusions [org.slf4j/slf4j-api
junit/junit]}
@@ -127,8 +125,8 @@
:exclusions [org.slf4j/slf4j-api
org.slf4j/jcl-over-slf4j]}
org.apache.xmlgraphics/batik-all {:mvn/version "1.17"} ; SVG -> image
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.77"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.77"}
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.78"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.78"}
org.clj-commons/hickory {:mvn/version "0.7.4" ; Parse HTML into Clojure data structures
:exclusions [org.jsoup/jsoup]}
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"} ; LDAP client
@@ -309,7 +307,8 @@
;; alias for CI-specific options.
:ci
{:jvm-opts ["-Xmx2g"
{:jvm-opts ["-Xmx12g"
"-Xms12g"
;; normally CircleCI sets `CI` as an env var, so this is mostly to replicate that locally. Hawk will not
;; print the progress bar in output when this is set. Progress bars aren't very CI friendly
"-Dci=TRUE"]}
@@ -530,9 +529,7 @@
["metabase\\.driver\\.mysql.*"
"metabase\\.driver\\.postgres.*"]}
;; different port from `:test` so you can run it at the same time as `:test`.
:jvm-opts ["-Dmb.jetty.port=3002"
;; default heap size is 1GB, we were getting OOM on CI so we need to bump this
"-Xmx4g"]}
:jvm-opts ["-Dmb.jetty.port=3002"]}
;; clj -T:cljfmt fix '{:paths ["src/metabase/events/view_log.clj"]}'
:cljfmt

View File

@@ -13,8 +13,7 @@
;; - [Getting started with backend development](https://github.com/metabase/metabase/blob/master/docs/developers-guide/devenv.md#backend-development)
;; - [Additional notes on using tools.deps](https://github.com/metabase/metabase/wiki/Migrating-from-Leiningen-to-tools.deps)
;; - [Use the dev-scripts repo to run various local DBs](https://github.com/metabase/dev-scripts)
;; - [If you're on mac and need a VM to run Windows, Linux.. etc checkout UTM](https://mac.getutm.app/)
;; - [Other tips](https://github.com/metabase/metabase/wiki/Metabase-Backend-Dev-Secrets)
;; - If you're on a Mac and need a VM to run Windows or Linux, [check out UTM](https://mac.getutm.app/)
;;
;; ## Important Parts of the Codebase
;;
@@ -31,6 +30,20 @@
;; - [Liquibase](https://docs.liquibase.com/concepts/changelogs/changeset.html) for database migrations
;; - [Compojure](https://github.com/weavejester/compojure) on top of [Ring](https://github.com/ring-clojure/ring) for our API
;;
;; ## Other Helpful Things
;;
;; [Tips on our Github wiki](https://github.com/metabase/metabase/wiki/Metabase-Backend-Dev-Secrets)
;;
;; ### The Dev Debug Page
;; If you want an easy way to GET/POST to an endpoint and display the results in a webpage, check out the [Dev Debug
;; Page](https://github.com/metabase/metabase/pull/40580). Cherry-pick the commit from that PR, modify `DevDebug.jsx` as
;; you see fit ([here](https://github.com/metabase/metabase/commit/4c5723f44424dca2a68a753b83e31ec8129da0fb) is an
;; example from the ParseSQL project), and then play with the results at `/dev_debug`. *Don't forget to remove the
;; commit before merging to `master`!*
;;
;; ### Lifecycle of a Query
;; Dan wrote a nice guide [here](https://www.notion.so/metabase/Lifecycle-of-a-query-58e212402b7e444d937aba7757f9ec06?pvs=4)
;;
;; <hr />
@@ -251,8 +264,7 @@
([]
(migrate! :up))
([direction & [version]]
(mdb/migrate! (mdb/db-type) (mdb/data-source)
direction version)))
(mdb/migrate! (mdb/data-source) direction version)))
(methodical/defmethod t2.connection/do-with-connection :model/Database
"Support running arbitrary queries against data warehouse DBs for easy REPL debugging. Only works for SQL+JDBC drivers

View File

@@ -53,6 +53,7 @@ Metabase's reference documentation.
- [Commands](./installation-and-operation/commands.md)
- [Supported browsers](./installation-and-operation/supported-browsers.md)
- [Privacy](./installation-and-operation/privacy.md)
- [About the anonymous usage data we collect](./installation-and-operation/information-collection.md)
### Databases
@@ -90,6 +91,7 @@ Metabase's reference documentation.
- [Sharing answers](./questions/sharing/answers.md)
- [Alerts](./questions/sharing/alerts.md)
- [Public sharing](./questions/sharing/public-links.md)
- [Exporting data](./questions/sharing/exporting-results.md)
### Dashboards

View File

@@ -15,10 +15,6 @@ Metabase gives you the ability to automatically cache question results that meet
If your questions share a common model, you can enable [model caching](../data-modeling/models.md#model-caching) instead.
## Caching doesn't work with data sandboxing
Just something to keep in mind: if someone in a [sandboxed](../permissions/data-sandboxes.md) group views a table, Metabase will skip the cached results and query the table directly, returning only the results the person is allowed to see.
## Enabling global caching
1. Go to **Admin settings** > **Caching** (in the sidebar).

View File

@@ -165,7 +165,7 @@ Sections include:
### Dashboard width
You change the width of a dashboard.
You can change the width of a dashboard.
![Change dashboard width](./images/toggle-width.png)
@@ -236,9 +236,9 @@ If your Metabase administrator has enabled [public sharing](../questions/sharing
Public links can be viewed by anyone, even if they don't have access to Metabase. You can also use the public embedding code to embed your question or dashboard in a simple web page or blog post. Check out examples of simple apps with embedded dashboards in our [embedding-reference-apps repository](https://github.com/metabase/embedding-reference-apps). To learn more about [embedding](../embedding/introduction.md), check out our article on [How to use Metabase to deliver analytics to your customers](https://www.metabase.com/blog/external-facing-analytics), as well as an article on how to combine branding, Single Sign-On, full app embedding, and data sandboxing to deliver [multi-tenant, self-service analytics](https://www.metabase.com/blog/embedding).
## Exporting dashboards as PDFs
## Exporting results from a dashboard
To export a dashboard as a PDF, wait until the dashboard is finished loading, then click on the three dot menu (**...**) in the upper right and select **Export as PDF**.
See [Exporting results](../questions/sharing/exporting-results.md#exporting-results-of-a-dashboard).
## Configuring a dashboard through its URL

View File

@@ -29,14 +29,57 @@ Let's say we want to email a dashboard. We'll click on the **Email it** option i
For emails, we can:
- **Add subscribers**. Add email addresses to register subscribers. On Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise), admins can limit email recipients to [approved domains for notifications](../configuring-metabase/email.md#approved-domains-for-notifications) and [configure which recipients Metabase suggests](../configuring-metabase/email.md#suggest-recipients-on-dashboard-subscriptions-and-alerts).
- **Determine frequency and timing**. Tell Metabase how often it should send the dashboard (daily, weekly, or monthly), and what time of day to send the dashboard.
- **Send email now** sends an email to all subscribers.
- **Skip updates without results**. If there are no results, we can tell Metabase to skip sending the email.
- **Attach results**. Tell Metabase if it should attach results to the email as a file, in addition to displaying the table in the email body. You can choose between CSV and XLSX file formats. The attached file will include up to 2000 rows by default. If you're self-hosting Metabase, you can adjust this row limit by setting the environment variable [MB_UNAGGREGATED_QUERY_ROW_LIMIT](../configuring-metabase/environment-variables.md#mb_unaggregated_query_row_limit).
### Add subscribers
Add email addresses to register subscribers. On Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise), admins can limit email recipients to [approved domains for notifications](../configuring-metabase/email.md#approved-domains-for-notifications) and [configure which recipients Metabase suggests](../configuring-metabase/email.md#suggest-recipients-on-dashboard-subscriptions-and-alerts).
### Determine frequency and timing
Tell Metabase how often it should send the dashboard:
- Hourly
- Daily
- Weekly
- Monthly
And what time of day to send the dashboard.
### Send email now
A button that sends an email to all subscribers each time you touch it.
### Don't send if there aren't results
If there are no results, you can tell Metabase to skip sending the email.
### Set filter values for when this gets sent
Here you can set filters to apply when Metabase sends the subscription.
### Attach results
Tell Metabase if it should attach results to the email as a file, in addition to displaying the table in the email body.
If you've added filters to your dashboard and set default values for those filters, Metabase will apply those default values to your subscriptions, filtering the results of all questions that are connected to those filters when the subscriptions are sent. To learn more, check out [dashboard filters](./filters.md).
#### File format
You can choose between the following file formats:
- .csv
- .xlsx
- .json
#### Use unformatted values in attachments
If you check this box, Metabase will drop any visualization settings applied to the questions' results (e.g., date formatting) and send only the raw, unformatted results.
#### Questions to attach
Here you can specify which questions Metabase should attach results for.
The attached files will include up to 2000 rows by default. If you're self-hosting Metabase, you can adjust this row limit by setting the environment variable [MB_UNAGGREGATED_QUERY_ROW_LIMIT](../configuring-metabase/environment-variables.md#mb_unaggregated_query_row_limit).
## Slack subscription options
For Slack subscriptions, you can set up a subscription for a channel (like #general), or for a single person via their Slack username.

View File

@@ -4,7 +4,7 @@ redirect_from:
- /docs/latest/administration-guide/19-formatting-settings
---
# Formatting defaults
# Formatting defaults
There are Metabase users around the world, each with different preferences for how dates, times, numbers, and currencies should be formatted and displayed. Metabase allows you to customize these formatting options at three different levels:
@@ -38,3 +38,20 @@ Currency field formatting settings include all the same options as in the global
## Question-level formatting
Lastly, you can override all formatting settings in any specific saved question or dashboard card by clicking on the gear to open up the visualization options. To reset any overridden setting to the default, just click on the rotating arrow icon next to the setting's label. This will reset the setting to the field-level setting if there is one; otherwise it will be reset to the global default.
Formatting options vary depending on the type of visualization:
- [Combo chart](../questions/sharing/visualizations/combo-chart.md)
- [Detail](../questions/sharing/visualizations/detail.md)
- [Funnel](../questions/sharing/visualizations/funnel.md)
- [Gauge](../questions/sharing/visualizations/gauge.md)
- [Line, Bar, and area charts](../questions/sharing/visualizations/line-bar-and-area-charts.md)
- [Maps](../questions/sharing/visualizations/map.md)
- [Numbers](../questions/sharing/visualizations/numbers.md)
- [Pie or donut chart](../questions/sharing/visualizations/pie-or-donut-chart.md)
- [Pivot table](../questions/sharing/visualizations/pivot-table.md)
- [Progress bar](../questions/sharing/visualizations/progress-bar.md)
- [Scatter plot or bubble chart](../questions/sharing/visualizations/scatterplot-or-bubble-chart.md)
- [Tables](../questions/sharing/visualizations/table.md)
- [Trend](../questions/sharing/visualizations/trend.md)
- [Waterfall chart](../questions/sharing/visualizations/waterfall-chart.md)

View File

@@ -182,7 +182,7 @@ To set up model caching:
1. [Enable model caching in Metabase](#enable-model-caching-in-metabase).
2. [Create a schema to store cached models](#create-a-schema-to-store-cached-models).
> Model caching doesn't work with [data sandboxing](../configuring-metabase/caching.md#caching-doesnt-work-with-data-sandboxing).
> Model caching doesn't work with [data sandboxing](../permissions/data-sandboxes.md).
### Enable model caching in Metabase

View File

@@ -14,6 +14,7 @@ Uploading CSV data is best suited for ad hoc analysis of spreadsheet data. If yo
- [PostgreSQL](../databases/connections/postgresql.md)
- [MySQL](../databases/connections/mysql.md)
- [Redshift](../databases/connections/redshift.md)
## Enabling uploads

View File

@@ -13,6 +13,47 @@ title: Driver interface changelog
efficient way possible. This is currently only required for drivers that support the `:uploads` feature, and has
a default implementation for JDBC-based drivers.
- New feature `:window-functions` has been added. Drivers that implement this method are expected to implement the
cumulative sum (`:cum-sum`) and cumulative count (`:cum-count`) aggregation clauses in their native query language.
For non-SQL drivers (drivers not based on our `:sql` or `:sql-jdbc` drivers), this feature flag is set to `false` by
default; the old (broken) post-processing implementations of cumulative aggregations will continue to be used. (See
issues [#13634](https://github.com/metabase/metabase/issues/13634) and
[#15118](https://github.com/metabase/metabase/issues/15118) for more information on why the old implementation is
broken.)
Non-SQL drivers should be updated to implement cumulative aggregations natively if possible.
The SQL implementation uses `OVER (...)` expressions. It will automatically move `GROUP BY` expressions like
`date_trunc()` into a `SUBSELECT` so fussy databases like BigQuery can reference plain column identifiers. The
actual SQL generated will look something like
```sql
SELECT
created_at_month,
sum(sum(total) OVER (ORDER BY created_at_month ROWS UNBOUNDED PRECEDING) AS cumulative_sum
FROM (
SELECT
date_trunc('month', created_at) AS created_at_month,
total
FROM
my_table
) source
GROUP BY
created_at_month
ORDER BY
created_at_month
```
Non-SQL drivers can use
`metabase.query-processor.util.transformations.nest-breakouts/nest-breakouts-in-stages-with-cumulative-aggregation`
if they want to leverage the same query transformation. See the default `:sql` implementation of
`metabase.driver.sql.query-processor/preprocess` for an example of using this transformation when needed.
You can run the new tests in `metabase.query-processor-test.cumulative-aggregation-test` to verify that your driver
implementation is working correctly.
- `metabase.driver.common/class->base-type` no longer supports Joda Time classes. They have been deprecated since 2019.
## Metabase 0.49.1
- Another driver feature has been added: `describe-fields`. If a driver opts-in to supporting this feature, The

View File

@@ -476,9 +476,9 @@ describe("CollectionHeader", () => {
collection,
});
userEvent.clear(screen.getByDisplayValue("Old name"));
userEvent.type(screen.getByPlaceholderText("Add title"), "New title");
userEvent.tab();
await userEvent.clear(screen.getByDisplayValue("Old name"));
await userEvent.type(screen.getByPlaceholderText("Add title"), "New title");
await userEvent.tab();
expect(onUpdateCollection).toHaveBeenCalledWith({
...collection,

View File

@@ -0,0 +1,24 @@
---
title: MBQL Library changelog
---
# MBQL Library Changelog
Changes made to the library API for manipulating MBQL queries, found in `metabase.lib.js`. The latest API documentation
can be found [here](https://metabase-dev-docs.github.io/metabase/metabase.lib.js.html).
This library is mainly used by Metabase's own frontend, but it is treated as a proper API surface which is versioned
and documented in this changelog.
## Metabase 0.50.0
- Created this file and began versioning this API.
- New function `as-returned` has been added. It handles a tricky case when adding filters or expressions to a query
with aggregations.
Suppose we have a query with aggregations in its last stage. When adding a filter or expression to that stage, it's
applied **before** the aggregations. That may be the desired behavior, but if we want a filter or custom expression
based on the aggregations and breakouts in the last stage, there was no good support in this API.
`as-returned` looks at the query and stage, and shifts to a later stage if necessary. If a later stage is needed but
we were already on the last stage, a new empty stage is appended.

View File

@@ -35,6 +35,7 @@ This guide contains detailed information on how to work on Metabase codebase.
- [Autogenerated API docs](../api-documentation.md)
- [Working with the Metabase API](https://www.metabase.com/learn/administration/metabase-api.html)
- [Driver Interface Changelog](driver-changelog.md)
- [MBQL Library Changelog](mbql-library-changelog.md)
## Database drivers

View File

@@ -18,9 +18,9 @@ You'll embed the full Metabase application in your app. Once logged in, people c
## [Static embedding](./static-embedding.md)
Also known as Signed Embedding, Static embedding offers drill-through with custom destinations, so you can define what happens when people click on a chart, like sending people to another chart or URL–all while securing the underlying data.
Also known as Signed Embedding, Static embedding is a secure way to embed charts and dashboards. Static embeds are view only; people won't be able to drill-through charts and tables.
## [Parameters for signed embeds](./static-embedding-parameters.md)
## [Parameters for static embeds](./static-embedding-parameters.md)
You can pass parameters between Metabase and your website via the embedding URL to specify how Metabase items should look and behave inside the iframe on your website.

View File

@@ -12,19 +12,26 @@ Events and timelines are a way to capture that chronological knowledge and make
## Events
![An event on a time series](./images/event-on-a-time-series.png)
An event is basically a date + a title + a description + an icon. You can add events to Metabase to show important milestones, launches, or anything else, right alongside your data.
Metabase will only display events on time series charts when viewing an individual question. Metabase won't display events on charts in dashboard cards.
## Timelines
You can group events into timelines, and associate those timelines with [collections](collections.md). For example, you may want to have a timeline that contains important email or sales dates, or an outages timeline that tracks downtime. You can move events between timelines, and move timelines from collection to collection.
Timelines are groups of events associated with a [collection](collections.md).
Collections can have timelines, and timelines can contain events. In practice what this means is that events you've added to a timeline will show up on time series questions stored in the same collection as that timeline. Events are invisible on dashboards
![Timeline sidebar](./images/timeline-sidebar.png)
## Event and timeline permissions
For example, you may want to have a timeline that contains important email or sales dates, or an outages timeline that tracks downtime. You can move events between timelines, and move timelines from collection to collection.
Anyone with [view access](../permissions/collections.md) to a collection can view the collection's events and timelines.
Collections can have timelines, and timelines can contain events. In practice what this means is that events you've added to a timeline will show up on time series questions stored in the same collection as that timeline.
- If you don't explicitly create a timeline yet, but you do create events, Metabase will automatically create a timeline for you (which acts as the default timeline for the collection).
- You can have multiple timelines for the same collection.
- Timelines associated with collections do not apply to that collection's sub-collections.
Anyone with curate access to a collection can add events and timelines to that collection.
### Adding events when viewing a collection
@@ -34,14 +41,14 @@ When viewing a [collection](collections.md), you can view, add, or edit events b
Once you create an event, the event will show up in charts in the collection, provided:
- the date of the event falls within the chart's time range, and
- the timeline is visible (more on that [below](#adding-events-when-viewing-a-question))
- The date of the event falls within the chart's time range.
- The timeline is visible (more on that [below](#adding-events-when-viewing-a-question)).
You'll see an icon along the x-axis that plots the event. A vertical line will extend from the event to show when the data plotted on the chart intersects with the event.
![An event on a chart](./images/example-event.png)
### Adding events when viewing a question
## Adding events when viewing a question
If your question is a time series, you can click on the **Calendar** in the bottom right of the question, and Metabase will open the timeline sidebar. Metabase will list any timelines and their events that fall in the range of your time series. You can:
@@ -49,24 +56,73 @@ If your question is a time series, you can click on the **Calendar** in the bott
- Add a new event (even if you haven't saved the question yet).
- Edit, move, or archive an event.
## Archiving and deleting
## Viewing events and timelines on a chart from a different collection
### Archiving events
If you're viewing a question with a time series chart from a _different_ collection, you can temporarily apply a timeline to the chart by clicking on the **calendar** icon in the bottom right of the question and selecting the timeline and events you want to display.
You can archive both events and timelines to get them out of the way, or unarchive them to resurrect them.
These selections are temporary; if you reload the question, Metabase will drop the events from the chart.
To archive an event, click on the **...** next to the event and select **Archive event**.
### To hide the timeline and its events on a chart
To view archived events, navigate to the relevant collection and click on the **Calendar** icon, then the **...** menu to **View archived events**.
To temporarily hide the events from a chart:
### Archiving timelines
1. Click on the **calendar** icon in the bottom right.
2. Uncheck the timeline or event.
To archive a timeline, click on the **...** next to the timeline and select **Edit timeline details**. Metabase will pop up an edit modal; click on **Archive timeline and all events**.
Your selections will reset on reload.
To resurrect an archived timeline, click on the **Calendar** icon in the relevant collection, then click on the **...** menu. Next to the timeline you want to unarchive, click on the **...** menu and select **Unarchive timeline**.
To permanently hide the timeline and its events, you'll need to [archive the timeline](#archiving-timelines). Alternatively, you could move the question to a different collection.
To permanently delete an event or timeline, you must first archive the event or timeline. Then you can delete the archived events from the **View archived events** modal, or timelines from the **View archived timelines** modal.
## Edit an event
## Disabling events for a question
![Edit an event](./images/edit-an-event.png)
If a collection includes timelines, those events will show up for any time series in that collection (provided the timeline and the question's date range overlap). If you want to disable the visibility of events by default for a particular question, you'll need to move that question to a collection that lacks timelines and events.
To edit an event:
1. Click the calendar icon in the top right of a collection.
2. Go to the timeline that contains the event, click on the event's three-dot menu (**...**).
3. Select:
- **Edit event**: its title, description, and icon.
- **Move event**: to another timeline.
- **Archive event**: to hide the event from charts.
## Archiving timelines
To archive a timeline:
1. Go to the timeline's collection and click on the **calendar** icon in the top right.
2. Select the timeline, then click on the three-dot menu (**...**). If the collection only has one timeline, click on the three-dot menu (**...**).
3. Select **Edit timeline details**.
4. Click on the red **Archive timeline and all events**.
### View archived events and timelines
> Archived events and timelines can only be viewed from the collection. They don't show up in the [main archive](../exploration-and-organization/history.md).
To view (and resurrect) archived timelines and events:
1. Click on the **Calendar** icon in the relevant collection.
2. Click on the three-dot menu **...** menu.
3. Select **View archived timelines**. Metabase will display archived events or timelines.
4. If you want to unarchive an item, click on the three-dot menu **...** next to the event or timeline and select the **Unarchive** option.
To permanently delete an archived event or timeline, click on the three-dot menu (**...**) and select **Delete**.
Then you can delete the archived events from the **View archived events** modal, or timelines from the **View archived timelines** modal.
## Event and timeline permissions
Event and timeline permissions depend on your [collection permissions](../permissions/collections.md).
- **View access**: you can view the collection's events and timelines. You can also temporarily apply timelines and events to time series in other collections.
- **Curate access**. Anyone with curate access to a collection can add events and timelines to that collection.
### Make a timeline and its events available for everyone
If you want the event and timeline to be available to everyone, create the timeline in a collection that the [All Users group](../people-and-groups/managing.md#all-users) has access to, as by default everyone is in the All Users group.
Note that for questions outside of that collection, you can only temporarily apply those timelines to charts. The timelines won't appear automatically when you reload the chart.
## Further reading
- [Keeping your Metabase organized](https://www.metabase.com/learn/administration/same-page)

View File

@@ -4,7 +4,7 @@ title: History
# History
For questions, dashboards, and models, Metabase keeps a version history for the previous fifteen versions of that item. You can view changes, and revert to previous versions.
For questions, dashboards, and models, Metabase keeps a version history for the previous fifteen versions of that item. You can view changes, and revert to previous versions.
You can also archive, unarchive, and permanently delete outdated items.
@@ -52,6 +52,8 @@ Archiving a collection archives all of the collection's contents as well. You ca
2. Click the `...` beside the "Collections" header in the sidebar.
3. Click **View archive**.
To view archived [events and timelines](./events-and-timelines.md), go to the relevant unarchived collection, click on the collection's **Calendar** icon, then the three-dot menu (**...**) and select the option to view the archived events or timelines.
## Unarchiving items
1. Open the main Metabase sidebar.

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -1,104 +1,46 @@
---
title: About the information we collect
title: About the anonymous usage data we collect
redirect_from:
- /docs/latest/information-collection
---
# About the information we collect
# About the anonymous usage data we collect
Metabase uses Google Analytics and Snowplow to collect anonymous usage information from the installed servers that enable this feature. Below is a representative list of the events we have instrumented, as well as the information we collect about the user performing the action and the instance being used.
If you're self-hosting Metabase and you've opted in to provide us with anonymous usage data (thank you!), Metabase will phone home some data collected via Google Analytics and Snowplow.
We also will phone home some anonymized metrics from the metabase application server on a nightly basis. Note that we don't collect any usernames, any emails, the server IP, database details of any kind or any personally identifiable information in this process.
**[We only collect anonymous Metabase data; we don't collect any of your data](https://www.metabase.com/security).** We don't collect any usernames, emails, server IPs, database details of any kind, or any personally identifiable information (PII).
While this list of anonymous information we collect might seem long, it’s useful to compare this to other alternatives. With a typical SaaS platform, not only will this information be collected, but it will also be accompanied by information about your data, how often it is accessed, the specific queries that you use, specific numbers of records all tied to your company and current plan.
This anonymous data helps us understand how people are actually using Metabase, which in turn helps us prioritize what to work on next.
We collect this information to improve your experience and the quality of Metabase. We use the information you voluntarily share to understand how our users are actually using our product, what kinds of features to prioritize, and how many items to target in our design process. For example, without knowing what the distribution of the number of accounts on each instance in our install base is, we can't know whether the tools we provide can't scale until someone complains. And even then, we only hear complaints and not the people who are quietly happily using us. We're striving to create the best product possible.
## Examples of the anonymous data we collect and how we use it
If you prefer not to provide us with this anonymous usage data, you can always go to your instance’s admin section and toggle off the option for `Anonymous Tracking`.
In a nutshell: we track some basic events to find opportunities for improving the product and evaluating project outcomes. We _don't_ use the data for sales or advertising (and the data's anonymous, so we couldn't use it for that anyway).
If you're in the process of setting up your Metabase, an admin can also toggle off tracking during the `Usage Data Preferences` onboarding step. We collect a few anonymous events before that point, but won't do so anymore if you choose to opt out.
We collect things like:
In the list below, we spell out exactly why we collect each bit of information.
- Events in the product (for example, when people create models)
- Some performance data
- The number and types of DBs you've connected to your Metabase
- The number of questions and dashboards and other items you've created
## Example questions we want to answer
This anonymous data helps us figure out things like:
- Is our query interface working?
- Are users stopping halfway through a question?
- Are users using filters?
- Are users using groupings?
- How often are users using bare rows vs other aggregation options?
- Are people clicking on column headings to sort or manually adding a sort clause?
- How often are users writing SQL instead of using the query interface?
- Are these queries written by a select group of analysts or is the entire company SQL literate?
- Are people using dashboards as a starting point for queries?
- How many clicks are there on dashboard cards?
- How many of these clicks result in modified queries that are executed?
- How often are questions saved?
- How often are saved questions added to dashboards?
- Which features you're using
- Where people get stuck
- How performance for key workflows (like querying or loading) changes over time
## What we will do with the answers to these questions
## Opting out of anonymous usage data collection for self-hosted Metabases
- Prioritize improvements in the query interface vs the SQL interface
- Optimize the product for common usage patterns
- Stay on top of browser incompatibilities
- Optimize our dashboards for either passive consumption or as a starting point for further exploration depending on how they are being used
If you're self-hosting Metabase, you can opt out of providing us with your anonymous usage data:
While we will closely follow reported issues and feature requests, we aim to make as many of our users happy and provide them with improvements in features that matter to them. Allowing us to collect information about your instance gives your users a vote in future improvements in a direct way.
1. Click on the gear icon.
2. Select **Admin settings**.
3. Go to the **Settings** tab.
4. Click **General**
5. Toggle the **Anonymous tracking** option.
## The data we collect
If you're in the process of setting up your Metabase, you can also toggle off tracking during the `Usage Data Preferences` onboarding step. We collect a few anonymous events before that point, but won't do so anymore if you choose to opt out.
NOTE: We never capture any specific details in any of our tracking methodology such as user details, table names, field names, etc. Collected data is limited to the types of actions users are taking with the product.
## Google Analytics Events
| Category | Action | Why we collect this |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Links and Page Views | General website tracking of what pages are most used | This provides better understanding of what parts of the application are liked and used by customers so we know what's popular and potentially what needs more improvement. |
| Dashboards | When the dashboard dropdown is used, when dashboards are created and updated, what types of edits occur such as adding/removing cards and repositioning. | We use this information to understand how dashboards are being used and what types of activities users most commonly do on their dashboards. |
| Pulses | When pulses are created and updated, what types of pulses are created, and how many cards typically go in a pulse. | This is used to have a sense for how teams are structuring their push based communication. When and where is information most often sent and how much information allows Metabase to continue improving features around push based data interactions. |
| Query Builder | When questions are saved and viewed along with what types of choices are made such as chart types and query clauses used. | Helps the Metabase team understand the basic patterns around how users are accessing their data. |
| SQL Query | When a SQL query is saved or run. | This mostly just gives us a sense for when users are bypassing the GUI query interface. We never capture the actual SQL written. |
| Admin Settings | We capture some very basic stats about when settings are updated and if there are ever errors. We also capture non-intrusive settings such as the chosen timezone. | We use this information to make sure that users aren't having problems managing their Metabase instance and it provides us some sense for the most common configuration choices so we can optimize for those cases. |
| Databases | We simply capture when databases are created or removed and what types of databases are being used | This helps Metabase ensure that we spend the most time and attention on the types of databases that are most popular to users. |
| Table Metadata | The saving and updates on tables, fields, segments, and metrics are all counted, along with a few other details such as what types of special metadata choices are made. | We use this data to help ensure that Metabase provides an appropriate set of options for users to describe their data and also gives us a sense for how much time users spend marking up their schemas. |
## Snowplow Events
| Category | Action | Why we collect this |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Set up | General clickthrough tracking of what steps are completed vs. abandoned | This provides us with a better understanding of how we can improve the on-ramp for new Metabase users. |
| Database configuration | Whether your database connection attempt is successful vs. unsuccessful, which database you connect with. We don't collect any of your database credentials here. | Helps us understand which databases are most popular and performing as expected so we can invest in improving how Metabase works with them. |
| Dashboard | When you create a new dashboard or add a new question to a dashboard. | We can use this to determine if Metabase is adding value to your organization, and therefore if we're doing our jobs correctly. |
| Account | When a new user is created or a new instance is set up. | This powers basic reporting to help us understand how your Metabase team is growing. |
## Server-side Analytics
| Metric | An example of why we collect this |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Number of Users/Admins and whether SSO is enabled | To understand which auth methods are being used, and whether to prioritize features that scale with number of users. |
| Number of user groups | To understand how complicated a permissions model most of our users have, and to make sure that we don't over-simplify our designs. |
| Number of Dashboards | Whether we need to provide ways to organize dashboards. |
| Number of Cards per Dashboards | Do we need to provide more structure to make long dashboards easier to parse? |
| Number of Dashboards per Card | Are our users only creating a card to put it in a dashboard or are they used in many places? |
| Types of Databases | Which database driver bugs to prioritize |
| Number of pulses with attachments | Are people using attachments? |
| Number of alerts | Are people using alerts? Do they typically have a few or does each user have them? |
| Number of Collections | Do we need to add additional organization tools? |
| Number of Databases | Are users using a single DB or many? How large should the icons for a database in the databrowser be? |
| Number of Schema | Are users actively using namespaces on redshift? Do we actually need to design for 100s of schemas or is that just a small percentage of our users? |
| Number of Tables | What kind of data models are people using? Do we need table search? |
| Number of Fields | Can we pre-fetch all the fields in our metadata api to improve performance for most usres, or should we fetch them per table to scale more efficiently? |
| Number of Segments | Are people using segments widely? If so, should we surface them up higher in the UI? |
| Number of Metrics | Are metrics common? If not, should we remove the Metrics option in the New Question Flow |
| Number of Queries Run | How many queries do our most active instances run per day? Do we need to improve caching? |
| Number of Query Errors | Do we need to change how we display errors in the logs? Are they being spammed? |
| Query Latencies | What percentage of our user base runs queries that allow for iterative querying (less than 1 second) |
| Timezone | We have a bug in a certain timezone, how many users are in that timezone? |
| Language | How many non-English speaking users do we have? How fast should we be pushing internationalization? |
| OS and JVM Version | Can we deprecate Java 7 yet? |
Note this is meant to be representative. The actual code that is running to generate this can be audited at [https://github.com/metabase/metabase/blob/master/src/metabase/analytics/](https://github.com/metabase/metabase/blob/master/src/metabase/analytics).
## Data privacy
## Further reading
Check out our page on [data privacy and security](https://www.metabase.com/security).

View File

@@ -252,7 +252,6 @@ site-name
application-font-files
loading-message
report-timezone
show-lighthouse-illustration
persisted-models-enabled
enable-content-management?
subscription-allowed-domains

View File

@@ -42,6 +42,10 @@ Export Metabase metrics for viewing with Prometheus.
Rest in peace, Internet Explorer.
## [About the anonymous usage data we collect](./information-collection.md).
We collect anonymous Metabase usage data to improve the product. We don't collect your data.
## [Command line commands](./commands.md)
Metabase's CLI.

View File

@@ -113,6 +113,32 @@ This action will delete any dashboard subscriptions or alerts the person has cre
This action doesn't affect email distribution lists that are managed outside of Metabase.
## Default user accounts
Metabase includes default user accounts to handle various tasks. We're documenting these accounts here so you know they're legitimate accounts and not someone trying to spy on your Metabase. Some things to know about them:
- Customers are not charged for these accounts.
- No one can log in to these user accounts.
- Metabase excludes these user accounts from the **Admin settings** > **People** tab.
### Anonymous user account
- ID: 0
- First name: External
- Last name: User
- Email: null
Metabase uses this anonymous user account to identify anonymous views, for example views of a [public question or dashboard](../questions/sharing/public-links.md). This account is a virtual user: the account doesn't exist in the application database. You'll see this account show up in [usage analytics](../usage-and-performance-tools/usage-analytics.md).
### Metabase internal account
- ID: 13371338
- First name: Internal
- Last name: Metabase
- Email: internal@metabase.com
Metabase uses this account to load content into Metabase (like the [Metabase analytics](../usage-and-performance-tools/usage-analytics.md) collection). You may see this `internal@metabase.com` account in the logs.
## Groups
To determine [who has access to what](../permissions/start.md), you’ll need to

View File

@@ -31,13 +31,14 @@ When the "Set up Single Sign-On with SAML" page appears, you'll see an option fo
![AZAzureStep1](images/saml-azure-step-1.png)
Fill out the following fields as follows and click "Save":
- **Identifier (Entity ID)**: `Metabase`
- **Reply URL (Assertion Consumer Service URL)**: go to your Metabase instance in Settings -> Admin-> Authentication -> SAML and insert the value that your Metabase instance reports in the "Configure your identity provider (IdP)" box.
In a new tab, visit the "App Federation Metadata URL". On the Metadata page, note the:
- "Login URL"
- "Azure AD Identifier"
- "Azure AD Identifier"
You'll need these URLs to complete the SSO setup in Metabase.
@@ -49,8 +50,8 @@ Log in to Metabase as an administrator and go to **Admin** -> **Settings** -> **
Under "Tell Metabase about your identity provider", enter the following:
- **SAML Identity Provider URL**: the "Login URL" you got on Step 4 on the Azure AD SAML SSO configuration
- **SAML Identity Provider Certificate**: copy and paste the super long string under the "<X509Certificate>" tag in the "App Federation Metadata Url". Make sure you copy and paste the whole string; if you miss any character, the integration won't work.
- **SAML Identity Provider URL**: the "Login URL" you got on Step 4 on the Azure AD SAML SSO configuration
- **SAML Identity Provider Certificate**: copy and paste the super long string under the `<X509Certificate>` tag in the "App Federation Metadata Url". Make sure you copy and paste the whole string; if you miss any character, the integration won't work.
- **SAML Application Name**: "Metabase"
- **SAML Identity Provider Issuer**: the "Azure AD Identifier" URL you got from the Azure AD SAML SSO configuration.

View File

@@ -6,37 +6,45 @@ title: Notification permissions
Notifications in Metabase include [alerts](../questions/sharing/alerts.md) and [dashboard subscriptions](../dashboards/subscriptions.md#setting-up-a-dashboard-subscription).
## Who can edit dashboard subscriptions and alerts
What you can do with alerts and dashboard subscriptions depends on whether you're in the Administrators group or in a sandboxed group.
- [All Users group](#all-users-group-notification-permissions)
- [Sandboxed group](#sandboxed-accounts-notification-permissions)
- [Administrators group](#administrators-group-notification-permissions)
### All Users group notification permissions
Everyone's in the All Users group. Which means that everyone can:
- Create [alerts](../questions/sharing/alerts.md) and [dashboard subscriptions](../dashboards/subscriptions.md#setting-up-a-dashboard-subscription).
- Add new recipients to dashboard subscriptions that they created. Non-admins can only add themselves to alerts.
- Unsubscribe from any alert or subscription in their [Account settings](../people-and-groups/account-settings.md).
When a notification creator adds new recipients to an alert or subscription, Metabase will display data to the recipients using the **creator's** [data permissions](../permissions/data.md) and [collection permissions](../permissions/collections.md).
### Sandboxed accounts notification permissions
Same as everyone in the All Users group, but with a special case: **people in sandboxed groups will only see themselves in the list of recipients** when creating an alert or subscription.
### Administrators group notification permissions
People in the admin group can:
- View all subscriptions and alerts.
- Add or remove recipients from an existing subscription or alert. Admins can safely add and remove recipients without changing the permissions of the alert or subscription. For example, if an admin adds Anya to a subscription created by Beau, Anya will receive emails with the same data that Beau can see (not what the admin can see).
- Delete subscriptions or alerts.
Admins can also use Metabase's [auditing tools](../usage-and-performance-tools/audit.md#subscriptions-and-alerts) to manage notifications.
## What notification recipients can see
Notification **recipients** can see whatever the notification **creator** can see. For example, if:
- Beau creates a subscription to a dashboard saved in their [personal collection](../exploration-and-organization/collections.md#your-personal-collection).
- Beau adds Anya to the dashboard subscription.
- Anya will see the dashboard in her email, even though she doesn't have permissions to view that dashboard in Beau's personal collection.
## All accounts
From [Account settings](../people-and-groups/account-settings.md), all accounts can:
- Create [alerts](../questions/sharing/alerts.md) and [dashboard subscriptions](../dashboards/subscriptions.md#setting-up-a-dashboard-subscription).
- Add new recipients to dashboard subscriptions that they own. Non-admins can only add themselves to alerts.
- Unsubscribe from any alert or subscription.
When a notification creator adds new recipients to an alert or subscription, Metabase will display data to the recipients using the **creator's** [data permissions](../permissions/data.md) and [collection permissions](../permissions/collections.md).
## Sandboxed accounts
Same as [all accounts](#all-accounts), but **people using sandboxed accounts will only see themselves in the list of recipients** when creating an alert or subscription.
## Admins
{% include plans-blockquote.html feature="Auditing tools" %}
From Metabase's [auditing tools](../usage-and-performance-tools/audit.md#subscriptions-and-alerts), admins can:
- View all subscriptions and alerts
- Add or remove recipients from an existing subscription or alert
- Delete subscriptions or alerts
Admins can add recipients without changing the permissions of the alert or subscription. For example, if an admin adds Anya to a subscription created by Beau, Anya will receive emails with the same data that the Beau can see.
- Anya will see the dashboard results in her email, even though she doesn't have permissions to view that dashboard in Beau's personal collection.
## More control over email options

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 132 KiB

View File

@@ -6,7 +6,7 @@ redirect_from:
## Referencing models and saved questions
With SQL databases, we can use a [model][model] or an existing question as the basis for a new query, or as a common table expression [CTE][CTE].
With SQL databases, we can use a [model][model] or an existing question as the basis for a new query, or as a common table expression [CTE][cte].
For example, let's say we have a lot of data spread across a number of tables, but people are most interested in a subset of that data. We can perform a complicated query once to return those results, and save that question as a model, which people can refer to in their queries just like they would with any other table.
@@ -37,14 +37,21 @@ FROM gizmo_orders
When this query is run, the `{% raw %}{{#5-gizmo-orders-in-2019}}{% endraw %}` tag will be substituted with the SQL query of the referenced question, surrounded by parentheses. So it'll look like this under the hood:
```
WITH gizmo_orders AS (SELECT *
FROM orders AS o
INNER JOIN products AS p
ON o.product_id = p.id
WHERE p.category = 'Gizmo'
AND o.created_at BETWEEN '2019-01-01' AND '2019-12-31')
SELECT count(*)
FROM gizmo_orders
WITH
gizmo_orders AS (
SELECT
*
FROM
orders AS o
INNER JOIN products AS p ON o.product_id = p.id
WHERE
p.category = 'Gizmo'
AND o.created_at BETWEEN '2019-01-01' AND '2019-12-31'
)
SELECT
count(*)
FROM
gizmo_orders
```
## Limitations and tradeoffs
@@ -61,6 +68,5 @@ FROM gizmo_orders
- [SQL troubleshooting guide](../../troubleshooting-guide/sql.md).
- [Segments and Metrics](../../data-modeling/segments-and-metrics.md)
[cte]: https://www.metabase.com/learn/sql-questions/sql-cte
[model]: ../../data-modeling/models.md

View File

@@ -18,7 +18,7 @@ Field Filter, a special type of filter, have a [slightly different syntax](#fiel
This example defines a **Text** variable called `category`:
```sql
```
{% raw %}
SELECT
count(*)
@@ -31,7 +31,7 @@ WHERE
Metabase will read the variable and attach a filter widget to the query, which people can use to change the value inserted into the `cat` variable with quotes. So if someone entered "Gizmo" into the filter widget, the query Metabase would run would be:
```sql
```
SELECT
count(*)
FROM
@@ -118,7 +118,7 @@ Let's say you want to create a Field Filter that filters the `People` table by s
The syntax for Field Filters differs from a Text, Number, or Date variable.
```sql
```
{% raw %}
SELECT
*
@@ -196,7 +196,7 @@ The reason is that field filters generate SQL based on the mapped field; Metabas
Your main query should be aware of all the tables that your Field Filter variable is pointing to, otherwise you'll get a SQL syntax error. For example, let's say that your main query includes a field filter like this:
```sql
```
{% raw %}
SELECT
*
@@ -209,7 +209,7 @@ WHERE
Let's say the `{% raw %}{{ product_category }}{% endraw %}` variable refers to another question that uses the `Products` table. For the field filter to work, you'll need to include a join to `Products` in your main query.
```sql
```
{% raw %}
SELECT
*
@@ -248,7 +248,7 @@ In the variables sidebar, you can set a default value for your variable. This va
You can also define default values directly in your query by enclosing comment syntax inside the end brackets of an optional parameter.
```sql
```
WHERE column = [[ {% raw %}{{ your_parameter }}{% endraw %} --]] your_default_value
```
@@ -256,7 +256,7 @@ The comment will "activate" whenever you pass a value to `your_parameter`.
This is useful when defining complex default values (for example, if your default value is a function like `CURRENT_DATE`). Here's a PostgreSQL example that sets the default value of a Date filter to the current date using `CURRENT_DATE`:
```sql
```
{% raw %}
SELECT
*
@@ -286,7 +286,7 @@ To make a clause optional in your native query, type `[[brackets around a {% raw
In this example, if no value is given to `cat`, then the query will just select all the rows from the `products` table. But if `cat` does have a value, like "Widget", then the query will only grab the products with a category type of Widget:
```sql
```
{% raw %}
SELECT
count(*)
@@ -297,7 +297,7 @@ FROM
To use multiple optional clauses, you must include at least one regular `WHERE` clause followed by optional clauses, each starting with `AND`:
```sql
```
{% raw %}
SELECT
count(*)

View File

@@ -57,7 +57,7 @@ where **Sighting Type** is a custom column with the expression:
```
case([Has Wings] = TRUE AND [Has Face] = TRUE, "Bird",
[Has Wings] = TRUE AND [Has Face] = FALSE, "Plane",
[Has Wings] = FALSE AND [Has Face] = TRUE, "Superman"), "Unknown")
[Has Wings] = FALSE AND [Has Face] = TRUE, "Superman", "Unknown")
```
You can use the columns holding your "labels" to:

View File

@@ -0,0 +1,74 @@
---
title: Exporting results
---
# Exporting results
You can export the results of a question or dashboard.
## Exporting results of a question
To export the results of a questions, click on the **Download** button in the lower right of a question.
![Exporting results of a question](../images/exporting-the-results-of-a-question.png)
You can export the results of a question as:
- .csv
- .xlsx
- .json
If you don't see this option, you may not have [permissions to download results](../../permissions/data.md#download-results).
### Maximum number of rows you can download
You can download up to 1 million rows.
### Exporting unformatted results
You can export the unformatted results of a question by holding the `option` key on a Mac or the `Alt` key on PC when selecting the file format for your download.
Metabase will export the raw results of a question without applying any of the [formatting you applied](../../data-modeling/formatting.md) to the columns in the question. For example, if you formatted a floating point number to display only the first two decimal digits in the table results, exporting the unformatted results would include additional decimal digits (if any) found in the raw results.
## Exporting data via a public link
You can also create a [public link](../sharing/public-links.md#public-link-to-export-question-results-in-csv-xlsx-json) that people can use to download data in a specific format, as well as [raw, unformatted question results](public-links.md#exporting-raw-unformatted-question-results).
## Exporting question data via alerts
You can also export data by setting up an [alert](../sharing/alerts.md).
## Exporting results of a dashboard
You can export a dashboard as a PDF. Click on the three dot menu (**...**) and select **Export as PDF**.
![Exporting a dashboard as a PDF](../images/export-dashboard-as-pdf.png).
The PDF will only include screenshots of the charts as they are visible on the dashboard.
### Exporting results of a dashboard card
To export the results of a particular card, hover over the dashboard card, click on the three dot menu (**...**), and select **Download results**.
![Export results of a dashboard card](../images/download-card-results.png)
From here you can select:
- .csv
- .xlsx
- .json
- .png (if a chart)
To export the raw, unformatted results, hold down the `option` key for Macs, or `Alt` key for Windows, then click on the download file format.
If you don't see this option, you may not have [permissions to download results](../../permissions/data.md#download-results).
## Exporting results of a dashboard via dashboard subscriptions
You can use [dashboard subscriptions](../../dashboards/subscriptions.md) to regularly export data from all questions on a dashboard, and include those results as an attachment.
## Further reading
- [Alerts](../sharing/alerts.md)
- [Dashboard subscriptions](../../dashboards/subscriptions.md)
- [Tables](../sharing/visualizations/table.md)

View File

@@ -3,36 +3,25 @@ title: Public sharing
redirect_from:
- /docs/latest/administration-guide/12-public-links
- /docs/latest/embedding/12-public-links
- /docs/latest/questions/sharing/public-links
---
# Public sharing
Sometimes you'll want to share a dashboard or question with someone who isn't a part of your organization or company. To share your work with people who don't need access to your full Metabase instance, you can create public links and public embeds.
You can create and share public links (URLs) for questions and dashboards. You can view them as standalone destinations (URLs) or as embedded iframes in another page or app. In both cases, the public item will display static (view-only) results of your question or dashboard, so visitors won't be able to drill-down into the underlying data on their own.
## Enable public sharing in Metabase
## Create a public link for a question
![Enable public sharing](../images/enable-public-sharing.png)
![Create a public link for a question](../images/create-a-public-link.png)
In order to share Metabase items like questions and dashboards via a public link or an embedded iframe, an admin needs to enable public sharing for that Metabase instance by going to **Settings gear icon** > **Admin settings** > **Public sharing**.
You can find the **Sharing** icon at the bottom right corner of a question. You can create a link to view the question as is, or to [export the results of a question](#public-link-to-export-question-results-in-csv-xlsx-json).
Once toggled on, the **Public sharing** section will display Metabase questions, dashboards, and actions with active public links. To deactivate a public link, click the **X** in the **Revoke link** column for that item.
## Create a public link for a dashboard
## Enable sharing on your saved question or dashboard
To share a dashboard via a public link, click on the **Sharing** button in the top right menu.
Once [public sharing](#enable-sharing-on-your-saved-question-or-dashboard) is enabled for your Metabase, you'll find the **Sharing and embedding icon** on saved questions and dashboards (it looks like a box with an arrow pointing to the upper right).
![Sharing a dashboard](../images/dashboard-sharing.png)
You can find the **Sharing and embedding icon** icon at the bottom right corner of a question, or the top right corner of a dashboard.
To enable public sharing on a question or dashboard, click on the **Sharing and embedding icon** icon to bring up the sharing settings modal, then click on the toggle.
![Enable sharing](../images/enable-links.png)
For more information about the option to **Embed this item in an application**, see the docs on [static embedding](../../embedding/static-embedding.md).
## Public links
Once you've [enabled sharing on your question or dashboard](#enable-sharing-on-your-saved-question-or-dashboard), you can copy and share the public link URL with whomever you please. The public link URL will display static (view-only) results of your question or dashboard, so visitors won't be able to drill-down into the underlying data on their own.
To embed a dashboard, see [static embedding](../../embedding/static-embedding.md).
## Public link to export question results in CSV, XLSX, JSON
@@ -40,21 +29,34 @@ The export option is only available for questions, not dashboards.
To create a public link that people can use to download the results of a question:
1. Click on the **Sharing and embedding** icon for the question.
2. Click the toggle to [enable sharing](#enable-sharing-on-your-saved-question-or-dashboard).
3. Click on the file format you want (below the **Public link** URL).
4. Open the public link in a new tab to test the download.
1. Click on the **Sharing** icon for the question.
2. Select **Public link**.
3. Click on the file format you want (below the **Public link** URL): CSV, XLSX, or JSON.
![Public export](../images/public-export.png)
Open the public link in a new tab to test the download.
### Exporting raw, unformatted question results
To export the raw, unformatted rows, you'll need to append `?format_rows=false` to the URL Metabase generates. For example, if you create a public link for a CSV download, the URL would look like:
```html
https://www.example.com/public/question/cf347ce0-90bb-4669-b73b-56c73edd10cb.csv?format_rows=false
```
By default, Metabase will export the results of a question that include any formatting you added (for example, if you formatted a column with floats to display as a percentage (0.42 -> 42%)).
See docs for the [export format endpoint](https://www.metabase.com/docs/latest/api/public#get-apipubliccarduuidqueryexport-format).
## Simulating drill-through with public links
Metabase's automatic [drill-through](https://www.metabase.com/learn/questions/drill-through) won't work on public dashboards because public links don't give people access to your raw data.
You can simulate drill-through on a public dashboard by setting up a [custom click behaviour](../../dashboards/interactive.md) that sends people from one public link to another public link.
You can simulate drill-through on a public dashboard by setting up a [custom click behavior](../../dashboards/interactive.md) that sends people from one public link to another public link.
1. Create a second dashboard to act as the destination dashboard.
2. [Enable sharing](#enable-sharing-on-your-saved-question-or-dashboard) on the destination dashboard.
2. [Create a public link](#create-a-public-link-for-a-dashboard) for the destination dashboard.
3. Copy the destination dashboard's public link.
4. On your primary dashboard, create a [custom destination](../../dashboards/interactive.md#custom-destinations) with type "URL".
5. Set the custom destination to the destination dashboard's public link.
@@ -69,12 +71,15 @@ For example, if you have a primary public dashboard that displays **Invoices** d
## Public embeds
If you want to embed your question or dashboard in a simple web page or blog post:
![Public embed](../images/public-embed.png)
1. Click on the **Sharing and embedding** icon for your question or dashboard.
2. Click the toggle to [enable sharing](#enable-sharing-on-your-saved-question-or-dashboard).
3. Copy the **Public embed** iframe snippet.
4. Paste the iframe snippet in your destination of choice.
If you want to embed your question or dashboard as an iframe in a simple web page or app:
1. Click on the **Sharing** icon for your question or dashboard.
2. Click **Embed**.
3. Click **Public embed**.
4. Copy the iframe snippet Metabase generates for you.
5. Paste the iframe snippet in your destination of choice.
To customize the appearance of your question or dashboard, you can update the link in the `src` attribute with [public embed parameters](#public-embed-parameters).
@@ -157,6 +162,42 @@ Note that the name of the filter in the URL should be specified in lower case, a
/dashboard/42?filter_for_user_zip_code=02116
```
## Disable public sharing in Metabase
Public sharing is enabled by default.
![Enable public sharing](../images/enable-public-sharing.png)
To disable public sharing:
1. Click on the **Gear** icon in the upper right.
2. Select **Admin settings**.
3. In the **Settings** tab, select **Public sharing**.
4. Toggle off **Public sharing**.
Once toggled on, the **Public sharing** section will display Metabase questions, dashboards, and actions with active public links.
If you disable public sharing, then re-enable public sharing, all your previously generated public links will still work (as long as you didn't deactivate them).
## Deactivating public links and embeds
### Individual question or dashboard links and embeds
1. Visit the question or dashboard.
2. Click on **Sharing** icon.
3. Select **Public link** or **Embed**.
4. Click **Remove public link**.
## Deactivating multiple public links and embeds
Admins can view and deactivate all public links for a Metabase.
1. Click on the **gear** icon in the upper right.
2. Select **Admin settings**.
3. Go to the **Settings** tab.
4. Go to the **Public sharing** tab in the left sidebar.
5. For each item you want to deactivate, click on the **X** to revoke its public link.
## Further reading
- [Publishing data visualizations to the web](https://www.metabase.com/learn/embedding/embedding-charts-and-dashboards).

View File

@@ -22,29 +22,126 @@ To format the display of any column in a table, click on the column heading and
The options you see will differ depending on the type of column you're viewing:
### Dates
## Text formatting options
- **Date style** gives you a bunch of different choices for how to display the date.
- **Abbreviate names of days and months**, when turned on, will turn things like `January` to `Jan`, and `Monday` to `Mon`.
- **Show the time** lets you decide whether or not to display the time, and if so, how. You can include hours and minutes, and additionally seconds and milliseconds.
### Title
### Numbers
You can rename the column.
- **Show a mini bar chart** will display a small horizontal bar next to each number in this column to show its size relative to the other values in the column.
- **Style** lets you choose to display the number as a plain number, a percent, in scientific notation, or as a currency.
- **Separator style** gives you various options for how commas and periods are used to separate the number.
- **Minimum number of decimal places** forces the number to be displayed with exactly this many decimal places.
- **Multiply by a number** multiplies each number in this column by whatever you type here. Just don't type an emoji here; it almost always causes a temporal vortex to manifest.
- **Add a prefix/suffix** lets you put a symbol, word, or whatever before or after each cell's value.
### Display as
### Currency
Currency columns have all the same options as numbers, plus the following:
You can display the text as is, or you can display the text as a link. If you select link, you can also change the text displayed, and use parameters for both the link text and the link's URL.
- **Unit of Currency** lets you change the unit of currency from whatever the system default is.
- **Currency label style** allows you to switch between displaying the currency label as a symbol, a code like (USD), or the full name of the currency.
- **Where to display the unit of currency** lets you toggle between showing the currency label in the column heading or in every cell in the column.
For example, you could create a dynamic URL using a parameter from another column in the results:
### Conditional table formatting
```html
{% raw %}
https://www.example.com/{{category}}
{% endraw %}
```
In the above example, Metabase would take the value for the `category` column for that row (in this case `widget`), and insert it into the URL:
```
https://www.example.com/widget
```
## Date formatting options
Date formatting options include the same options as [Text formatting](#text-formatting-options), as well as:
### Date style
Styles include:
- April 1, 2024
- 1 April 2024
- Monday, April 1, 2024
- 4/1/2024
- 1/4/2024
- 2024/4/1
### Abbreviate days and months
If you select a date style that includes words, this toggle will abbreviate them. For example:
- Unabbreviated: Monday, April 1, 2024
- Abbreviated: Mon, Apr 1, 2024
### Show the time
This option determines whether Metabase includes the time along with the date.
- Off
- HH:MM
- HH:MM:SS
- HH:MM:SS:MS
### Time style
You can choose either 12-hour clock style, or 24-hour clock style.
- 5:24 PM (12-hour clock)
- 17:24 (24-hour clock)
## Number formatting options
### Show a mini bar chart
This option displays a small horizontal bar next to each number in this column to show its size relative to the other values in the column.
### Style
Number styles include:
- Number
- Percent
- Scientific
- Currency
### Separator style
Gives you various options for how commas and periods are used to separate the number.
- 100,000.00
- 100 000,00
- 100.000,00
- 100000.00
- 100'000.00
### Minimum number of decimal places
Forces the number to be displayed with exactly this many decimal places.
### Multiply by a number
Multiplies each number in this column by whatever you type here. Just don't type an emoji here; there is a nontrivial chance that it will manifest a temporal vortex.
### Add a prefix/suffix
Lets you put a symbol, word, or whatever before or after each cell's value.
## Currency formatting options
Currency columns have all the same options as [numbers](#number-formatting-options), plus the following:
### Unit of Currency
Lets you change the unit of currency from whatever the system default is.
### Currency label style
Allows you to switch between displaying the currency label as:
- a symbol (like $)
- a code (like USD)
- the full name of the currency (like "US dollars")
### Where to display the unit of currency
Lets you toggle between showing the currency label in the column heading or in every cell in the column.
## Conditional table formatting
Sometimes it's helpful to highlight certain rows or columns in your tables when they meet a specific condition. You can set up conditional formatting rules by going to the visualization settings while looking at any table, then clicking on the **Conditional Formatting** tab.

View File

@@ -59,3 +59,7 @@ Get results via email or Slack, either on a schedule, or only when something int
### [Public sharing](./sharing/public-links.md)
Create links or embeds for the good people of the internet.
### [Exporting](./sharing/exporting-results.md)
The different ways you can export the results of questions and dashboards.

View File

@@ -19,8 +19,11 @@ To see what's new, check out all the [major release announcements](https://www.m
## Metabase Enterprise Edition releases
- [v1.49.3](https://github.com/metabase/metabase/releases/tag/v1.49.3)
- [v1.49.2](https://github.com/metabase/metabase/releases/tag/v1.49.2)
- [v1.49.1](https://github.com/metabase/metabase/releases/tag/v1.49.1)
- [v1.49.0](https://github.com/metabase/metabase/releases/tag/v1.49.0)
- [v1.48.10](https://github.com/metabase/metabase/releases/tag/v1.48.10)
- [v1.48.9](https://github.com/metabase/metabase/releases/tag/v1.48.9)
- [v1.48.8](https://github.com/metabase/metabase/releases/tag/v1.48.8)
- [v1.48.7](https://github.com/metabase/metabase/releases/tag/v1.48.7)
@@ -151,8 +154,11 @@ To see what's new, check out all the [major release announcements](https://www.m
## Metabase Open Source Edition releases
- [v0.49.3](https://github.com/metabase/metabase/releases/tag/v0.49.3)
- [v0.49.2](https://github.com/metabase/metabase/releases/tag/v0.49.2)
- [v0.49.1](https://github.com/metabase/metabase/releases/tag/v0.49.1)
- [v0.49.0](https://github.com/metabase/metabase/releases/tag/v0.49.0)
- [v0.48.10](https://github.com/metabase/metabase/releases/tag/v0.48.10)
- [v0.48.9](https://github.com/metabase/metabase/releases/tag/v0.48.9)
- [v0.48.8](https://github.com/metabase/metabase/releases/tag/v0.48.8)
- [v0.48.7](https://github.com/metabase/metabase/releases/tag/v0.48.7)

View File

@@ -0,0 +1,60 @@
---
title: Creating a HAR file for troubleshooting
---
# Creating a HAR file for troubleshooting
HAR (short for _HTTP Archive_) files record network requests generated by your browser. HAR files can be helpful when troubleshooting issues with Metabase performance.
> HAR files can contain sensitive information that you submit during the recording, like passwords and API keys. Before sharing the HAR file with anyone, review the file contents in a text editor.
To create the HAR file, follow the instructions for your web browser:
- [Chrome](#create-a-har-file-in-chrome)
- [Edge](#create-a-har-file-in-edge)
- [Firefox](#create-a-har-file-in-firefox)
- [Safari](#create-a-har-file-in-safari)
## Create a HAR file in Chrome
1. Open Chrome **Developer tools**.
You can right-click anywhere on the page and select "Inspect".
2. In Developer tools, switch to the **Network** tab.
The network log recording should start automatically.
3. With the Network tab open and recording in progress, repeat the steps to reproduce the issue.
4. Once you're finished reproducing the issue, right-click anywhere in the table of network calls and select **Save all as HAR with content**.
## Create a HAR file in Edge
1. Open Edge **Developer tools**. You can right-click anywhere on the page and select "Inspect".
2. In Developer tools, switch to the **Network** tab (look for the Wi-Fi icon). The network log recording should start automatically.
3. With the Network tab open and recording in progress, repeat the steps to reproduce the issue.
4. Once you're finished reproducing the issue, right-click anywhere in the table and select **Save all as HAR with content**
## Create a HAR file in Firefox
1. Open Firefox **Developer tools**. You can right-click anywhere on the page and select "Inspect".
2. In Developer tools, switch to the **Network** tab.
3. With the Network tab open and recording in progress, repeat the steps to reproduce the issue.
4. Once you're finished reproducing the issue, right-click anywhere in the table of network calls and select **Save All As HAR**.
## Create a HAR file in Safari
1. If you haven't yet, enable the **Develop** menu by going to **Safari > Settings > Advanced**, and select **Show features for web developers**.
2. Open the Safari developer tools by going to **Develop > Show Web Inspector**.
3. In Developer tools, switch to the **Network** tab. The network log recording should start automatically.
4. With the Network tab open and recording in progress, repeat the steps to reproduce the issue.
5. Once you're finished reproducing the issue, click on **Export** in the top right of the Network tab.

View File

@@ -76,6 +76,8 @@ Metabase will log errors, both on the server and in the browser console, dependi
- [Safari][safari]
- [Edge][edge]
**Creating a HAR file**: HAR files record network requests generated by your browser. HAR files can be helpful when troubleshooting issues with Metabase performance. Check out [Creating HAR files for troubleshooting](./create-har-file.md).
## Metabase tutorials
For tutorials that walk you through how to use Metabase features, check out [Learn Metabase][learn].
@@ -86,7 +88,7 @@ To see if someone else has run into a similar issue, check out [our forum on Dis
## Upgrading Metabase
Metabase adds new features and squashes bugs with each release. [Upgrading to the latest and greatest][upgrade] may resolve your issue. If you're using [Metabase Cloud][cloud], we'll handle the upgrades for you. You can checkout the [release notes][releases] to see what's new.
Metabase adds new features and squashes bugs with each release. [Upgrading to the latest and greatest][upgrade] may resolve your issue. If you're using [Metabase Cloud][cloud], we'll handle the upgrades for you. To see what's new, check out the [release notes][releases].
[appdb]: ./loading-from-h2.md
[bugs]: ./bugs.md

View File

@@ -7,7 +7,14 @@ import {
SAMPLE_DB_TABLES,
METABASE_SECRET_KEY,
} from "e2e/support/cypress_data";
import { snapshot, restore, withSampleDatabase } from "e2e/support/helpers";
import {
snapshot,
restore,
withSampleDatabase,
setTokenFeatures,
describeEE,
deleteToken,
} from "e2e/support/helpers";
const {
STATIC_ORDERS_ID,
@@ -63,6 +70,29 @@ describe("snapshots", () => {
});
});
describeEE("default-ee", () => {
it("default-ee", () => {
restore("blank");
setup();
updateSettings();
setTokenFeatures("all");
addUsersAndGroups(true);
createCollections();
withSampleDatabase(SAMPLE_DATABASE => {
ensureTableIdsAreCorrect(SAMPLE_DATABASE);
hideNewSampleTables(SAMPLE_DATABASE);
createQuestionsAndDashboards(SAMPLE_DATABASE);
createModels(SAMPLE_DATABASE);
cy.writeFile(
"e2e/support/cypress_sample_database.json",
SAMPLE_DATABASE,
);
});
deleteToken();
snapshot("default-ee");
});
});
function setup() {
cy.request("GET", "/api/session/properties").then(
({ body: properties }) => {
@@ -86,10 +116,13 @@ describe("snapshots", () => {
function updateSettings() {
cy.request("PUT", "/api/setting/enable-public-sharing", { value: true });
cy.request("PUT", "/api/setting/enable-embedding", { value: true });
cy.request("PUT", "/api/setting/embedding-secret-key", {
value: METABASE_SECRET_KEY,
});
cy.request("PUT", "/api/setting/enable-embedding", { value: true }).then(
() => {
cy.request("PUT", "/api/setting/embedding-secret-key", {
value: METABASE_SECRET_KEY,
});
},
);
// update the Sample db connection string so it is valid in both CI and locally
cy.request("GET", `/api/database/${SAMPLE_DB_ID}`).then(response => {
@@ -99,26 +132,28 @@ describe("snapshots", () => {
});
}
function addUsersAndGroups() {
function addUsersAndGroups(isEE = false) {
const lowest_read_data_permission = isEE ? "blocked" : "unrestricted";
// groups
cy.request("POST", "/api/permissions/group", { name: "collection" }).then(
({ body }) => {
expect(body.id).to.eq(COLLECTION_GROUP); // 4
expect(body.id).to.eq(COLLECTION_GROUP); // 3
},
);
cy.request("POST", "/api/permissions/group", { name: "data" }).then(
({ body }) => {
expect(body.id).to.eq(DATA_GROUP); // 5
expect(body.id).to.eq(DATA_GROUP); // 4
},
);
cy.request("POST", "/api/permissions/group", { name: "readonly" }).then(
({ body }) => {
expect(body.id).to.eq(READONLY_GROUP); // 6
expect(body.id).to.eq(READONLY_GROUP); // 5
},
);
cy.request("POST", "/api/permissions/group", { name: "nosql" }).then(
({ body }) => {
expect(body.id).to.eq(NOSQL_GROUP); // 7
expect(body.id).to.eq(NOSQL_GROUP); // 6
},
);
@@ -132,19 +167,35 @@ describe("snapshots", () => {
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: { data: { schemas: "none", native: "none" } },
[SAMPLE_DB_ID]: {
// set the data permission so the UI doesn't warn us that "all users has higher permissions than X"
"view-data": lowest_read_data_permission,
"create-queries": "no",
},
},
[DATA_GROUP]: {
[SAMPLE_DB_ID]: { data: { schemas: "all", native: "write" } },
[SAMPLE_DB_ID]: {
"view-data": "unrestricted",
"create-queries": "query-builder-and-native",
},
},
[NOSQL_GROUP]: {
[SAMPLE_DB_ID]: { data: { schemas: "all", native: "none" } },
[SAMPLE_DB_ID]: {
"view-data": "unrestricted",
"create-queries": "query-builder",
},
},
[COLLECTION_GROUP]: {
[SAMPLE_DB_ID]: { data: { schemas: "none", native: "none" } },
[SAMPLE_DB_ID]: {
"view-data": lowest_read_data_permission,
"create-queries": "no",
},
},
[READONLY_GROUP]: {
[SAMPLE_DB_ID]: { data: { schemas: "none", native: "none" } },
[SAMPLE_DB_ID]: {
"view-data": lowest_read_data_permission,
"create-queries": "no",
},
},
});

View File

@@ -0,0 +1,203 @@
Id No Name Type1 Type2
1 1 Bulbasaur Grass Poison
2 2 Ivysaur Grass Poison
3 3 Venusaur Grass Poison
4 3 Mega Venusaur Grass Poison
5 4 Charmander Fire
6 5 Charmeleon Fire
7 6 Charizard Fire Flying
8 6 Mega Charizard X Fire Dragon
9 6 Mega Charizard Y Fire Flying
10 7 Squirtle Water
11 8 Wartortle Water
12 9 Blastoise Water
13 9 Mega Blastoise Water
14 10 Caterpie Bug
15 11 Metapod Bug
16 12 Butterfree Bug Flying
17 13 Weedle Bug Poison
18 14 Kakuna Bug Poison
19 15 Beedrill Bug Poison
20 15 Mega Beedrill Bug Poison
21 16 Pidgey Normal Flying
22 17 Pidgeotto Normal Flying
23 18 Pidgeot Normal Flying
24 18 Mega Pidgeot Normal Flying
25 19 Rattata Normal
26 19 Alolan Rattata Dark Normal
27 20 Raticate Normal
28 20 Alolan Raticate Dark Normal
29 21 Spearow Normal Flying
30 22 Fearow Normal Flying
31 23 Ekans Poison
32 24 Arbok Poison
33 25 Pikachu Electric
34 26 Raichu Electric
35 26 Alolan Raichu Electric Psychic
36 27 Sandshrew Ground
37 27 Alolan Sandshrew Ice Steel
38 28 Sandslash Ground
39 28 Alolan Sandslash Ice Steel
40 29 Nidoran F Poison
41 30 Nidorina Poison
42 31 Nidoqueen Poison Ground
43 32 Nidoran M Poison
44 33 Nidorino Poison
45 34 Nidoking Poison Ground
46 35 Clefairy Fairy
47 36 Clefable Fairy
48 37 Vulpix Fire
49 37 Alolan Vulpix Ice
50 38 Ninetales Fire
51 38 Alolan Ninetales Ice Fairy
52 39 Jigglypuff Normal Fairy
53 40 Wigglytuff Normal Fairy
54 41 Zubat Poison Flying
55 42 Golbat Poison Flying
56 43 Oddish Grass Poison
57 44 Gloom Grass Poison
58 45 Vileplume Grass Poison
59 46 Paras Bug Grass
60 47 Parasect Bug Grass
61 48 Venonat Bug Poison
62 49 Venomoth Bug Poison
63 50 Diglett Ground
64 50 Alolan Diglett Ground Steel
65 51 Dugtrio Ground
66 51 Alolan Dugtrio Ground Steel
67 52 Meowth Normal
68 52 Alolan Meowth Dark
69 52 Galarian Meowth Steel
70 53 Persian Normal
71 53 Alolan Persian Dark
72 54 Psyduck Water
73 55 Golduck Water
74 56 Mankey Fighting
75 57 Primeape Fighting
76 58 Growlithe Fire
77 58 Hisuian Growlithe Fire Rock
78 59 Arcanine Fire
79 59 Hisuian Arcanine Fire Rock
80 60 Poliwag Water
81 61 Poliwhirl Water
82 62 Poliwrath Water Fighting
83 63 Abra Psychic
84 64 Kadabra Psychic
85 65 Alakazam Psychic
86 65 Mega Alakazam Psychic
87 66 Machop Fighting
88 67 Machoke Fighting
89 68 Machamp Fighting
90 69 Bellsprout Grass Poison
91 70 Weepinbell Grass Poison
92 71 Victreebel Grass Poison
93 72 Tentacool Water Poison
94 73 Tentacruel Water Poison
95 74 Geodude Rock Ground
96 74 Alolan Geodude Rock Electric
97 75 Graveler Rock Ground
98 75 Alolan Graveler Rock Electric
99 76 Golem Rock Ground
100 76 Alolan Golem Rock Electric
101 77 Ponyta Fire
102 77 Galarian Ponyta Psychic
103 78 Rapidash Fire
104 78 Galarian Rapidash Psychic Fairy
105 79 Slowpoke Water Psychic
106 79 Galarian Slowpoke Psychic
107 80 Slowbro Water Psychic
108 80 Galarian Slowbro Poison Psychic
109 80 Mega Slowbro Water Psychic
110 81 Magnemite Electric Steel
111 82 Magneton Electric Steel
112 83 Farfetch'd Normal Flying
113 83 Galarian Farfetch'd Fighting
114 84 Doduo Normal Flying
115 85 Dodrio Normal Flying
116 86 Seel Water
117 87 Dewgong Water Ice
118 88 Grimer Poison
119 88 Alolan Grimer Poison Dark
120 89 Muk Poison
121 89 Alolan Muk Poison Dark
122 90 Shellder Water
123 91 Cloyster Water Ice
124 92 Gastly Ghost Poison
125 93 Haunter Ghost Poison
126 94 Gengar Ghost Poison
127 94 Mega Gengar Ghost Poison
128 95 Onix Rock Ground
129 96 Drowzee Psychic
130 97 Hypno Psychic
131 98 Krabby Water
132 99 Kingler Water
133 100 Voltorb Electric
134 100 Hisuian Voltorb Electric Grass
135 101 Electrode Electric
136 101 Hisuian Electrode Electric Grass
137 102 Exeggcute Grass Psychic
138 103 Exeggutor Grass Psychic
139 103 Alolan Exeggutor Grass Dragon
140 104 Cubone Ground
141 105 Marowak Ground
142 105 Alolan Marowak Fire Ghost
143 106 Hitmonlee Fighting
144 107 Hitmonchan Fighting
145 108 Lickitung Normal
146 109 Koffing Poison
147 110 Weezing Poison
148 110 Galarian Weezing Poison Fairy
149 111 Rhyhorn Ground Rock
150 112 Rhydon Ground Rock
151 113 Chansey Normal
152 114 Tangela Grass
153 115 Kangaskhan Normal
154 115 Mega Kangaskhan Normal
155 116 Horsea Water
156 117 Seadra Water
157 118 Goldeen Water
158 119 Seaking Water
159 120 Staryu Water
160 121 Starmie Water Psychic
161 122 Mr. Mime Psychic Fairy
162 122 Galarian Mr. Mime Ice Psychic
163 123 Scyther Bug Flying
164 124 Jynx Ice Psychic
165 125 Electabuzz Electric
166 126 Magmar Fire
167 127 Pinsir Bug
168 127 Mega Pinsir Bug Flying
169 128 Tauros Fighting
170 128 Paldean Tauros Fighting Fire
171 128 Paldean Tauros Fighting Water
172 128 Tauros Normal
173 129 Magikarp Water
174 130 Gyarados Water Flying
175 130 Mega Gyarados Water Dark
176 131 Lapras Water Ice
177 132 Ditto Normal
178 133 Eevee Normal
179 134 Vaporeon Water
180 135 Jolteon Electric
181 136 Flareon Fire
182 137 Porygon Normal
183 138 Omanyte Rock Water
184 139 Omastar Rock Water
185 140 Kabuto Rock Water
186 141 Kabutops Rock Water
187 142 Aerodactyl Rock Flying
188 142 Mega Aerodactyl Rock Flying
189 143 Snorlax Normal
190 144 Articuno Ice Flying
191 144 Galarian Articuno Psychic Flying
192 145 Zapdos Electric Flying
193 145 Galarian Zapdos Fighting Flying
194 146 Moltres Fire Flying
195 146 Galarian Moltres Dark Flying
196 147 Dratini Dragon
197 148 Dragonair Dragon
198 149 Dragonite Dragon Flying
199 150 Mewtwo Psychic
200 150 Mega Mewtwo X Psychic Fighting
201 150 Mega Mewtwo Y Psychic
202 151 Mew Psychic
1 Id No Name Type1 Type2
2 1 1 Bulbasaur Grass Poison
3 2 2 Ivysaur Grass Poison
4 3 3 Venusaur Grass Poison
5 4 3 Mega Venusaur Grass Poison
6 5 4 Charmander Fire
7 6 5 Charmeleon Fire
8 7 6 Charizard Fire Flying
9 8 6 Mega Charizard X Fire Dragon
10 9 6 Mega Charizard Y Fire Flying
11 10 7 Squirtle Water
12 11 8 Wartortle Water
13 12 9 Blastoise Water
14 13 9 Mega Blastoise Water
15 14 10 Caterpie Bug
16 15 11 Metapod Bug
17 16 12 Butterfree Bug Flying
18 17 13 Weedle Bug Poison
19 18 14 Kakuna Bug Poison
20 19 15 Beedrill Bug Poison
21 20 15 Mega Beedrill Bug Poison
22 21 16 Pidgey Normal Flying
23 22 17 Pidgeotto Normal Flying
24 23 18 Pidgeot Normal Flying
25 24 18 Mega Pidgeot Normal Flying
26 25 19 Rattata Normal
27 26 19 Alolan Rattata Dark Normal
28 27 20 Raticate Normal
29 28 20 Alolan Raticate Dark Normal
30 29 21 Spearow Normal Flying
31 30 22 Fearow Normal Flying
32 31 23 Ekans Poison
33 32 24 Arbok Poison
34 33 25 Pikachu Electric
35 34 26 Raichu Electric
36 35 26 Alolan Raichu Electric Psychic
37 36 27 Sandshrew Ground
38 37 27 Alolan Sandshrew Ice Steel
39 38 28 Sandslash Ground
40 39 28 Alolan Sandslash Ice Steel
41 40 29 Nidoran F Poison
42 41 30 Nidorina Poison
43 42 31 Nidoqueen Poison Ground
44 43 32 Nidoran M Poison
45 44 33 Nidorino Poison
46 45 34 Nidoking Poison Ground
47 46 35 Clefairy Fairy
48 47 36 Clefable Fairy
49 48 37 Vulpix Fire
50 49 37 Alolan Vulpix Ice
51 50 38 Ninetales Fire
52 51 38 Alolan Ninetales Ice Fairy
53 52 39 Jigglypuff Normal Fairy
54 53 40 Wigglytuff Normal Fairy
55 54 41 Zubat Poison Flying
56 55 42 Golbat Poison Flying
57 56 43 Oddish Grass Poison
58 57 44 Gloom Grass Poison
59 58 45 Vileplume Grass Poison
60 59 46 Paras Bug Grass
61 60 47 Parasect Bug Grass
62 61 48 Venonat Bug Poison
63 62 49 Venomoth Bug Poison
64 63 50 Diglett Ground
65 64 50 Alolan Diglett Ground Steel
66 65 51 Dugtrio Ground
67 66 51 Alolan Dugtrio Ground Steel
68 67 52 Meowth Normal
69 68 52 Alolan Meowth Dark
70 69 52 Galarian Meowth Steel
71 70 53 Persian Normal
72 71 53 Alolan Persian Dark
73 72 54 Psyduck Water
74 73 55 Golduck Water
75 74 56 Mankey Fighting
76 75 57 Primeape Fighting
77 76 58 Growlithe Fire
78 77 58 Hisuian Growlithe Fire Rock
79 78 59 Arcanine Fire
80 79 59 Hisuian Arcanine Fire Rock
81 80 60 Poliwag Water
82 81 61 Poliwhirl Water
83 82 62 Poliwrath Water Fighting
84 83 63 Abra Psychic
85 84 64 Kadabra Psychic
86 85 65 Alakazam Psychic
87 86 65 Mega Alakazam Psychic
88 87 66 Machop Fighting
89 88 67 Machoke Fighting
90 89 68 Machamp Fighting
91 90 69 Bellsprout Grass Poison
92 91 70 Weepinbell Grass Poison
93 92 71 Victreebel Grass Poison
94 93 72 Tentacool Water Poison
95 94 73 Tentacruel Water Poison
96 95 74 Geodude Rock Ground
97 96 74 Alolan Geodude Rock Electric
98 97 75 Graveler Rock Ground
99 98 75 Alolan Graveler Rock Electric
100 99 76 Golem Rock Ground
101 100 76 Alolan Golem Rock Electric
102 101 77 Ponyta Fire
103 102 77 Galarian Ponyta Psychic
104 103 78 Rapidash Fire
105 104 78 Galarian Rapidash Psychic Fairy
106 105 79 Slowpoke Water Psychic
107 106 79 Galarian Slowpoke Psychic
108 107 80 Slowbro Water Psychic
109 108 80 Galarian Slowbro Poison Psychic
110 109 80 Mega Slowbro Water Psychic
111 110 81 Magnemite Electric Steel
112 111 82 Magneton Electric Steel
113 112 83 Farfetch'd Normal Flying
114 113 83 Galarian Farfetch'd Fighting
115 114 84 Doduo Normal Flying
116 115 85 Dodrio Normal Flying
117 116 86 Seel Water
118 117 87 Dewgong Water Ice
119 118 88 Grimer Poison
120 119 88 Alolan Grimer Poison Dark
121 120 89 Muk Poison
122 121 89 Alolan Muk Poison Dark
123 122 90 Shellder Water
124 123 91 Cloyster Water Ice
125 124 92 Gastly Ghost Poison
126 125 93 Haunter Ghost Poison
127 126 94 Gengar Ghost Poison
128 127 94 Mega Gengar Ghost Poison
129 128 95 Onix Rock Ground
130 129 96 Drowzee Psychic
131 130 97 Hypno Psychic
132 131 98 Krabby Water
133 132 99 Kingler Water
134 133 100 Voltorb Electric
135 134 100 Hisuian Voltorb Electric Grass
136 135 101 Electrode Electric
137 136 101 Hisuian Electrode Electric Grass
138 137 102 Exeggcute Grass Psychic
139 138 103 Exeggutor Grass Psychic
140 139 103 Alolan Exeggutor Grass Dragon
141 140 104 Cubone Ground
142 141 105 Marowak Ground
143 142 105 Alolan Marowak Fire Ghost
144 143 106 Hitmonlee Fighting
145 144 107 Hitmonchan Fighting
146 145 108 Lickitung Normal
147 146 109 Koffing Poison
148 147 110 Weezing Poison
149 148 110 Galarian Weezing Poison Fairy
150 149 111 Rhyhorn Ground Rock
151 150 112 Rhydon Ground Rock
152 151 113 Chansey Normal
153 152 114 Tangela Grass
154 153 115 Kangaskhan Normal
155 154 115 Mega Kangaskhan Normal
156 155 116 Horsea Water
157 156 117 Seadra Water
158 157 118 Goldeen Water
159 158 119 Seaking Water
160 159 120 Staryu Water
161 160 121 Starmie Water Psychic
162 161 122 Mr. Mime Psychic Fairy
163 162 122 Galarian Mr. Mime Ice Psychic
164 163 123 Scyther Bug Flying
165 164 124 Jynx Ice Psychic
166 165 125 Electabuzz Electric
167 166 126 Magmar Fire
168 167 127 Pinsir Bug
169 168 127 Mega Pinsir Bug Flying
170 169 128 Tauros Fighting
171 170 128 Paldean Tauros Fighting Fire
172 171 128 Paldean Tauros Fighting Water
173 172 128 Tauros Normal
174 173 129 Magikarp Water
175 174 130 Gyarados Water Flying
176 175 130 Mega Gyarados Water Dark
177 176 131 Lapras Water Ice
178 177 132 Ditto Normal
179 178 133 Eevee Normal
180 179 134 Vaporeon Water
181 180 135 Jolteon Electric
182 181 136 Flareon Fire
183 182 137 Porygon Normal
184 183 138 Omanyte Rock Water
185 184 139 Omastar Rock Water
186 185 140 Kabuto Rock Water
187 186 141 Kabutops Rock Water
188 187 142 Aerodactyl Rock Flying
189 188 142 Mega Aerodactyl Rock Flying
190 189 143 Snorlax Normal
191 190 144 Articuno Ice Flying
192 191 144 Galarian Articuno Psychic Flying
193 192 145 Zapdos Electric Flying
194 193 145 Galarian Zapdos Fighting Flying
195 194 146 Moltres Fire Flying
196 195 146 Galarian Moltres Dark Flying
197 196 147 Dratini Dragon
198 197 148 Dragonair Dragon
199 198 149 Dragonite Dragon Flying
200 199 150 Mewtwo Psychic
201 200 150 Mega Mewtwo X Psychic Fighting
202 201 150 Mega Mewtwo Y Psychic
203 202 151 Mew Psychic

View File

@@ -2,15 +2,18 @@ import { cypressWaitAll } from "e2e/support/helpers";
Cypress.Commands.add(
"createDashboardWithQuestions",
({ dashboardName, questions }) => {
({ dashboardName, dashboardDetails, questions }) => {
return cy
.createDashboard({ name: dashboardName })
.createDashboard({ name: dashboardName, ...dashboardDetails })
.then(({ body: dashboard }) => {
return cypressWaitAll(
questions.map(query =>
cy.createQuestionAndAddToDashboard(query, dashboard.id),
),
).then(questions => {
).then(dashcardResponses => {
const questions = dashcardResponses.map(
dashcardResponse => dashcardResponse.body.card,
);
return {
questions,
dashboard,

View File

@@ -26,7 +26,7 @@ Cypress.Commands.add(
})
.then(response => ({
...response,
body: response.body.dashcards[0],
body: response.body.dashcards.at(-1),
})),
),
),

View File

@@ -20,14 +20,17 @@ Cypress.Commands.add(
const attr = Object.keys(attribute_remappings).join(", "); // Account for the possiblity of passing multiple user attributes
cy.log(`Sandbox "${name}" table on "${attr}"`);
cy.updatePermissionsSchemas({
schemas: {
[schema]: {
[table_id]: { query: "segmented", read: "all" },
cy.updatePermissionsGraph({
[group_id]: {
[db_id]: {
"view-data": {
[schema]: {
[table_id]: "sandboxed",
},
},
"create-queries": "query-builder",
},
},
user_group: group_id,
database_id: db_id,
});
cy.request("POST", "/api/mt/gtap", {
attribute_remappings,

View File

@@ -66,7 +66,7 @@ export function startNewNativeQuestion(alias = "editor") {
*/
export function visitQuestionAdhoc(
question,
{ callback, mode, autorun = true } = {},
{ callback, mode, autorun = true, skipWaiting = false } = {},
) {
const questionMode = mode === "notebook" ? "/notebook" : "";
@@ -78,7 +78,7 @@ export function visitQuestionAdhoc(
runQueryIfNeeded(question, autorun);
if (mode !== "notebook") {
if (mode !== "notebook" && !skipWaiting) {
cy.wait("@" + alias).then(xhr => callback && callback(xhr));
}
}
@@ -110,18 +110,35 @@ export function openTable({
);
}
/**
*
* @typedef {{mode?: "notebook", limit?: number, callback?: function }} OpenTablesProps
*/
/**
* @param {OpenTablesProps} props
*/
export function openProductsTable({ mode, limit, callback } = {}) {
return openTable({ table: STATIC_PRODUCTS_ID, mode, limit, callback });
}
/**
* @param {OpenTablesProps} props
*/
export function openOrdersTable({ mode, limit, callback } = {}) {
return openTable({ table: STATIC_ORDERS_ID, mode, limit, callback });
}
/**
* @param {OpenTablesProps} props
*/
export function openPeopleTable({ mode, limit, callback } = {}) {
return openTable({ table: STATIC_PEOPLE_ID, mode, limit, callback });
}
/**
* @param {OpenTablesProps} props
*/
export function openReviewsTable({ mode, limit, callback } = {}) {
return openTable({ table: STATIC_REVIEWS_ID, mode, limit, callback });
}

View File

@@ -1,4 +1,4 @@
import { getFullName, modal, popover } from "e2e/support/helpers";
import { entityPickerModal, getFullName, popover } from "e2e/support/helpers";
/**
* Clicks the "+" icon on the collection page and selects one of the menu options
@@ -75,13 +75,12 @@ export const moveOpenedCollectionTo = newParent => {
openCollectionMenu();
popover().within(() => cy.findByText("Move").click());
cy.findAllByTestId("item-picker-item").contains(newParent).click();
modal().within(() => {
entityPickerModal().within(() => {
cy.findByText(newParent).click();
cy.button("Move").click();
});
// Make sure modal closed
modal().should("not.exist");
entityPickerModal().should("not.exist");
};
export function pickEntity({ path, select }) {

View File

@@ -1,5 +1,9 @@
export const commandPalette = () => cy.findByTestId("command-palette");
export const openCommandPalette = () => cy.get("body").type("{ctrl+k}");
export const openCommandPalette = () => cy.get("body").type("{ctrl+k}{cmd+k}");
export const closeCommandPalette = () => cy.get("body").type("{esc}");
export const pressPageUp = () => cy.get("body").type("{pageup}");
export const pressPageDown = () => cy.get("body").type("{pagedown}");
export const pressHome = () => cy.get("body").type("{home}");
export const pressEnd = () => cy.get("body").type("{end}");
export const commandPaletteSearch = () =>
cy.findByPlaceholderText("Jump to...");

View File

@@ -1,3 +1,7 @@
export function expressionEditorWidget() {
return cy.findByTestId("expression-editor");
}
export function enterCustomColumnDetails({ formula, name } = {}) {
cy.get(".ace_text-input")
.first()

View File

@@ -1,5 +1,5 @@
import { visitDashboard } from "./e2e-misc-helpers";
import { menu, popover } from "./e2e-ui-elements-helpers";
import { menu, popover, sidebar } from "./e2e-ui-elements-helpers";
// Metabase utility functions for commonly-used patterns
export function selectDashboardFilter(selection, filterName) {
@@ -124,18 +124,21 @@ export function checkFilterLabelAndValue(label, value) {
cy.get("fieldset").contains(value);
}
export function setFilter(type, subType) {
export function setFilter(type, subType, name) {
cy.icon("filter").click();
cy.findByText("What do you want to filter?");
popover().within(() => {
cy.findByText(type).click();
popover().findByText(type).click();
if (subType) {
cy.findByText(subType).click();
}
});
if (subType) {
sidebar().findByText("Filter operator").next().click();
popover().findByText(subType).click();
}
if (name) {
sidebar().findByLabelText("Label").clear().type(name);
}
}
export function getRequiredToggle() {

View File

@@ -29,6 +29,7 @@ export function downloadAndAssert(
downloadUrl,
isEmbed,
isDashboard,
enableFormatting = true,
} = {},
callback,
) {
@@ -76,6 +77,9 @@ export function downloadAndAssert(
cy.findByTestId("download-button").click();
}
// Initiate the file download
if (!enableFormatting) {
cy.window().trigger("keydown", { key: "Alt" });
}
popover().findByText(`.${fileType}`).click();
cy.wait("@fileDownload")
@@ -110,15 +114,15 @@ export function assertSheetRowsCount(expectedCount) {
function getEndpoint(fileType, questionId, publicUid, dashcardId, dashboardId) {
if (dashcardId != null && dashboardId != null) {
return `api/dashboard/${dashboardId}/dashcard/${dashcardId}/card/${questionId}/query/${fileType}`;
return `api/dashboard/${dashboardId}/dashcard/${dashcardId}/card/${questionId}/query/${fileType}**`;
}
if (publicUid) {
return `/public/question/${publicUid}.${fileType}**`;
}
const questionEndpoint = `/api/card/${questionId}/query/${fileType}`;
const queryEndpoint = `/api/dataset/${fileType}`;
const questionEndpoint = `/api/card/${questionId}/query/${fileType}**`;
const queryEndpoint = `/api/dataset/${fileType}**`;
return questionId ? questionEndpoint : queryEndpoint;
}

View File

@@ -262,7 +262,7 @@ export function createPublicDashboardLink(dashboardId) {
return cy.request("POST", `/api/dashboard/${dashboardId}/public_link`, {});
}
export const visitFullAppEmbeddingUrl = ({ url, qs }) => {
export const visitFullAppEmbeddingUrl = ({ url, qs, onBeforeLoad }) => {
cy.visit({
url,
qs,
@@ -270,6 +270,7 @@ export const visitFullAppEmbeddingUrl = ({ url, qs }) => {
// cypress runs all tests in an iframe and the app uses this property to avoid embedding mode for all tests
// by removing the property the app would work in embedding mode
window.Cypress = undefined;
onBeforeLoad?.(window);
},
});
};

View File

@@ -65,3 +65,19 @@ export const setTokenFeatures = featuresScope => {
},
});
};
export const deleteToken = () => {
if (!isEE) {
throw new Error(
"You must run Metabase® Enterprise Edition™ for token to make sense.\nMake sure you have `MB_EDITION=ee` in your environment variables.",
);
}
return cy.request({
method: "PUT",
url: "/api/setting/premium-embedding-token",
failOnStatusCode: false,
body: {
value: null,
},
});
};

View File

@@ -1,4 +1,8 @@
import { modal, popover } from "e2e/support/helpers/e2e-ui-elements-helpers";
import {
entityPickerModal,
modal,
popover,
} from "e2e/support/helpers/e2e-ui-elements-helpers";
export function setDropdownFilterType() {
cy.findByText("Dropdown list").click();
@@ -16,10 +20,8 @@ export function setFilterQuestionSource({ question, field }) {
cy.findByText("Pick a model or question…").click();
});
modal().within(() => {
cy.findByPlaceholderText(/Search for a question/).type(question);
entityPickerModal().within(() => {
cy.findByText(question).click();
cy.button("Done").click();
});
modal().within(() => {

View File

@@ -99,28 +99,29 @@ export function interceptPromise(method, path) {
* cy.visit(`/dashboard/1`);
* });
*/
const cypressWaitAllRecursive = (results, currentCommand, commands) => {
return currentCommand.then(result => {
const cypressWaitAllRecursive = (results, commands) => {
const [nextCommand, ...restCommands] = commands;
if (!nextCommand) {
return;
}
return nextCommand.then(result => {
results.push(result);
const [nextCommand, ...rest] = Array.from(commands);
if (nextCommand == null) {
return results;
return;
}
return cypressWaitAllRecursive(results, nextCommand, rest);
return cypressWaitAllRecursive(results, restCommands);
});
};
export const cypressWaitAll = function (commands) {
const results = [];
return cypressWaitAllRecursive(
results,
cy.wrap(null, { log: false }),
commands,
);
return cy.wrap(results).then(() => {
cypressWaitAllRecursive(results, commands);
});
};
/**

View File

@@ -1,17 +1,12 @@
// various Metabase-specific "scoping" functions like inside popover/modal/navbar/main/sidebar content area
export const POPOVER_ELEMENT =
".popover[data-state~='visible'],[data-position]:not(.emotion-HoverCard-dropdown)";
".popover[data-state~='visible'],[data-element-id=mantine-popover]";
export function popover() {
cy.get(POPOVER_ELEMENT).should("be.visible");
return cy.get(POPOVER_ELEMENT);
}
export function mantinePopover() {
const MANTINE_POPOVER = "[data-popover=mantine-popover]";
return cy.get(MANTINE_POPOVER).should("be.visible");
}
const HOVERCARD_ELEMENT = ".emotion-HoverCard-dropdown[role='dialog']:visible";
export function hovercard() {
@@ -28,8 +23,8 @@ export function menu() {
}
export function modal() {
const LEGACY_MODAL_SELECTOR = ".Modal";
const MODAL_SELECTOR = ".emotion-Modal-content[role='dialog']";
const LEGACY_MODAL_SELECTOR = "[data-testid=modal]";
return cy.get([MODAL_SELECTOR, LEGACY_MODAL_SELECTOR].join(","));
}
@@ -179,7 +174,9 @@ export const moveDnDKitElement = (
button: 0,
})
.wait(200)
.trigger("pointermove", 5, 5, {
// This initial move needs to be greater than the activation constraint
// of the pointer sensor
.trigger("pointermove", 20, 20, {
force: true,
isPrimary: true,
button: 0,
@@ -216,5 +213,5 @@ export const undoToastList = () => {
};
export function dashboardCards() {
return cy.get("#Dashboard-Cards-Container");
return cy.get("[data-element-id=dashboard-cards-container]");
}

View File

@@ -102,6 +102,24 @@ export const many_data_types = async dbClient => {
return null;
};
export const uuid_pk_table = async dbClient => {
const tableName = "uuid_pk_table";
await dbClient.schema.dropTableIfExists(tableName);
await dbClient.schema.createTable(tableName, table => {
table.uuid("id").primary();
table.string("name");
});
await dbClient(tableName).insert([
{ id: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", name: "Duck" },
{ id: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", name: "Rabbit" },
]);
return null;
};
export const composite_pk_table = async dbClient => {
const tableName = "composite_pk_table";

View File

@@ -18,6 +18,7 @@ import {
createModelFromTableName,
queryWritableDB,
setTokenFeatures,
entityPickerModal,
} from "e2e/support/helpers";
import { getCreatePostgresRoleIfNotExistSql } from "e2e/support/test_roles";
import { createMockActionParameter } from "metabase-types/api/mocks";
@@ -219,7 +220,10 @@ describe(
});
modal().eq(1).findByText("Select a model").click();
popover().findByText("Order").click();
entityPickerModal().within(() => {
cy.findByText("Order").click();
});
cy.findByRole("button", { name: "Create" }).click();
cy.get("@modelId").then(modelId => {
@@ -239,12 +243,19 @@ describe(
// to test database picker behavior in the action editor
setActionsEnabledForDB(SAMPLE_DB_ID);
setTokenFeatures("all");
cy.updatePermissionsGraph({
[USER_GROUPS.ALL_USERS_GROUP]: {
[WRITABLE_DB_ID]: { data: { schemas: "none", native: "none" } },
[WRITABLE_DB_ID]: {
"view-data": "blocked",
"create-queries": "no",
},
},
[USER_GROUPS.DATA_GROUP]: {
[WRITABLE_DB_ID]: { data: { schemas: "all", native: "write" } },
[WRITABLE_DB_ID]: {
"view-data": "unrestricted",
"create-queries": "query-builder-and-native",
},
},
});
@@ -805,7 +816,16 @@ describe(
cy.updatePermissionsGraph(
{
[USER_GROUPS.ALL_USERS_GROUP]: {
[WRITABLE_DB_ID]: { data: { schemas: "all", native: "write" } },
[WRITABLE_DB_ID]: {
"view-data": "impersonated",
"create-queries": "query-builder-and-native",
},
},
// By default, all groups get `unrestricted` access that will override the impersonation.
[USER_GROUPS.COLLECTION_GROUP]: {
[WRITABLE_DB_ID]: {
"view-data": "blocked",
},
},
},
[

View File

@@ -37,7 +37,7 @@ const normalUserName = getFullName(normal);
describe("scenarios > admin > people", () => {
beforeEach(() => {
cy.intercept("GET", "/api/permissions/group").as("getGroups");
cy.intercept("GET", "/api/api-key").as("getApiKeys");
cy.intercept("GET", "/api/api-key").as("listApiKeys");
restore();
cy.signInAsAdmin();
});
@@ -335,7 +335,7 @@ describe("scenarios > admin > people", () => {
cy.intercept("DELETE", "/api/permissions/group/*").as("deleteGroup");
cy.visit("/admin/people/groups");
cy.wait(["@getGroups", "@getApiKeys"]);
cy.wait(["@getGroups", "@listApiKeys"]);
cy.findByTestId("admin-panel").within(() => {
cy.button("Create a group").click();
@@ -358,7 +358,7 @@ describe("scenarios > admin > people", () => {
it("should display api keys included in a group and display a warning when deleting the group", () => {
createApiKey("MyApiKey", COLLECTION_GROUP_ID);
cy.visit("/admin/people/groups");
cy.wait(["@getGroups", "@getApiKeys"]);
cy.wait(["@getGroups", "@listApiKeys"]);
cy.findByTestId("admin-panel")
.findByText("collection")

View File

@@ -52,7 +52,7 @@ describeEE("scenarios > admin > settings > SSO > JWT", () => {
setupJwt();
cy.visit("/admin/settings/authentication/jwt");
getUserProvisioningInput().click();
getUserProvisioningInput().label.click();
cy.button("Save changes").click();
cy.wait("@updateSettings");

View File

@@ -65,15 +65,13 @@ describe(
getLdapCard().findByText("Active").should("exist");
});
it("should allow the user to enable/disable user provisioning", () => {
it("should not show the user provision UI to OSS users", () => {
setupLdap();
cy.visit("/admin/settings/authentication/ldap");
getUserProvisioningInput().click();
cy.button("Save changes").click();
cy.wait("@updateLdapSettings");
getSuccessUi().should("exist");
cy.findByTestId("admin-layout-content")
.findByText("User Provisioning")
.should("not.exist");
});
it("should allow to reset ldap settings", () => {
@@ -172,6 +170,20 @@ describeEE(
restore();
cy.signInAsAdmin();
setTokenFeatures("all");
cy.intercept("PUT", "/api/ldap/settings").as("updateLdapSettings");
});
it("should allow the user to enable/disable user provisioning", () => {
setupLdap();
cy.visit("/admin/settings/authentication/ldap");
const { label, input } = getUserProvisioningInput();
input.should("be.checked");
label.click();
cy.button("Save changes").click();
cy.wait("@updateLdapSettings");
getSuccessUi().should("exist");
});
it("should show the login form when ldap is enabled but password login isn't (metabase#25661)", () => {

View File

@@ -81,7 +81,7 @@ describeEE("scenarios > admin > settings > SSO > SAML", () => {
setupSaml();
cy.visit("/admin/settings/authentication/saml");
getUserProvisioningInput().click();
getUserProvisioningInput().label.click();
cy.button("Save changes").click();
cy.wait("@updateSamlSettings");

View File

@@ -90,7 +90,7 @@ const addGroupsToMapping = (mappingName, groups) => {
cy.findByText(group).click();
cy.findByText(group)
.closest(".List-section")
.closest("[data-element-id=list-section]")
.within(() => {
cy.icon("check");
});

View File

@@ -1,7 +1,12 @@
export function getUserProvisioningInput() {
return cy
.findByTestId("admin-layout-content")
.findByText("User Provisioning");
return {
label: cy
.findByTestId("admin-layout-content")
.findByText("User Provisioning"),
input: cy
.findByTestId("admin-layout-content")
.findByLabelText("User Provisioning"),
};
}
export function getSuccessUi() {

View File

@@ -4,6 +4,7 @@ import {
setTokenFeatures,
appBar,
main,
modal,
} from "e2e/support/helpers";
const TOOLS_ERRORS_URL = "/admin/tools/errors";
@@ -157,7 +158,7 @@ function fixQuestion(name) {
cy.findByText("Save").click();
cy.get(".Modal").within(() => {
modal().within(() => {
cy.button("Save").click();
});
}

View File

@@ -2,11 +2,15 @@ import { ORDERS_QUESTION_ID } from "e2e/support/cypress_sample_instance_data";
import {
appBar,
describeEE,
entityPickerModal,
main,
modal,
popover,
restore,
setTokenFeatures,
undoToast,
visitDashboard,
visitQuestion,
} from "e2e/support/helpers";
function checkFavicon(url) {
@@ -17,10 +21,12 @@ function checkFavicon(url) {
function checkLogo() {
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(logo_data => {
cy.get(`img[src="data:image/jpeg;base64,${logo_data}"]`);
cy.get(`img[src="data:image/jpeg;base64,${logo_data}"]`).should("exist");
});
}
const MB = 1024 * 1024;
describeEE("formatting > whitelabel", () => {
beforeEach(() => {
restore();
@@ -60,73 +66,424 @@ describeEE("formatting > whitelabel", () => {
});
});
describe("company logo", () => {
beforeEach(() => {
cy.log("Add a logo");
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(logo_data => {
cy.request("PUT", "/api/setting/application-logo-url", {
value: `data:image/jpeg;base64,${logo_data}`,
describe("image uploads", () => {
describe("company logo", () => {
beforeEach(() => {
cy.log("Add a logo");
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(
logo_data => {
cy.request("PUT", "/api/setting/application-logo-url", {
value: `data:image/jpeg;base64,${logo_data}`,
});
},
);
});
it("changes should reflect on admin's dashboard", () => {
cy.visit("/");
checkLogo();
});
it("changes should reflect while signed out", () => {
cy.signOut();
cy.visit("/");
checkLogo();
});
it("changes should reflect on user's dashboard", () => {
cy.signInAsNormalUser();
cy.visit("/");
checkLogo();
});
});
describe("favicon", () => {
it("should work for people that set favicon URL before we change the input to file input", () => {
const faviconUrl =
"https://cdn.ecosia.org/assets/images/ico/favicon.ico";
cy.request("PUT", "/api/setting/application-favicon-url", {
value: faviconUrl,
});
checkFavicon(faviconUrl);
cy.signInAsNormalUser();
cy.visit("/");
cy.get('head link[rel="icon"]')
.get('[href="https://cdn.ecosia.org/assets/images/ico/favicon.ico"]')
.should("have.length", 1);
});
it("should show up in user's HTML", () => {
cy.visit("/admin/settings/whitelabel");
cy.log("Add favicon");
cy.findByLabelText("Favicon").selectFile(
{
contents: "e2e/support/assets/favicon.ico",
mimeType: "image/jpeg",
},
{ force: true },
);
undoToast().findByText("Changes saved").should("be.visible");
cy.readFile("e2e/support/assets/favicon.ico", "base64").then(
base64Url => {
const faviconUrl = `data:image/jpeg;base64,${base64Url}`;
cy.wrap(faviconUrl).as("faviconUrl");
checkFavicon(faviconUrl);
},
);
cy.signInAsNormalUser();
cy.visit("/");
cy.get("@faviconUrl").then(faviconUrl => {
cy.get('head link[rel="icon"]')
.get(`[href="${faviconUrl}"]`)
.should("have.length", 1);
});
});
});
it("changes should reflect on admin's dashboard", () => {
cy.visit("/");
checkLogo();
});
describe("custom illustrations", () => {
describe("login page illustration", () => {
it("should only allow uploading a valid image files (PNG, JPG, SVG) and display on login page", () => {
/**
* Unfortunately, we couldn't test the browser file selector with Cypress yet.
* With `input.selectFile`, we still can select any files unlike the browser file selector
* which would respect the `accept` attribute (which specifies the accepted MIME types).
*/
cy.visit("/admin/settings/whitelabel/conceal-metabase");
it("changes should reflect while signed out", () => {
cy.signOut();
cy.visit("/");
checkLogo();
});
cy.log("test error message for file size > 2MB");
cy.findByRole("searchbox", {
name: "Login and unsubscribe pages",
}).click();
popover().findByText("Custom").click();
/**
* Clicking "Choose File" doesn't actually open the file browser on Cypress,
* so I need to use `selectFile` with the file input instead.
*/
cy.findByTestId("login-page-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: Cypress.Buffer.from("a".repeat(2 * MB + 1)),
fileName: "big-file.jpg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText(
"The image you chose is larger than 2MB. Please choose another one.",
).should("be.visible");
cy.findByText("big-file.jpg").should("not.exist");
});
cy.findByRole("searchbox", {
name: "Login and unsubscribe pages",
}).click();
popover().findByText("Custom").click();
cy.log("test uploading a corrupted file");
cy.findByTestId("login-page-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: Cypress.Buffer.from("a".repeat(2 * MB)),
fileName: "corrupted-file.jpg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText(
"The image you chose is corrupted. Please choose another one.",
)
.scrollIntoView()
.should("be.visible");
cy.findByText("corrupted-file.jpg").should("not.exist");
});
it("changes should reflect on user's dashboard", () => {
cy.signInAsNormalUser();
cy.visit("/");
checkLogo();
});
});
cy.log('test replacing the "corrupted" file with a valid one');
cy.findByTestId("login-page-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: "e2e/support/assets/logo.jpeg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText("logo.jpeg").should("be.visible");
describe("favicon", () => {
it("should work for people that set favicon URL before we change the input to file input", () => {
const faviconUrl = "https://cdn.ecosia.org/assets/images/ico/favicon.ico";
cy.request("PUT", "/api/setting/application-favicon-url", {
value: faviconUrl,
cy.findByText(
"The image you chose is corrupted. Please choose another one.",
).should("not.exist");
});
undoToast().findByText("Changes saved").should("be.visible");
undoToast().icon("close").click();
cy.log("test removing the custom illustration");
cy.findByTestId("login-page-illustration-setting").within(() => {
cy.button("Remove custom illustration").click();
cy.log(
"the default option should be selected once removing the custom illustration",
);
cy.findByDisplayValue("Lighthouse").should("be.visible");
});
undoToast().findByText("Changes saved").should("be.visible");
undoToast().icon("close").click();
cy.log("test uploading a valid image file");
cy.findByTestId("login-page-illustration-setting")
.findByRole("searchbox", { name: "Login and unsubscribe pages" })
.click();
popover().findByText("Custom").click();
cy.findByTestId("login-page-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: "e2e/support/assets/logo.jpeg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText("logo.jpeg").should("be.visible");
});
undoToast().findByText("Changes saved").should("be.visible");
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(
logo_data => {
const backgroundImage = `url("data:image/jpeg;base64,${logo_data}")`;
cy.signOut();
cy.visit("/");
cy.findByTestId("login-page-illustration").should(
"have.css",
"background-image",
backgroundImage,
);
cy.visit("/unsubscribe?hash=hash&email=email&pulse-id=pulse-id");
cy.findByTestId("unsubscribe-page-illustration").should(
"have.css",
"background-image",
backgroundImage,
);
},
);
cy.log("test no illustration");
cy.signInAsAdmin();
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByRole("searchbox", {
name: "Login and unsubscribe pages",
}).click();
popover().findByText("No illustration").click();
cy.signOut();
cy.visit("/");
cy.findByTestId("login-page-illustration").should("not.exist");
cy.visit("/unsubscribe?hash=hash&email=email&pulse-id=pulse-id");
cy.findByTestId("unsubscribe-page-illustration").should("not.exist");
});
});
checkFavicon(faviconUrl);
cy.signInAsNormalUser();
cy.visit("/");
cy.get('head link[rel="icon"]')
.get('[href="https://cdn.ecosia.org/assets/images/ico/favicon.ico"]')
.should("have.length", 1);
});
it("should show up in user's HTML", () => {
cy.visit("/admin/settings/whitelabel");
cy.log("Add favicon");
describe("landing page illustration", () => {
it("should allow display the selected illustration on the landing page", () => {
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByLabelText("Favicon").selectFile(
{
contents: "e2e/support/assets/favicon.ico",
mimeType: "image/jpeg",
},
{ force: true },
);
undoToast().findByText("Changes saved").should("be.visible");
cy.readFile("e2e/support/assets/favicon.ico", "base64").then(
base64Url => {
const faviconUrl = `data:image/jpeg;base64,${base64Url}`;
cy.wrap(faviconUrl).as("faviconUrl");
checkFavicon(faviconUrl);
},
);
cy.signInAsNormalUser();
cy.visit("/");
cy.get("@faviconUrl").then(faviconUrl => {
cy.get('head link[rel="icon"]')
.get(`[href="${faviconUrl}"]`)
.should("have.length", 1);
cy.findByRole("searchbox", { name: "Landing page" }).should(
"have.value",
"Lighthouse",
);
cy.findByRole("searchbox", { name: "Landing page" }).click();
popover().findByText("Custom").click();
cy.findByTestId("landing-page-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: "e2e/support/assets/logo.jpeg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText("logo.jpeg").should("be.visible");
});
undoToast().findByText("Changes saved").should("be.visible");
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(
logo_data => {
const backgroundImage = `url("data:image/jpeg;base64,${logo_data}")`;
cy.visit("/");
cy.findByTestId("landing-page-illustration").should(
"have.css",
"background-image",
backgroundImage,
);
},
);
cy.log("test no illustration");
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByLabelText("Landing page").click();
popover().findByText("No illustration").click();
cy.visit("/");
cy.findByTestId("landing-page-illustration").should("not.exist");
});
});
describe("no data illustration", () => {
it("should allow display the selected illustration at relevant places", () => {
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByRole("searchbox", {
name: "When calculations return no results",
}).should("have.value", "Sailboat");
cy.findByRole("searchbox", {
name: "When calculations return no results",
}).click();
popover().findByText("Custom").click();
cy.findByTestId("no-data-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: "e2e/support/assets/logo.jpeg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText("logo.jpeg").should("be.visible");
});
undoToast().findByText("Changes saved").should("be.visible");
cy.createDashboardWithQuestions({
dashboardName: "No results dashboard",
questions: [
{
name: "No results question",
native: {
query: "select * from products where id = 999999999",
},
},
],
}).then(({ dashboard, questions }) => {
cy.wrap(dashboard.id).as("dashboardId");
cy.wrap(questions[0].id).as("questionId");
});
cy.log("test custom illustration");
visitDashboard("@dashboardId");
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(
logo_data => {
const imageDataUrl = `data:image/jpeg;base64,${logo_data}`;
cy.wrap(imageDataUrl).as("imageDataUrl");
cy.findByAltText("No results").should(
"have.attr",
"src",
imageDataUrl,
);
},
);
visitQuestion("@questionId");
cy.get("@imageDataUrl").then(imageDataUrl => {
cy.findByAltText("No results").should(
"have.attr",
"src",
imageDataUrl,
);
});
cy.log("test no illustration");
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByRole("searchbox", {
name: "When calculations return no results",
}).click();
popover().findByText("No illustration").click();
visitDashboard("@dashboardId");
cy.findByAltText("No results").should("not.exist");
visitQuestion("@questionId");
cy.findByAltText("No results").should("not.exist");
});
});
describe("no object illustration", () => {
it("should allow display the selected illustration at relevant places", () => {
const emptyCollectionName = "Empty Collection";
cy.request("POST", "/api/collection", { name: emptyCollectionName });
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByRole("searchbox", {
name: "When no objects can be found",
}).should("have.value", "Sailboat");
cy.findByRole("searchbox", {
name: "When no objects can be found",
}).click();
popover().findByText("Custom").click();
cy.findByTestId("no-object-illustration-setting").within(() => {
cy.findByTestId("file-input").selectFile(
{
contents: "e2e/support/assets/logo.jpeg",
mimeType: "image/jpeg",
},
{ force: true },
);
cy.findByText("logo.jpeg").should("be.visible");
});
undoToast().findByText("Changes saved").should("be.visible");
cy.log("test custom illustration");
cy.findByRole("navigation").findByText("Exit admin").click();
appBar().findByText("New").click();
popover().findByText("Dashboard").click();
modal().findByTestId("collection-picker-button").click();
entityPickerModal().within(() => {
cy.readFile("e2e/support/assets/logo.jpeg", "base64").then(
logo_data => {
const imageDataUrl = `data:image/jpeg;base64,${logo_data}`;
cy.wrap(imageDataUrl).as("imageDataUrl");
},
);
cy.log("test search not found illustration");
cy.findByPlaceholderText("Search…").type(
"This aren't the objects you're looking for",
);
cy.get("@imageDataUrl").then(imageDataUrl => {
cy.findByAltText("No results").should(
"have.attr",
"src",
imageDataUrl,
);
});
});
cy.log("test no illustration");
cy.visit("/admin/settings/whitelabel/conceal-metabase");
cy.findByRole("searchbox", {
name: "When no objects can be found",
}).click();
popover().findByText("No illustration").click();
cy.findByRole("navigation").findByText("Exit admin").click();
appBar().findByText("New").click();
popover().findByText("Dashboard").click();
modal().findByTestId("collection-picker-button").click();
entityPickerModal().within(() => {
cy.findByText(emptyCollectionName).click();
cy.findByAltText("No results").should("not.exist");
cy.log("test search not found illustration");
cy.findByPlaceholderText("Search…").type(
"This aren't the objects you're looking for",
);
cy.findByAltText("No results").should("not.exist");
});
});
});
});
});

View File

@@ -1,172 +0,0 @@
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
import {
appBar,
describeWithSnowplow,
enableTracking,
expectGoodSnowplowEvents,
expectNoBadSnowplowEvents,
resetSnowplow,
restore,
rightSidebar,
visitDashboard,
describeEE,
setTokenFeatures,
} from "e2e/support/helpers";
describeEE("database prompt banner", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
setTokenFeatures("all");
});
it("should show a database prompt banner when logged in as an admin, an instance is on a paid plan, only have a single sample dataset, and is not white labelled", () => {
cy.visit("/");
cy.findByRole("main").findByText("Loading...").should("not.exist");
cy.findAllByRole("banner")
.first()
.within(() => {
cy.findByText(
"Connect to your database to get the most from Metabase.",
).should("exist");
cy.findByRole("link", { name: "Get help connecting" })
.should("have.attr", "href")
.and(
"eq",
"https://metabase.com/help/connect?email=admin%40metabase.test&site_url=http%3A%2F%2Flocalhost%3A4000",
);
cy.findByRole("link", { name: "Connect your database" }).click();
cy.url().should("include", "/admin/databases/create");
});
// Assert that database form is rendered
cy.findByRole("main").within(() => {
cy.findByText("Add Database").should("exist");
cy.findByLabelText("Database type").should("exist");
cy.findByLabelText("Database name").should("exist");
});
});
it("should not show a database prompt banner when logged in as an admin, an instance is on a paid plan, and only have a single sample dataset, but is white labelled", () => {
cy.request("PUT", "/api/setting/application-name", { value: "Acme Corp." });
cy.visit("/");
cy.findByRole("main").findByText("Loading...").should("not.exist");
cy.findAllByRole("banner")
.first()
.within(() => {
cy.findByText(
"Connect to your database to get the most from Metabase.",
).should("not.exist");
});
});
// Until we enable multi-browser support, this repro will be skipped by Cypress in CI
// Issue was specific to Firefox only - it is still possible to test it locally
it(
"should show info sidebar correctly on Firefox",
{ browser: "firefox" },
function () {
visitDashboard(ORDERS_DASHBOARD_ID);
cy.findByRole("main").findByText("Loading...").should("not.exist");
cy.findByRole("main").icon("info").click();
rightSidebar().within(() => {
cy.findByRole("heading", { name: "About" }).should("be.visible");
cy.findByRole("heading", { name: "History" }).should("be.visible");
});
},
);
describe("embeddings", () => {
// Public and signed embeds are tested in `PublicQuestion.unit.spec.tsx`
describe("full-app embeddings", () => {
it("should render database prompt banner when logged in as an admin, an instance is on a paid plan, only have a single sample dataset, and is not white labeling", () => {
visitFullAppEmbeddingUrl({
url: "/",
qs: { side_nav: false, logo: false },
});
cy.findByRole("link", { name: "Metabase tips" }).should("exist");
// Test that we're in full-app embedding since parameters are working.
appBar().should("not.exist");
cy.findAllByRole("banner")
.first()
.within(() => {
cy.findByText(
"Connect to your database to get the most from Metabase.",
).should("exist");
});
});
it("should not render for any other condition", () => {
// Adding a second database should prevent the database prompt
cy.addSQLiteDatabase();
visitFullAppEmbeddingUrl({
url: "/",
qs: { side_nav: false, logo: false },
});
cy.findByRole("link", { name: "Metabase tips" }).should("exist");
// Test that we're in full-app embedding since parameters are working.
appBar().should("not.exist");
// Since there wouldn't be a database prompt banner, we can assert that there is no banner at all
cy.findAllByRole("banner").should("not.exist");
});
});
});
});
describeWithSnowplow("database prompt banner", () => {
const PAGE_VIEW_EVENT = 1;
beforeEach(() => {
restore();
resetSnowplow();
cy.signInAsAdmin();
setTokenFeatures("all");
enableTracking();
cy.visit("/");
cy.findByRole("main").findByText("Loading...").should("not.exist");
});
afterEach(() => {
expectNoBadSnowplowEvents();
});
it("should send snowplow events when clicking on links in the database prompt banner", () => {
expectNoBadSnowplowEvents();
expectGoodSnowplowEvents(PAGE_VIEW_EVENT);
cy.findAllByRole("banner")
.first()
.within(() => {
cy.findByRole("link", { name: "Get help connecting" }).click();
expectGoodSnowplowEvents(PAGE_VIEW_EVENT + 1);
cy.findByRole("link", { name: "Connect your database" }).click();
// clicking this link also brings us to the admin page causing a new page_view event
expectGoodSnowplowEvents(2 * PAGE_VIEW_EVENT + 2);
});
});
});
const visitFullAppEmbeddingUrl = ({ url, qs }) => {
cy.visit({
url,
qs,
onBeforeLoad(window) {
// cypress runs all tests in an iframe and the app uses this property to avoid embedding mode for all tests
// by removing the property the app would work in embedding mode
window.Cypress = undefined;
},
});
};

View File

@@ -297,6 +297,7 @@ describe("scenarios > admin > databases > sample database", () => {
cy.findByText("Browse data").click();
});
cy.findByRole("tab", { name: "Databases" }).click();
cy.findByTestId("database-browser").within(() => {
cy.findByText("Sample Database").should("exist");
});

View File

@@ -14,6 +14,7 @@ import {
startNewQuestion,
setTokenFeatures,
openTable,
moveDnDKitElement,
} from "e2e/support/helpers";
const { ORDERS, ORDERS_ID, PRODUCTS_ID, REVIEWS, REVIEWS_ID, PEOPLE_ID } =
@@ -283,7 +284,10 @@ describe("scenarios > admin > datamodel > editor", () => {
it("should allow sorting fields in the custom order", () => {
visitTableMetadata({ tableId: PRODUCTS_ID });
moveField(0, 200);
//moveField(0, 200);
moveDnDKitElement(cy.findAllByTestId("grabber").first(), {
vertical: 200,
});
cy.wait("@updateFieldOrder");
openProductsTable();
assertTableHeader([
@@ -658,28 +662,6 @@ const getFieldSection = fieldName => {
return cy.findByLabelText(fieldName);
};
const moveField = (fieldIndex, deltaY) => {
cy.findAllByTestId("grabber")
.eq(fieldIndex)
.trigger("pointerdown", 0, 0, { force: true, button: 0, isPrimary: true })
.wait(200)
.trigger("pointermove", 5, 5, { force: true, button: 0, isPrimary: true })
.wait(200)
//cy.get("#ColumnsList")
.trigger("pointermove", 10, deltaY, {
force: true,
button: 0,
isPrimary: true,
})
.wait(200)
.trigger("pointerup", 10, deltaY, {
force: true,
button: 0,
isPrimary: true,
})
.wait(200);
};
const setTableOrder = order => {
cy.findByLabelText("Sort").click();
popover().findByText(order).click();

View File

@@ -166,7 +166,10 @@ describe("scenarios > admin > datamodel > field", () => {
cy.log("Make sure custom mapping appears in QB");
openTable({ database: dbId, table: NUMBER_WITH_NULLS_ID });
cy.get(".cellData").should("contain", remappedNullValue);
cy.get("[data-testid=cell-data]").should(
"contain",
remappedNullValue,
);
},
);
});

View File

@@ -127,8 +127,10 @@ describe("scenarios > admin > datamodel > metadata", () => {
cy.findByText("Count of rows").click();
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Pick a column to group by").click();
cy.get(".List-section-header").contains("Created At").click();
cy.get(".List-section--expanded .List-item-title")
cy.get("[data-element-id=list-section-header]")
.contains("Created At")
.click();
cy.get("[data-element-id=list-section] [data-element-id=list-item-title]")
.contains("Created At")
.should("have.length", 1);
});
@@ -149,10 +151,16 @@ describe("scenarios > admin > datamodel > metadata", () => {
cy.findAllByTestId("dimension-list-item")
.eq(3)
.should("have.text", "Rating");
cy.get(".List-section-header").should("have.length", 3);
cy.get(".List-section-header").eq(0).should("have.text", "Review");
cy.get(".List-section-header").eq(1).should("have.text", "Product");
cy.get(".List-section-header").eq(2).should("have.text", "Rating");
cy.get("[data-element-id=list-section-header]").should("have.length", 3);
cy.get("[data-element-id=list-section-header]")
.eq(0)
.should("have.text", "Review");
cy.get("[data-element-id=list-section-header]")
.eq(1)
.should("have.text", "Product");
cy.get("[data-element-id=list-section-header]")
.eq(2)
.should("have.text", "Rating");
});
});

View File

@@ -64,14 +64,20 @@ describe("scenarios > admin > datamodel > metrics", () => {
.first()
.as("tableHeader")
.within(() => {
cy.get(".cellData").eq(1).invoke("text").should("eq", "Revenue");
cy.get("[data-testid=cell-data]")
.eq(1)
.invoke("text")
.should("eq", "Revenue");
});
cy.get("@table")
.last()
.as("tableBody")
.within(() => {
cy.get(".cellData").eq(1).invoke("text").should("eq", "50,072.98");
cy.get("[data-testid=cell-data]")
.eq(1)
.invoke("text")
.should("eq", "50,072.98");
});
});
@@ -264,7 +270,7 @@ describe("scenarios > admin > datamodel > metrics", () => {
it("should save the metric using custom expressions (metabase#13022)", () => {
createMetric({
name: "13022_Metric",
desription: "desc",
description: "desc",
table_id: ORDERS_ID,
definition: {
"source-table": ORDERS_ID,

View File

@@ -39,7 +39,7 @@ describe("issue 17768", () => {
popover().within(() => {
cy.findByText("ID")
.closest(".List-section")
.closest("[data-element-id=list-section]")
.realHover()
.contains("Auto bin")
.should("not.exist");

View File

@@ -1,4 +1,9 @@
import { restore, openNativeEditor, runNativeQuery } from "e2e/support/helpers";
import {
restore,
openNativeEditor,
runNativeQuery,
modal,
} from "e2e/support/helpers";
const nativeQuery = "select (random() * random() * random()), pg_sleep(2)";
@@ -85,7 +90,7 @@ function saveQuestion(name) {
cy.findByLabelText("Name").type(name);
cy.get(".Modal").button("Save").click();
modal().button("Save").click();
cy.findByText("Not now").click();
@@ -93,7 +98,7 @@ function saveQuestion(name) {
}
function getCellText() {
return cy.get(".cellData").eq(-1).invoke("text");
return cy.get("[data-testid=cell-data]").eq(-1).invoke("text");
}
function refresh() {

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