diff --git a/helm/superset/Chart.yaml b/helm/superset/Chart.yaml index 10e4fadd4abdb..ef017b70f9ce5 100644 --- a/helm/superset/Chart.yaml +++ b/helm/superset/Chart.yaml @@ -22,7 +22,7 @@ maintainers: - name: craig-rueda email: craig@craigrueda.com url: https://github.com/craig-rueda -version: 0.7.3 +version: 0.7.4 dependencies: - name: postgresql version: 11.1.22 diff --git a/helm/superset/templates/deployment-worker.yaml b/helm/superset/templates/deployment-worker.yaml index 71f90ace481a8..7d79f2edbfb46 100644 --- a/helm/superset/templates/deployment-worker.yaml +++ b/helm/superset/templates/deployment-worker.yaml @@ -33,6 +33,10 @@ spec: matchLabels: app: {{ template "superset.name" . }}-worker release: {{ .Release.Name }} + {{- if .Values.supersetWorker.strategy }} + strategy: + {{- toYaml .Values.supersetWorker.strategy | nindent 4 }} + {{- end }} template: metadata: annotations: diff --git a/helm/superset/templates/deployment.yaml b/helm/superset/templates/deployment.yaml index 7c3bdac6dfde3..3388fcc72c3eb 100644 --- a/helm/superset/templates/deployment.yaml +++ b/helm/superset/templates/deployment.yaml @@ -29,6 +29,10 @@ metadata: {{- end }} spec: replicas: {{ .Values.supersetNode.replicaCount }} + {{- if .Values.supersetNode.strategy }} + strategy: + {{- toYaml .Values.supersetNode.strategy | nindent 4 }} + {{- end }} selector: matchLabels: app: {{ template "superset.name" . }} diff --git a/helm/superset/values.schema.json b/helm/superset/values.schema.json index d554cbcc3b06c..40edb991ddd88 100644 --- a/helm/superset/values.schema.json +++ b/helm/superset/values.schema.json @@ -343,6 +343,9 @@ }, "containerSecurityContext": { "type": "object" + }, + "strategy": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.23.0/_definitions.json##/definitions/io.k8s.api.apps.v1.DeploymentStrategy" } }, "required": [ @@ -386,6 +389,9 @@ }, "containerSecurityContext": { "type": "object" + }, + "strategy": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.23.0/_definitions.json##/definitions/io.k8s.api.apps.v1.DeploymentStrategy" } }, "required": [ diff --git a/helm/superset/values.yaml b/helm/superset/values.yaml index df93336447238..00a0916227351 100644 --- a/helm/superset/values.yaml +++ b/helm/superset/values.yaml @@ -289,6 +289,12 @@ supersetNode: # memory: 128Mi podSecurityContext: {} containerSecurityContext: {} + strategy: {} + # type: RollingUpdate + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + ## ## Superset worker configuration supersetWorker: @@ -322,6 +328,12 @@ supersetWorker: # memory: 128Mi podSecurityContext: {} containerSecurityContext: {} + strategy: {} + # type: RollingUpdate + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + ## ## Superset beat configuration (to trigger scheduled jobs like reports) supersetCeleryBeat: diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 9b06bb3b9b075..9577cd9656c34 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -45,6 +45,7 @@ import { ComparisionType, isAdhocColumn, isPhysicalColumn, + ensureIsArray, } from '@superset-ui/core'; import { @@ -57,7 +58,13 @@ import { DEFAULT_NUMBER_FORMAT, } from '../utils'; import { TIME_FILTER_LABELS } from '../constants'; -import { SharedControlConfig, Dataset, ColumnMeta } from '../types'; +import { + SharedControlConfig, + Dataset, + ColumnMeta, + ControlState, + ControlPanelState, +} from '../types'; import { dndAdhocFilterControl, @@ -340,6 +347,16 @@ const show_empty_columns: SharedControlConfig<'CheckboxControl'> = { description: t('Show empty columns'), }; +const datetime_columns_lookup: SharedControlConfig<'HiddenControl'> = { + type: 'HiddenControl', + initialValue: (control: ControlState, state: ControlPanelState) => + Object.fromEntries( + ensureIsArray>(state?.datasource?.columns) + .filter(option => option.is_dttm) + .map(option => [option.column_name ?? option.name, option.is_dttm]), + ), +}; + export default { metrics: dndAdhocMetricsControl, metric: dndAdhocMetricControl, @@ -376,4 +393,5 @@ export default { truncate_metric, x_axis: dndXAxisControl, show_empty_columns, + datetime_columns_lookup, }; diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/package.json b/superset-frontend/plugins/plugin-chart-pivot-table/package.json index a796b90836e96..bed12a2e7250e 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/package.json +++ b/superset-frontend/plugins/plugin-chart-pivot-table/package.json @@ -32,7 +32,8 @@ "@ant-design/icons": "^4.2.2", "react": "^16.13.1", "react-dom": "^16.13.1", - "prop-types": "*" + "prop-types": "*", + "lodash": "^4.17.11" }, "devDependencies": { "@babel/types": "^7.13.12", diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/buildQuery.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/buildQuery.ts index 677902b796800..8068ace2fdd74 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/buildQuery.ts +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/buildQuery.ts @@ -16,9 +16,15 @@ * specific language governing permissions and limitations * under the License. */ +import omit from 'lodash/omit'; + import { + AdhocColumn, buildQueryContext, ensureIsArray, + FeatureFlag, + isFeatureEnabled, + isPhysicalColumn, QueryFormColumn, QueryFormOrderBy, } from '@superset-ui/core'; @@ -27,10 +33,29 @@ import { PivotTableQueryFormData } from '../types'; export default function buildQuery(formData: PivotTableQueryFormData) { const { groupbyColumns = [], groupbyRows = [] } = formData; // TODO: add deduping of AdhocColumns - const groupbySet = new Set([ - ...ensureIsArray(groupbyColumns), - ...ensureIsArray(groupbyRows), - ]); + const columns = Array.from( + new Set([ + ...ensureIsArray(groupbyColumns), + ...ensureIsArray(groupbyRows), + ]), + ).map(col => { + if ( + isPhysicalColumn(col) && + formData.time_grain_sqla && + isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) && + formData?.datetime_columns_lookup?.[col] + ) { + return { + timeGrain: formData.time_grain_sqla, + columnType: 'BASE_AXIS', + sqlExpression: col, + label: col, + expressionType: 'SQL', + } as AdhocColumn; + } + return col; + }); + return buildQueryContext(formData, baseQueryObject => { const { series_limit_metric, metrics, order_desc } = baseQueryObject; let orderBy: QueryFormOrderBy[] | undefined; @@ -41,9 +66,11 @@ export default function buildQuery(formData: PivotTableQueryFormData) { } return [ { - ...baseQueryObject, + ...(isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) + ? omit(baseQueryObject, ['extras.time_grain_sqla']) + : baseQueryObject), orderby: orderBy, - columns: [...groupbySet], + columns, }, ]; }); diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx index 5427410b162a8..fe69188b253ba 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx @@ -19,6 +19,10 @@ import React from 'react'; import { ensureIsArray, + FeatureFlag, + isAdhocColumn, + isFeatureEnabled, + isPhysicalColumn, QueryFormMetric, smartDateFormatter, t, @@ -38,7 +42,7 @@ import { MetricsLayoutEnum } from '../types'; const config: ControlPanelConfig = { controlPanelSections: [ - { ...sections.legacyTimeseriesTime, expanded: false }, + { ...sections.genericTime, expanded: false }, { label: t('Query'), expanded: true, @@ -63,6 +67,41 @@ const config: ControlPanelConfig = { }, }, ], + [ + isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) + ? { + name: 'time_grain_sqla', + config: { + ...sharedControls.time_grain_sqla, + visibility: ({ controls }) => { + const dttmLookup = Object.fromEntries( + ensureIsArray(controls?.groupbyColumns?.options).map( + option => [option.column_name, option.is_dttm], + ), + ); + + return [ + ...ensureIsArray(controls?.groupbyColumns.value), + ...ensureIsArray(controls?.groupbyRows.value), + ] + .map(selection => { + if (isAdhocColumn(selection)) { + return true; + } + if (isPhysicalColumn(selection)) { + return !!dttmLookup[selection]; + } + return false; + }) + .some(Boolean); + }, + }, + } + : null, + isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) + ? 'datetime_columns_lookup' + : null, + ], [ { name: 'metrics', diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx deleted file mode 100644 index 565c4f9f28b54..0000000000000 --- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorScheme.test.jsx +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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. - */ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import { Select } from 'src/components'; -import { getCategoricalSchemeRegistry } from '@superset-ui/core'; -import { styledMount as mount } from 'spec/helpers/theming'; -import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeControl'; - -const defaultProps = { - name: 'color_scheme', - label: 'Color Scheme', - options: getCategoricalSchemeRegistry() - .keys() - .map(s => [s, s]), -}; - -describe('ColorSchemeControl', () => { - let wrapper; - beforeEach(() => { - wrapper = mount(); - }); - - it('renders a Select', () => { - expect(wrapper.find(Select)).toExist(); - }); -}); diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx index 1f19d8a2c3a89..9e760aab13166 100644 --- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx +++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeControl.test.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; import { render, screen, waitFor } from 'spec/helpers/testing-library'; -import ColorSchemeControl from '.'; +import ColorSchemeControl, { ColorSchemes } from '.'; const defaultProps = { hasCustomLabelColors: false, @@ -28,7 +28,7 @@ const defaultProps = { value: 'supersetDefault', clearable: true, choices: [], - schemes: () => null, + schemes: () => ({} as ColorSchemes), isLinear: false, }; diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx deleted file mode 100644 index d5855207859d1..0000000000000 --- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.jsx +++ /dev/null @@ -1,184 +0,0 @@ -/** - * 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 React from 'react'; -import PropTypes from 'prop-types'; -import { isFunction } from 'lodash'; -import { Select } from 'src/components'; -import { Tooltip } from 'src/components/Tooltip'; -import { styled, t } from '@superset-ui/core'; -import Icons from 'src/components/Icons'; -import ControlHeader from 'src/explore/components/ControlHeader'; -import ColorSchemeLabel from './ColorSchemeLabel'; - -const propTypes = { - hasCustomLabelColors: PropTypes.bool, - dashboardId: PropTypes.number, - description: PropTypes.string, - label: PropTypes.string, - labelMargin: PropTypes.number, - name: PropTypes.string.isRequired, - onChange: PropTypes.func, - value: PropTypes.string, - clearable: PropTypes.bool, - default: PropTypes.string, - choices: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.array), - PropTypes.func, - ]), - schemes: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), - isLinear: PropTypes.bool, -}; - -const defaultProps = { - choices: [], - hasCustomLabelColors: false, - label: t('Color scheme'), - schemes: {}, - clearable: false, - onChange: () => {}, -}; - -const StyledAlert = styled(Icons.AlertSolid)` - color: ${({ theme }) => theme.colors.alert.base}; -`; - -export default class ColorSchemeControl extends React.PureComponent { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.renderOption = this.renderOption.bind(this); - this.renderLabel = this.renderLabel.bind(this); - this.dashboardColorSchemeAlert = t( - `The color scheme is determined by the related dashboard. - Edit the color scheme in the dashboard properties.`, - ); - } - - onChange(value) { - this.props.onChange(value); - } - - renderOption(value) { - const { isLinear } = this.props; - const currentScheme = this.schemes[value]; - - // For categorical scheme, display all the colors - // For sequential scheme, show 10 or interpolate to 10. - // Sequential schemes usually have at most 10 colors. - let colors = []; - if (currentScheme) { - colors = isLinear ? currentScheme.getColors(10) : currentScheme.colors; - } - - return ( - - ); - } - - renderLabel() { - const { dashboardId, hasCustomLabelColors, label } = this.props; - - if (hasCustomLabelColors || dashboardId) { - const alertTitle = hasCustomLabelColors - ? t( - `This color scheme is being overriden by custom label colors. - Check the JSON metadata in the Advanced settings`, - ) - : this.dashboardColorSchemeAlert; - return ( - <> - {label}{' '} - - - - - ); - } - return label; - } - - render() { - const { choices, dashboardId, schemes } = this.props; - let options = dashboardId - ? [ - { - value: 'dashboard', - label: 'dashboard', - customLabel: ( - - {t('Dashboard scheme')} - - ), - }, - ] - : []; - let currentScheme = dashboardId ? 'dashboard' : undefined; - - // if related to a dashboard the scheme is dictated by the dashboard - if (!dashboardId) { - this.schemes = isFunction(schemes) ? schemes() : schemes; - const controlChoices = isFunction(choices) ? choices() : choices; - const allColorOptions = []; - const filteredColorOptions = controlChoices.filter(o => { - const option = o[0]; - const isValidColorOption = - option !== 'SUPERSET_DEFAULT' && !allColorOptions.includes(option); - allColorOptions.push(option); - return isValidColorOption; - }); - - options = filteredColorOptions.map(([value]) => ({ - customLabel: this.renderOption(value), - label: this.schemes?.[value]?.label || value, - value, - })); - - currentScheme = this.props.value || this.props.default; - - if (currentScheme === 'SUPERSET_DEFAULT') { - currentScheme = this.schemes?.SUPERSET_DEFAULT?.id; - } - } - - const selectProps = { - ariaLabel: t('Select color scheme'), - allowClear: this.props.clearable, - disabled: !!dashboardId, - name: `select-${this.props.name}`, - onChange: this.onChange, - options, - placeholder: t('Select scheme'), - value: currentScheme, - }; - - return ( - + } + /> + } + ariaLabel={t('Select color scheme')} + allowClear={clearable} + disabled={!!dashboardId} + name={`select-${name}`} + onChange={handleOnChange} + options={options} + placeholder={t('Select scheme')} + value={currentScheme} + /> + ); +}; + +export default ColorSchemeControl;