mirror of
https://github.com/apache/superset.git
synced 2026-05-01 05:54:26 +00:00
Compare commits
6 Commits
fix-duckdb
...
fix-webpac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98212189b8 | ||
|
|
1e4bc6ee78 | ||
|
|
db178cf527 | ||
|
|
5901320933 | ||
|
|
23bb4f88c0 | ||
|
|
4130b92966 |
@@ -163,7 +163,7 @@ services:
|
|||||||
# configuring the dev-server to use the host.docker.internal to connect to the backend
|
# configuring the dev-server to use the host.docker.internal to connect to the backend
|
||||||
superset: "http://superset-light:8088"
|
superset: "http://superset-light:8088"
|
||||||
# Webpack dev server configuration
|
# Webpack dev server configuration
|
||||||
WEBPACK_DEVSERVER_HOST: "${WEBPACK_DEVSERVER_HOST:-127.0.0.1}"
|
WEBPACK_DEVSERVER_HOST: "${WEBPACK_DEVSERVER_HOST:-0.0.0.0}"
|
||||||
WEBPACK_DEVSERVER_PORT: "${WEBPACK_DEVSERVER_PORT:-9000}"
|
WEBPACK_DEVSERVER_PORT: "${WEBPACK_DEVSERVER_PORT:-9000}"
|
||||||
ports:
|
ports:
|
||||||
- "${NODE_PORT:-9001}:9000" # Parameterized port, accessible on all interfaces
|
- "${NODE_PORT:-9001}:9000" # Parameterized port, accessible on all interfaces
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ dependencies = [
|
|||||||
"packaging",
|
"packaging",
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed)
|
# pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed)
|
||||||
"pandas[excel]>=2.0.3, <2.1",
|
"pandas[excel]>=2.0.3, <2.2",
|
||||||
"bottleneck", # recommended performance dependency for pandas, see https://pandas.pydata.org/docs/getting_started/install.html#performance-dependencies-recommended
|
"bottleneck", # recommended performance dependency for pandas, see https://pandas.pydata.org/docs/getting_started/install.html#performance-dependencies-recommended
|
||||||
# --------------------------
|
# --------------------------
|
||||||
"parsedatetime",
|
"parsedatetime",
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ greenlet==3.1.1
|
|||||||
# via
|
# via
|
||||||
# apache-superset (pyproject.toml)
|
# apache-superset (pyproject.toml)
|
||||||
# shillelagh
|
# shillelagh
|
||||||
|
# sqlalchemy
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
# via apache-superset (pyproject.toml)
|
# via apache-superset (pyproject.toml)
|
||||||
h11==0.16.0
|
h11==0.16.0
|
||||||
@@ -266,7 +267,7 @@ packaging==25.0
|
|||||||
# limits
|
# limits
|
||||||
# marshmallow
|
# marshmallow
|
||||||
# shillelagh
|
# shillelagh
|
||||||
pandas==2.0.3
|
pandas==2.1.4
|
||||||
# via apache-superset (pyproject.toml)
|
# via apache-superset (pyproject.toml)
|
||||||
paramiko==3.5.1
|
paramiko==3.5.1
|
||||||
# via
|
# via
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ greenlet==3.1.1
|
|||||||
# apache-superset
|
# apache-superset
|
||||||
# gevent
|
# gevent
|
||||||
# shillelagh
|
# shillelagh
|
||||||
|
# sqlalchemy
|
||||||
grpcio==1.71.0
|
grpcio==1.71.0
|
||||||
# via
|
# via
|
||||||
# apache-superset
|
# apache-superset
|
||||||
@@ -536,7 +537,7 @@ packaging==25.0
|
|||||||
# pytest
|
# pytest
|
||||||
# shillelagh
|
# shillelagh
|
||||||
# sqlalchemy-bigquery
|
# sqlalchemy-bigquery
|
||||||
pandas==2.0.3
|
pandas==2.1.4
|
||||||
# via
|
# via
|
||||||
# -c requirements/base-constraint.txt
|
# -c requirements/base-constraint.txt
|
||||||
# apache-superset
|
# apache-superset
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
import { t } from '@superset-ui/core';
|
import { t } from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
ControlPanelConfig,
|
ControlPanelConfig,
|
||||||
ControlStateMapping,
|
|
||||||
ControlSubSectionHeader,
|
ControlSubSectionHeader,
|
||||||
D3_FORMAT_DOCS,
|
D3_FORMAT_DOCS,
|
||||||
D3_FORMAT_OPTIONS,
|
D3_FORMAT_OPTIONS,
|
||||||
@@ -197,15 +196,6 @@ const config: ControlPanelConfig = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onInit(state: ControlStateMapping) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
row_limit: {
|
|
||||||
...state.row_limit,
|
|
||||||
value: state.row_limit.default,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
formDataOverrides: formData => ({
|
formDataOverrides: formData => ({
|
||||||
...formData,
|
...formData,
|
||||||
metric: getStandardizedControls().shiftMetric(),
|
metric: getStandardizedControls().shiftMetric(),
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ export default function transformProps(chartProps: EchartsGanttChartProps) {
|
|||||||
show: true,
|
show: true,
|
||||||
position: 'start',
|
position: 'start',
|
||||||
formatter: '{b}',
|
formatter: '{b}',
|
||||||
|
color: theme.colorText,
|
||||||
},
|
},
|
||||||
data: categoryLines,
|
data: categoryLines,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ import {
|
|||||||
isDerivedSeries,
|
isDerivedSeries,
|
||||||
} from '@superset-ui/chart-controls';
|
} from '@superset-ui/chart-controls';
|
||||||
import type { EChartsCoreOption } from 'echarts/core';
|
import type { EChartsCoreOption } from 'echarts/core';
|
||||||
import type { LineStyleOption } from 'echarts/types/src/util/types';
|
import type {
|
||||||
|
LineStyleOption,
|
||||||
|
CallbackDataParams,
|
||||||
|
} from 'echarts/types/src/util/types';
|
||||||
import type { SeriesOption } from 'echarts';
|
import type { SeriesOption } from 'echarts';
|
||||||
import {
|
import {
|
||||||
EchartsTimeseriesChartProps,
|
EchartsTimeseriesChartProps,
|
||||||
@@ -575,16 +578,31 @@ export default function transformProps(
|
|||||||
const xValue: number = richTooltip
|
const xValue: number = richTooltip
|
||||||
? params[0].value[xIndex]
|
? params[0].value[xIndex]
|
||||||
: params.value[xIndex];
|
: params.value[xIndex];
|
||||||
const forecastValue: any[] = richTooltip ? params : [params];
|
const forecastValue: CallbackDataParams[] = richTooltip
|
||||||
|
? params
|
||||||
|
: [params];
|
||||||
const sortedKeys = extractTooltipKeys(
|
const sortedKeys = extractTooltipKeys(
|
||||||
forecastValue,
|
forecastValue,
|
||||||
yIndex,
|
yIndex,
|
||||||
richTooltip,
|
richTooltip,
|
||||||
tooltipSortByMetric,
|
tooltipSortByMetric,
|
||||||
);
|
);
|
||||||
|
const filteredForecastValue = forecastValue.filter(
|
||||||
|
(item: CallbackDataParams) =>
|
||||||
|
!annotationLayers.some(
|
||||||
|
(annotation: AnnotationLayer) =>
|
||||||
|
item.seriesName === annotation.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
const forecastValues: Record<string, ForecastValue> =
|
const forecastValues: Record<string, ForecastValue> =
|
||||||
extractForecastValuesFromTooltipParams(forecastValue, isHorizontal);
|
extractForecastValuesFromTooltipParams(forecastValue, isHorizontal);
|
||||||
|
|
||||||
|
const filteredForecastValues: Record<string, ForecastValue> =
|
||||||
|
extractForecastValuesFromTooltipParams(
|
||||||
|
filteredForecastValue,
|
||||||
|
isHorizontal,
|
||||||
|
);
|
||||||
|
|
||||||
const isForecast = Object.values(forecastValues).some(
|
const isForecast = Object.values(forecastValues).some(
|
||||||
value =>
|
value =>
|
||||||
value.forecastTrend || value.forecastLower || value.forecastUpper,
|
value.forecastTrend || value.forecastLower || value.forecastUpper,
|
||||||
@@ -595,7 +613,7 @@ export default function transformProps(
|
|||||||
: (getCustomFormatter(customFormatters, metrics) ?? defaultFormatter);
|
: (getCustomFormatter(customFormatters, metrics) ?? defaultFormatter);
|
||||||
|
|
||||||
const rows: string[][] = [];
|
const rows: string[][] = [];
|
||||||
const total = Object.values(forecastValues).reduce(
|
const total = Object.values(filteredForecastValues).reduce(
|
||||||
(acc, value) =>
|
(acc, value) =>
|
||||||
value.observation !== undefined ? acc + value.observation : acc,
|
value.observation !== undefined ? acc + value.observation : acc,
|
||||||
0,
|
0,
|
||||||
@@ -617,7 +635,16 @@ export default function transformProps(
|
|||||||
seriesName: key,
|
seriesName: key,
|
||||||
formatter,
|
formatter,
|
||||||
});
|
});
|
||||||
if (showPercentage && value.observation !== undefined) {
|
|
||||||
|
const annotationRow = annotationLayers.some(
|
||||||
|
item => item.name === key,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
showPercentage &&
|
||||||
|
value.observation !== undefined &&
|
||||||
|
!annotationRow
|
||||||
|
) {
|
||||||
row.push(
|
row.push(
|
||||||
percentFormatter.format(value.observation / (total || 1)),
|
percentFormatter.format(value.observation / (total || 1)),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -257,6 +257,7 @@ describe('Gantt transformProps', () => {
|
|||||||
show: true,
|
show: true,
|
||||||
position: 'start',
|
position: 'start',
|
||||||
formatter: '{b}',
|
formatter: '{b}',
|
||||||
|
color: 'rgba(0,0,0,0.88)',
|
||||||
},
|
},
|
||||||
lineStyle: expect.objectContaining({
|
lineStyle: expect.objectContaining({
|
||||||
color: '#00000000',
|
color: '#00000000',
|
||||||
|
|||||||
@@ -460,48 +460,26 @@ const ExtraOptions = ({
|
|||||||
),
|
),
|
||||||
children: (
|
children: (
|
||||||
<>
|
<>
|
||||||
<StyledInputContainer>
|
<StyledInputContainer
|
||||||
<div className="control-label">{t('Secure extra')}</div>
|
css={!isFileUploadSupportedByEngine ? no_margin_bottom : {}}
|
||||||
|
>
|
||||||
<div className="input-container">
|
<div className="input-container">
|
||||||
<StyledJsonEditor
|
<Checkbox
|
||||||
name="masked_encrypted_extra"
|
id="per_user_caching"
|
||||||
value={db?.masked_encrypted_extra || ''}
|
name="per_user_caching"
|
||||||
placeholder={t('Secure extra')}
|
indeterminate={false}
|
||||||
onChange={(json: string) =>
|
checked={!!extraJson?.per_user_caching}
|
||||||
onEditorChange({ json, name: 'masked_encrypted_extra' })
|
onChange={onExtraInputChange}
|
||||||
}
|
>
|
||||||
width="100%"
|
{t('Per user caching')}
|
||||||
height="160px"
|
</Checkbox>
|
||||||
annotations={secureExtraAnnotations}
|
<InfoTooltip
|
||||||
/>
|
tooltip={t(
|
||||||
</div>
|
'Cache data separately for each user based on their data access roles and permissions. ' +
|
||||||
<div className="helper">
|
'When disabled, a single cache will be used for all users.',
|
||||||
<div>
|
|
||||||
{t(
|
|
||||||
'JSON string containing additional connection configuration. ' +
|
|
||||||
'This is used to provide connection information for systems ' +
|
|
||||||
'like Hive, Presto and BigQuery which do not conform to the ' +
|
|
||||||
'username:password syntax normally used by SQLAlchemy.',
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</StyledInputContainer>
|
|
||||||
<StyledInputContainer>
|
|
||||||
<div className="control-label">{t('Root certificate')}</div>
|
|
||||||
<div className="input-container">
|
|
||||||
<Input.TextArea
|
|
||||||
name="server_cert"
|
|
||||||
value={db?.server_cert || ''}
|
|
||||||
placeholder={t('Enter CA_BUNDLE')}
|
|
||||||
onChange={onTextChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="helper">
|
|
||||||
{t(
|
|
||||||
'Optional CA_BUNDLE contents to validate HTTPS requests. Only ' +
|
|
||||||
'available on certain database engines.',
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
<StyledInputContainer
|
<StyledInputContainer
|
||||||
css={!isFileUploadSupportedByEngine ? no_margin_bottom : {}}
|
css={!isFileUploadSupportedByEngine ? no_margin_bottom : {}}
|
||||||
@@ -569,6 +547,49 @@ const ExtraOptions = ({
|
|||||||
</div>
|
</div>
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
)}
|
)}
|
||||||
|
<StyledInputContainer>
|
||||||
|
<div className="control-label">{t('Secure extra')}</div>
|
||||||
|
<div className="input-container">
|
||||||
|
<StyledJsonEditor
|
||||||
|
name="masked_encrypted_extra"
|
||||||
|
value={db?.masked_encrypted_extra || ''}
|
||||||
|
placeholder={t('Secure extra')}
|
||||||
|
onChange={(json: string) =>
|
||||||
|
onEditorChange({ json, name: 'masked_encrypted_extra' })
|
||||||
|
}
|
||||||
|
width="100%"
|
||||||
|
height="160px"
|
||||||
|
annotations={secureExtraAnnotations}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="helper">
|
||||||
|
<div>
|
||||||
|
{t(
|
||||||
|
'JSON string containing additional connection configuration. ' +
|
||||||
|
'This is used to provide connection information for systems ' +
|
||||||
|
'like Hive, Presto and BigQuery which do not conform to the ' +
|
||||||
|
'username:password syntax normally used by SQLAlchemy.',
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</StyledInputContainer>
|
||||||
|
<StyledInputContainer>
|
||||||
|
<div className="control-label">{t('Root certificate')}</div>
|
||||||
|
<div className="input-container">
|
||||||
|
<Input.TextArea
|
||||||
|
name="server_cert"
|
||||||
|
value={db?.server_cert || ''}
|
||||||
|
placeholder={t('Enter CA_BUNDLE')}
|
||||||
|
onChange={onTextChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="helper">
|
||||||
|
{t(
|
||||||
|
'Optional CA_BUNDLE contents to validate HTTPS requests. Only ' +
|
||||||
|
'available on certain database engines.',
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</StyledInputContainer>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -246,6 +246,7 @@ export interface ExtraJson {
|
|||||||
disable_data_preview?: boolean; // in SQL Lab
|
disable_data_preview?: boolean; // in SQL Lab
|
||||||
disable_drill_to_detail?: boolean;
|
disable_drill_to_detail?: boolean;
|
||||||
allow_multi_catalog?: boolean;
|
allow_multi_catalog?: boolean;
|
||||||
|
per_user_caching?: boolean; // in Security
|
||||||
engine_params?: {
|
engine_params?: {
|
||||||
catalog?: Record<string, string>;
|
catalog?: Record<string, string>;
|
||||||
connect_args?: {
|
connect_args?: {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class ExcelReader(BaseDataReader):
|
|||||||
"na_values": self._options.get("null_values")
|
"na_values": self._options.get("null_values")
|
||||||
if self._options.get("null_values") # None if an empty list
|
if self._options.get("null_values") # None if an empty list
|
||||||
else None,
|
else None,
|
||||||
"parse_dates": self._options.get("column_dates"),
|
"parse_dates": self._options.get("column_dates") or False,
|
||||||
"skiprows": self._options.get("skip_rows", 0),
|
"skiprows": self._options.get("skip_rows", 0),
|
||||||
"sheet_name": self._options.get("sheet_name", 0),
|
"sheet_name": self._options.get("sheet_name", 0),
|
||||||
"nrows": self._options.get("rows_to_read"),
|
"nrows": self._options.get("rows_to_read"),
|
||||||
|
|||||||
@@ -454,13 +454,19 @@ class QueryObject: # pylint: disable=too-many-instance-attributes
|
|||||||
cache_dict["annotation_layers"] = annotation_layers
|
cache_dict["annotation_layers"] = annotation_layers
|
||||||
|
|
||||||
# Add an impersonation key to cache if impersonation is enabled on the db
|
# Add an impersonation key to cache if impersonation is enabled on the db
|
||||||
# or if the CACHE_QUERY_BY_USER flag is on
|
# or if the CACHE_QUERY_BY_USER flag is on or per_user_caching is enabled on
|
||||||
|
# the database
|
||||||
try:
|
try:
|
||||||
database = self.datasource.database # type: ignore
|
database = self.datasource.database # type: ignore
|
||||||
|
extra = json.loads(database.extra or "{}")
|
||||||
if (
|
if (
|
||||||
feature_flag_manager.is_feature_enabled("CACHE_IMPERSONATION")
|
(
|
||||||
and database.impersonate_user
|
feature_flag_manager.is_feature_enabled("CACHE_IMPERSONATION")
|
||||||
) or feature_flag_manager.is_feature_enabled("CACHE_QUERY_BY_USER"):
|
and database.impersonate_user
|
||||||
|
)
|
||||||
|
or feature_flag_manager.is_feature_enabled("CACHE_QUERY_BY_USER")
|
||||||
|
or extra.get("per_user_caching", False)
|
||||||
|
):
|
||||||
if key := database.db_engine_spec.get_impersonation_key(
|
if key := database.db_engine_spec.get_impersonation_key(
|
||||||
getattr(g, "user", None)
|
getattr(g, "user", None)
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -831,6 +831,7 @@ class ImportV1DatabaseExtraSchema(Schema):
|
|||||||
disable_data_preview = fields.Boolean(required=False)
|
disable_data_preview = fields.Boolean(required=False)
|
||||||
disable_drill_to_detail = fields.Boolean(required=False)
|
disable_drill_to_detail = fields.Boolean(required=False)
|
||||||
allow_multi_catalog = fields.Boolean(required=False)
|
allow_multi_catalog = fields.Boolean(required=False)
|
||||||
|
per_user_caching = fields.Boolean(required=False)
|
||||||
version = fields.String(required=False, allow_none=True)
|
version = fields.String(required=False, allow_none=True)
|
||||||
schema_options = fields.Dict(keys=fields.Str(), values=fields.Raw())
|
schema_options = fields.Dict(keys=fields.Str(), values=fields.Raw())
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ def data_loader(
|
|||||||
pandas_loader_configuration: PandasLoaderConfigurations,
|
pandas_loader_configuration: PandasLoaderConfigurations,
|
||||||
table_to_df_convertor: TableToDfConvertor,
|
table_to_df_convertor: TableToDfConvertor,
|
||||||
) -> DataLoader:
|
) -> DataLoader:
|
||||||
|
if example_db_engine.dialect.name == PRESTO:
|
||||||
|
example_db_engine.dialect.get_view_names = Mock(return_value=[])
|
||||||
return PandasDataLoader(
|
return PandasDataLoader(
|
||||||
example_db_engine, pandas_loader_configuration, table_to_df_convertor
|
example_db_engine, pandas_loader_configuration, table_to_df_convertor
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user