You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/10/08 04:29:21 UTC
(superset) branch master updated: feat(semantic-layer): show provider queries from chart results (#44206)
This is an automated email from the ASF dual-hosted git repository.
aminghadersohi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new dbd546908a8 feat(semantic-layer): show provider queries from chart results (#44206)
dbd546908a8 is described below
commit dbd546908a89fabe025beb7557ca4398ed65e9f8
Author: Mike Bridge <mi...@preset.io>
AuthorDate: Wed Oct 7 22:29:10 2026 -0600
feat(semantic-layer): show provider queries from chart results (#44206)
Co-authored-by: Mike Bridge <mi...@ext.preset.io>
---
.../creating-your-first-dashboard.mdx | 29 ++
.../SliceHeaderControls.test.tsx | 37 +++
.../components/SliceHeaderControls/index.tsx | 1 +
.../controls/SemanticRequestView.test.tsx | 111 ++++++++
.../components/controls/SemanticRequestView.tsx | 71 +++++
.../components/controls/ViewQueryModal.test.tsx | 300 ++++++++++++++++++++-
.../explore/components/controls/ViewQueryModal.tsx | 98 ++++++-
.../useExploreAdditionalActionsMenu/index.tsx | 1 +
.../useExploreAdditionalActionsMenu.test.tsx | 33 +++
superset/semantic_layers/models.py | 8 +-
superset/translations/ar/LC_MESSAGES/messages.po | 11 +
superset/translations/ca/LC_MESSAGES/messages.po | 11 +
superset/translations/cs/LC_MESSAGES/messages.po | 11 +
superset/translations/de/LC_MESSAGES/messages.po | 11 +
superset/translations/en/LC_MESSAGES/messages.po | 11 +
superset/translations/es/LC_MESSAGES/messages.po | 11 +
superset/translations/fa/LC_MESSAGES/messages.po | 11 +
superset/translations/fi/LC_MESSAGES/messages.po | 11 +
superset/translations/fr/LC_MESSAGES/messages.po | 11 +
superset/translations/it/LC_MESSAGES/messages.po | 11 +
superset/translations/ja/LC_MESSAGES/messages.po | 11 +
superset/translations/ko/LC_MESSAGES/messages.po | 11 +
superset/translations/lv/LC_MESSAGES/messages.po | 11 +
superset/translations/messages.pot | 11 +
superset/translations/mi/LC_MESSAGES/messages.po | 11 +
superset/translations/nl/LC_MESSAGES/messages.po | 11 +
superset/translations/pl/LC_MESSAGES/messages.po | 11 +
superset/translations/pt/LC_MESSAGES/messages.po | 11 +
.../translations/pt_BR/LC_MESSAGES/messages.po | 11 +
superset/translations/ro/LC_MESSAGES/messages.po | 11 +
superset/translations/ru/LC_MESSAGES/messages.po | 11 +
superset/translations/sk/LC_MESSAGES/messages.po | 11 +
superset/translations/sl/LC_MESSAGES/messages.po | 11 +
superset/translations/sr/LC_MESSAGES/messages.po | 11 +
.../translations/sr_Latn/LC_MESSAGES/messages.po | 11 +
superset/translations/ta/LC_MESSAGES/messages.po | 11 +
superset/translations/th/LC_MESSAGES/messages.po | 11 +
superset/translations/tr/LC_MESSAGES/messages.po | 11 +
superset/translations/uk/LC_MESSAGES/messages.po | 11 +
superset/translations/zh/LC_MESSAGES/messages.po | 11 +
.../translations/zh_TW/LC_MESSAGES/messages.po | 11 +
tests/unit_tests/semantic_layers/models_test.py | 75 +++++-
42 files changed, 1083 insertions(+), 22 deletions(-)
diff --git a/docs/docs/using-superset/creating-your-first-dashboard.mdx b/docs/docs/using-superset/creating-your-first-dashboard.mdx
index bbae3b74c6f..0f0e3449202 100644
--- a/docs/docs/using-superset/creating-your-first-dashboard.mdx
+++ b/docs/docs/using-superset/creating-your-first-dashboard.mdx
@@ -190,6 +190,35 @@ Semantic views and native filter support for them are part of the experimental S
feature and require the `SEMANTIC_LAYERS` feature flag to be enabled.
:::
+### Viewing provider requests for semantic views
+
+With the experimental `SEMANTIC_LAYERS` feature flag enabled, you can inspect the provider requests
+that produced a semantic-view chart's data:
+
+1. Run the chart and wait for its data to load.
+2. In Explore, open the chart's more options menu and select **View query**. On a dashboard, select
+ **View query** from the chart's header menu.
+3. Select **Copy** below a displayed block to copy its complete text, including the request-kind
+ headers. Long requests are scrollable and are not truncated when copied.
+
+View query shows the requests from the chart's latest completed run in the current session. After
+changing filters or other chart settings, let the chart run again to inspect the updated requests.
+Opening View query does not execute a query or provide a preview before the chart runs.
+
+Requests are displayed verbatim, in the order reported, with headers identifying their kind, such as
+SQL, JSON, or GraphQL. Recognized kinds receive syntax highlighting; other kinds appear as plain
+text. The text is not reformatted, and a block can contain multiple requests from the same run.
+
+If no run report is available, the message is **The provider query will be available after the chart
+runs.** Run the chart to make its reported requests available. If a run report is present but has no
+request text or an error for an entry, the message is **No provider query is available for this run.** This can
+occur when a provider does not report request text; it does not mean the chart returned no data.
+
+If the run fails, View query displays the chart's error message. For responses containing both
+requests and errors, each error appears alongside the reported requests.
+
+View query for regular dataset charts is unchanged.
+
### Creating charts in Explore view
Superset has 2 main interfaces for exploring data:
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
index 26544dd1150..573ded42dac 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
@@ -29,6 +29,7 @@ import { cachedSupersetGet } from 'src/utils/cachedSupersetGet';
import downloadAsImage from 'src/utils/downloadAsImage';
import downloadAsPdf from 'src/utils/downloadAsPdf';
import SliceHeaderControls, { SliceHeaderControlsProps } from '.';
+import { chart } from 'src/components/Chart/chartReducer';
jest.mock('src/utils/cachedSupersetGet');
jest.mock('src/explore/components/DataTablesPane', () => ({
@@ -147,6 +148,42 @@ const openMenu = async () => {
await userEvent.click(screen.getByRole('button', { name: 'More Options' }));
};
+test('View query uses the dashboard slice when form data omits slice_id', async () => {
+ const props = createProps();
+ const request = '-- SQL\nSELECT saved_dashboard_chart';
+ render(
+ <SliceHeaderControls
+ {...props}
+ formData={{ datasource: '12__semantic_view', viz_type: 'table' }}
+ />,
+ {
+ useRedux: true,
+ useRouter: true,
+ initialState: {
+ ...mockState,
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [{ query: '-- SQL\nSELECT unrelated' }],
+ },
+ [SLICE_ID]: {
+ ...chart,
+ id: SLICE_ID,
+ queriesResponse: [{ query: request }],
+ },
+ },
+ },
+ },
+ );
+ await openMenu();
+ await userEvent.click(await screen.findByText('View query'));
+ await waitFor(() =>
+ expect(screen.getByRole('dialog').querySelector('pre')?.textContent).toBe(
+ request,
+ ),
+ );
+});
+
const mockDownloadAsImage = downloadAsImage as jest.MockedFunction<
typeof downloadAsImage
>;
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
index 004250f7d30..fcbec4601cc 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
@@ -596,6 +596,7 @@ const SliceHeaderControls = (
modalTitle={t('View query')}
modalBody={
<ViewQueryModal
+ chartId={props.slice.slice_id}
latestQueryFormData={props.formData}
ownState={props.ownState}
/>
diff --git a/superset-frontend/src/explore/components/controls/SemanticRequestView.test.tsx b/superset-frontend/src/explore/components/controls/SemanticRequestView.test.tsx
new file mode 100644
index 00000000000..81c31e5d985
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/SemanticRequestView.test.tsx
@@ -0,0 +1,111 @@
+/**
+ * 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 {
+ render,
+ screen,
+ userEvent,
+ waitFor,
+} from 'spec/helpers/testing-library';
+import copyTextToClipboard from 'src/utils/copy';
+import * as syntaxHighlighter from '@superset-ui/core/components/CodeSyntaxHighlighter';
+import SemanticRequestView, {
+ REQUEST_KIND_LANGUAGES,
+} from './SemanticRequestView';
+
+jest.mock('src/utils/copy', () => ({
+ __esModule: true,
+ default: jest.fn(() => Promise.resolve()),
+}));
+
+afterEach(() => {
+ jest.restoreAllMocks();
+ jest.clearAllMocks();
+});
+
+test('copies the complete raw provider request including headers and whitespace', async () => {
+ const requestText = '-- SQL\n SELECT 1;\n\n-- SQL\nSELECT\t2;\n';
+ const { container } = render(
+ <SemanticRequestView requestText={requestText} />,
+ {
+ useRedux: true,
+ },
+ );
+ await waitFor(() =>
+ expect(container.querySelector('pre')?.textContent).toBe(requestText),
+ );
+ await userEvent.click(screen.getByRole('button', { name: 'Copy' }));
+ const copyMock = jest.mocked(copyTextToClipboard);
+ expect(copyMock).toHaveBeenCalledTimes(1);
+ await expect(copyMock.mock.calls[0][0]()).resolves.toBe(requestText);
+});
+
+test.each(['SQL', 'sql', 'json'])(
+ 'uses the known %s kind only as a highlight hint',
+ async kind => {
+ const highlighter = jest.spyOn(syntaxHighlighter, 'default');
+ const requestText = `-- ${kind}\n{ "unformatted" : 1 }\n\n-- other\nprovider text`;
+ const { container } = render(
+ <SemanticRequestView requestText={requestText} />,
+ {
+ useRedux: true,
+ },
+ );
+ expect(highlighter).toHaveBeenCalledWith(
+ expect.objectContaining({
+ language: REQUEST_KIND_LANGUAGES.get(kind.toLowerCase()),
+ children: requestText,
+ showCopyButton: false,
+ }),
+ expect.anything(),
+ );
+ await waitFor(() =>
+ expect(container.querySelector('pre')?.textContent).toBe(requestText),
+ );
+ },
+);
+
+test.each(['snowflake-sql', 'graphql', 'constructor', 'unknown'])(
+ 'renders unknown kind %s as plain verbatim text',
+ kind => {
+ const highlighter = jest.spyOn(syntaxHighlighter, 'default');
+ const requestText = `-- ${kind}\n query { field }\n\n-- SQL\nSELECT 1;\n`;
+ const { container } = render(
+ <SemanticRequestView requestText={requestText} />,
+ {
+ useRedux: true,
+ },
+ );
+ expect(container.querySelector('pre')?.textContent).toBe(requestText);
+ expect(container.querySelector('pre')?.children).toHaveLength(0);
+ expect(highlighter).not.toHaveBeenCalled();
+ },
+);
+
+test('does not interpret provider text without a first-line host header', () => {
+ const highlighter = jest.spyOn(syntaxHighlighter, 'default');
+ const requestText = 'SELECT 1;\n-- SQL\nSELECT 2;';
+ const { container } = render(
+ <SemanticRequestView requestText={requestText} />,
+ {
+ useRedux: true,
+ },
+ );
+ expect(container.querySelector('pre')?.textContent).toBe(requestText);
+ expect(highlighter).not.toHaveBeenCalled();
+});
diff --git a/superset-frontend/src/explore/components/controls/SemanticRequestView.tsx b/superset-frontend/src/explore/components/controls/SemanticRequestView.tsx
new file mode 100644
index 00000000000..a2e3a3d6467
--- /dev/null
+++ b/superset-frontend/src/explore/components/controls/SemanticRequestView.tsx
@@ -0,0 +1,71 @@
+/**
+ * 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 { t } from '@apache-superset/core/translation';
+import { styled, type SupersetTheme } from '@apache-superset/core/theme';
+import { Button } from '@superset-ui/core/components';
+import CodeSyntaxHighlighter, {
+ SupportedLanguage,
+} from '@superset-ui/core/components/CodeSyntaxHighlighter';
+import { CopyToClipboard } from 'src/components';
+
+export interface SemanticRequestViewProps {
+ requestText: string;
+}
+
+export const REQUEST_KIND_LANGUAGES: ReadonlyMap<string, SupportedLanguage> =
+ new Map([
+ ['sql', 'sql'],
+ ['json', 'json'],
+ ]);
+
+const RequestTextContainer = styled.div`
+ max-height: ${({ theme }: { theme: SupersetTheme }) => theme.sizeUnit * 80}px;
+ overflow: auto;
+`;
+
+export default function SemanticRequestView({
+ requestText,
+}: SemanticRequestViewProps) {
+ // Only inspect the first host-authored header; provider text stays opaque.
+ const requestKind = /^-- ([^\r\n]*)/.exec(requestText)?.[1].toLowerCase();
+ const highlightLanguage = REQUEST_KIND_LANGUAGES.get(requestKind ?? '');
+
+ return (
+ <div>
+ <RequestTextContainer>
+ {highlightLanguage ? (
+ <CodeSyntaxHighlighter
+ language={highlightLanguage}
+ showCopyButton={false}
+ >
+ {requestText}
+ </CodeSyntaxHighlighter>
+ ) : (
+ // An undefined highlighter language defaults to SQL, not plain text.
+ <pre>{requestText}</pre>
+ )}
+ </RequestTextContainer>
+ <CopyToClipboard
+ text={requestText}
+ shouldShowText={false}
+ copyNode={<Button>{t('Copy')}</Button>}
+ />
+ </div>
+ );
+}
diff --git a/superset-frontend/src/explore/components/controls/ViewQueryModal.test.tsx b/superset-frontend/src/explore/components/controls/ViewQueryModal.test.tsx
index 8f79cd2a8d4..f773abacecc 100644
--- a/superset-frontend/src/explore/components/controls/ViewQueryModal.test.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQueryModal.test.tsx
@@ -17,11 +17,19 @@
* under the License.
*/
-import { screen, render, waitFor } from 'spec/helpers/testing-library';
+import {
+ act,
+ createStore,
+ screen,
+ render,
+ waitFor,
+} from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
+import { SupersetClient } from '@superset-ui/core';
import * as chartAction from 'src/components/Chart/chartAction';
import type { ChartDataRequestResponse } from 'src/components/Chart/chartAction';
-import ViewQueryModal from './ViewQueryModal';
+import ViewQueryModal, { getSemanticReportState } from './ViewQueryModal';
+import chartReducer, { chart } from 'src/components/Chart/chartReducer';
const mockFormData = {
datasource: '1__table',
@@ -39,6 +47,10 @@ const mockChartDataResponse: ChartDataRequestResponse = {
const chartDataEndpoint = 'glob:*/api/v1/chart/data*';
+beforeAll(() => {
+ SupersetClient.configure({ csrfToken: 'test-csrf-token' });
+});
+
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
@@ -217,3 +229,287 @@ test('falls back to empty ownState when prop is omitted', async () => {
}),
);
});
+
+test('shows semantic requests verbatim in entry order without fetching', async () => {
+ const getChartDataRequestSpy = jest.spyOn(chartAction, 'getChartDataRequest');
+ const requestTexts = [
+ '-- SQL\nSELECT 1\n\n-- SQL\nSELECT 2;\n',
+ '-- SQL\nSELECT 3;',
+ ];
+ const { container } = render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: {
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: requestTexts.map(query => ({ query })),
+ },
+ },
+ },
+ },
+ );
+
+ await waitFor(() =>
+ expect(
+ Array.from(container.querySelectorAll('pre'), pre => pre.textContent),
+ ).toEqual(requestTexts),
+ );
+ expect(screen.getAllByRole('button', { name: 'Copy' })).toHaveLength(2);
+ expect(getChartDataRequestSpy).not.toHaveBeenCalled();
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+ expect(screen.queryByRole('switch')).not.toBeInTheDocument();
+ expect(screen.queryByText('Run in SQL Lab')).not.toBeInTheDocument();
+});
+
+test('updates semantic request text when the saved chart run is replaced', async () => {
+ const getChartDataRequestSpy = jest.spyOn(chartAction, 'getChartDataRequest');
+ const store = createStore(
+ {
+ charts: {
+ 42: {
+ ...chart,
+ id: 42,
+ queriesResponse: [{ query: '-- SQL\nSELECT 1' }],
+ },
+ },
+ },
+ { charts: chartReducer },
+ );
+ const { container } = render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ slice_id: 42,
+ }}
+ />,
+ { store },
+ );
+ await waitFor(() =>
+ expect(container.querySelector('pre')?.textContent).toBe(
+ '-- SQL\nSELECT 1',
+ ),
+ );
+
+ act(() => {
+ store.dispatch({
+ type: chartAction.CHART_UPDATE_SUCCEEDED,
+ key: 42,
+ queriesResponse: [{ query: '-- SQL\nSELECT 2' }],
+ });
+ });
+ await waitFor(() =>
+ expect(container.querySelector('pre')?.textContent).toBe(
+ '-- SQL\nSELECT 2',
+ ),
+ );
+ expect(container.textContent).not.toContain('SELECT 1');
+ expect(getChartDataRequestSpy).not.toHaveBeenCalled();
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+});
+
+test.each<{
+ report: { query?: string }[] | null | undefined;
+ expected: ReturnType<typeof getSemanticReportState>;
+}>([
+ { report: undefined, expected: 'not-run' },
+ { report: null, expected: 'not-run' },
+ { report: [], expected: 'not-run' },
+ { report: [{ query: '' }], expected: 'none-reported' },
+ { report: [{}], expected: 'none-reported' },
+ { report: [{ query: 'x' }], expected: 'has-requests' },
+ { report: [{}, { query: 'x' }], expected: 'has-requests' },
+])('classifies $report as $expected', ({ report, expected }) => {
+ expect(getSemanticReportState(report)).toBe(expected);
+});
+
+test.each<{
+ report: { query?: string }[] | null;
+ message: string;
+ otherMessage: string;
+}>([
+ {
+ report: null,
+ message: 'The provider query will be available after the chart runs.',
+ otherMessage: 'No provider query is available for this run.',
+ },
+ {
+ report: [{ query: '' }],
+ message: 'No provider query is available for this run.',
+ otherMessage: 'The provider query will be available after the chart runs.',
+ },
+ {
+ report: [{}],
+ message: 'No provider query is available for this run.',
+ otherMessage: 'The provider query will be available after the chart runs.',
+ },
+])(
+ 'shows an honest empty state for $report',
+ ({ report, message, otherMessage }) => {
+ const getChartDataRequestSpy = jest.spyOn(
+ chartAction,
+ 'getChartDataRequest',
+ );
+ render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: { charts: { 0: { ...chart, queriesResponse: report } } },
+ },
+ );
+ expect(screen.getByRole('alert')).toHaveTextContent(message);
+ expect(screen.queryByText(otherMessage)).not.toBeInTheDocument();
+ expect(getChartDataRequestSpy).not.toHaveBeenCalled();
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+ },
+);
+
+test('preserves empty entries alongside reported requests in a mixed run', () => {
+ const requestText = '-- graphql\nquery { measures }';
+ const { container } = render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: {
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [{}, { query: requestText }, { query: '' }],
+ },
+ },
+ },
+ },
+ );
+ const entries = container.querySelectorAll('[role="alert"], pre');
+ expect(Array.from(entries, entry => entry.textContent)).toEqual([
+ 'No provider query is available for this run.',
+ requestText,
+ 'No provider query is available for this run.',
+ ]);
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+});
+
+test('uses the owning chart ID when query form data has no saved-chart ID', () => {
+ const request = '-- SQL\nSELECT saved_chart';
+ const { container } = render(
+ <ViewQueryModal
+ chartId={42}
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: {
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [{ query: '-- SQL\nSELECT unrelated' }],
+ },
+ 42: { ...chart, id: 42, queriesResponse: [{ query: request }] },
+ },
+ },
+ },
+ );
+ expect(container.querySelector('pre')?.textContent).toBe(request);
+ expect(container.textContent).not.toContain('unrelated');
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+});
+
+test.each([
+ {
+ response: [{ error: 'Provider rejected the selected dimension.' }],
+ message: 'Provider rejected the selected dimension.',
+ },
+ { response: undefined, message: 'Network error.' },
+])('shows the actual chart failure: $message', ({ response, message }) => {
+ const store = createStore(
+ {
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [{ query: '-- SQL\nSELECT previous' }],
+ },
+ },
+ },
+ { charts: chartReducer },
+ );
+ const { container } = render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ { store },
+ );
+ act(() => {
+ store.dispatch({
+ type: chartAction.CHART_UPDATE_FAILED,
+ key: 0,
+ queriesResponse: response,
+ });
+ });
+ expect(screen.getByRole('alert')).toHaveTextContent(message);
+ expect(container.textContent).not.toContain('No provider query is available');
+ expect(container.textContent).not.toContain('after the chart runs');
+ expect(container.textContent).not.toContain('SELECT previous');
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+});
+
+test('keeps per-entry errors beside provider requests in a mixed response', () => {
+ const { container } = render(
+ <ViewQueryModal
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: {
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [
+ { query: '-- SQL\nSELECT first' },
+ { error: 'Second query failed.' },
+ { query: '-- SQL\nSELECT third', error: 'Third query failed.' },
+ ],
+ },
+ },
+ },
+ },
+ );
+ expect(
+ Array.from(
+ container.querySelectorAll('[role="alert"], pre'),
+ entry => entry.textContent,
+ ),
+ ).toEqual([
+ '-- SQL\nSELECT first',
+ 'Second query failed.',
+ 'Third query failed.',
+ '-- SQL\nSELECT third',
+ ]);
+ expect(container.textContent).not.toContain('No provider query is available');
+ expect(fetchMock.callHistory.calls()).toHaveLength(0);
+});
diff --git a/superset-frontend/src/explore/components/controls/ViewQueryModal.tsx b/superset-frontend/src/explore/components/controls/ViewQueryModal.tsx
index 1f572278d3f..05683fc0a2d 100644
--- a/superset-frontend/src/explore/components/controls/ViewQueryModal.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQueryModal.tsx
@@ -17,25 +17,33 @@
* under the License.
*/
import { FC, Fragment, useCallback, useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
import { omit } from 'lodash-es';
import { t } from '@apache-superset/core/translation';
import {
+ ClientErrorObject,
+ DatasourceKey,
+ DatasourceType,
ensureIsArray,
getClientErrorObject,
JsonObject,
QueryFormData,
} from '@superset-ui/core';
import { Alert } from '@apache-superset/core/components';
-import { styled } from '@apache-superset/core/theme';
+import { styled, type SupersetTheme } from '@apache-superset/core/theme';
import { Loading } from '@superset-ui/core/components';
import { SupportedLanguage } from '@superset-ui/core/components/CodeSyntaxHighlighter';
import { getChartDataRequest } from 'src/components/Chart/chartAction';
import ViewQuery from 'src/explore/components/controls/ViewQuery';
+import type { ChartState } from 'src/explore/types';
+import type { RootState } from 'src/views/store';
+import SemanticRequestView from './SemanticRequestView';
interface Props {
latestQueryFormData: QueryFormData;
ownState?: JsonObject;
+ chartId?: number;
}
type Result = {
@@ -44,14 +52,36 @@ type Result = {
error?: string;
};
+export function getSemanticReportState(
+ queriesResponse: readonly { query?: string }[] | null | undefined,
+): 'has-requests' | 'none-reported' | 'not-run' {
+ if (!queriesResponse?.length) {
+ return 'not-run';
+ }
+ return queriesResponse.some(entry => entry.query)
+ ? 'has-requests'
+ : 'none-reported';
+}
+
const ViewQueryModalContainer = styled.div`
height: 100%;
display: flex;
flex-direction: column;
- gap: ${({ theme }) => theme.sizeUnit * 4}px;
+ gap: ${({ theme }: { theme: SupersetTheme }) => theme.sizeUnit * 4}px;
`;
-const ViewQueryModal: FC<Props> = ({ latestQueryFormData, ownState }) => {
+const ViewQueryModal: FC<Props> = ({
+ latestQueryFormData,
+ ownState,
+ chartId,
+}) => {
+ const isSemanticView =
+ new DatasourceKey(latestQueryFormData.datasource).type ===
+ DatasourceType.SemanticView;
+ const chartState = useSelector<RootState, ChartState | undefined>(
+ state => state.charts?.[chartId ?? latestQueryFormData.slice_id ?? 0],
+ );
+ const queriesResponse = chartState?.queriesResponse;
const [result, setResult] = useState<Result[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -76,22 +106,62 @@ const ViewQueryModal: FC<Props> = ({ latestQueryFormData, ownState }) => {
setError(null);
})
.catch(response => {
- getClientErrorObject(response).then(({ error, message }) => {
- setError(
- error ||
- message ||
- response.statusText ||
- t('Sorry, An error occurred'),
- );
- setIsLoading(false);
- });
+ getClientErrorObject(response).then(
+ ({ error, message }: ClientErrorObject) => {
+ setError(
+ error ||
+ message ||
+ response.statusText ||
+ t('Sorry, An error occurred'),
+ );
+ setIsLoading(false);
+ },
+ );
});
},
[latestQueryFormData, ownState],
);
useEffect(() => {
- loadChartData('query');
- }, [loadChartData]);
+ if (!isSemanticView) {
+ loadChartData('query');
+ }
+ }, [isSemanticView, loadChartData]);
+
+ if (isSemanticView) {
+ const reportState = getSemanticReportState(queriesResponse);
+ const noRequestMessage = t('No provider query is available for this run.');
+ const chartError =
+ chartState?.chartStatus === 'failed' ? chartState.chartAlert : null;
+ return (
+ <ViewQueryModalContainer>
+ {chartError && !queriesResponse?.some(entry => entry.error) && (
+ <Alert type="error" message={chartError} closable={false} />
+ )}
+ {reportState === 'not-run'
+ ? !chartError && (
+ <Alert
+ type="info"
+ message={t(
+ 'The provider query will be available after the chart runs.',
+ )}
+ />
+ )
+ : queriesResponse?.map((entry, index) => (
+ <Fragment key={index}>
+ {entry.error && (
+ <Alert type="error" message={entry.error} closable={false} />
+ )}
+ {entry.query && (
+ <SemanticRequestView requestText={entry.query} />
+ )}
+ {!entry.error && !entry.query && (
+ <Alert type="info" message={noRequestMessage} />
+ )}
+ </Fragment>
+ ))}
+ </ViewQueryModalContainer>
+ );
+ }
if (isLoading) {
return <Loading />;
diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx
index bcaa029b47a..56ece0349fd 100644
--- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx
+++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx
@@ -1138,6 +1138,7 @@ export const useExploreAdditionalActionsMenu = (
modalTitle={t('View query')}
modalBody={
<ViewQueryModal
+ chartId={slice?.slice_id}
latestQueryFormData={latestQueryFormData as QueryFormData}
ownState={ownState}
/>
diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/useExploreAdditionalActionsMenu.test.tsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/useExploreAdditionalActionsMenu.test.tsx
index 9e829c3549a..1602db06770 100644
--- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/useExploreAdditionalActionsMenu.test.tsx
+++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/useExploreAdditionalActionsMenu.test.tsx
@@ -29,6 +29,7 @@ import {
} from './index';
import * as exploreUtils from 'src/explore/exploreUtils';
import { Slice } from 'src/types/Chart';
+import { chart } from 'src/components/Chart/chartReducer';
jest.mock('src/explore/exploreUtils', () => ({
__esModule: true,
@@ -121,6 +122,38 @@ const TestComponent = (props: TestComponentProps) => {
return <div>{menu}</div>;
};
+test('View query uses the saved Explore chart when form data omits slice_id', async () => {
+ const request = '-- SQL\nSELECT saved_explore_chart';
+ render(
+ <TestComponent
+ {...defaultProps}
+ latestQueryFormData={{
+ datasource: '12__semantic_view',
+ viz_type: 'table',
+ }}
+ />,
+ {
+ useRedux: true,
+ initialState: {
+ explore: {},
+ charts: {
+ 0: {
+ ...chart,
+ queriesResponse: [{ query: '-- SQL\nSELECT unrelated' }],
+ },
+ 1: { ...chart, id: 1, queriesResponse: [{ query: request }] },
+ },
+ },
+ },
+ );
+ await userEvent.click(await screen.findByText('View query'));
+ await waitFor(() =>
+ expect(screen.getByRole('dialog').querySelector('pre')?.textContent).toBe(
+ request,
+ ),
+ );
+});
+
beforeEach(() => {
jest.clearAllMocks();
mockExportChart.mockResolvedValue(undefined);
diff --git a/superset/semantic_layers/models.py b/superset/semantic_layers/models.py
index d17cef6de62..fd7f8432ba8 100644
--- a/superset/semantic_layers/models.py
+++ b/superset/semantic_layers/models.py
@@ -395,7 +395,13 @@ class SemanticView(AuditMixinNullable, Model):
return result
def get_query_str(self, query_obj: QueryObjectDict) -> str:
- return "Not implemented for semantic layers"
+ """Reject previews because provider queries are returned with chart data."""
+ raise QueryObjectValidationError(
+ _(
+ "A semantic view's provider query is produced when the chart runs "
+ "and returned with its data."
+ )
+ )
@property
def normalize_columns(self) -> bool:
diff --git a/superset/translations/ar/LC_MESSAGES/messages.po b/superset/translations/ar/LC_MESSAGES/messages.po
index 3d709351fd4..0609b7f5f77 100644
--- a/superset/translations/ar/LC_MESSAGES/messages.po
+++ b/superset/translations/ar/LC_MESSAGES/messages.po
@@ -1297,6 +1297,11 @@ msgstr "سيتم حفظ مجموعة بيانات قابلة لإعادة الا
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11476,6 +11481,9 @@ msgstr "لم يتم العثور على سجلات مطابقة"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "لم تتم إضافة أي فلاتر حاليًا إلى لوحة التحكم هذه."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr " لم يتم العثور على أية سجلات "
@@ -17447,6 +17455,9 @@ msgstr "يتم استخدام المقياس الأساسي لتحديد أحج
msgid "The provided table was not found in the provided database"
msgstr "لم يتم العثور على الجدول المقدم في قاعدة البيانات المقدمة"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "تم حذف الاستعلام المرتبط بالنتائج."
diff --git a/superset/translations/ca/LC_MESSAGES/messages.po b/superset/translations/ca/LC_MESSAGES/messages.po
index e22caf4535f..fc6bf087f5a 100644
--- a/superset/translations/ca/LC_MESSAGES/messages.po
+++ b/superset/translations/ca/LC_MESSAGES/messages.po
@@ -1164,6 +1164,11 @@ msgstr "Es desarà un conjunt de dades reutilitzable amb el teu gràfic."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11054,6 +11059,9 @@ msgstr "Cap registre coincident trobat"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Cap filtre està afegit actualment a aquest dashboard."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Cap registre trobat"
@@ -16906,6 +16914,9 @@ msgstr "La mètrica primària s'usa per definir les mides dels segments d'arc"
msgid "The provided table was not found in the provided database"
msgstr "La taula proporcionada no s'ha trobat a la base de dades proporcionada"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "La consulta associada amb els resultats va ser eliminada."
diff --git a/superset/translations/cs/LC_MESSAGES/messages.po b/superset/translations/cs/LC_MESSAGES/messages.po
index 6084be02156..89557f54576 100644
--- a/superset/translations/cs/LC_MESSAGES/messages.po
+++ b/superset/translations/cs/LC_MESSAGES/messages.po
@@ -1169,6 +1169,11 @@ msgstr "S vaším grafem bude uložena znovupoužitelná sada dat."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10675,6 +10680,9 @@ msgstr "Žádné odpovídající výsledky"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Na této nástěnce nejsou v současné době přidány žádné filtry."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Nebyly nalezeny žádné záznamy"
@@ -16332,6 +16340,9 @@ msgstr "Primární metrika se používá k definování velikostí segmentů obl
msgid "The provided table was not found in the provided database"
msgstr "Zadaná tabulka nebyla nalezena v zadané databázi"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Dotaz spojený s výsledky byl smazán."
diff --git a/superset/translations/de/LC_MESSAGES/messages.po b/superset/translations/de/LC_MESSAGES/messages.po
index 61133608c9d..3eef3320b91 100644
--- a/superset/translations/de/LC_MESSAGES/messages.po
+++ b/superset/translations/de/LC_MESSAGES/messages.po
@@ -1129,6 +1129,11 @@ msgstr "Ein wiederverwendbarer Datensatz wird mit Ihrem Diagramm gespeichert."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10402,6 +10407,9 @@ msgstr ""
"Derzeit sind keine mehrschichtigen Deck.gl-Diagramme zu diesem Dashboard "
"hinzugefügt."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Keine Einträge gefunden"
@@ -15948,6 +15956,9 @@ msgstr "Die primäre Metrik wird verwendet, um die Bogensegmentgrößen zu defin
msgid "The provided table was not found in the provided database"
msgstr "Die angegebene Tabelle wurde in der angegebenen Datenbank nicht gefunden"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Die den Ergebnissen zugeordnete Abfrage wurde gelöscht."
diff --git a/superset/translations/en/LC_MESSAGES/messages.po b/superset/translations/en/LC_MESSAGES/messages.po
index c23e0da9cd7..8a0a8889454 100644
--- a/superset/translations/en/LC_MESSAGES/messages.po
+++ b/superset/translations/en/LC_MESSAGES/messages.po
@@ -1041,6 +1041,11 @@ msgstr ""
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -9695,6 +9700,9 @@ msgstr ""
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr ""
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr ""
@@ -14743,6 +14751,9 @@ msgstr ""
msgid "The provided table was not found in the provided database"
msgstr ""
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr ""
diff --git a/superset/translations/es/LC_MESSAGES/messages.po b/superset/translations/es/LC_MESSAGES/messages.po
index 7161b61d7a7..3c482ca154d 100644
--- a/superset/translations/es/LC_MESSAGES/messages.po
+++ b/superset/translations/es/LC_MESSAGES/messages.po
@@ -1152,6 +1152,11 @@ msgstr "Se guardará un conjunto de datos reutilizable con tu gráfico."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10942,6 +10947,9 @@ msgstr "No se han encontrado registros coincidentes"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Actualmente no se han añadido filtros a este panel de control."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "No se han encontrado registros"
@@ -16789,6 +16797,9 @@ msgstr ""
"La tabla proporcionada no se ha encontrado en la base de datos "
"proporcionada"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "La consulta asociada con los resultados se ha eliminado."
diff --git a/superset/translations/fa/LC_MESSAGES/messages.po b/superset/translations/fa/LC_MESSAGES/messages.po
index 835e9a0a90f..dff26c94110 100644
--- a/superset/translations/fa/LC_MESSAGES/messages.po
+++ b/superset/translations/fa/LC_MESSAGES/messages.po
@@ -1176,6 +1176,11 @@ msgstr "یک مجموعه داده قابل استفاده مجدد با چار
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11134,6 +11139,9 @@ msgstr "هیچ رکوردی پیدا نشد"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "فعلاً هیچ فیلتری به این داشبورد اضافه نشده است."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "هیچ رکوردی یافت نشد"
@@ -17005,6 +17013,9 @@ msgstr "معیار اصلی برای تعریف اندازههای بخش
msgid "The provided table was not found in the provided database"
msgstr "جدول ارائه شده در پایگاه داده ارائه شده یافت نشد."
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "کوئری مرتبط با نتایج حذف شد."
diff --git a/superset/translations/fi/LC_MESSAGES/messages.po b/superset/translations/fi/LC_MESSAGES/messages.po
index 9e04bd8594a..e8dc28d769c 100644
--- a/superset/translations/fi/LC_MESSAGES/messages.po
+++ b/superset/translations/fi/LC_MESSAGES/messages.po
@@ -34476,3 +34476,14 @@ msgstr[1] ""
#, fuzzy
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/fr/LC_MESSAGES/messages.po b/superset/translations/fr/LC_MESSAGES/messages.po
index a9787c9529e..cc2edfe4024 100644
--- a/superset/translations/fr/LC_MESSAGES/messages.po
+++ b/superset/translations/fr/LC_MESSAGES/messages.po
@@ -1149,6 +1149,11 @@ msgstr "Un jeu de données réutilisable sera enregistré avec votre graphique."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10583,6 +10588,9 @@ msgstr ""
"Aucun graphique deck.gl multi-couches n'est actuellement ajouté à ce "
"tableau de bord."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Aucun enregistrement n'a été trouvé"
@@ -16250,6 +16258,9 @@ msgstr ""
msgid "The provided table was not found in the provided database"
msgstr "La table est introuvable dans la base de données."
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "La requête associée aux résutlats a été supprimée."
diff --git a/superset/translations/it/LC_MESSAGES/messages.po b/superset/translations/it/LC_MESSAGES/messages.po
index bafe4b037e5..31c9efe34c3 100644
--- a/superset/translations/it/LC_MESSAGES/messages.po
+++ b/superset/translations/it/LC_MESSAGES/messages.po
@@ -26419,3 +26419,14 @@ msgstr[1] ""
# sr_Latn]
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/ja/LC_MESSAGES/messages.po b/superset/translations/ja/LC_MESSAGES/messages.po
index 16160d631f7..fdc6665fdd2 100644
--- a/superset/translations/ja/LC_MESSAGES/messages.po
+++ b/superset/translations/ja/LC_MESSAGES/messages.po
@@ -1053,6 +1053,11 @@ msgstr "再利用可能なデータセットがチャートと共に保存され
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -9718,6 +9723,9 @@ msgstr "一致する結果が見つかりません"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "このダッシュボードにはマルチレイヤーdeck.glチャートはまだ追加されていません。"
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "レコードが見つかりません"
@@ -14810,6 +14818,9 @@ msgstr "主要メトリックは、円セグメントのサイズを定義する
msgid "The provided table was not found in the provided database"
msgstr "指定されたデータベース内にテーブルが見つかりませんでした"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "結果に関連付けられたクエリが削除されました。"
diff --git a/superset/translations/ko/LC_MESSAGES/messages.po b/superset/translations/ko/LC_MESSAGES/messages.po
index 9f37a027108..3e8fd66c0e7 100644
--- a/superset/translations/ko/LC_MESSAGES/messages.po
+++ b/superset/translations/ko/LC_MESSAGES/messages.po
@@ -31416,3 +31416,14 @@ msgstr[0] ""
#, fuzzy
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/lv/LC_MESSAGES/messages.po b/superset/translations/lv/LC_MESSAGES/messages.po
index 85918530566..b291c645480 100644
--- a/superset/translations/lv/LC_MESSAGES/messages.po
+++ b/superset/translations/lv/LC_MESSAGES/messages.po
@@ -1168,6 +1168,11 @@ msgstr "Ar jūsu diagrammu tiks saglabāta atkārtoti lietojama datu kopa."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10470,6 +10475,9 @@ msgstr "Nav atrasti atbilstoši rezultāti"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Šim informācijas panelim pašlaik nav pievienotu filtru."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Ieraksti nav atrasti"
@@ -16023,6 +16031,9 @@ msgstr "Primārā metrika tiek izmantota loka segmentu izmēru noteikšanai"
msgid "The provided table was not found in the provided database"
msgstr "Norādītā tabula netika atrasta norādītajā datubāzē"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Ar rezultātiem saistītais vaicājums tika dzēsts."
diff --git a/superset/translations/messages.pot b/superset/translations/messages.pot
index 106cd86ba01..9d5ab528de6 100644
--- a/superset/translations/messages.pot
+++ b/superset/translations/messages.pot
@@ -988,6 +988,11 @@ msgstr ""
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid "A set of parameters that become available in the query using Jinja templating syntax"
msgstr ""
@@ -9190,6 +9195,9 @@ msgstr ""
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr ""
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr ""
@@ -13843,6 +13851,9 @@ msgstr ""
msgid "The provided table was not found in the provided database"
msgstr ""
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr ""
diff --git a/superset/translations/mi/LC_MESSAGES/messages.po b/superset/translations/mi/LC_MESSAGES/messages.po
index a374795eeef..1f269eb77bc 100644
--- a/superset/translations/mi/LC_MESSAGES/messages.po
+++ b/superset/translations/mi/LC_MESSAGES/messages.po
@@ -1170,6 +1170,11 @@ msgstr ""
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10979,6 +10984,9 @@ msgstr "Kāore i kitea he rekoata hāngai"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Kāore he tātari kua tāpiritia ki tēnei papatohu i tēnei wā."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Kāore i kitea he rekoata"
@@ -16813,6 +16821,9 @@ msgstr "Ka whakamahia te ine matua hei tautuhi i ngā rahi wāhanga kopeka"
msgid "The provided table was not found in the provided database"
msgstr "Kāore i kitea te ripanga kua tukuna i te pātengi raraunga kua tukuna"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "I mukua te pātai e hāngai ana ki ngā hua."
diff --git a/superset/translations/nl/LC_MESSAGES/messages.po b/superset/translations/nl/LC_MESSAGES/messages.po
index e90004dcdba..4c6880f7005 100644
--- a/superset/translations/nl/LC_MESSAGES/messages.po
+++ b/superset/translations/nl/LC_MESSAGES/messages.po
@@ -1182,6 +1182,11 @@ msgstr "Een herbruikbaar dataset wordt opgeslagen met uw grafiek."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11375,6 +11380,9 @@ msgstr "Geen overeenkomende records gevonden"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Er zijn momenteel geen filters toegevoegd aan dit dashboard."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Geen gegevens gevonden"
@@ -17405,6 +17413,9 @@ msgstr "De primaire metriek wordt gebruikt om de boogsegmentformaten te definië
msgid "The provided table was not found in the provided database"
msgstr "De opgegeven tabel is niet gevonden in de opgegeven database"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "De query die is gekoppeld aan de resultaten is verwijderd."
diff --git a/superset/translations/pl/LC_MESSAGES/messages.po b/superset/translations/pl/LC_MESSAGES/messages.po
index c5e880d469d..39bdc63b65d 100644
--- a/superset/translations/pl/LC_MESSAGES/messages.po
+++ b/superset/translations/pl/LC_MESSAGES/messages.po
@@ -1213,6 +1213,11 @@ msgstr "Zostanie zapisany wielokrotnego użytku zestaw danych z twoim wykresem."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11544,6 +11549,9 @@ msgstr "Nie znaleziono pasujących rekordów"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Do tego dashboardu nie dodano żadnych filtrów."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Nie znaleziono rekordów"
@@ -17724,6 +17732,9 @@ msgstr "Główna metryka jest używana do określenia rozmiarów segmentów łuk
msgid "The provided table was not found in the provided database"
msgstr "Podana tabela nie została znaleziona w podanej bazie danych."
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Zapytanie powiązane z wynikami zostało usunięte."
diff --git a/superset/translations/pt/LC_MESSAGES/messages.po b/superset/translations/pt/LC_MESSAGES/messages.po
index 8008cc1d7c6..6053a58d69a 100644
--- a/superset/translations/pt/LC_MESSAGES/messages.po
+++ b/superset/translations/pt/LC_MESSAGES/messages.po
@@ -27241,3 +27241,14 @@ msgstr[1] ""
# sr_Latn]
msgid "№"
msgstr "Nº"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/pt_BR/LC_MESSAGES/messages.po b/superset/translations/pt_BR/LC_MESSAGES/messages.po
index b2d168f9130..0d4aea7e0bf 100644
--- a/superset/translations/pt_BR/LC_MESSAGES/messages.po
+++ b/superset/translations/pt_BR/LC_MESSAGES/messages.po
@@ -1169,6 +1169,11 @@ msgstr "Um conjunto de dados reutilizável vai ser salvo com seu gráfico."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11431,6 +11436,9 @@ msgstr ""
"Nenhum gráfico deck.gl multicamadas foi adicionado a este painel no "
"momento."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Não foram encontrados registos"
@@ -17539,6 +17547,9 @@ msgstr "A métrica primária é usada para definir os tamanhos dos segmentos de
msgid "The provided table was not found in the provided database"
msgstr "A tabela fornecida não foi encontrada no banco de dados fornecido"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "A consulta associada aos resultados foi excluída."
diff --git a/superset/translations/ro/LC_MESSAGES/messages.po b/superset/translations/ro/LC_MESSAGES/messages.po
index c7fee5a75a7..9aa21033dd9 100644
--- a/superset/translations/ro/LC_MESSAGES/messages.po
+++ b/superset/translations/ro/LC_MESSAGES/messages.po
@@ -1226,6 +1226,11 @@ msgstr "Un set de date reutilizabil va fi salvat cu graficul dvs."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11204,6 +11209,9 @@ msgstr ""
"Nu există diagrame deck.gl cu mai multe straturi adăugate în prezent la "
"acest tablou de bord."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Nicio înregistrare găsită"
@@ -17101,6 +17109,9 @@ msgstr ""
msgid "The provided table was not found in the provided database"
msgstr "Tabelul furnizat nu a fost găsit în baza de date furnizată"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Interogarea asociată cu rezultatele a fost ștearsă."
diff --git a/superset/translations/ru/LC_MESSAGES/messages.po b/superset/translations/ru/LC_MESSAGES/messages.po
index 1fdfadc02e1..0586ffc5f09 100644
--- a/superset/translations/ru/LC_MESSAGES/messages.po
+++ b/superset/translations/ru/LC_MESSAGES/messages.po
@@ -1154,6 +1154,11 @@ msgstr "Переиспользуемый датасет будет сохран
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10309,6 +10314,9 @@ msgstr "Совпадений не найдено"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "На этот дашборд не добавлена ни одна диаграмма deck.gl."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Записи не найдены"
@@ -15727,6 +15735,9 @@ msgstr "Основная мера используется для определ
msgid "The provided table was not found in the provided database"
msgstr "Таблица не найдена в базе данных"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Запрос, связанный с этими результатами, был удален"
diff --git a/superset/translations/sk/LC_MESSAGES/messages.po b/superset/translations/sk/LC_MESSAGES/messages.po
index 750f3c55eda..001794c899e 100644
--- a/superset/translations/sk/LC_MESSAGES/messages.po
+++ b/superset/translations/sk/LC_MESSAGES/messages.po
@@ -1162,6 +1162,11 @@ msgstr "S vaším grafem bude uložena znovupoužitelná sada dat."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10684,6 +10689,9 @@ msgstr "Žiadne zodpovedajúci výsledky"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Na tejto nástenke nesú v současné dobe pridanýy žiadne filtre."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Nebyly nalezeny žiadne záznamy"
@@ -16336,6 +16344,9 @@ msgstr "Primární metrika sa používa k definovanie veľkostí segmentů oblou
msgid "The provided table was not found in the provided database"
msgstr "Zadaná tabuľka nebyla nalezena v zadané databázy"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Dotaz spojený s výsledky bol zmazaný."
diff --git a/superset/translations/sl/LC_MESSAGES/messages.po b/superset/translations/sl/LC_MESSAGES/messages.po
index 56d58d4f7c6..93055ef7089 100644
--- a/superset/translations/sl/LC_MESSAGES/messages.po
+++ b/superset/translations/sl/LC_MESSAGES/messages.po
@@ -1199,6 +1199,11 @@ msgstr "Podatkovni set bo shranjen skupaj z grafikonom."
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10623,6 +10628,9 @@ msgstr "Ni ustreznih rezultatov"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Na to nadzorno ploščo trenutno ni dodanih večplastnih grafikonov deck.gl."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Ni zapisov"
@@ -16204,6 +16212,9 @@ msgstr "Primarna mera določa velikost lokov segmentov"
msgid "The provided table was not found in the provided database"
msgstr "Podana tabela ni bila najdena v podani podatkovni bazi"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Poizvedba, povezana z rezultati, je bila izbrisana."
diff --git a/superset/translations/sr/LC_MESSAGES/messages.po b/superset/translations/sr/LC_MESSAGES/messages.po
index 5a11a9ca255..6fd655505a5 100644
--- a/superset/translations/sr/LC_MESSAGES/messages.po
+++ b/superset/translations/sr/LC_MESSAGES/messages.po
@@ -25127,3 +25127,14 @@ msgstr[2] ""
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/sr_Latn/LC_MESSAGES/messages.po b/superset/translations/sr_Latn/LC_MESSAGES/messages.po
index a6fe356aa96..f08af8588f2 100644
--- a/superset/translations/sr_Latn/LC_MESSAGES/messages.po
+++ b/superset/translations/sr_Latn/LC_MESSAGES/messages.po
@@ -25145,3 +25145,14 @@ msgstr[2] ""
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/ta/LC_MESSAGES/messages.po b/superset/translations/ta/LC_MESSAGES/messages.po
index 46d4038a7c0..53635083e4f 100644
--- a/superset/translations/ta/LC_MESSAGES/messages.po
+++ b/superset/translations/ta/LC_MESSAGES/messages.po
@@ -1133,6 +1133,11 @@ msgstr ""
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10322,6 +10327,9 @@ msgstr "பொருத்தமான முடிவுகள் எதுவ
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "இந்த டேஷ்போர்டில் தற்போது வடிகட்டிகள் எதுவும் சேர்க்கப்படவில்லை."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "பதிவுகள் எதுவும் காணப்படவில்லை"
@@ -15606,6 +15614,9 @@ msgstr "வில் துண்டு அளவுகளை வரையறு
msgid "The provided table was not found in the provided database"
msgstr "வழங்கப்பட்ட தரவுத்தளத்தில் வழங்கப்பட்ட அட்டவணை காணப்படவில்லை"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "முடிவுகளுடன் தொடர்புடைய வினவல் நீக்கப்பட்டது."
diff --git a/superset/translations/th/LC_MESSAGES/messages.po b/superset/translations/th/LC_MESSAGES/messages.po
index bc1e6fcc287..cb666797978 100644
--- a/superset/translations/th/LC_MESSAGES/messages.po
+++ b/superset/translations/th/LC_MESSAGES/messages.po
@@ -34075,3 +34075,14 @@ msgstr[0] ""
#, fuzzy
msgid "№"
msgstr "№"
+
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
+msgid "No provider query is available for this run."
+msgstr ""
+
+msgid "The provider query will be available after the chart runs."
+msgstr ""
diff --git a/superset/translations/tr/LC_MESSAGES/messages.po b/superset/translations/tr/LC_MESSAGES/messages.po
index dfd9c66f21a..932843dfecd 100644
--- a/superset/translations/tr/LC_MESSAGES/messages.po
+++ b/superset/translations/tr/LC_MESSAGES/messages.po
@@ -1139,6 +1139,11 @@ msgstr "Yeniden kullanılabilir bir veri kümesi, grafiğinizle kurtarılacaktı
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10952,6 +10957,9 @@ msgstr "Sonuç bulunamadı"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Bu panoya şu anda hiçbir çok katmanlı deck.gl grafiği eklenmemiş."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Hiçbir kayıt bulunamadı"
@@ -16755,6 +16763,9 @@ msgstr "İlk metrik, ark segment boyutlarını tanımlamak için kullanılır"
msgid "The provided table was not found in the provided database"
msgstr "Verilen masa, sağlanan veritabanında bulunamadı"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Sonuçlarla ilgili sorgu silindi."
diff --git a/superset/translations/uk/LC_MESSAGES/messages.po b/superset/translations/uk/LC_MESSAGES/messages.po
index 22897bfabd0..6b89ad7725b 100644
--- a/superset/translations/uk/LC_MESSAGES/messages.po
+++ b/superset/translations/uk/LC_MESSAGES/messages.po
@@ -1193,6 +1193,11 @@ msgstr ""
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10471,6 +10476,9 @@ msgstr "Не знайдено відповідних результатів"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "Наразі до цього звіту не додано жодного фільтра."
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "Не знайдено жодного запису"
@@ -16027,6 +16035,9 @@ msgstr ""
msgid "The provided table was not found in the provided database"
msgstr "Таблицю не знайдено в базі даних"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "Запит, який повʼязано з результатом, вилучено."
diff --git a/superset/translations/zh/LC_MESSAGES/messages.po b/superset/translations/zh/LC_MESSAGES/messages.po
index c78222bfd97..3746f87c8df 100644
--- a/superset/translations/zh/LC_MESSAGES/messages.po
+++ b/superset/translations/zh/LC_MESSAGES/messages.po
@@ -1184,6 +1184,11 @@ msgstr "一个可重复使用的数据集将与您的图表一起保存。"
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -10835,6 +10840,9 @@ msgstr "未找到匹配结果"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "当前仪表板未添加任何多层 deck.gl 图表。"
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "没有找到任何记录"
@@ -16505,6 +16513,9 @@ msgstr "主计量指标用于定义弧段大小。"
msgid "The provided table was not found in the provided database"
msgstr "该表在数据库中不存在"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "与结果关联的查询已删除。"
diff --git a/superset/translations/zh_TW/LC_MESSAGES/messages.po b/superset/translations/zh_TW/LC_MESSAGES/messages.po
index 64336a7019c..318080717cb 100644
--- a/superset/translations/zh_TW/LC_MESSAGES/messages.po
+++ b/superset/translations/zh_TW/LC_MESSAGES/messages.po
@@ -1204,6 +1204,11 @@ msgstr "可重複使用的資料集將與您的圖表一同儲存。"
msgid "A rule with this name already exists."
msgstr ""
+msgid ""
+"A semantic view's provider query is produced when the chart runs and "
+"returned with its data."
+msgstr ""
+
msgid ""
"A set of parameters that become available in the query using Jinja "
"templating syntax"
@@ -11557,6 +11562,9 @@ msgstr "没有找到任何紀錄"
msgid "No multilayer deck.gl charts are currently added to this dashboard."
msgstr "此看板中没有過濾條件。"
+msgid "No provider query is available for this run."
+msgstr ""
+
msgid "No records found"
msgstr "没有找到任何紀錄"
@@ -17607,6 +17615,9 @@ msgstr "主計量指標用於定義弧段大小。"
msgid "The provided table was not found in the provided database"
msgstr "該表在資料庫中不存在"
+msgid "The provider query will be available after the chart runs."
+msgstr ""
+
msgid "The query associated with the results was deleted."
msgstr "與结果關聯的查詢已删除。"
diff --git a/tests/unit_tests/semantic_layers/models_test.py b/tests/unit_tests/semantic_layers/models_test.py
index 4306ba4aff0..0a1ced8de29 100644
--- a/tests/unit_tests/semantic_layers/models_test.py
+++ b/tests/unit_tests/semantic_layers/models_test.py
@@ -19,12 +19,16 @@
from __future__ import annotations
+import os
import uuid
+from pathlib import Path
from typing import Any, cast
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock, patch, PropertyMock
import pyarrow as pa
import pytest
+from flask.testing import FlaskClient
+from pytest_mock import MockerFixture
from sqlalchemy import inspect, select
from sqlalchemy.engine import Connection, Engine
from sqlalchemy.orm import Session
@@ -38,7 +42,9 @@ from superset_core.semantic_layers.types import (
SemanticResult,
)
from superset_core.semantic_layers.view import SemanticViewFeature
+from werkzeug.test import TestResponse
+from superset.exceptions import QueryObjectValidationError
from superset.semantic_layers.models import (
ColumnMetadata,
get_column_type,
@@ -413,10 +419,69 @@ def test_semantic_view_query_language() -> None:
def test_semantic_view_get_query_str() -> None:
- """Test SemanticView get_query_str method."""
- view = SemanticView()
- result = view.get_query_str({})
- assert result == "Not implemented for semantic layers"
+ """Reject query previews that cannot provide a semantic provider request."""
+ view: SemanticView = SemanticView()
+ with pytest.raises(
+ QueryObjectValidationError, match="produced when the chart runs"
+ ):
+ view.get_query_str({})
+
+
+def test_semantic_query_placeholder_is_absent() -> None:
+ """Keep the retired placeholder out of backend and frontend source."""
+ root: Path = Path(__file__).resolve().parents[3]
+ directory: Path
+ current: str
+ directories: list[str]
+ filenames: list[str]
+ filename: str
+ for directory in (root / "superset", root / "superset-frontend" / "src"):
+ for current, directories, filenames in os.walk(directory):
+ directories[:] = sorted(set(directories) - {"static", "__pycache__"})
+ for filename in filenames:
+ source: Path = Path(current) / filename
+ if source.suffix in {".py", ".ts", ".tsx", ".js", ".jsx"}:
+ assert (
+ b"Not implemented for semantic layers"
+ not in source.read_bytes()
+ ), str(source)
+
+
+def test_semantic_view_query_endpoint_returns_error(
+ client: FlaskClient,
+ full_api_access: None,
+ mocker: MockerFixture,
+) -> None:
+ """Return a semantic query validation error in the chart-data envelope."""
+ view: SemanticView = SemanticView(id=1)
+ implementation: MagicMock = MagicMock()
+ implementation.get_dimensions.return_value = []
+ implementation.get_metrics.return_value = []
+ mocker.patch.object(
+ SemanticView,
+ "implementation",
+ new_callable=PropertyMock,
+ return_value=implementation,
+ )
+ mocker.patch(
+ "superset.daos.datasource.DatasourceDAO.get_datasource", return_value=view
+ )
+ mocker.patch.object(SemanticView, "raise_for_access")
+
+ response: TestResponse = client.post(
+ "/api/v1/chart/data",
+ json={
+ "datasource": {"id": 1, "type": "semantic_view"},
+ "queries": [{}],
+ "result_type": "query",
+ "result_format": "json",
+ },
+ )
+
+ assert response.status_code == 200
+ assert response.json["result"][0]["error"]
+ assert response.json["result"][0]["language"] is None
+ assert "query" not in response.json["result"][0]
def test_semantic_view_get_extra_cache_keys() -> None: