mirror of
https://github.com/apache/superset.git
synced 2026-05-03 15:04:28 +00:00
Compare commits
23 Commits
fix/postgr
...
ce/issue-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdc1397033 | ||
|
|
5fe3a1c2cd | ||
|
|
90f8fafbb4 | ||
|
|
7774ec7e3c | ||
|
|
6da04fa51d | ||
|
|
7c4b2b137c | ||
|
|
9ccd37de1c | ||
|
|
b791f4c2cd | ||
|
|
44d1f50b7c | ||
|
|
b9de3dba95 | ||
|
|
4c4905f689 | ||
|
|
2b13e07521 | ||
|
|
7c24214857 | ||
|
|
37bf729f75 | ||
|
|
0b78ffbb9c | ||
|
|
41823a3057 | ||
|
|
4cc4d62486 | ||
|
|
dece5415a7 | ||
|
|
ea8a8f8ac7 | ||
|
|
a216b23d5a | ||
|
|
6ad1583eb5 | ||
|
|
9a7938899e | ||
|
|
30bd490b84 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -37,6 +37,10 @@ updates:
|
||||
# `just-handlerbars-helpers` library in plugin-chart-handlebars requires `currencyformatter`` to be < 2
|
||||
- dependency-name: "currencyformatter.js"
|
||||
update-types: ["version-update:semver-major"]
|
||||
# TODO: remove below clause once https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/940 lands onto a future release
|
||||
# and confirm the issue https://github.com/apache/superset/issues/39600 is fixed
|
||||
- dependency-name: "react-checkbox-tree"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
storybook:
|
||||
applies-to: version-updates
|
||||
|
||||
@@ -29,7 +29,7 @@ ARG BUILD_TRANSLATIONS="false"
|
||||
######################################################################
|
||||
# superset-node-ci used as a base for building frontend assets and CI
|
||||
######################################################################
|
||||
FROM --platform=${BUILDPLATFORM} node:20-trixie-slim AS superset-node-ci
|
||||
FROM --platform=${BUILDPLATFORM} node:22-trixie-slim AS superset-node-ci
|
||||
ARG BUILD_TRANSLATIONS
|
||||
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
|
||||
ARG DEV_MODE="false" # Skip frontend build in dev mode
|
||||
|
||||
@@ -64,7 +64,7 @@ There are two approaches to making dashboards publicly accessible:
|
||||
3. Edit each dashboard's properties and add the "Public" role
|
||||
4. Only dashboards with the Public role explicitly assigned are visible to anonymous users
|
||||
|
||||
See the [Public role documentation](/admin-docs/security/security#public) for more details.
|
||||
See the [Public role documentation](/admin-docs/security/#public) for more details.
|
||||
|
||||
#### Embedding a Public Dashboard
|
||||
|
||||
@@ -111,7 +111,7 @@ FEATURE_FLAGS = {
|
||||
This flag only hides the logout button when Superset detects it is running inside an iframe. Users accessing Superset directly (not embedded) will still see the logout button regardless of this setting.
|
||||
|
||||
:::note
|
||||
When embedding with SSO, also set `SESSION_COOKIE_SAMESITE = 'None'` and `SESSION_COOKIE_SECURE = True`. See [Security documentation](/docs/security/securing_superset) for details.
|
||||
When embedding with SSO, also set `SESSION_COOKIE_SAMESITE = 'None'` and `SESSION_COOKIE_SECURE = True`. See [Security documentation](/admin-docs/security/securing_superset) for details.
|
||||
:::
|
||||
|
||||
## CSRF settings
|
||||
|
||||
@@ -20,7 +20,7 @@ To help make the problem somewhat tractable—given that Apache Superset has no
|
||||
|
||||
To strive for data consistency (regardless of the timezone of the client) the Apache Superset backend tries to ensure that any timestamp sent to the client has an explicit (or semi-explicit as in the case with [Epoch time](https://en.wikipedia.org/wiki/Unix_time) which is always in reference to UTC) timezone encoded within.
|
||||
|
||||
The challenge however lies with the slew of [database engines](/admin-docs/databases#installing-drivers-in-docker) which Apache Superset supports and various inconsistencies between their [Python Database API (DB-API)](https://www.python.org/dev/peps/pep-0249/) implementations combined with the fact that we use [Pandas](https://pandas.pydata.org/) to read SQL into a DataFrame prior to serializing to JSON. Regrettably Pandas ignores the DB-API [type_code](https://www.python.org/dev/peps/pep-0249/#type-objects) relying by default on the underlying Python type returned by the DB-API. Currently only a subset of the supported database engines work correctly with Pandas, i.e., ensuring timestamps without an explicit timestamp are serializd to JSON with the server timezone, thus guaranteeing the client will display timestamps in a consistent manner irrespective of the client's timezone.
|
||||
The challenge however lies with the slew of [database engines](/user-docs/databases#installing-drivers-in-docker) which Apache Superset supports and various inconsistencies between their [Python Database API (DB-API)](https://www.python.org/dev/peps/pep-0249/) implementations combined with the fact that we use [Pandas](https://pandas.pydata.org/) to read SQL into a DataFrame prior to serializing to JSON. Regrettably Pandas ignores the DB-API [type_code](https://www.python.org/dev/peps/pep-0249/#type-objects) relying by default on the underlying Python type returned by the DB-API. Currently only a subset of the supported database engines work correctly with Pandas, i.e., ensuring timestamps without an explicit timestamp are serialized to JSON with the server timezone, thus guaranteeing the client will display timestamps in a consistent manner irrespective of the client's timezone.
|
||||
|
||||
For example the following is a comparison of MySQL and Presto,
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ For production clusters it's recommended to build own image with this step done
|
||||
Superset requires a Python DB-API database driver and a SQLAlchemy
|
||||
dialect to be installed for each datastore you want to connect to.
|
||||
|
||||
See [Install Database Drivers](/admin-docs/databases#installing-database-drivers) for more information.
|
||||
See [Install Database Drivers](/user-docs/databases#installing-database-drivers) for more information.
|
||||
It is recommended that you refer to versions listed in
|
||||
[pyproject.toml](https://github.com/apache/superset/blob/master/pyproject.toml)
|
||||
instead of hard-coding them in your bootstrap script, as seen below.
|
||||
|
||||
@@ -239,26 +239,143 @@ based on the roles and permissions that were attributed.
|
||||
### Row Level Security
|
||||
|
||||
Using Row Level Security filters (under the **Security** menu) you can create filters
|
||||
that are assigned to a particular table, as well as a set of roles.
|
||||
that are assigned to a particular dataset, as well as a set of roles.
|
||||
If you want members of the Finance team to only have access to
|
||||
rows where `department = "finance"`, you could:
|
||||
|
||||
- Create a Row Level Security filter with that clause (`department = "finance"`)
|
||||
- Then assign the clause to the **Finance** role and the table it applies to
|
||||
- Then assign the clause to the **Finance** role and the dataset it applies to
|
||||
|
||||
The **clause** field, which can contain arbitrary text, is then added to the generated
|
||||
SQL statement’s WHERE clause. So you could even do something like create a filter
|
||||
SQL statement's WHERE clause. So you could even do something like create a filter
|
||||
for the last 30 days and apply it to a specific role, with a clause
|
||||
like `date_field > DATE_SUB(NOW(), INTERVAL 30 DAY)`. It can also support
|
||||
multiple conditions: `client_id = 6` AND `advertiser="foo"`, etc.
|
||||
|
||||
All relevant Row level security filters will be combined together (under the hood,
|
||||
the different SQL clauses are combined using AND statements). This means it's
|
||||
possible to create a situation where two roles conflict in such a way as to limit a table subset to empty.
|
||||
RLS clauses also support **Jinja templating** when `ENABLE_TEMPLATE_PROCESSING` is enabled, so you can write dynamic filters such as
|
||||
`user_id = '{{ current_username() }}'` to restrict rows based on the logged-in user.
|
||||
|
||||
For example, the filters `client_id=4` and `client_id=5`, applied to a role,
|
||||
will result in users of that role having `client_id=4` AND `client_id=5`
|
||||
added to their query, which can never be true.
|
||||
#### Filter Types
|
||||
|
||||
There are two types of RLS filters:
|
||||
|
||||
- **Regular** — The filter clause is applied when the querying user belongs to one of the
|
||||
roles assigned to the filter. Use this to restrict what specific roles can see.
|
||||
- **Base** — The filter clause is applied to **all** users _except_ those in the assigned
|
||||
roles. Use this to define a default restriction that privileged roles (e.g. Admin) are
|
||||
exempt from. For example, a Base filter with clause `1 = 0` and the Admin role would
|
||||
hide all rows from everyone except Admin — useful as a deny-by-default baseline.
|
||||
|
||||
#### Group Keys and Filter Combination
|
||||
|
||||
All applicable RLS filters are combined before being added to the query. The combination
|
||||
rules are:
|
||||
|
||||
- Filters that share the **same group key** are combined with **OR** (any match within
|
||||
the group is sufficient).
|
||||
- Different filter groups (different group keys, or no group key) are combined with
|
||||
**AND** (all groups must match).
|
||||
- Filters with **no group key** are each treated as their own group and are always AND'd.
|
||||
|
||||
For example, if a dataset has three filters:
|
||||
|
||||
| Filter | Clause | Group Key |
|
||||
|--------|--------|-----------|
|
||||
| F1 | `department = 'Finance'` | `department` |
|
||||
| F2 | `department = 'Marketing'` | `department` |
|
||||
| F3 | `region = 'Europe'` | `region` |
|
||||
|
||||
The resulting WHERE clause would be:
|
||||
|
||||
```sql
|
||||
(department = 'Finance' OR department = 'Marketing') AND (region = 'Europe')
|
||||
```
|
||||
|
||||
:::caution Conflicting filters
|
||||
It is possible to create filters that conflict and produce an empty result set. For
|
||||
example, the filters `client_id = 4` and `client_id = 5` **without a shared group key**
|
||||
will be AND'd together, producing `client_id = 4 AND client_id = 5`, which can never
|
||||
be true.
|
||||
|
||||
If you intend for these to be alternatives, assign them the **same group key** so they
|
||||
are OR'd instead.
|
||||
:::
|
||||
|
||||
#### RLS and Virtual (SQL-Based) Datasets
|
||||
|
||||
RLS filters are assigned to **datasets**, not to underlying database tables directly. This
|
||||
has important implications when working with virtual (SQL-based) datasets:
|
||||
|
||||
- **Physical datasets** (backed directly by a table or view) — RLS filters assigned to
|
||||
the dataset are added as WHERE clauses to the query.
|
||||
- **Virtual datasets** (defined by a custom SQL query) — RLS filters assigned directly to
|
||||
the virtual dataset are applied to the _outer_ query that wraps the dataset's SQL.
|
||||
Additionally, RLS filters on the **underlying physical datasets** referenced by the
|
||||
virtual dataset's SQL are injected into the inner subquery for each referenced table.
|
||||
|
||||
For example, if you have:
|
||||
|
||||
1. A physical dataset `orders` with RLS filter `region = 'US'`
|
||||
2. A virtual dataset defined as `SELECT * FROM orders WHERE status = 'active'`
|
||||
|
||||
A user affected by the RLS filter will effectively see:
|
||||
|
||||
```sql
|
||||
SELECT * FROM (
|
||||
SELECT * FROM orders WHERE (region = 'US') AND status = 'active'
|
||||
) ...
|
||||
```
|
||||
|
||||
**Key considerations for virtual datasets:**
|
||||
|
||||
- You generally do **not** need to duplicate RLS filters on both the physical and virtual
|
||||
dataset — filters on the physical dataset are applied automatically at query time.
|
||||
- If you assign an RLS filter directly to a virtual dataset, the clause must reference
|
||||
columns available in the virtual dataset's _output_, not necessarily the underlying
|
||||
table's columns.
|
||||
- In **SQL Lab**, RLS is enforced only when the `RLS_IN_SQLLAB` feature flag is enabled:
|
||||
queries run against tables that have associated datasets with RLS filters will then have
|
||||
the appropriate predicates injected automatically.
|
||||
|
||||
#### Checking RLS Filters via the API
|
||||
|
||||
You can use the RLS REST API to audit which filters are configured and which datasets
|
||||
they affect. This requires the `can_read` permission on the `Row Level Security` resource.
|
||||
|
||||
**List all RLS rules:**
|
||||
|
||||
```
|
||||
GET /api/v1/rowlevelsecurity/
|
||||
```
|
||||
|
||||
**Filter RLS rules for a specific dataset** (using [Rison](https://github.com/Nanonid/rison) query syntax):
|
||||
|
||||
```
|
||||
GET /api/v1/rowlevelsecurity/?q=(filters:!((col:tables,opr:rel_m_m,value:<dataset_id>)))
|
||||
```
|
||||
|
||||
**Filter RLS rules by role:**
|
||||
|
||||
```
|
||||
GET /api/v1/rowlevelsecurity/?q=(filters:!((col:roles,opr:rel_m_m,value:<role_id>)))
|
||||
```
|
||||
|
||||
**View details of a specific rule** (including clause, assigned datasets, and roles):
|
||||
|
||||
```
|
||||
GET /api/v1/rowlevelsecurity/<id>
|
||||
```
|
||||
|
||||
The response includes the filter's `name`, `filter_type` (Regular or Base), `clause`,
|
||||
`group_key`, assigned `tables` (with id, schema, and table\_name), and assigned `roles`
|
||||
(with id and name).
|
||||
|
||||
:::tip Auditing RLS for virtual datasets
|
||||
To find all RLS rules that could affect a particular virtual dataset, query the list
|
||||
endpoint filtered by that dataset's ID for any directly-assigned rules. Then also check
|
||||
the physical datasets referenced in the virtual dataset's SQL, since their RLS filters
|
||||
are applied at query time too.
|
||||
:::
|
||||
|
||||
### User Sessions
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
#### Core Resources
|
||||
|
||||
<details>
|
||||
<summary><strong>Dashboards</strong> (26 endpoints) — Create, read, update, and delete dashboards.</summary>
|
||||
<summary><strong>Dashboards</strong> (28 endpoints) — Create, read, update, and delete dashboards.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
@@ -68,23 +68,25 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `POST` | [Create a new dashboard](/developer-docs/api/create-a-new-dashboard) | `/api/v1/dashboard/` |
|
||||
| `GET` | [Get metadata information about this API resource (dashboard--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-dashboard-info) | `/api/v1/dashboard/_info` |
|
||||
| `GET` | [Get a dashboard detail information](/developer-docs/api/get-a-dashboard-detail-information) | `/api/v1/dashboard/{id_or_slug}` |
|
||||
| `GET` | [Get a dashboard's chart definitions.](/developer-docs/api/get-a-dashboard-s-chart-definitions) | `/api/v1/dashboard/{id_or_slug}/charts` |
|
||||
| `GET` | [Get a dashboard's chart definitions.](/developer-docs/api/get-a-dashboards-chart-definitions) | `/api/v1/dashboard/{id_or_slug}/charts` |
|
||||
| `POST` | [Create a copy of an existing dashboard](/developer-docs/api/create-a-copy-of-an-existing-dashboard) | `/api/v1/dashboard/{id_or_slug}/copy/` |
|
||||
| `GET` | [Get dashboard's datasets](/developer-docs/api/get-dashboard-s-datasets) | `/api/v1/dashboard/{id_or_slug}/datasets` |
|
||||
| `DELETE` | [Delete a dashboard's embedded configuration](/developer-docs/api/delete-a-dashboard-s-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `GET` | [Get the dashboard's embedded configuration](/developer-docs/api/get-the-dashboard-s-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `POST` | [Set a dashboard's embedded configuration](/developer-docs/api/set-a-dashboard-s-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `GET` | [Get dashboard's datasets](/developer-docs/api/get-dashboards-datasets) | `/api/v1/dashboard/{id_or_slug}/datasets` |
|
||||
| `DELETE` | [Delete a dashboard's embedded configuration](/developer-docs/api/delete-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `GET` | [Get the dashboard's embedded configuration](/developer-docs/api/get-the-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `POST` | [Set a dashboard's embedded configuration](/developer-docs/api/set-a-dashboards-embedded-configuration) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `PUT` | [Update dashboard by id_or_slug embedded](/developer-docs/api/update-dashboard-by-id-or-slug-embedded) | `/api/v1/dashboard/{id_or_slug}/embedded` |
|
||||
| `GET` | [Get dashboard's tabs](/developer-docs/api/get-dashboard-s-tabs) | `/api/v1/dashboard/{id_or_slug}/tabs` |
|
||||
| `GET` | [Get dashboard's tabs](/developer-docs/api/get-dashboards-tabs) | `/api/v1/dashboard/{id_or_slug}/tabs` |
|
||||
| `DELETE` | [Delete a dashboard](/developer-docs/api/delete-a-dashboard) | `/api/v1/dashboard/{pk}` |
|
||||
| `PUT` | [Update a dashboard](/developer-docs/api/update-a-dashboard) | `/api/v1/dashboard/{pk}` |
|
||||
| `POST` | [Compute and cache a screenshot (dashboard-pk-cache-dashboard-screenshot)](/developer-docs/api/compute-and-cache-a-screenshot-dashboard-pk-cache-dashboard-screenshot) | `/api/v1/dashboard/{pk}/cache_dashboard_screenshot/` |
|
||||
| `PUT` | [Update chart customizations configuration for a dashboard.](/developer-docs/api/update-chart-customizations-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/chart_customizations` |
|
||||
| `PUT` | [Update colors configuration for a dashboard.](/developer-docs/api/update-colors-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/colors` |
|
||||
| `GET` | [Export dashboard as example bundle](/developer-docs/api/export-dashboard-as-example-bundle) | `/api/v1/dashboard/{pk}/export_as_example/` |
|
||||
| `DELETE` | [Remove the dashboard from the user favorite list](/developer-docs/api/remove-the-dashboard-from-the-user-favorite-list) | `/api/v1/dashboard/{pk}/favorites/` |
|
||||
| `POST` | [Mark the dashboard as favorite for the current user](/developer-docs/api/mark-the-dashboard-as-favorite-for-the-current-user) | `/api/v1/dashboard/{pk}/favorites/` |
|
||||
| `PUT` | [Update native filters configuration for a dashboard.](/developer-docs/api/update-native-filters-configuration-for-a-dashboard) | `/api/v1/dashboard/{pk}/filters` |
|
||||
| `GET` | [Get a computed screenshot from cache (dashboard-pk-screenshot-digest)](/developer-docs/api/get-a-computed-screenshot-from-cache-dashboard-pk-screenshot-digest) | `/api/v1/dashboard/{pk}/screenshot/{digest}/` |
|
||||
| `GET` | [Get dashboard's thumbnail](/developer-docs/api/get-dashboard-s-thumbnail) | `/api/v1/dashboard/{pk}/thumbnail/{digest}/` |
|
||||
| `GET` | [Get dashboard's thumbnail](/developer-docs/api/get-dashboards-thumbnail) | `/api/v1/dashboard/{pk}/thumbnail/{digest}/` |
|
||||
| `GET` | [Download multiple dashboards as YAML files](/developer-docs/api/download-multiple-dashboards-as-yaml-files) | `/api/v1/dashboard/export/` |
|
||||
| `GET` | [Check favorited dashboards for current user](/developer-docs/api/check-favorited-dashboards-for-current-user) | `/api/v1/dashboard/favorite_status/` |
|
||||
| `POST` | [Import dashboard(s) with associated charts/datasets/databases](/developer-docs/api/import-dashboard-s-with-associated-charts-datasets-databases) | `/api/v1/dashboard/import/` |
|
||||
@@ -101,8 +103,8 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `GET` | [Get a list of charts](/developer-docs/api/get-a-list-of-charts) | `/api/v1/chart/` |
|
||||
| `POST` | [Create a new chart](/developer-docs/api/create-a-new-chart) | `/api/v1/chart/` |
|
||||
| `GET` | [Get metadata information about this API resource (chart--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-chart-info) | `/api/v1/chart/_info` |
|
||||
| `GET` | [Get a chart detail information](/developer-docs/api/get-a-chart-detail-information) | `/api/v1/chart/{id_or_uuid}` |
|
||||
| `DELETE` | [Delete a chart](/developer-docs/api/delete-a-chart) | `/api/v1/chart/{pk}` |
|
||||
| `GET` | [Get a chart detail information](/developer-docs/api/get-a-chart-detail-information) | `/api/v1/chart/{pk}` |
|
||||
| `PUT` | [Update a chart](/developer-docs/api/update-a-chart) | `/api/v1/chart/{pk}` |
|
||||
| `GET` | [Compute and cache a screenshot (chart-pk-cache-screenshot)](/developer-docs/api/compute-and-cache-a-screenshot-chart-pk-cache-screenshot) | `/api/v1/chart/{pk}/cache_screenshot/` |
|
||||
| `GET` | [Return payload data response for a chart](/developer-docs/api/return-payload-data-response-for-a-chart) | `/api/v1/chart/{pk}/data/` |
|
||||
@@ -121,7 +123,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Datasets</strong> (18 endpoints) — Manage datasets (tables) used for building charts.</summary>
|
||||
<summary><strong>Datasets</strong> (19 endpoints) — Manage datasets (tables) used for building charts.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
@@ -129,13 +131,14 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `GET` | [Get a list of datasets](/developer-docs/api/get-a-list-of-datasets) | `/api/v1/dataset/` |
|
||||
| `POST` | [Create a new dataset](/developer-docs/api/create-a-new-dataset) | `/api/v1/dataset/` |
|
||||
| `GET` | [Get metadata information about this API resource (dataset--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-dataset-info) | `/api/v1/dataset/_info` |
|
||||
| `GET` | [Get a dataset](/developer-docs/api/get-a-dataset) | `/api/v1/dataset/{id_or_uuid}` |
|
||||
| `GET` | [Get charts and dashboards count associated to a dataset](/developer-docs/api/get-charts-and-dashboards-count-associated-to-a-dataset) | `/api/v1/dataset/{id_or_uuid}/related_objects` |
|
||||
| `DELETE` | [Delete a dataset](/developer-docs/api/delete-a-dataset) | `/api/v1/dataset/{pk}` |
|
||||
| `GET` | [Get a dataset](/developer-docs/api/get-a-dataset) | `/api/v1/dataset/{pk}` |
|
||||
| `PUT` | [Update a dataset](/developer-docs/api/update-a-dataset) | `/api/v1/dataset/{pk}` |
|
||||
| `DELETE` | [Delete a dataset column](/developer-docs/api/delete-a-dataset-column) | `/api/v1/dataset/{pk}/column/{column_id}` |
|
||||
| `GET` | [Get dataset drill info](/developer-docs/api/get-dataset-drill-info) | `/api/v1/dataset/{pk}/drill_info/` |
|
||||
| `DELETE` | [Delete a dataset metric](/developer-docs/api/delete-a-dataset-metric) | `/api/v1/dataset/{pk}/metric/{metric_id}` |
|
||||
| `PUT` | [Refresh and update columns of a dataset](/developer-docs/api/refresh-and-update-columns-of-a-dataset) | `/api/v1/dataset/{pk}/refresh` |
|
||||
| `GET` | [Get charts and dashboards count associated to a dataset](/developer-docs/api/get-charts-and-dashboards-count-associated-to-a-dataset) | `/api/v1/dataset/{pk}/related_objects` |
|
||||
| `GET` | [Get distinct values from field data (dataset-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-dataset-distinct-column-name) | `/api/v1/dataset/distinct/{column_name}` |
|
||||
| `POST` | [Duplicate a dataset](/developer-docs/api/duplicate-a-dataset) | `/api/v1/dataset/duplicate` |
|
||||
| `GET` | [Download multiple datasets as YAML files](/developer-docs/api/download-multiple-datasets-as-yaml-files) | `/api/v1/dataset/export/` |
|
||||
@@ -147,7 +150,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Database</strong> (31 endpoints) — Manage database connections and metadata.</summary>
|
||||
<summary><strong>Database</strong> (30 endpoints) — Manage database connections and metadata.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
@@ -165,7 +168,6 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `GET` | [Get all schemas from a database](/developer-docs/api/get-all-schemas-from-a-database) | `/api/v1/database/{pk}/schemas/` |
|
||||
| `GET` | [Get database select star for table (database-pk-select-star-table-name)](/developer-docs/api/get-database-select-star-for-table-database-pk-select-star-table-name) | `/api/v1/database/{pk}/select_star/{table_name}/` |
|
||||
| `GET` | [Get database select star for table (database-pk-select-star-table-name-schema-name)](/developer-docs/api/get-database-select-star-for-table-database-pk-select-star-table-name-schema-name) | `/api/v1/database/{pk}/select_star/{table_name}/{schema_name}/` |
|
||||
| `DELETE` | [Delete a SSH tunnel](/developer-docs/api/delete-a-ssh-tunnel) | `/api/v1/database/{pk}/ssh_tunnel/` |
|
||||
| `POST` | [Re-sync all permissions for a database connection](/developer-docs/api/re-sync-all-permissions-for-a-database-connection) | `/api/v1/database/{pk}/sync_permissions/` |
|
||||
| `GET` | [Get table extra metadata (database-pk-table-extra-table-name-schema-name)](/developer-docs/api/get-table-extra-metadata-database-pk-table-extra-table-name-schema-name) | `/api/v1/database/{pk}/table_extra/{table_name}/{schema_name}/` |
|
||||
| `GET` | [Get table metadata](/developer-docs/api/get-table-metadata) | `/api/v1/database/{pk}/table_metadata/` |
|
||||
@@ -177,7 +179,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `GET` | [Get names of databases currently available](/developer-docs/api/get-names-of-databases-currently-available) | `/api/v1/database/available/` |
|
||||
| `GET` | [Download database(s) and associated dataset(s) as a zip file](/developer-docs/api/download-database-s-and-associated-dataset-s-as-a-zip-file) | `/api/v1/database/export/` |
|
||||
| `POST` | [Import database(s) with associated datasets](/developer-docs/api/import-database-s-with-associated-datasets) | `/api/v1/database/import/` |
|
||||
| `GET` | [Receive personal access tokens from OAuth2](/developer-docs/api/receive-personal-access-tokens-from-oauth2) | `/api/v1/database/oauth2/` |
|
||||
| `GET` | [Receive personal access tokens from OAuth2](/developer-docs/api/receive-personal-access-tokens-from-o-auth-2) | `/api/v1/database/oauth2/` |
|
||||
| `GET` | [Get related fields data (database-related-column-name)](/developer-docs/api/get-related-fields-data-database-related-column-name) | `/api/v1/database/related/{column_name}` |
|
||||
| `POST` | [Test a database connection](/developer-docs/api/test-a-database-connection) | `/api/v1/database/test_connection/` |
|
||||
| `POST` | [Upload a file and returns file metadata](/developer-docs/api/upload-a-file-and-returns-file-metadata) | `/api/v1/database/upload_metadata/` |
|
||||
@@ -197,13 +199,14 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>SQL Lab</strong> (6 endpoints) — Execute SQL queries and manage SQL Lab sessions.</summary>
|
||||
<summary><strong>SQL Lab</strong> (7 endpoints) — Execute SQL queries and manage SQL Lab sessions.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get the bootstrap data for SqlLab page](/developer-docs/api/get-the-bootstrap-data-for-sqllab-page) | `/api/v1/sqllab/` |
|
||||
| `GET` | [Get the bootstrap data for SqlLab page](/developer-docs/api/get-the-bootstrap-data-for-sql-lab-page) | `/api/v1/sqllab/` |
|
||||
| `POST` | [Estimate the SQL query execution cost](/developer-docs/api/estimate-the-sql-query-execution-cost) | `/api/v1/sqllab/estimate/` |
|
||||
| `POST` | [Execute a SQL query](/developer-docs/api/execute-a-sql-query) | `/api/v1/sqllab/execute/` |
|
||||
| `POST` | [Export SQL query results to CSV with streaming](/developer-docs/api/export-sql-query-results-to-csv-with-streaming) | `/api/v1/sqllab/export_streaming/` |
|
||||
| `GET` | [Export the SQL query results to a CSV](/developer-docs/api/export-the-sql-query-results-to-a-csv) | `/api/v1/sqllab/export/{client_id}/` |
|
||||
| `POST` | [Format SQL code](/developer-docs/api/format-sql-code) | `/api/v1/sqllab/format_sql/` |
|
||||
| `GET` | [Get the result of a SQL query execution](/developer-docs/api/get-the-result-of-a-sql-query-execution) | `/api/v1/sqllab/results/` |
|
||||
@@ -236,20 +239,21 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Datasources</strong> (1 endpoints) — Query datasource metadata and column values.</summary>
|
||||
<summary><strong>Datasources</strong> (2 endpoints) — Query datasource metadata and column values.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get possible values for a datasource column](/developer-docs/api/get-possible-values-for-a-datasource-column) | `/api/v1/datasource/{datasource_type}/{datasource_id}/column/{column_name}/values/` |
|
||||
| `POST` | [Validate a SQL expression against a datasource](/developer-docs/api/validate-a-sql-expression-against-a-datasource) | `/api/v1/datasource/{datasource_type}/{datasource_id}/validate_expression/` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Advanced Data Type</strong> (2 endpoints) — Endpoints for advanced data type operations and conversions.</summary>
|
||||
<summary><strong>Advanced Data Type</strong> (2 endpoints) — Advanced data type operations and conversions.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Return an AdvancedDataTypeResponse](/developer-docs/api/return-an-advanceddatatyperesponse) | `/api/v1/advanced_data_type/convert` |
|
||||
| `GET` | [Return an AdvancedDataTypeResponse](/developer-docs/api/return-an-advanced-data-type-response) | `/api/v1/advanced_data_type/convert` |
|
||||
| `GET` | [Return a list of available advanced data types](/developer-docs/api/return-a-list-of-available-advanced-data-types) | `/api/v1/advanced_data_type/types` |
|
||||
|
||||
</details>
|
||||
@@ -320,32 +324,32 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
#### Sharing & Embedding
|
||||
|
||||
<details>
|
||||
<summary><strong>Dashboard Permanent Link</strong> (2 endpoints) — Create and retrieve permanent links to dashboard states.</summary>
|
||||
<summary><strong>Dashboard Permanent Link</strong> (2 endpoints) — Permanent links to dashboard states.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | [Create a new dashboard's permanent link](/developer-docs/api/create-a-new-dashboard-s-permanent-link) | `/api/v1/dashboard/{pk}/permalink` |
|
||||
| `GET` | [Get dashboard's permanent link state](/developer-docs/api/get-dashboard-s-permanent-link-state) | `/api/v1/dashboard/permalink/{key}` |
|
||||
| `POST` | [Create a new dashboard's permanent link](/developer-docs/api/create-a-new-dashboards-permanent-link) | `/api/v1/dashboard/{pk}/permalink` |
|
||||
| `GET` | [Get dashboard's permanent link state](/developer-docs/api/get-dashboards-permanent-link-state) | `/api/v1/dashboard/permalink/{key}` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Explore Permanent Link</strong> (2 endpoints) — Create and retrieve permanent links to chart explore states.</summary>
|
||||
<summary><strong>Explore Permanent Link</strong> (2 endpoints) — Permanent links to chart explore states.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | [Create a new permanent link (explore-permalink)](/developer-docs/api/create-a-new-permanent-link-explore-permalink) | `/api/v1/explore/permalink` |
|
||||
| `GET` | [Get chart's permanent link state](/developer-docs/api/get-chart-s-permanent-link-state) | `/api/v1/explore/permalink/{key}` |
|
||||
| `GET` | [Get chart's permanent link state](/developer-docs/api/get-charts-permanent-link-state) | `/api/v1/explore/permalink/{key}` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>SQL Lab Permanent Link</strong> (2 endpoints) — Create and retrieve permanent links to SQL Lab states.</summary>
|
||||
<summary><strong>SQL Lab Permanent Link</strong> (2 endpoints) — Permanent links to SQL Lab states.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | [Create a new permanent link (sqllab-permalink)](/developer-docs/api/create-a-new-permanent-link-sqllab-permalink) | `/api/v1/sqllab/permalink` |
|
||||
| `GET` | [Get permanent link state for SQLLab editor.](/developer-docs/api/get-permanent-link-state-for-sqllab-editor) | `/api/v1/sqllab/permalink/{key}` |
|
||||
| `GET` | [Get permanent link state for SQLLab editor.](/developer-docs/api/get-permanent-link-state-for-sql-lab-editor) | `/api/v1/sqllab/permalink/{key}` |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -363,10 +367,10 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | [Create a dashboard's filter state](/developer-docs/api/create-a-dashboard-s-filter-state) | `/api/v1/dashboard/{pk}/filter_state` |
|
||||
| `DELETE` | [Delete a dashboard's filter state value](/developer-docs/api/delete-a-dashboard-s-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
| `GET` | [Get a dashboard's filter state value](/developer-docs/api/get-a-dashboard-s-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
| `PUT` | [Update a dashboard's filter state value](/developer-docs/api/update-a-dashboard-s-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
| `POST` | [Create a dashboard's filter state](/developer-docs/api/create-a-dashboards-filter-state) | `/api/v1/dashboard/{pk}/filter_state` |
|
||||
| `DELETE` | [Delete a dashboard's filter state value](/developer-docs/api/delete-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
| `GET` | [Get a dashboard's filter state value](/developer-docs/api/get-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
| `PUT` | [Update a dashboard's filter state value](/developer-docs/api/update-a-dashboards-filter-state-value) | `/api/v1/dashboard/{pk}/filter_state/{key}` |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -406,16 +410,17 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
#### Security & Access Control
|
||||
|
||||
<details>
|
||||
<summary><strong>Security Roles</strong> (10 endpoints) — Manage security roles and their permissions.</summary>
|
||||
<summary><strong>Security Roles</strong> (11 endpoints) — Manage security roles and their permissions.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security roles](/developer-docs/api/get-security-roles) | `/api/v1/security/roles/` |
|
||||
| `POST` | [Create security roles](/developer-docs/api/create-security-roles) | `/api/v1/security/roles/` |
|
||||
| `GET` | [Get security roles info](/developer-docs/api/get-security-roles-info) | `/api/v1/security/roles/_info` |
|
||||
| `GET` | [Get security roles info](/developer-docs/api/get-security-roles-info) | `/api/v1/security/roles/_info` |
|
||||
| `DELETE` | [Delete security roles by pk](/developer-docs/api/delete-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
|
||||
| `GET` | [Get security roles by pk](/developer-docs/api/get-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
|
||||
| `PUT` | [Update security roles by pk](/developer-docs/api/update-security-roles-by-pk) | `/api/v1/security/roles/{pk}` |
|
||||
| `PUT` | [Update security roles by role_id groups](/developer-docs/api/update-security-roles-by-role-id-groups) | `/api/v1/security/roles/{role_id}/groups` |
|
||||
| `POST` | [Create security roles by role_id permissions](/developer-docs/api/create-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions` |
|
||||
| `GET` | [Get security roles by role_id permissions](/developer-docs/api/get-security-roles-by-role-id-permissions) | `/api/v1/security/roles/{role_id}/permissions/` |
|
||||
| `PUT` | [Update security roles by role_id users](/developer-docs/api/update-security-roles-by-role-id-users) | `/api/v1/security/roles/{role_id}/users` |
|
||||
@@ -430,7 +435,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security users](/developer-docs/api/get-security-users) | `/api/v1/security/users/` |
|
||||
| `POST` | [Create security users](/developer-docs/api/create-security-users) | `/api/v1/security/users/` |
|
||||
| `GET` | [Get security users info](/developer-docs/api/get-security-users-info) | `/api/v1/security/users/_info` |
|
||||
| `GET` | [Get security users info](/developer-docs/api/get-security-users-info) | `/api/v1/security/users/_info` |
|
||||
| `DELETE` | [Delete security users by pk](/developer-docs/api/delete-security-users-by-pk) | `/api/v1/security/users/{pk}` |
|
||||
| `GET` | [Get security users by pk](/developer-docs/api/get-security-users-by-pk) | `/api/v1/security/users/{pk}` |
|
||||
| `PUT` | [Update security users by pk](/developer-docs/api/update-security-users-by-pk) | `/api/v1/security/users/{pk}` |
|
||||
@@ -443,7 +448,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security permissions](/developer-docs/api/get-security-permissions) | `/api/v1/security/permissions/` |
|
||||
| `GET` | [Get security permissions info](/developer-docs/api/get-security-permissions-info) | `/api/v1/security/permissions/_info` |
|
||||
| `GET` | [Get security permissions info](/developer-docs/api/get-security-permissions-info) | `/api/v1/security/permissions/_info` |
|
||||
| `GET` | [Get security permissions by pk](/developer-docs/api/get-security-permissions-by-pk) | `/api/v1/security/permissions/{pk}` |
|
||||
|
||||
</details>
|
||||
@@ -455,7 +460,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security resources](/developer-docs/api/get-security-resources) | `/api/v1/security/resources/` |
|
||||
| `POST` | [Create security resources](/developer-docs/api/create-security-resources) | `/api/v1/security/resources/` |
|
||||
| `GET` | [Get security resources info](/developer-docs/api/get-security-resources-info) | `/api/v1/security/resources/_info` |
|
||||
| `GET` | [Get security resources info](/developer-docs/api/get-security-resources-info) | `/api/v1/security/resources/_info` |
|
||||
| `DELETE` | [Delete security resources by pk](/developer-docs/api/delete-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
|
||||
| `GET` | [Get security resources by pk](/developer-docs/api/get-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
|
||||
| `PUT` | [Update security resources by pk](/developer-docs/api/update-security-resources-by-pk) | `/api/v1/security/resources/{pk}` |
|
||||
@@ -463,13 +468,13 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Security Permissions on Resources (View Menus)</strong> (6 endpoints) — Manage permission-resource mappings.</summary>
|
||||
<summary><strong>Security Permissions on Resources (View Menus)</strong> (6 endpoints) — Permission-resource mappings.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security permissions resources](/developer-docs/api/get-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
|
||||
| `POST` | [Create security permissions resources](/developer-docs/api/create-security-permissions-resources) | `/api/v1/security/permissions-resources/` |
|
||||
| `GET` | [Get security permissions resources info](/developer-docs/api/get-security-permissions-resources-info) | `/api/v1/security/permissions-resources/_info` |
|
||||
| `GET` | [Get security permissions resources info](/developer-docs/api/get-security-permissions-resources-info) | `/api/v1/security/permissions-resources/_info` |
|
||||
| `DELETE` | [Delete security permissions resources by pk](/developer-docs/api/delete-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
|
||||
| `GET` | [Get security permissions resources by pk](/developer-docs/api/get-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
|
||||
| `PUT` | [Update security permissions resources by pk](/developer-docs/api/update-security-permissions-resources-by-pk) | `/api/v1/security/permissions-resources/{pk}` |
|
||||
@@ -477,7 +482,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Row Level Security</strong> (8 endpoints) — Manage row-level security rules for data access control.</summary>
|
||||
<summary><strong>Row Level Security</strong> (8 endpoints) — Manage row-level security rules for data access.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
@@ -495,7 +500,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
#### Import/Export & Administration
|
||||
|
||||
<details>
|
||||
<summary><strong>Import/export</strong> (2 endpoints) — Import and export Superset assets (dashboards, charts, databases).</summary>
|
||||
<summary><strong>Import/export</strong> (2 endpoints) — Import and export Superset assets.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
@@ -528,11 +533,12 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
#### User & System
|
||||
|
||||
<details>
|
||||
<summary><strong>Current User</strong> (2 endpoints) — Get information about the currently authenticated user.</summary>
|
||||
<summary><strong>Current User</strong> (3 endpoints) — Get information about the authenticated user.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get the user object](/developer-docs/api/get-the-user-object) | `/api/v1/me/` |
|
||||
| `PUT` | [Update the current user](/developer-docs/api/update-the-current-user) | `/api/v1/me/` |
|
||||
| `GET` | [Get the user roles](/developer-docs/api/get-the-user-roles) | `/api/v1/me/roles/` |
|
||||
|
||||
</details>
|
||||
@@ -578,7 +584,23 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get api by version openapi](/developer-docs/api/get-api-by-version-openapi) | `/api/{version}/_openapi` |
|
||||
| `GET` | [Get api by version openapi](/developer-docs/api/get-api-by-version-openapi) | `/api/{version}/_openapi` |
|
||||
|
||||
</details>
|
||||
|
||||
#### Other
|
||||
|
||||
<details>
|
||||
<summary><strong>Security Groups</strong> (6 endpoints) — Endpoints related to Security Groups.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security groups](/developer-docs/api/get-security-groups) | `/api/v1/security/groups/` |
|
||||
| `POST` | [Create security groups](/developer-docs/api/create-security-groups) | `/api/v1/security/groups/` |
|
||||
| `GET` | [Get security groups info](/developer-docs/api/get-security-groups-info) | `/api/v1/security/groups/_info` |
|
||||
| `DELETE` | [Delete security groups by pk](/developer-docs/api/delete-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
|
||||
| `GET` | [Get security groups by pk](/developer-docs/api/get-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
|
||||
| `PUT` | [Update security groups by pk](/developer-docs/api/update-security-groups-by-pk) | `/api/v1/security/groups/{pk}` |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -590,7 +612,7 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
| `DELETE` | [Bulk delete themes](/developer-docs/api/bulk-delete-themes) | `/api/v1/theme/` |
|
||||
| `GET` | [Get a list of themes](/developer-docs/api/get-a-list-of-themes) | `/api/v1/theme/` |
|
||||
| `POST` | [Create a theme](/developer-docs/api/create-a-theme) | `/api/v1/theme/` |
|
||||
| `GET` | [Get metadata information about this API resource (theme-info)](/developer-docs/api/get-metadata-information-about-this-api-resource-theme-info) | `/api/v1/theme/_info` |
|
||||
| `GET` | [Get metadata information about this API resource (theme--info)](/developer-docs/api/get-metadata-information-about-this-api-resource-theme-info) | `/api/v1/theme/_info` |
|
||||
| `DELETE` | [Delete a theme](/developer-docs/api/delete-a-theme) | `/api/v1/theme/{pk}` |
|
||||
| `GET` | [Get a theme](/developer-docs/api/get-a-theme) | `/api/v1/theme/{pk}` |
|
||||
| `PUT` | [Update a theme](/developer-docs/api/update-a-theme) | `/api/v1/theme/{pk}` |
|
||||
@@ -604,6 +626,22 @@ curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>UserRegistrationsRestAPI</strong> (8 endpoints) — Endpoints related to UserRegistrationsRestAPI.</summary>
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | [Get security user registrations](/developer-docs/api/get-security-user-registrations) | `/api/v1/security/user_registrations/` |
|
||||
| `POST` | [Create security user registrations](/developer-docs/api/create-security-user-registrations) | `/api/v1/security/user_registrations/` |
|
||||
| `GET` | [Get security user registrations info](/developer-docs/api/get-security-user-registrations-info) | `/api/v1/security/user_registrations/_info` |
|
||||
| `DELETE` | [Delete security user registrations by pk](/developer-docs/api/delete-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
|
||||
| `GET` | [Get security user registrations by pk](/developer-docs/api/get-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
|
||||
| `PUT` | [Update security user registrations by pk](/developer-docs/api/update-security-user-registrations-by-pk) | `/api/v1/security/user_registrations/{pk}` |
|
||||
| `GET` | [Get distinct values from field data (security-user-registrations-distinct-column-name)](/developer-docs/api/get-distinct-values-from-field-data-security-user-registrations-distinct-column-name) | `/api/v1/security/user_registrations/distinct/{column_name}` |
|
||||
| `GET` | [Get related fields data (security-user-registrations-related-column-name)](/developer-docs/api/get-related-fields-data-security-user-registrations-related-column-name) | `/api/v1/security/user_registrations/related/{column_name}` |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Additional Resources
|
||||
|
||||
@@ -156,7 +156,7 @@ function SelectFilters() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { DropdownContainer } from '@superset/components';
|
||||
import { DropdownContainer } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -186,7 +186,7 @@ function JustifyAlign() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Flex } from '@superset/components';
|
||||
import { Flex } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -181,7 +181,7 @@ function AlignmentDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import Grid from '@superset/components';
|
||||
import { Grid } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -128,7 +128,7 @@ function RightSidebar() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Layout } from '@superset/components';
|
||||
import { Layout } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -163,7 +163,7 @@ function FullMetadata() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import MetadataBar from '@superset/components';
|
||||
import { MetadataBar } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -157,7 +157,7 @@ function SpaceSizes() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Space } from '@superset/components';
|
||||
import { Space } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -300,7 +300,7 @@ function LoadingTable() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Table } from '@superset/components';
|
||||
import { Table } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -23,7 +23,16 @@ sidebar_position: 0
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# Superset Design System
|
||||
import { ComponentIndex } from '@site/src/components/ui-components';
|
||||
import componentData from '@site/static/data/components.json';
|
||||
|
||||
# UI Components
|
||||
|
||||
<ComponentIndex data={componentData} />
|
||||
|
||||
---
|
||||
|
||||
## Design System
|
||||
|
||||
A design system is a complete set of standards intended to manage design at scale using reusable components and patterns.
|
||||
|
||||
@@ -35,19 +44,6 @@ The Superset Design System uses [Atomic Design](https://bradfrost.com/blog/post/
|
||||
|
||||
<img src="/img/atomic-design.png" alt="Atoms = Foundations, Molecules = Components, Organisms = Patterns, Templates = Templates, Pages / Screens = Features" style={{maxWidth: '100%'}} />
|
||||
|
||||
---
|
||||
|
||||
## Component Library
|
||||
|
||||
Interactive documentation for Superset's UI component library. **53 components** documented across 2 categories.
|
||||
|
||||
### [Core Components](./ui/)
|
||||
46 components — Buttons, inputs, modals, selects, and other fundamental UI elements.
|
||||
|
||||
### [Layout Components](./design-system/)
|
||||
7 components — Grid, Layout, Table, Flex, Space, and container components for page structure.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
All components are exported from `@superset-ui/core/components`:
|
||||
|
||||
@@ -204,7 +204,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { AutoComplete } from '@superset/components';
|
||||
import { AutoComplete } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -129,7 +129,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Avatar } from '@superset/components';
|
||||
import { Avatar } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -149,7 +149,7 @@ function ColorGallery() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Badge } from '@superset/components';
|
||||
import { Badge } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -82,7 +82,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Breadcrumb } from '@superset/components';
|
||||
import { Breadcrumb } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -43,7 +43,7 @@ The Button component from Superset's UI library.
|
||||
<StoryWithControls
|
||||
component="Button"
|
||||
props={{
|
||||
buttonStyle: "default",
|
||||
buttonStyle: "primary",
|
||||
buttonSize: "default",
|
||||
children: "Button!"
|
||||
}}
|
||||
@@ -111,7 +111,7 @@ Edit the code below to experiment with the component:
|
||||
function Demo() {
|
||||
return (
|
||||
<Button
|
||||
buttonStyle="default"
|
||||
buttonStyle="primary"
|
||||
buttonSize="default"
|
||||
>
|
||||
Button!
|
||||
@@ -124,14 +124,14 @@ function Demo() {
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `buttonStyle` | `string` | `"default"` | The style variant of the button. |
|
||||
| `buttonStyle` | `string` | `"primary"` | The style variant of the button. |
|
||||
| `buttonSize` | `string` | `"default"` | The size of the button. |
|
||||
| `children` | `string` | `"Button!"` | The button text or content. |
|
||||
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Button } from '@superset/components';
|
||||
import { Button } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { ButtonGroup } from '@superset/components';
|
||||
import { ButtonGroup } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -68,7 +68,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { CachedLabel } from '@superset/components';
|
||||
import { CachedLabel } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -131,7 +131,7 @@ function CardStates() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Card } from '@superset/components';
|
||||
import { Card } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -130,7 +130,7 @@ function SelectAllDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Checkbox } from '@superset/components';
|
||||
import { Checkbox } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -95,7 +95,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Collapse } from '@superset/components';
|
||||
import { Collapse } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -99,7 +99,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { DatePicker } from '@superset/components';
|
||||
import { DatePicker } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -133,7 +133,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Divider } from '@superset/components';
|
||||
import { Divider } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -161,7 +161,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { EditableTitle } from '@superset/components';
|
||||
import { EditableTitle } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -136,7 +136,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { EmptyState } from '@superset/components';
|
||||
import { EmptyState } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -85,7 +85,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { FaveStar } from '@superset/components';
|
||||
import { FaveStar } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -95,7 +95,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { IconButton } from '@superset/components';
|
||||
import { IconButton } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -241,7 +241,7 @@ function IconWithText() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Icons } from '@superset/components';
|
||||
import { Icons } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -89,7 +89,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { IconTooltip } from '@superset/components';
|
||||
import { IconTooltip } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -95,7 +95,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { InfoTooltip } from '@superset/components';
|
||||
import { InfoTooltip } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -151,7 +151,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Input } from '@superset/components';
|
||||
import { Input } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -94,7 +94,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Label } from '@superset/components';
|
||||
import { Label } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -106,7 +106,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { List } from '@superset/components';
|
||||
import { List } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -121,7 +121,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { ListViewCard } from '@superset/components';
|
||||
import { ListViewCard } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -176,7 +176,7 @@ function ContextualDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Loading } from '@superset/components';
|
||||
import { Loading } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -163,7 +163,7 @@ function MenuWithIcons() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Menu } from '@superset/components';
|
||||
import { Menu } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -196,7 +196,7 @@ function ConfirmationDialogs() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Modal } from '@superset/components';
|
||||
import { Modal } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -181,7 +181,7 @@ function DraggableModal() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { ModalTrigger } from '@superset/components';
|
||||
import { ModalTrigger } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -188,7 +188,7 @@ function RichPopover() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Popover } from '@superset/components';
|
||||
import { Popover } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -195,7 +195,7 @@ function CustomColors() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { ProgressBar } from '@superset/components';
|
||||
import { ProgressBar } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -126,7 +126,7 @@ function VerticalDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Radio } from '@superset/components';
|
||||
import { Radio } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -74,7 +74,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { SafeMarkdown } from '@superset/components';
|
||||
import { SafeMarkdown } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -297,7 +297,7 @@ function OneLineDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Select } from '@superset/components';
|
||||
import { Select } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -129,7 +129,7 @@ function Demo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Skeleton } from '@superset/components';
|
||||
import { Skeleton } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -242,7 +242,7 @@ function VerticalDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Slider } from '@superset/components';
|
||||
import { Slider } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -261,7 +261,7 @@ function DotAndSmall() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Steps } from '@superset/components';
|
||||
import { Steps } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -182,7 +182,7 @@ function SettingsPanel() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Switch } from '@superset/components';
|
||||
import { Switch } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -52,12 +52,6 @@ function Demo() {
|
||||
|
||||
|
||||
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { TableCollection } from '@superset/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
:::tip[Improve this page]
|
||||
|
||||
@@ -283,7 +283,7 @@ function SortingDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { TableView } from '@superset/components';
|
||||
import { TableView } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -212,7 +212,7 @@ function IconTabs() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Tabs } from '@superset/components';
|
||||
import { Tabs } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -161,7 +161,7 @@ function StartStop() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Timer } from '@superset/components';
|
||||
import { Timer } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -160,7 +160,7 @@ function Triggers() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Tooltip } from '@superset/components';
|
||||
import { Tooltip } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -257,7 +257,7 @@ function LinesAndIcons() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Tree } from '@superset/components';
|
||||
import { Tree } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -275,7 +275,7 @@ function TreeLinesDemo() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { TreeSelect } from '@superset/components';
|
||||
import { TreeSelect } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -225,7 +225,7 @@ function TextStyles() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Typography } from '@superset/components';
|
||||
import { Typography } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -115,7 +115,7 @@ function CustomTitle() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { UnsavedChangesModal } from '@superset/components';
|
||||
import { UnsavedChangesModal } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -125,7 +125,7 @@ function DragDrop() {
|
||||
## Import
|
||||
|
||||
```tsx
|
||||
import { Upload } from '@superset/components';
|
||||
import { Upload } from '@superset-ui/core/components';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -43,7 +43,7 @@ Extensions can provide:
|
||||
|
||||
## UI Components for Extensions
|
||||
|
||||
Extension developers have access to pre-built UI components via `@apache-superset/core/components`. Browse all available components on the [UI Components](/docs/components/) page and filter by **Extension Compatible** to see components available to extensions.
|
||||
Extension developers have access to pre-built UI components via `@apache-superset/core/components`. Browse all available components on the [UI Components](/developer-docs/components/) page and filter by **Extension Compatible** to see components available to extensions.
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ Access to dashboards is managed via owners and permissions. Non-owner access can
|
||||
through dataset permissions or dashboard-level roles (using the `DASHBOARD_RBAC` feature flag).
|
||||
|
||||
For detailed information on configuring dashboard access, see the
|
||||
[Dashboard Access Control](/admin-docs/security/security#dashboard-access-control) section in the
|
||||
[Dashboard Access Control](/admin-docs/security/#dashboard-access-control) section in the
|
||||
Security documentation.
|
||||
|
||||
<img src={useBaseUrl("/img/tutorial/tutorial_dashboard_access.png" )} />
|
||||
|
||||
@@ -178,7 +178,7 @@ if (!versionsConfig.admin_docs.disabled) {
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
to: '/admin-docs/security/security',
|
||||
to: '/admin-docs/security/',
|
||||
activeBaseRegex: '^/admin-docs/security/',
|
||||
},
|
||||
],
|
||||
@@ -227,35 +227,28 @@ if (!versionsConfig.developer_docs.disabled && !versionsConfig.developer_docs.hi
|
||||
});
|
||||
}
|
||||
|
||||
// Docusaurus Faster: Rspack bundler, SWC transpilation, and other build
|
||||
// optimizations. Only enabled for local development — CI runners (GitHub
|
||||
// Actions, Netlify) have ~8GB RAM and these features push memory usage over
|
||||
// the limit. See https://docusaurus.io/blog/releases/3.6#docusaurus-faster
|
||||
const isCI = process.env.CI === 'true';
|
||||
|
||||
const config: Config = {
|
||||
...(!isCI && {
|
||||
future: {
|
||||
v4: {
|
||||
removeLegacyPostBuildHeadAttribute: true,
|
||||
// Disabled: CSS cascade layers change specificity and cause antd
|
||||
// styles (from Storybook component pages) to override theme styles
|
||||
useCssCascadeLayers: false,
|
||||
},
|
||||
experimental_faster: {
|
||||
swcJsLoader: true,
|
||||
swcJsMinimizer: true,
|
||||
swcHtmlMinimizer: true,
|
||||
lightningCssMinimizer: true,
|
||||
rspackBundler: true,
|
||||
mdxCrossCompilerCache: true,
|
||||
rspackPersistentCache: true,
|
||||
// SSG worker threads spawn parallel Node processes, each consuming
|
||||
// significant memory. Disabled to keep total usage reasonable.
|
||||
ssgWorkerThreads: false,
|
||||
},
|
||||
future: {
|
||||
v4: {
|
||||
removeLegacyPostBuildHeadAttribute: true,
|
||||
// Disabled: CSS cascade layers change specificity and cause antd
|
||||
// styles (from Storybook component pages) to override theme styles
|
||||
useCssCascadeLayers: false,
|
||||
},
|
||||
}),
|
||||
faster: {
|
||||
swcJsLoader: false,
|
||||
swcJsMinimizer: true,
|
||||
swcHtmlMinimizer: true,
|
||||
lightningCssMinimizer: true,
|
||||
rspackBundler: true,
|
||||
mdxCrossCompilerCache: true,
|
||||
rspackPersistentCache: true,
|
||||
// SSG worker threads spawn parallel Node processes, each consuming
|
||||
// significant memory. Disabled to keep total usage reasonable.
|
||||
ssgWorkerThreads: false,
|
||||
},
|
||||
},
|
||||
title: 'Superset',
|
||||
tagline:
|
||||
'Apache Superset is a modern data exploration and visualization platform',
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"version:remove:components": "node scripts/manage-versions.mjs remove components"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.1.1",
|
||||
"@ant-design/icons": "^6.2.0",
|
||||
"@docusaurus/core": "^3.10.0",
|
||||
"@docusaurus/faster": "^3.10.0",
|
||||
"@docusaurus/plugin-client-redirects": "^3.10.0",
|
||||
@@ -68,9 +68,9 @@
|
||||
"@storybook/theming": "^8.6.15",
|
||||
"@superset-ui/core": "^0.20.4",
|
||||
"@swc/core": "^1.15.30",
|
||||
"antd": "^6.3.6",
|
||||
"baseline-browser-mapping": "^2.10.21",
|
||||
"caniuse-lite": "^1.0.30001790",
|
||||
"antd": "^6.3.7",
|
||||
"baseline-browser-mapping": "^2.10.23",
|
||||
"caniuse-lite": "^1.0.30001791",
|
||||
"docusaurus-plugin-openapi-docs": "^5.0.1",
|
||||
"docusaurus-theme-openapi-docs": "^5.0.1",
|
||||
"js-yaml": "^4.1.1",
|
||||
|
||||
@@ -185,6 +185,76 @@ const SKIP_STORIES = [
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Collect the set of value names exported from a barrel file, following
|
||||
* `export * from './X'` re-exports one level deep. Used to verify that a
|
||||
* component the docs claim is importable is actually re-exported from the
|
||||
* public package entry point.
|
||||
*/
|
||||
function collectBarrelExports(barrelPath, visited = new Set()) {
|
||||
const exports = new Set();
|
||||
if (!fs.existsSync(barrelPath) || visited.has(barrelPath)) return exports;
|
||||
visited.add(barrelPath);
|
||||
|
||||
const content = fs.readFileSync(barrelPath, 'utf8');
|
||||
|
||||
for (const m of content.matchAll(/export\s+\{([\s\S]*?)\}(?:\s+from\s+['"][^'"]+['"])?/g)) {
|
||||
for (const part of m[1].split(',')) {
|
||||
const cleaned = part.trim().replace(/^type\s+/, '');
|
||||
if (!cleaned) continue;
|
||||
const asMatch = cleaned.match(/(?:^|\s)as\s+([A-Za-z_]\w*)\s*$/);
|
||||
if (asMatch) {
|
||||
exports.add(asMatch[1]);
|
||||
} else {
|
||||
const plain = cleaned.match(/^([A-Za-z_]\w*)\s*$/);
|
||||
if (plain) exports.add(plain[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const m of content.matchAll(
|
||||
/export\s+(?:const|let|var|function|class)\s+([A-Za-z_]\w*)/g
|
||||
)) {
|
||||
exports.add(m[1]);
|
||||
}
|
||||
|
||||
for (const m of content.matchAll(/export\s+\*\s+from\s+['"]([^'"]+)['"]/g)) {
|
||||
const target = m[1];
|
||||
if (!target.startsWith('.')) continue;
|
||||
const baseDir = path.dirname(barrelPath);
|
||||
const candidates = [
|
||||
path.resolve(baseDir, `${target}.ts`),
|
||||
path.resolve(baseDir, `${target}.tsx`),
|
||||
path.resolve(baseDir, target, 'index.ts'),
|
||||
path.resolve(baseDir, target, 'index.tsx'),
|
||||
];
|
||||
const resolved = candidates.find(p => fs.existsSync(p));
|
||||
if (resolved) {
|
||||
for (const name of collectBarrelExports(resolved, visited)) {
|
||||
exports.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
const SOURCE_PUBLIC_EXPORTS = new Map();
|
||||
function getPublicExports(sourceConfig) {
|
||||
if (SOURCE_PUBLIC_EXPORTS.has(sourceConfig)) {
|
||||
return SOURCE_PUBLIC_EXPORTS.get(sourceConfig);
|
||||
}
|
||||
const sourceDir = path.join(FRONTEND_DIR, sourceConfig.path);
|
||||
const candidates = [
|
||||
path.join(sourceDir, 'index.ts'),
|
||||
path.join(sourceDir, 'index.tsx'),
|
||||
];
|
||||
const barrel = candidates.find(p => fs.existsSync(p));
|
||||
const result = barrel ? collectBarrelExports(barrel) : null;
|
||||
SOURCE_PUBLIC_EXPORTS.set(sourceConfig, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find all story files in a directory
|
||||
*/
|
||||
@@ -1048,6 +1118,28 @@ function generateMDX(component, storyContent) {
|
||||
// Use resolved import path if available, otherwise fall back to source config
|
||||
const componentImportPath = resolvedImportPath || sourceConfig.importPrefix;
|
||||
|
||||
// The displayed import in user docs should reflect the public package path,
|
||||
// not the internal storybook alias.
|
||||
const docImportPath = sourceConfig.importPrefix.startsWith('@superset/')
|
||||
? sourceConfig.docImportPrefix
|
||||
: componentImportPath;
|
||||
|
||||
// When the source uses the internal storybook alias, the public package
|
||||
// re-exports components as named exports (e.g. `export { default as Foo }`),
|
||||
// so users must use named imports even when the story uses a default import.
|
||||
const useDefaultImport =
|
||||
isDefaultExport && !sourceConfig.importPrefix.startsWith('@superset/');
|
||||
|
||||
// Only render the import snippet if the component is actually re-exported
|
||||
// from the public package barrel; otherwise the snippet would mislead users
|
||||
// copy-pasting it (e.g. TableCollection, which has a story but is not
|
||||
// re-exported from `@superset-ui/core/components`).
|
||||
const publicExports = sourceConfig.importPrefix.startsWith('@superset/')
|
||||
? getPublicExports(sourceConfig)
|
||||
: null;
|
||||
const isPubliclyExported =
|
||||
!publicExports || publicExports.has(componentName);
|
||||
|
||||
// Determine component description based on source
|
||||
const defaultDesc = sourceConfig.category === 'ui'
|
||||
? `The ${componentName} component from Superset's UI library.`
|
||||
@@ -1134,13 +1226,13 @@ ${Object.keys(args).length > 0 ? `## Props
|
||||
|------|------|---------|-------------|
|
||||
${propsTable}` : ''}
|
||||
|
||||
## Import
|
||||
${isPubliclyExported ? `## Import
|
||||
|
||||
\`\`\`tsx
|
||||
${isDefaultExport ? `import ${componentName} from '${componentImportPath}';` : `import { ${componentName} } from '${componentImportPath}';`}
|
||||
${useDefaultImport ? `import ${componentName} from '${docImportPath}';` : `import { ${componentName} } from '${docImportPath}';`}
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
---` : '---'}
|
||||
|
||||
:::tip[Improve this page]
|
||||
This documentation is auto-generated from the component's Storybook story.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"generated": "2026-02-24T20:28:17.222Z",
|
||||
"generated": "2026-04-25T02:18:43.905Z",
|
||||
"statistics": {
|
||||
"totalDatabases": 72,
|
||||
"withDocumentation": 72,
|
||||
"withConnectionString": 72,
|
||||
"totalDatabases": 73,
|
||||
"withDocumentation": 73,
|
||||
"withConnectionString": 73,
|
||||
"withDrivers": 36,
|
||||
"withAuthMethods": 4,
|
||||
"supportsJoins": 68,
|
||||
"supportsSubqueries": 69,
|
||||
"withAuthMethods": 5,
|
||||
"supportsJoins": 69,
|
||||
"supportsSubqueries": 70,
|
||||
"supportsDynamicSchema": 15,
|
||||
"supportsCatalog": 9,
|
||||
"averageScore": 31,
|
||||
@@ -23,6 +23,7 @@
|
||||
"Amazon Athena",
|
||||
"Google BigQuery",
|
||||
"Databend",
|
||||
"Google Datastore",
|
||||
"IBM Db2",
|
||||
"Denodo",
|
||||
"Dremio",
|
||||
@@ -177,10 +178,12 @@
|
||||
],
|
||||
"Cloud - Google": [
|
||||
"Google BigQuery",
|
||||
"Google Datastore",
|
||||
"Google Sheets"
|
||||
],
|
||||
"Search & NoSQL": [
|
||||
"Couchbase",
|
||||
"Google Datastore",
|
||||
"Amazon DynamoDB",
|
||||
"Elasticsearch",
|
||||
"MongoDB",
|
||||
@@ -751,14 +754,14 @@
|
||||
"OPEN_SOURCE"
|
||||
],
|
||||
"pypi_packages": [
|
||||
"clickhouse-connect>=0.6.8"
|
||||
"clickhouse-connect>=0.13.0"
|
||||
],
|
||||
"connection_string": "clickhousedb://{username}:{password}@{host}:{port}/{database}",
|
||||
"default_port": 8123,
|
||||
"drivers": [
|
||||
{
|
||||
"name": "clickhouse-connect (Recommended)",
|
||||
"pypi_package": "clickhouse-connect>=0.6.8",
|
||||
"pypi_package": "clickhouse-connect>=0.13.0",
|
||||
"connection_string": "clickhousedb://{username}:{password}@{host}:{port}/{database}",
|
||||
"is_recommended": true,
|
||||
"notes": "Official ClickHouse Python driver with native protocol support."
|
||||
@@ -781,7 +784,7 @@
|
||||
"connection_string": "clickhousedb://localhost/default"
|
||||
}
|
||||
],
|
||||
"install_instructions": "echo \"clickhouse-connect>=0.6.8\" >> ./docker/requirements-local.txt",
|
||||
"install_instructions": "echo \"clickhouse-connect>=0.13.0\" >> ./docker/requirements-local.txt",
|
||||
"compatible_databases": [
|
||||
{
|
||||
"name": "ClickHouse Cloud",
|
||||
@@ -794,7 +797,7 @@
|
||||
"HOSTED_OPEN_SOURCE"
|
||||
],
|
||||
"pypi_packages": [
|
||||
"clickhouse-connect>=0.6.8"
|
||||
"clickhouse-connect>=0.13.0"
|
||||
],
|
||||
"connection_string": "clickhousedb://{username}:{password}@{host}:8443/{database}?secure=true",
|
||||
"parameters": {
|
||||
@@ -816,7 +819,7 @@
|
||||
"HOSTED_OPEN_SOURCE"
|
||||
],
|
||||
"pypi_packages": [
|
||||
"clickhouse-connect>=0.6.8"
|
||||
"clickhouse-connect>=0.13.0"
|
||||
],
|
||||
"connection_string": "clickhousedb://{username}:{password}@{host}/{database}?secure=true",
|
||||
"docs_url": "https://docs.altinity.com/"
|
||||
@@ -1013,7 +1016,7 @@
|
||||
"documentation": {
|
||||
"description": "CrateDB is a distributed SQL database for machine data and IoT workloads.",
|
||||
"logo": "cratedb.svg",
|
||||
"homepage_url": "https://crate.io/",
|
||||
"homepage_url": "https://cratedb.com",
|
||||
"categories": [
|
||||
"TIME_SERIES",
|
||||
"OPEN_SOURCE"
|
||||
@@ -1296,6 +1299,114 @@
|
||||
"query_cost_estimation": false,
|
||||
"sql_validation": false
|
||||
},
|
||||
"Google Datastore": {
|
||||
"engine": "google_datastore",
|
||||
"engine_name": "Google Datastore",
|
||||
"module": "datastore",
|
||||
"documentation": {
|
||||
"description": "Google Cloud Datastore is a highly scalable NoSQL database for your applications.",
|
||||
"logo": "datastore.png",
|
||||
"homepage_url": "https://cloud.google.com/datastore/",
|
||||
"categories": [
|
||||
"CLOUD_GCP",
|
||||
"SEARCH_NOSQL",
|
||||
"PROPRIETARY"
|
||||
],
|
||||
"pypi_packages": [
|
||||
"python-datastore-sqlalchemy"
|
||||
],
|
||||
"connection_string": "datastore://{project_id}/?database={database_id}",
|
||||
"authentication_methods": [
|
||||
{
|
||||
"name": "Service Account JSON",
|
||||
"description": "Upload service account credentials JSON or paste in Secure Extra",
|
||||
"secure_extra": {
|
||||
"credentials_info": {
|
||||
"type": "service_account",
|
||||
"project_id": "...",
|
||||
"private_key_id": "...",
|
||||
"private_key": "...",
|
||||
"client_email": "...",
|
||||
"client_id": "...",
|
||||
"auth_uri": "...",
|
||||
"token_uri": "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"notes": "Create a Service Account via GCP console with access to datastore datasets.",
|
||||
"docs_url": "https://github.com/splasky/Python-datastore-sqlalchemy",
|
||||
"custom_errors": [
|
||||
{
|
||||
"regex_name": "CONNECTION_DATABASE_PERMISSIONS_REGEX",
|
||||
"message_template": "Unable to connect. Verify that the following roles are set on the service account: \"Cloud Datastore Viewer\", \"Cloud Datastore User\", \"Cloud Datastore Creator\"",
|
||||
"error_type": "CONNECTION_DATABASE_PERMISSIONS_ERROR",
|
||||
"category": "Permissions",
|
||||
"description": "Insufficient permissions",
|
||||
"issue_codes": [
|
||||
1017
|
||||
]
|
||||
},
|
||||
{
|
||||
"regex_name": "TABLE_DOES_NOT_EXIST_REGEX",
|
||||
"message_template": "The table \"%(table)s\" does not exist. A valid table must be used to run this query.",
|
||||
"error_type": "TABLE_DOES_NOT_EXIST_ERROR",
|
||||
"category": "Query",
|
||||
"description": "Table not found",
|
||||
"issue_codes": [
|
||||
1003,
|
||||
1005
|
||||
]
|
||||
},
|
||||
{
|
||||
"regex_name": "COLUMN_DOES_NOT_EXIST_REGEX",
|
||||
"message_template": "We can't seem to resolve column \"%(column)s\" at line %(location)s.",
|
||||
"error_type": "COLUMN_DOES_NOT_EXIST_ERROR",
|
||||
"category": "Query",
|
||||
"description": "Column not found",
|
||||
"issue_codes": [
|
||||
1003,
|
||||
1004
|
||||
]
|
||||
},
|
||||
{
|
||||
"regex_name": "SCHEMA_DOES_NOT_EXIST_REGEX",
|
||||
"message_template": "The schema \"%(schema)s\" does not exist. A valid schema must be used to run this query.",
|
||||
"error_type": "SCHEMA_DOES_NOT_EXIST_ERROR",
|
||||
"category": "Query",
|
||||
"description": "Schema not found",
|
||||
"issue_codes": [
|
||||
1003,
|
||||
1016
|
||||
]
|
||||
},
|
||||
{
|
||||
"regex_name": "SYNTAX_ERROR_REGEX",
|
||||
"message_template": "Please check your query for syntax errors at or near \"%(syntax_error)s\". Then, try running your query again.",
|
||||
"error_type": "SYNTAX_ERROR",
|
||||
"category": "Query",
|
||||
"description": "SQL syntax error",
|
||||
"issue_codes": [
|
||||
1030
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"time_grains": {},
|
||||
"score": 0,
|
||||
"max_score": 0,
|
||||
"joins": true,
|
||||
"subqueries": true,
|
||||
"supports_dynamic_schema": false,
|
||||
"supports_catalog": false,
|
||||
"supports_dynamic_catalog": false,
|
||||
"ssh_tunneling": false,
|
||||
"query_cancelation": false,
|
||||
"supports_file_upload": false,
|
||||
"user_impersonation": false,
|
||||
"query_cost_estimation": false,
|
||||
"sql_validation": false
|
||||
},
|
||||
"IBM Db2": {
|
||||
"engine": "ibm_db2",
|
||||
"engine_name": "IBM Db2",
|
||||
@@ -4754,9 +4865,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "IAM Credentials (Serverless)",
|
||||
"description": "Use IAM-based credentials for Redshift Serverless",
|
||||
"requirements": "IAM role must have redshift-serverless:GetCredentials and redshift-serverless:GetWorkgroup permissions",
|
||||
"name": "IAM Role (Serverless)",
|
||||
"description": "Authenticate using the IAM role attached to the environment (EC2 instance profile, ECS task role, etc.). No credentials needed.",
|
||||
"requirements": "The attached IAM role must have redshift-serverless:GetCredentials and redshift-serverless:GetWorkgroup permissions.",
|
||||
"connection_string": "redshift+redshift_connector://",
|
||||
"engine_parameters": {
|
||||
"connect_args": {
|
||||
@@ -4768,6 +4879,26 @@
|
||||
"user": "IAMR:<superset iam role name>"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "IAM Access Key (Serverless)",
|
||||
"description": "Authenticate using explicit AWS access key and secret. Suitable for local development or CI environments without an attached IAM role.",
|
||||
"requirements": "The IAM user must have redshift-serverless:GetCredentials and redshift-serverless:GetWorkgroup permissions.",
|
||||
"connection_string": "redshift+redshift_connector://",
|
||||
"engine_parameters": {
|
||||
"connect_args": {
|
||||
"iam": true,
|
||||
"is_serverless": true,
|
||||
"serverless_acct_id": "<aws account number>",
|
||||
"serverless_work_group": "<redshift work group>",
|
||||
"database": "<database>",
|
||||
"host": "<endpoint>",
|
||||
"port": 5439,
|
||||
"region": "<aws region>",
|
||||
"access_key_id": "<aws access key id>",
|
||||
"secret_access_key": "<aws secret access key>"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"custom_errors": [
|
||||
|
||||
@@ -717,7 +717,7 @@ export default function Home(): JSX.Element {
|
||||
</span>
|
||||
</div>
|
||||
<img src="/img/community/line.png" alt="line" />
|
||||
<StyledButton className="default-button-theme" href="/docs/intro">
|
||||
<StyledButton className="default-button-theme" href="/user-docs/intro">
|
||||
Get Started
|
||||
</StyledButton>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
dependencies:
|
||||
"@algolia/client-common" "5.40.0"
|
||||
|
||||
"@ant-design/colors@^8.0.0", "@ant-design/colors@^8.0.1":
|
||||
"@ant-design/colors@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz"
|
||||
integrity sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==
|
||||
@@ -207,19 +207,19 @@
|
||||
resolved "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz"
|
||||
integrity sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==
|
||||
|
||||
"@ant-design/icons-svg@^4.4.0":
|
||||
"@ant-design/icons-svg@^4.4.2":
|
||||
version "4.4.2"
|
||||
resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz"
|
||||
integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==
|
||||
|
||||
"@ant-design/icons@^6.1.1":
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.1.1.tgz#068963d3de44ff7034dce32c9cec3ff7d343fe6b"
|
||||
integrity sha512-AMT4N2y++TZETNHiM77fs4a0uPVCJGuL5MTonk13Pvv7UN7sID1cNEZOc1qNqx6zLKAOilTEFAdAoAFKa0U//Q==
|
||||
"@ant-design/icons@^6.1.1", "@ant-design/icons@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.0.tgz#d5a1a364c3e795e06ef166ddf8171cfee9837150"
|
||||
integrity sha512-TQuzMZM+F/lpn3V1Z7NBxuc2VDo3z8VZduYvMZR5dUJP28gDlOZ6ar6B7vM9W13SxUT/FbNYBw2JoighULKgQA==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^8.0.0"
|
||||
"@ant-design/icons-svg" "^4.4.0"
|
||||
"@rc-component/util" "^1.3.0"
|
||||
"@ant-design/colors" "^8.0.1"
|
||||
"@ant-design/icons-svg" "^4.4.2"
|
||||
"@rc-component/util" "^1.10.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@ant-design/react-slick@~2.0.0":
|
||||
@@ -2986,10 +2986,10 @@
|
||||
"@rc-component/util" "^1.2.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/form@~1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.0.tgz#ed565337a69ebb6cfa20d1ad0dd58e443a71313a"
|
||||
integrity sha512-eUD5KKYnIZWmJwRA0vnyO/ovYUfHGU1svydY1OrqU5fw8Oz9Tdqvxvrlh0wl6xI/EW69dT7II49xpgOWzK3T5A==
|
||||
"@rc-component/form@~1.8.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.1.tgz#d811fb52df41bf72297938ebfe5cf4a4774588d4"
|
||||
integrity sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ==
|
||||
dependencies:
|
||||
"@rc-component/async-validator" "^5.1.0"
|
||||
"@rc-component/util" "^1.6.2"
|
||||
@@ -5482,10 +5482,10 @@ ansis@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/ansis/-/ansis-3.17.0.tgz#fa8d9c2a93fe7d1177e0c17f9eeb562a58a832d7"
|
||||
integrity sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==
|
||||
|
||||
antd@^6.3.6:
|
||||
version "6.3.6"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.6.tgz#e892b851cf45d62201d889fe9cac36f4d2412e5f"
|
||||
integrity sha512-zdCYjusrTUn4gNxEg4PH8MWlfuXYbKfuGOkjgZ0Rg6DpWbIVmG/MwvsZ5yvG6z3Y6UI/gzYpaQ82iTt4KdbeaA==
|
||||
antd@^6.3.7:
|
||||
version "6.3.7"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.7.tgz#620354ec04135356cbc5ce0a666871ddc73e4117"
|
||||
integrity sha512-WTHi4bHVNKpYXLHESzU0Tts7rRNQeL84Bph9dfI3Qw7mHbTulExDcYKNHny5CTXcrBBOpraXbU9miBAwUR5vaw==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^8.0.1"
|
||||
"@ant-design/cssinjs" "^2.1.2"
|
||||
@@ -5501,7 +5501,7 @@ antd@^6.3.6:
|
||||
"@rc-component/dialog" "~1.8.4"
|
||||
"@rc-component/drawer" "~1.4.2"
|
||||
"@rc-component/dropdown" "~1.0.2"
|
||||
"@rc-component/form" "~1.8.0"
|
||||
"@rc-component/form" "~1.8.1"
|
||||
"@rc-component/image" "~1.9.0"
|
||||
"@rc-component/input" "~1.1.2"
|
||||
"@rc-component/input-number" "~1.6.2"
|
||||
@@ -5794,10 +5794,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
baseline-browser-mapping@^2.10.21, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.21"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz#136f9f181ee0d7ca6e3edbf42d9559763d2c1141"
|
||||
integrity sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==
|
||||
baseline-browser-mapping@^2.10.23, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.23"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz#3a1a55d1a691a8c8d74688af7f1fd17eac23c184"
|
||||
integrity sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==
|
||||
|
||||
batch@0.6.1:
|
||||
version "0.6.1"
|
||||
@@ -6035,10 +6035,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001790:
|
||||
version "1.0.30001790"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz#04660c7de15f445d86dd10ac88a8936ac0698e45"
|
||||
integrity sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001791:
|
||||
version "1.0.30001791"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz#dfb93d85c40ad380c57123e72e10f3c575786b51"
|
||||
integrity sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==
|
||||
|
||||
ccount@^2.0.0:
|
||||
version "2.0.1"
|
||||
|
||||
@@ -52,7 +52,7 @@ attrs==25.3.0
|
||||
# referencing
|
||||
# requests-cache
|
||||
# trio
|
||||
authlib==1.6.7
|
||||
authlib==1.6.9
|
||||
# via fastmcp
|
||||
babel==2.17.0
|
||||
# via
|
||||
|
||||
434
superset-frontend/package-lock.json
generated
434
superset-frontend/package-lock.json
generated
@@ -115,7 +115,7 @@
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^17.0.2",
|
||||
"react-arborist": "^3.5.0",
|
||||
"react-checkbox-tree": "^2.0.1",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
@@ -151,7 +151,7 @@
|
||||
"use-query-params": "^2.2.2",
|
||||
"uuid": "^14.0.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.28.6",
|
||||
@@ -260,7 +260,7 @@
|
||||
"jest-html-reporter": "^4.4.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"js-yaml-loader": "^1.2.2",
|
||||
"jsdom": "^29.0.2",
|
||||
"jsdom": "^29.1.0",
|
||||
"lerna": "^9.0.4",
|
||||
"lightningcss": "^1.32.0",
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
@@ -463,14 +463,15 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.6.tgz",
|
||||
"integrity": "sha512-BXWCh8dHs9GOfpo/fWGDJtDmleta2VePN9rn6WQt3GjEbxzutVF4t0x2pmH+7dbMCLtuv3MlwqRsAuxlzFXqFg==",
|
||||
"version": "5.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
|
||||
"integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/css-calc": "^3.1.1",
|
||||
"@csstools/css-color-parser": "^4.0.2",
|
||||
"@asamuzakjp/generational-cache": "^1.0.1",
|
||||
"@csstools/css-calc": "^3.2.0",
|
||||
"@csstools/css-color-parser": "^4.1.0",
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
},
|
||||
@@ -479,12 +480,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/dom-selector": {
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.7.tgz",
|
||||
"integrity": "sha512-d2BgqDUOS1Hfp4IzKUZqCNz+Kg3Y88AkaBvJK/ZVSQPU1f7OpPNi7nQTH6/oI47Dkdg+Z3e8Yp6ynOu4UMINAQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz",
|
||||
"integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/generational-cache": "^1.0.1",
|
||||
"@asamuzakjp/nwsapi": "^2.3.9",
|
||||
"bidi-js": "^1.0.3",
|
||||
"css-tree": "^3.2.1",
|
||||
@@ -515,6 +517,16 @@
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/@asamuzakjp/generational-cache": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
|
||||
"integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/nwsapi": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
|
||||
@@ -2728,9 +2740,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz",
|
||||
"integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz",
|
||||
"integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2752,9 +2764,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-color-parser": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz",
|
||||
"integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz",
|
||||
"integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2769,7 +2781,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/color-helpers": "^6.0.2",
|
||||
"@csstools/css-calc": "^3.1.1"
|
||||
"@csstools/css-calc": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
@@ -12769,6 +12781,25 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
@@ -12793,6 +12824,59 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs/node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs/node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs/node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test-runner/node_modules/yargs/node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/test/node_modules/@storybook/instrumenter": {
|
||||
"version": "8.6.18",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.6.18.tgz",
|
||||
@@ -20396,6 +20480,25 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/concurrently/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/config-chain": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||
@@ -25418,15 +25521,6 @@
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.0.tgz",
|
||||
"integrity": "sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
@@ -27011,7 +27105,6 @@
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
|
||||
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -30709,6 +30802,25 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-cli/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-config": {
|
||||
"version": "30.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz",
|
||||
@@ -33697,28 +33809,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
"version": "29.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz",
|
||||
"integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==",
|
||||
"version": "29.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.0.tgz",
|
||||
"integrity": "sha512-YNUc7fB9QuvSSQWfrH0xF+TyABkxUwx8sswgIDaCrw4Hol8BghdZDkITtZheRJeMtzWlnTfsM3bBBusRvpO1wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/css-color": "^5.1.5",
|
||||
"@asamuzakjp/dom-selector": "^7.0.6",
|
||||
"@asamuzakjp/css-color": "^5.1.11",
|
||||
"@asamuzakjp/dom-selector": "^7.1.1",
|
||||
"@bramus/specificity": "^2.4.2",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.1",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.3",
|
||||
"@exodus/bytes": "^1.15.0",
|
||||
"css-tree": "^3.2.1",
|
||||
"data-urls": "^7.0.0",
|
||||
"decimal.js": "^10.6.0",
|
||||
"html-encoding-sniffer": "^6.0.0",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"lru-cache": "^11.2.7",
|
||||
"parse5": "^8.0.0",
|
||||
"lru-cache": "^11.3.5",
|
||||
"parse5": "^8.0.1",
|
||||
"saxes": "^6.0.0",
|
||||
"symbol-tree": "^3.2.4",
|
||||
"tough-cookie": "^6.0.1",
|
||||
"undici": "^7.24.5",
|
||||
"undici": "^7.25.0",
|
||||
"w3c-xmlserializer": "^5.0.0",
|
||||
"webidl-conversions": "^8.0.1",
|
||||
"whatwg-mimetype": "^5.0.0",
|
||||
@@ -33738,9 +33850,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz",
|
||||
"integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz",
|
||||
"integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -33810,22 +33922,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/entities": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
|
||||
"integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/lru-cache": {
|
||||
"version": "11.2.7",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
|
||||
"integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
|
||||
"version": "11.3.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz",
|
||||
"integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
@@ -33840,13 +33952,13 @@
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/jsdom/node_modules/parse5": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
|
||||
"integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
|
||||
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"entities": "^6.0.0"
|
||||
"entities": "^8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
@@ -34960,6 +35072,25 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lerna/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@@ -35471,6 +35602,7 @@
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
@@ -37901,6 +38033,25 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/nx/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/nyc": {
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz",
|
||||
@@ -41689,18 +41840,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-checkbox-tree": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-checkbox-tree/-/react-checkbox-tree-2.0.1.tgz",
|
||||
"integrity": "sha512-ZUh9strXP3a+RpXEGPSq5qWC0HSo3pjjGQEwNWYdmo1OfSNq0L61boy4ANIN2O+ybo/n80hadQYNAeMgwdQqRQ==",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-checkbox-tree/-/react-checkbox-tree-1.8.0.tgz",
|
||||
"integrity": "sha512-ufC4aorihOvjLpvY1beab2hjVLGZbDTFRzw62foG0+th+KX7e/sdmWu/nD1ZS/U5Yr0rWGwedGH5GOtR0IkUXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"fast-equals": "^6.0.0",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"lodash": "^4.17.10",
|
||||
"nanoid": "^3.0.0",
|
||||
"prop-types": "^15.5.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-checkbox-tree/node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-diff-viewer-continued": {
|
||||
@@ -47947,6 +48116,24 @@
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript-json-schema/node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/typewise": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz",
|
||||
@@ -48026,9 +48213,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz",
|
||||
"integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==",
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
|
||||
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -50364,21 +50551,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
|
||||
"integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"cliui": "^9.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"string-width": "^7.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
"yargs-parser": "^22.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
@@ -50390,6 +50576,108 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/ansi-regex": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/ansi-styles": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/cliui": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
|
||||
"integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^7.2.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/emoji-regex": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
|
||||
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/yargs/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/strip-ansi": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/wrap-ansi": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
|
||||
"integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/yargs-parser": {
|
||||
"version": "22.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
|
||||
"integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||
}
|
||||
},
|
||||
"node_modules/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
@@ -51364,7 +51652,7 @@
|
||||
"react-js-cron": "^5.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"react-ultimate-pagination": "^1.3.2",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
@@ -53046,7 +53334,7 @@
|
||||
"classnames": "^2.5.1",
|
||||
"d3-array": "^3.2.4",
|
||||
"lodash": "^4.18.1",
|
||||
"memoize-one": "^5.2.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"react-table": "^7.8.0",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"xss": "^1.0.15"
|
||||
@@ -53079,6 +53367,12 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"plugins/plugin-chart-table/node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"plugins/plugin-chart-word-cloud": {
|
||||
"name": "@superset-ui/plugin-chart-word-cloud",
|
||||
"version": "0.20.4",
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^17.0.2",
|
||||
"react-arborist": "^3.5.0",
|
||||
"react-checkbox-tree": "^2.0.1",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
@@ -232,7 +232,7 @@
|
||||
"use-query-params": "^2.2.2",
|
||||
"uuid": "^14.0.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.28.6",
|
||||
@@ -341,7 +341,7 @@
|
||||
"jest-html-reporter": "^4.4.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"js-yaml-loader": "^1.2.2",
|
||||
"jsdom": "^29.0.2",
|
||||
"jsdom": "^29.1.0",
|
||||
"lerna": "^9.0.4",
|
||||
"lightningcss": "^1.32.0",
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
|
||||
@@ -588,6 +588,7 @@ export type ControlFormItemSpec<T extends ControlType = ControlType> = {
|
||||
creatable?: boolean;
|
||||
minWidth?: number | string;
|
||||
validators?: ControlFormValueValidator<string>[];
|
||||
tokenSeparators?: string[];
|
||||
}
|
||||
: T extends 'RadioButtonControl'
|
||||
? {
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"react-js-cron": "^5.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"react-ultimate-pagination": "^1.3.2",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('useJsonValidation', () => {
|
||||
expect(result.current[0]).toMatchObject({
|
||||
type: 'error',
|
||||
row: 0,
|
||||
column: 0,
|
||||
column: 1,
|
||||
text: expect.stringContaining('Invalid JSON'),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,132 +55,138 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
|
||||
height,
|
||||
draggable,
|
||||
hideFooter,
|
||||
}) => css`
|
||||
${responsive &&
|
||||
css`
|
||||
max-width: ${maxWidth ?? '900px'};
|
||||
padding-left: ${theme.sizeUnit * 3}px;
|
||||
padding-right: ${theme.sizeUnit * 3}px;
|
||||
padding-bottom: 0;
|
||||
top: 0;
|
||||
`}
|
||||
}) => {
|
||||
const closeButtonWidth = theme.sizeUnit * 14;
|
||||
|
||||
.ant-modal-content {
|
||||
background-color: ${theme.colorBgContainer};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(100vh - ${theme.sizeUnit * 8}px);
|
||||
margin-bottom: ${theme.sizeUnit * 4}px;
|
||||
margin-top: ${theme.sizeUnit * 4}px;
|
||||
padding: 0;
|
||||
}
|
||||
return css`
|
||||
${responsive &&
|
||||
css`
|
||||
max-width: ${maxWidth ?? '900px'};
|
||||
padding-left: ${theme.sizeUnit * 3}px;
|
||||
padding-right: ${theme.sizeUnit * 3}px;
|
||||
padding-bottom: 0;
|
||||
top: 0;
|
||||
`}
|
||||
|
||||
.ant-modal-header {
|
||||
flex: 0 0 auto;
|
||||
border-radius: ${theme.borderRadius}px ${theme.borderRadius}px 0 0;
|
||||
padding: ${theme.sizeUnit * 4}px ${theme.sizeUnit * 4}px;
|
||||
|
||||
.ant-modal-title {
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
}
|
||||
|
||||
.ant-modal-title h4 {
|
||||
.ant-modal-content {
|
||||
background-color: ${theme.colorBgContainer};
|
||||
display: flex;
|
||||
margin: 0;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
max-height: calc(100vh - ${theme.sizeUnit * 8}px);
|
||||
margin-bottom: ${theme.sizeUnit * 4}px;
|
||||
margin-top: ${theme.sizeUnit * 4}px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
width: ${theme.sizeUnit * 14}px;
|
||||
height: ${theme.sizeUnit * 14}px;
|
||||
padding: ${theme.sizeUnit * 6}px ${theme.sizeUnit * 4}px
|
||||
${theme.sizeUnit * 4}px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.ant-modal-header {
|
||||
flex: 0 0 auto;
|
||||
border-radius: ${theme.borderRadius}px ${theme.borderRadius}px 0 0;
|
||||
padding: ${theme.sizeUnit * 4}px ${closeButtonWidth}px
|
||||
${theme.sizeUnit * 4}px ${theme.sizeUnit * 4}px;
|
||||
|
||||
.ant-modal-close:hover {
|
||||
background: transparent;
|
||||
}
|
||||
.ant-modal-title {
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
[data-test='close-modal-btn'] {
|
||||
.ant-modal-title h4 {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
width: ${closeButtonWidth}px;
|
||||
height: ${theme.sizeUnit * 14}px;
|
||||
padding: ${theme.sizeUnit * 6}px ${theme.sizeUnit * 4}px
|
||||
${theme.sizeUnit * 4}px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.close {
|
||||
flex: 1 1 auto;
|
||||
margin-bottom: ${theme.sizeUnit}px;
|
||||
color: ${theme.colorPrimaryText};
|
||||
font-weight: ${theme.fontWeightLight};
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
flex: 0 1 auto;
|
||||
padding: ${theme.sizeUnit * 4}px ${theme.sizeUnit * 6}px;
|
||||
|
||||
overflow: auto;
|
||||
${!resizable && height && `height: ${height};`}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
flex: 0 0 1;
|
||||
border-top: ${theme.sizeUnit / 4}px solid ${theme.colorSplit};
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
margin-top: 0;
|
||||
|
||||
.btn {
|
||||
font-size: 12px;
|
||||
.ant-modal-close:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.btn + .btn {
|
||||
margin-left: ${theme.sizeUnit * 2}px;
|
||||
.ant-modal-close-x {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
[data-test='close-modal-btn'] {
|
||||
justify-content: center;
|
||||
}
|
||||
.close {
|
||||
flex: 1 1 auto;
|
||||
margin-bottom: ${theme.sizeUnit}px;
|
||||
color: ${theme.colorPrimaryText};
|
||||
font-weight: ${theme.fontWeightLight};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.no-content-padding .ant-modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
.ant-modal-body {
|
||||
flex: 0 1 auto;
|
||||
padding: ${theme.sizeUnit * 4}px ${theme.sizeUnit * 6}px;
|
||||
|
||||
${draggable &&
|
||||
css`
|
||||
.ant-modal-header {
|
||||
overflow: auto;
|
||||
${!resizable && height && `height: ${height};`}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
flex: 0 0 1;
|
||||
border-top: ${theme.sizeUnit / 4}px solid ${theme.colorSplit};
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
margin-top: 0;
|
||||
|
||||
.btn {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn + .btn {
|
||||
margin-left: ${theme.sizeUnit * 2}px;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-content-padding .ant-modal-body {
|
||||
padding: 0;
|
||||
|
||||
.draggable-trigger {
|
||||
cursor: move;
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
${resizable &&
|
||||
css`
|
||||
.resizable {
|
||||
pointer-events: all;
|
||||
${draggable &&
|
||||
css`
|
||||
.ant-modal-header {
|
||||
padding: 0;
|
||||
|
||||
.resizable-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-modal-body {
|
||||
height: ${hideFooter
|
||||
? `calc(100% - ${MODAL_HEADER_HEIGHT}px)`
|
||||
: `calc(100% - ${MODAL_HEADER_HEIGHT}px - ${MODAL_FOOTER_HEIGHT}px)`};
|
||||
.draggable-trigger {
|
||||
cursor: move;
|
||||
padding: ${theme.sizeUnit * 4}px ${closeButtonWidth}px
|
||||
${theme.sizeUnit * 4}px ${theme.sizeUnit * 4}px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`}
|
||||
`}
|
||||
|
||||
${resizable &&
|
||||
css`
|
||||
.resizable {
|
||||
pointer-events: all;
|
||||
|
||||
.resizable-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-modal-body {
|
||||
height: ${hideFooter
|
||||
? `calc(100% - ${MODAL_HEADER_HEIGHT}px)`
|
||||
: `calc(100% - ${MODAL_HEADER_HEIGHT}px - ${MODAL_FOOTER_HEIGHT}px)`};
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
}}
|
||||
`;
|
||||
|
||||
const defaultResizableConfig = (hideFooter: boolean | undefined) => ({
|
||||
|
||||
@@ -20,6 +20,10 @@ import { t } from '@apache-superset/core/translation';
|
||||
import { Icons, Modal, Typography, Button } from '@superset-ui/core/components';
|
||||
import type { FC, ReactElement } from 'react';
|
||||
|
||||
// Ant Design's default modal zIndex is 1000. Using a higher value ensures
|
||||
// this dialog always renders above other open modals (e.g. a draggable View SQL modal).
|
||||
const UNSAVED_CHANGES_MODAL_Z_INDEX = 1100;
|
||||
|
||||
export type UnsavedChangesModalProps = {
|
||||
showModal: boolean;
|
||||
onHide: () => void;
|
||||
@@ -27,6 +31,7 @@ export type UnsavedChangesModalProps = {
|
||||
onConfirmNavigation: () => void;
|
||||
title?: string;
|
||||
body?: string;
|
||||
zIndex?: number;
|
||||
};
|
||||
|
||||
export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
|
||||
@@ -36,6 +41,7 @@ export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
|
||||
onConfirmNavigation,
|
||||
title = 'Unsaved Changes',
|
||||
body = "If you don't save, changes will be lost.",
|
||||
zIndex = UNSAVED_CHANGES_MODAL_Z_INDEX,
|
||||
}: UnsavedChangesModalProps): ReactElement => (
|
||||
<Modal
|
||||
centered
|
||||
@@ -43,6 +49,7 @@ export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
|
||||
onHide={onHide}
|
||||
show={showModal}
|
||||
width="444px"
|
||||
zIndex={zIndex}
|
||||
title={
|
||||
<>
|
||||
<Icons.WarningOutlined iconSize="m" style={{ marginRight: 8 }} />
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"classnames": "^2.5.1",
|
||||
"d3-array": "^3.2.4",
|
||||
"lodash": "^4.18.1",
|
||||
"memoize-one": "^5.2.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"react-table": "^7.8.0",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"xss": "^1.0.15"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/d3-scale": "^4.0.9",
|
||||
"d3-cloud": "^1.2.9",
|
||||
"d3-cloud": "^1.2.8",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -27,10 +27,11 @@ process.env.PATH = `./node_modules/.bin:${process.env.PATH}`;
|
||||
|
||||
const { spawnSync } = require('child_process');
|
||||
const fastGlob = require('fast-glob');
|
||||
const { argv } = require('yargs');
|
||||
const yargs = require('yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
|
||||
const { _: globs } = argv;
|
||||
const glob = globs.length > 1 ? `{${globs.join(',')}}` : globs[0] || '*';
|
||||
const { globs } = yargs(hideBin(process.argv)).parse();
|
||||
const glob = globs?.length > 1 ? `{${globs.join(',')}}` : globs?.[0] || '*';
|
||||
|
||||
const BABEL_CONFIG = '--config-file=../../babel.config.js';
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ function getCheckboxIcon(element: HTMLElement): Element {
|
||||
* checkbox state change is the fill color of the SVG icon.
|
||||
*/
|
||||
function getCheckboxState(name: string): CheckboxState {
|
||||
const element = screen.getByRole('button', { name });
|
||||
const element = screen.getByRole('link', { name });
|
||||
const svgPath = getCheckboxIcon(element).children[1].children[0].children[0];
|
||||
const fill = svgPath.getAttribute('fill');
|
||||
return fill === supersetTheme.colorPrimary
|
||||
@@ -183,7 +183,7 @@ function getCheckboxState(name: string): CheckboxState {
|
||||
|
||||
// Replace the original clickCheckbox function with the async version
|
||||
async function clickCheckbox(name: string) {
|
||||
const element = screen.getByRole('button', { name });
|
||||
const element = screen.getByRole('link', { name });
|
||||
const checkboxLabel = getCheckboxIcon(element);
|
||||
await userEvent.click(checkboxLabel);
|
||||
}
|
||||
@@ -204,11 +204,11 @@ test('renders with empty filters', () => {
|
||||
|
||||
test('renders with filters values', () => {
|
||||
render(<FilterScopeSelector {...createProps()} />, { useRedux: true });
|
||||
expect(screen.getByRole('button', { name: FILTER_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: FILTER_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: FILTER_C })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: TAB_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: TAB_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_C })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: TAB_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: TAB_B })).toBeInTheDocument();
|
||||
expect(screen.queryByText(CHART_A)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(CHART_B)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(CHART_C)).not.toBeInTheDocument();
|
||||
@@ -222,21 +222,21 @@ test('collapses/expands all filters', () => {
|
||||
useRedux: true,
|
||||
});
|
||||
userEvent.click(screen.getAllByRole('button', { name: COLLAPSE_ALL })[0]);
|
||||
expect(screen.getByRole('button', { name: ALL_FILTERS })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: ALL_FILTERS })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: FILTER_A }),
|
||||
screen.queryByRole('link', { name: FILTER_A }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: FILTER_B }),
|
||||
screen.queryByRole('link', { name: FILTER_B }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: FILTER_C }),
|
||||
screen.queryByRole('link', { name: FILTER_C }),
|
||||
).not.toBeInTheDocument();
|
||||
userEvent.click(screen.getAllByRole('button', { name: EXPAND_ALL })[0]);
|
||||
expect(screen.getByRole('button', { name: ALL_FILTERS })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: FILTER_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: FILTER_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: FILTER_C })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: ALL_FILTERS })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: FILTER_C })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('collapses/expands all charts', () => {
|
||||
@@ -251,10 +251,10 @@ test('collapses/expands all charts', () => {
|
||||
expect(screen.queryByText(CHART_D)).not.toBeInTheDocument();
|
||||
userEvent.click(screen.getAllByRole('button', { name: EXPAND_ALL })[1]);
|
||||
expect(screen.getByText(ALL_CHARTS)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: CHART_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: CHART_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: CHART_C })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: CHART_D })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: CHART_A })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: CHART_B })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: CHART_C })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: CHART_D })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('searches for a chart', () => {
|
||||
@@ -262,13 +262,9 @@ test('searches for a chart', () => {
|
||||
useRedux: true,
|
||||
});
|
||||
userEvent.type(screen.getByPlaceholderText('Search...'), CHART_C);
|
||||
expect(
|
||||
screen.queryByRole('button', { name: CHART_A }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: CHART_B }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: CHART_C })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('link', { name: CHART_A })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('link', { name: CHART_B })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: CHART_C })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Update all tests that use clickCheckbox to be async and await the function call
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SHARED_COLUMN_CONFIG_PROPS } from './constants';
|
||||
|
||||
const tokenSeparators =
|
||||
SHARED_COLUMN_CONFIG_PROPS.d3NumberFormat.tokenSeparators;
|
||||
|
||||
test('should allow commas in D3 format inputs', () => {
|
||||
expect(tokenSeparators).toBeDefined();
|
||||
expect(tokenSeparators).not.toContain(',');
|
||||
});
|
||||
|
||||
test('should have correct default token separators', () => {
|
||||
const expectedSeparators = ['\r\n', '\n', '\t', ';'];
|
||||
expect(tokenSeparators).toEqual(expectedSeparators);
|
||||
});
|
||||
@@ -58,6 +58,8 @@ const d3NumberFormat: ControlFormItemSpec<'Select'> = {
|
||||
creatable: true,
|
||||
minWidth: '14em',
|
||||
debounceDelay: 500,
|
||||
// default value tokenSeparators in superset-frontend/packages/superset-ui-core/src/components/Select/constants.ts
|
||||
tokenSeparators: ['\r\n', '\n', '\t', ';'],
|
||||
};
|
||||
|
||||
const d3TimeFormat: ControlFormItemSpec<'Select'> = {
|
||||
|
||||
@@ -34,6 +34,8 @@ import Chart from 'src/types/Chart';
|
||||
import { FacePile } from 'src/components';
|
||||
import { handleChartDelete, CardStyles } from 'src/views/CRUD/utils';
|
||||
import { assetUrl } from 'src/utils/assetUrl';
|
||||
import type { ListViewFetchDataConfig as FetchDataConfig } from 'src/components';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
|
||||
interface ChartCardProps {
|
||||
chart: Chart;
|
||||
@@ -42,7 +44,7 @@ interface ChartCardProps {
|
||||
bulkSelectEnabled: boolean;
|
||||
addDangerToast: (msg: string) => void;
|
||||
addSuccessToast: (msg: string) => void;
|
||||
refreshData: () => void;
|
||||
refreshData: (config?: FetchDataConfig | null) => void;
|
||||
loading?: boolean;
|
||||
saveFavoriteStatus: (id: number, isStarred: boolean) => void;
|
||||
favoriteStatus: boolean;
|
||||
@@ -50,6 +52,7 @@ interface ChartCardProps {
|
||||
userId?: string | number;
|
||||
showThumbnails?: boolean;
|
||||
handleBulkChartExport: (chartsToExport: Chart[]) => void;
|
||||
getData?: (tab: TableTab) => void;
|
||||
}
|
||||
|
||||
export default function ChartCard({
|
||||
@@ -67,6 +70,7 @@ export default function ChartCard({
|
||||
chartFilter,
|
||||
userId,
|
||||
handleBulkChartExport,
|
||||
getData,
|
||||
}: ChartCardProps) {
|
||||
const history = useHistory();
|
||||
const canEdit = hasPerm('can_write');
|
||||
@@ -136,6 +140,7 @@ export default function ChartCard({
|
||||
refreshData,
|
||||
chartFilter,
|
||||
userId,
|
||||
getData,
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -26,6 +26,7 @@ import { VizType } from '@superset-ui/core';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import handleResourceExport from 'src/utils/export';
|
||||
import { LocalStorageKeys } from 'src/utils/localStorageHelpers';
|
||||
import ChartTable from './ChartTable';
|
||||
|
||||
// Mock the export module
|
||||
@@ -53,12 +54,16 @@ const mockCharts = Array.from({ length: 3 }).map((_, i) => ({
|
||||
thumbnail_url: '',
|
||||
}));
|
||||
|
||||
fetchMock.get(chartsEndpoint, {
|
||||
result: mockCharts,
|
||||
});
|
||||
fetchMock.get(
|
||||
chartsEndpoint,
|
||||
{
|
||||
result: mockCharts,
|
||||
},
|
||||
{ name: chartsEndpoint },
|
||||
);
|
||||
|
||||
fetchMock.get(chartsInfoEndpoint, {
|
||||
permissions: ['can_add', 'can_edit', 'can_delete', 'can_export'],
|
||||
permissions: ['can_add', 'can_write', 'can_delete', 'can_export'],
|
||||
});
|
||||
|
||||
fetchMock.get(chartFavoriteStatusEndpoint, {
|
||||
@@ -99,6 +104,10 @@ const renderChartTable = (props: any) =>
|
||||
render(<ChartTable {...props} />, renderOptions);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
window.localStorage.removeItem(LocalStorageKeys.HomepageChartFilter);
|
||||
});
|
||||
|
||||
test('renders with EmptyState if no data present', async () => {
|
||||
await renderChartTable(mockedProps);
|
||||
expect(screen.getAllByRole('tab')).toHaveLength(3);
|
||||
@@ -178,3 +187,58 @@ test('handles chart export with correct ID and shows spinner', async () => {
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
});
|
||||
|
||||
test('refreshes other tab data after deleting a chart', async () => {
|
||||
fetchMock.removeRoute(chartsEndpoint);
|
||||
fetchMock.get(
|
||||
chartsEndpoint,
|
||||
{
|
||||
result: mockCharts.slice(1),
|
||||
count: mockCharts.length - 1,
|
||||
},
|
||||
{ name: chartsEndpoint },
|
||||
);
|
||||
fetchMock.delete('glob:*/api/v1/chart/0', {
|
||||
message: 'Chart deleted',
|
||||
});
|
||||
|
||||
await renderChartTable({
|
||||
...otherTabProps,
|
||||
otherTabTitle: 'All',
|
||||
});
|
||||
|
||||
expect(screen.getByText('cool chart 0')).toBeInTheDocument();
|
||||
|
||||
const refreshCallsBeforeDelete =
|
||||
fetchMock.callHistory.calls(chartsEndpoint).length;
|
||||
|
||||
const moreButtons = screen.getAllByRole('img', { name: /more/i });
|
||||
await userEvent.click(moreButtons[0]);
|
||||
|
||||
await userEvent.click(await screen.findByText('Delete'));
|
||||
|
||||
const deleteInput = screen.getByTestId('delete-modal-input');
|
||||
await userEvent.type(deleteInput, 'DELETE');
|
||||
await userEvent.click(screen.getByTestId('modal-confirm-button'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
fetchMock.callHistory.calls(/api\/v1\/chart\/0/, {
|
||||
method: 'DELETE',
|
||||
}),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchMock.callHistory.calls(chartsEndpoint).length).toBe(
|
||||
refreshCallsBeforeDelete + 1,
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('cool chart 0')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByText('cool chart 1')).toBeInTheDocument();
|
||||
expect(screen.getByText('cool chart 2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -112,18 +112,19 @@ function ChartTable({
|
||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
const getData = (tab: TableTab) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
pageSize: PAGE_SIZE,
|
||||
sortBy: [
|
||||
{
|
||||
id: 'changed_on_delta_humanized',
|
||||
desc: true,
|
||||
},
|
||||
],
|
||||
filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters),
|
||||
});
|
||||
const getChartFetchDataConfig = (tab: TableTab) => ({
|
||||
pageIndex: 0,
|
||||
pageSize: PAGE_SIZE,
|
||||
sortBy: [
|
||||
{
|
||||
id: 'changed_on_delta_humanized',
|
||||
desc: true,
|
||||
},
|
||||
],
|
||||
filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters),
|
||||
});
|
||||
|
||||
const getData = (tab: TableTab) => fetchData(getChartFetchDataConfig(tab));
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded || activeTab === TableTab.Favorite) {
|
||||
@@ -234,6 +235,7 @@ function ChartTable({
|
||||
refreshData={refreshData}
|
||||
addDangerToast={addDangerToast}
|
||||
addSuccessToast={addSuccessToast}
|
||||
getData={getData}
|
||||
favoriteStatus={favoriteStatus[e.id]}
|
||||
saveFavoriteStatus={saveFavoriteStatus}
|
||||
handleBulkChartExport={handleBulkChartExport}
|
||||
|
||||
@@ -327,6 +327,7 @@ export function handleChartDelete(
|
||||
refreshData: (arg0?: FetchDataConfig | null) => void,
|
||||
chartFilter?: string,
|
||||
userId?: string | number,
|
||||
getData?: (tab: TableTab) => void,
|
||||
) {
|
||||
const filters = {
|
||||
pageIndex: 0,
|
||||
@@ -350,6 +351,7 @@ export function handleChartDelete(
|
||||
}).then(
|
||||
() => {
|
||||
if (chartFilter === 'Mine') refreshData(filters);
|
||||
else if (chartFilter && getData) getData(chartFilter as TableTab);
|
||||
else refreshData();
|
||||
addSuccessToast(t('Deleted: %s', sliceName));
|
||||
},
|
||||
|
||||
@@ -36,11 +36,14 @@ const {
|
||||
} = require('webpack-manifest-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const parsedArgs = require('yargs').argv;
|
||||
const yargs = require('yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const Visualizer = require('webpack-visualizer-plugin2');
|
||||
const getProxyConfig = require('./webpack.proxy-config');
|
||||
const packageConfig = require('./package.json');
|
||||
|
||||
const parsedArgs = yargs(hideBin(process.argv)).parse();
|
||||
|
||||
// input dir
|
||||
const APP_DIR = path.resolve(__dirname, './');
|
||||
// output dir
|
||||
|
||||
@@ -20,8 +20,8 @@ const zlib = require('zlib');
|
||||
const { ZSTDDecompress } = require('simple-zstd');
|
||||
|
||||
const yargs = require('yargs');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const parsedArgs = yargs.argv;
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const parsedArgs = yargs(hideBin(process.argv)).parse();
|
||||
|
||||
const parsedEnvArg = () => {
|
||||
let envArgs = {};
|
||||
|
||||
@@ -127,6 +127,27 @@ def _resolve_metrics_and_groupby(
|
||||
return _resolve_metrics(form_data, viz_type), _resolve_groupby(form_data)
|
||||
|
||||
|
||||
def _resolve_engine(
|
||||
datasource_id: Any,
|
||||
datasource_type: str,
|
||||
) -> str:
|
||||
"""Return the DB engine name for *datasource_id*, or ``"base"`` on any error."""
|
||||
if not isinstance(datasource_id, (int, str)):
|
||||
return "base"
|
||||
try:
|
||||
from superset.daos.datasource import DatasourceDAO
|
||||
from superset.utils.core import DatasourceType
|
||||
|
||||
ds = DatasourceDAO.get_datasource(
|
||||
datasource_type=DatasourceType(datasource_type),
|
||||
database_id_or_uuid=datasource_id,
|
||||
)
|
||||
return ds.database.db_engine_spec.engine
|
||||
except Exception: # noqa: BLE001
|
||||
logger.debug("Could not resolve engine for datasource %s", datasource_id)
|
||||
return "base"
|
||||
|
||||
|
||||
def _build_query_context_from_form_data(
|
||||
form_data: dict[str, Any],
|
||||
chart: "Slice | None" = None,
|
||||
@@ -159,14 +180,35 @@ def _build_query_context_from_form_data(
|
||||
|
||||
metrics, groupby = _resolve_metrics_and_groupby(form_data, chart)
|
||||
|
||||
# Build a minimal query object; let QueryContextFactory handle temporal
|
||||
# fields (time_range, granularity_sqla), adhoc_filters, WHERE/HAVING
|
||||
# clauses, etc. from form_data — same approach as get_chart_data.
|
||||
# Preprocess adhoc_filters into where/having/filters on form_data so
|
||||
# that the QueryObject receives concrete filter clauses. This mirrors
|
||||
# the view-layer call in viz.py:process_query_filters.
|
||||
from superset.utils.core import (
|
||||
merge_extra_filters,
|
||||
split_adhoc_filters_into_base_filters,
|
||||
)
|
||||
|
||||
resolved_type_str: str = (
|
||||
datasource_type if isinstance(datasource_type, str) else "table"
|
||||
)
|
||||
engine = _resolve_engine(datasource_id, resolved_type_str)
|
||||
merge_extra_filters(form_data)
|
||||
split_adhoc_filters_into_base_filters(form_data, engine)
|
||||
|
||||
# Build query dict with temporal and filter fields.
|
||||
# QueryObjectFactory.create() accepts time_range as a top-level kwarg
|
||||
# and converts it to from_dttm/to_dttm for the QueryObject.
|
||||
query_dict: dict[str, Any] = {
|
||||
"columns": groupby,
|
||||
"metrics": metrics,
|
||||
}
|
||||
|
||||
if time_range := form_data.get("time_range"):
|
||||
query_dict["time_range"] = time_range
|
||||
|
||||
if filters := form_data.get("filters"):
|
||||
query_dict["filters"] = filters
|
||||
|
||||
if (row_limit := form_data.get("row_limit")) is not None:
|
||||
query_dict["row_limit"] = row_limit
|
||||
|
||||
@@ -179,12 +221,9 @@ def _build_query_context_from_form_data(
|
||||
"'datasource_id' or 'datasource'."
|
||||
)
|
||||
resolved_id: int | str = datasource_id
|
||||
resolved_type: str = (
|
||||
datasource_type if isinstance(datasource_type, str) else "table"
|
||||
)
|
||||
|
||||
return factory.create(
|
||||
datasource={"id": resolved_id, "type": resolved_type},
|
||||
datasource={"id": resolved_id, "type": resolved_type_str},
|
||||
queries=[query_dict],
|
||||
form_data=form_data,
|
||||
result_type=ChartDataResultType.QUERY,
|
||||
@@ -270,6 +309,54 @@ def _sql_from_saved_query_context(
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_datasource_name(
|
||||
form_data: dict[str, Any],
|
||||
chart: "Slice | None",
|
||||
) -> str | None:
|
||||
"""Resolve datasource name from form_data or chart.
|
||||
|
||||
For unsaved charts (chart=None), looks up the datasource by ID
|
||||
from form_data so that the response includes a meaningful name.
|
||||
"""
|
||||
if chart:
|
||||
return getattr(chart, "datasource_name", None)
|
||||
|
||||
# Unsaved chart — resolve from form_data
|
||||
datasource_id = form_data.get("datasource_id")
|
||||
datasource_type = form_data.get("datasource_type", "table")
|
||||
|
||||
if not datasource_id and (combined := form_data.get("datasource")):
|
||||
if isinstance(combined, str) and "__" in combined:
|
||||
parts = combined.split("__", 1)
|
||||
datasource_id = int(parts[0]) if parts[0].isdigit() else parts[0]
|
||||
datasource_type = parts[1] if len(parts) > 1 else "table"
|
||||
|
||||
if not datasource_id:
|
||||
return None
|
||||
|
||||
try:
|
||||
from superset.daos.datasource import DatasourceDAO
|
||||
from superset.daos.exceptions import (
|
||||
DatasourceNotFound,
|
||||
DatasourceTypeNotSupportedError,
|
||||
DatasourceValueIsIncorrect,
|
||||
)
|
||||
from superset.utils.core import DatasourceType
|
||||
|
||||
datasource = DatasourceDAO.get_datasource(
|
||||
datasource_type=DatasourceType(datasource_type),
|
||||
database_id_or_uuid=datasource_id,
|
||||
)
|
||||
return getattr(datasource, "name", None)
|
||||
except (
|
||||
ValueError,
|
||||
DatasourceNotFound,
|
||||
DatasourceTypeNotSupportedError,
|
||||
DatasourceValueIsIncorrect,
|
||||
):
|
||||
return None
|
||||
|
||||
|
||||
def _sql_from_form_data(
|
||||
form_data: dict[str, Any],
|
||||
chart: "Slice | None",
|
||||
@@ -286,7 +373,7 @@ def _sql_from_form_data(
|
||||
result,
|
||||
chart_id=getattr(chart, "id", None),
|
||||
chart_name=getattr(chart, "slice_name", None),
|
||||
datasource_name=getattr(chart, "datasource_name", None),
|
||||
datasource_name=_resolve_datasource_name(form_data, chart),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -59,10 +59,14 @@ class DatabaseFilter(ColumnOperator):
|
||||
"database_name",
|
||||
"expose_in_sqllab",
|
||||
"allow_file_upload",
|
||||
"created_by_fk",
|
||||
"changed_by_fk",
|
||||
] = Field(
|
||||
...,
|
||||
description="Column to filter on. Use get_schema(model_type='database') for "
|
||||
"available filter columns.",
|
||||
"available filter columns. Use created_by_fk with the user "
|
||||
"ID from get_instance_info's current_user to find "
|
||||
"databases created by a specific user.",
|
||||
)
|
||||
opr: ColumnOperatorEnum = Field(
|
||||
...,
|
||||
|
||||
@@ -52,7 +52,11 @@ from superset_core.queries.models import (
|
||||
)
|
||||
|
||||
from superset import security_manager
|
||||
from superset.exceptions import SupersetParseError, SupersetSecurityException
|
||||
from superset.exceptions import (
|
||||
SupersetException,
|
||||
SupersetParseError,
|
||||
SupersetSecurityException,
|
||||
)
|
||||
from superset.explorables.base import TimeGrainDict
|
||||
from superset.jinja_context import BaseTemplateProcessor, get_template_processor
|
||||
from superset.models.helpers import (
|
||||
@@ -98,6 +102,14 @@ class SqlTablesMixin: # pylint: disable=too-few-public-methods
|
||||
)
|
||||
except (SupersetSecurityException, SupersetParseError, TemplateError):
|
||||
return []
|
||||
except SupersetException as ex:
|
||||
# Jinja macros such as ``{{ dataset(id) }}`` or ``{{ metric(...) }}``
|
||||
# may reference resources that no longer exist (e.g. a deleted
|
||||
# dataset). Surfacing the failure here would break list endpoints
|
||||
# that include ``sql_tables`` in their payload, hiding every saved
|
||||
# query from the user. Treat it as a parse failure instead.
|
||||
logger.warning("Unable to extract tables from SQL via Jinja: %s", ex)
|
||||
return []
|
||||
|
||||
|
||||
class Query(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ from superset.mcp_service.chart.tool.get_chart_sql import (
|
||||
_build_query_context_from_form_data,
|
||||
_extract_sql_from_result,
|
||||
_find_chart_by_identifier,
|
||||
_resolve_datasource_name,
|
||||
_resolve_effective_form_data,
|
||||
_resolve_groupby,
|
||||
_resolve_metrics,
|
||||
@@ -356,9 +357,14 @@ class TestBuildQueryContextFromFormData:
|
||||
"""
|
||||
|
||||
@patch("superset.common.query_context_factory.QueryContextFactory")
|
||||
def test_temporal_fields_passed_to_factory(self, mock_factory_cls):
|
||||
"""time_range, granularity_sqla, adhoc_filters from form_data are
|
||||
forwarded via form_data= to the factory, not dropped."""
|
||||
@patch("superset.daos.datasource.DatasourceDAO.get_datasource")
|
||||
def test_temporal_fields_passed_to_factory(self, mock_get_ds, mock_factory_cls):
|
||||
"""time_range, adhoc_filters from form_data are processed and
|
||||
forwarded to the factory — not dropped."""
|
||||
mock_ds = Mock()
|
||||
mock_ds.database.db_engine_spec.engine = "postgresql"
|
||||
mock_get_ds.return_value = mock_ds
|
||||
|
||||
mock_factory = Mock()
|
||||
mock_factory.create.return_value = Mock()
|
||||
mock_factory_cls.return_value = mock_factory
|
||||
@@ -390,17 +396,22 @@ class TestBuildQueryContextFromFormData:
|
||||
mock_result_type.QUERY = "QUERY"
|
||||
_build_query_context_from_form_data(form_data, chart=None)
|
||||
|
||||
# Verify factory.create was called with form_data containing all fields
|
||||
call_kwargs = mock_factory.create.call_args[1]
|
||||
assert call_kwargs["form_data"] is form_data
|
||||
assert call_kwargs["form_data"]["time_range"] == "Last 7 days"
|
||||
assert call_kwargs["form_data"]["granularity_sqla"] == "created_at"
|
||||
assert call_kwargs["form_data"]["adhoc_filters"] is not None
|
||||
assert call_kwargs["form_data"]["where"] == "region = 'US'"
|
||||
assert call_kwargs["form_data"]["having"] == "count > 10"
|
||||
assert call_kwargs["form_data"]["filters"] == [
|
||||
{"col": "city", "op": "==", "val": "NYC"}
|
||||
]
|
||||
|
||||
# adhoc_filters are preprocessed by split_adhoc_filters_into_base_filters
|
||||
# into form_data["filters"]/["where"]/["having"], then included
|
||||
# in the query dict as concrete filter clauses.
|
||||
queries = call_kwargs["queries"]
|
||||
assert len(queries) == 1
|
||||
assert queries[0]["time_range"] == "Last 7 days"
|
||||
assert "adhoc_filters" not in queries[0]
|
||||
# The simple adhoc WHERE filter (status == active) should be
|
||||
# merged into filters by split_adhoc_filters_into_base_filters.
|
||||
filters = queries[0].get("filters", [])
|
||||
assert {"col": "status", "op": "==", "val": "active"} in filters
|
||||
|
||||
@patch("superset.common.query_context_factory.QueryContextFactory")
|
||||
def test_metrics_and_groupby_in_queries(self, mock_factory_cls):
|
||||
@@ -429,6 +440,76 @@ class TestBuildQueryContextFromFormData:
|
||||
assert queries[0]["columns"] == ["product"]
|
||||
|
||||
|
||||
class TestResolveDatasourceName:
|
||||
"""Tests for _resolve_datasource_name helper."""
|
||||
|
||||
def test_returns_chart_datasource_name_when_chart_exists(self):
|
||||
"""When a chart object is provided, use its datasource_name."""
|
||||
chart = Mock()
|
||||
chart.datasource_name = "my_dataset"
|
||||
result = _resolve_datasource_name({"datasource_id": 1}, chart)
|
||||
assert result == "my_dataset"
|
||||
|
||||
@patch(
|
||||
"superset.mcp_service.chart.tool.get_chart_sql.DatasourceDAO",
|
||||
create=True,
|
||||
)
|
||||
def test_resolves_from_form_data_when_chart_is_none(self, mock_dao):
|
||||
"""Unsaved charts resolve datasource name via DAO lookup."""
|
||||
mock_ds = Mock()
|
||||
mock_ds.name = "resolved_dataset"
|
||||
|
||||
with patch(
|
||||
"superset.daos.datasource.DatasourceDAO.get_datasource",
|
||||
return_value=mock_ds,
|
||||
):
|
||||
result = _resolve_datasource_name(
|
||||
{"datasource_id": 42, "datasource_type": "table"}, chart=None
|
||||
)
|
||||
assert result == "resolved_dataset"
|
||||
|
||||
def test_returns_none_when_no_datasource_id(self):
|
||||
"""Returns None when form_data has no datasource info."""
|
||||
result = _resolve_datasource_name({}, chart=None)
|
||||
assert result is None
|
||||
|
||||
def test_returns_none_when_datasource_not_found(self):
|
||||
"""Returns None when DAO raises DatasourceNotFound."""
|
||||
from superset.daos.exceptions import DatasourceNotFound
|
||||
|
||||
with patch(
|
||||
"superset.daos.datasource.DatasourceDAO.get_datasource",
|
||||
side_effect=DatasourceNotFound(),
|
||||
):
|
||||
result = _resolve_datasource_name(
|
||||
{"datasource_id": 999, "datasource_type": "table"}, chart=None
|
||||
)
|
||||
assert result is None
|
||||
|
||||
def test_returns_none_on_unsupported_type(self):
|
||||
"""Returns None when DAO raises DatasourceTypeNotSupportedError."""
|
||||
from superset.daos.exceptions import DatasourceTypeNotSupportedError
|
||||
|
||||
with patch(
|
||||
"superset.daos.datasource.DatasourceDAO.get_datasource",
|
||||
side_effect=DatasourceTypeNotSupportedError(),
|
||||
):
|
||||
result = _resolve_datasource_name(
|
||||
{"datasource_id": 1, "datasource_type": "invalid"}, chart=None
|
||||
)
|
||||
assert result is None
|
||||
|
||||
@patch("superset.daos.datasource.DatasourceDAO.get_datasource")
|
||||
def test_resolves_combined_datasource_field(self, mock_get_ds):
|
||||
"""Handles combined 'datasource' field like '123__table'."""
|
||||
mock_ds = Mock()
|
||||
mock_ds.name = "combined_dataset"
|
||||
mock_get_ds.return_value = mock_ds
|
||||
|
||||
result = _resolve_datasource_name({"datasource": "123__table"}, chart=None)
|
||||
assert result == "combined_dataset"
|
||||
|
||||
|
||||
class TestGetChartSqlTool:
|
||||
"""Integration-style tests for the get_chart_sql MCP tool via Client."""
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from fastmcp import Client
|
||||
from fastmcp.exceptions import ToolError
|
||||
from pydantic import ValidationError
|
||||
|
||||
from superset.mcp_service.app import mcp
|
||||
from superset.mcp_service.database.schemas import ListDatabasesRequest
|
||||
from superset.mcp_service.database.schemas import DatabaseFilter, ListDatabasesRequest
|
||||
from superset.mcp_service.privacy import DATA_MODEL_METADATA_ERROR_TYPE
|
||||
from superset.utils import json
|
||||
|
||||
@@ -39,6 +40,25 @@ get_database_info_module = importlib.import_module(
|
||||
)
|
||||
|
||||
|
||||
class TestDatabaseFilterSchema:
|
||||
"""Tests for DatabaseFilter schema — filterable columns."""
|
||||
|
||||
def test_created_by_fk_is_valid_filter_column(self):
|
||||
"""created_by_fk must be accepted as a filter column."""
|
||||
f = DatabaseFilter(col="created_by_fk", opr="eq", value=1)
|
||||
assert f.col == "created_by_fk"
|
||||
|
||||
def test_changed_by_fk_is_valid_filter_column(self):
|
||||
"""changed_by_fk must be accepted as a filter column."""
|
||||
f = DatabaseFilter(col="changed_by_fk", opr="eq", value=1)
|
||||
assert f.col == "changed_by_fk"
|
||||
|
||||
def test_invalid_filter_column_rejected(self):
|
||||
"""Columns not in the Literal set must be rejected."""
|
||||
with pytest.raises(ValidationError):
|
||||
DatabaseFilter(col="not_a_real_column", opr="eq", value=1)
|
||||
|
||||
|
||||
def create_mock_database(
|
||||
database_id: int = 1,
|
||||
database_name: str = "examples",
|
||||
@@ -249,10 +269,15 @@ async def test_list_databases_does_not_expose_user_directory_fields(
|
||||
|
||||
|
||||
def test_database_filter_rejects_user_directory_fields() -> None:
|
||||
"""Test user directory fields cannot be used for database filters."""
|
||||
with pytest.raises(ValueError, match="created_by_fk"):
|
||||
"""Test user directory string fields cannot be used for database filters.
|
||||
|
||||
created_by_fk / changed_by_fk are integer FK IDs and ARE valid filter
|
||||
columns. The user-directory *string* fields (created_by, created_by_name,
|
||||
etc.) must still be rejected.
|
||||
"""
|
||||
with pytest.raises(ValidationError, match="created_by_name"):
|
||||
ListDatabasesRequest(
|
||||
filters=[{"col": "created_by_fk", "opr": "eq", "value": 1}],
|
||||
filters=[{"col": "created_by_name", "opr": "eq", "value": "admin"}],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -21,8 +21,13 @@ from flask_appbuilder import Model
|
||||
from jinja2.exceptions import TemplateError
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from superset.commands.dataset.exceptions import DatasetNotFoundError
|
||||
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
|
||||
from superset.exceptions import SupersetParseError, SupersetSecurityException
|
||||
from superset.exceptions import (
|
||||
SupersetParseError,
|
||||
SupersetSecurityException,
|
||||
SupersetTemplateException,
|
||||
)
|
||||
from superset.models.sql_lab import Query, SavedQuery
|
||||
|
||||
|
||||
@@ -48,6 +53,11 @@ from superset.models.sql_lab import Query, SavedQuery
|
||||
message="Invalid SQL syntax",
|
||||
),
|
||||
TemplateError,
|
||||
# ``{{ dataset(id) }}`` referencing a deleted dataset previously
|
||||
# bubbled up through ``sql_tables`` and broke saved-query list
|
||||
# endpoints (see issue #32771).
|
||||
DatasetNotFoundError("Dataset 1 not found!"),
|
||||
SupersetTemplateException("Template rendering failed"),
|
||||
],
|
||||
)
|
||||
def test_sql_tables_mixin_sql_tables_exception(
|
||||
|
||||
Reference in New Issue
Block a user