codeant-ai-for-open-source[bot] commented on code in PR #37790:
URL: https://github.com/apache/superset/pull/37790#discussion_r2820584918
##########
superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx:
##########
@@ -36,58 +55,202 @@ const StyledInput = styled(Input)`
border-color: ${({ theme }) => theme.colorSplit};
`;
+const StyledTranslationInput = styled(TranslationInput)`
+ border-radius: ${({ theme }) => theme.borderRadius};
+ height: 26px;
+ padding-left: ${({ theme }) => theme.sizeUnit * 2.5}px;
+`;
+
export const DndColumnSelectPopoverTitle = ({
title,
onChange,
isEditDisabled,
hasCustomLabel,
+ translations,
+ onTranslationsChange,
}: DndColumnSelectPopoverTitleProps) => {
const theme = useTheme();
const [isHovered, setIsHovered] = useState(false);
const [isEditMode, setIsEditMode] = useState(false);
- const onMouseOver = useCallback(() => {
- setIsHovered(true);
- }, []);
+ const localizationEnabled = isFeatureEnabled(
+ FeatureFlag.EnableContentLocalization,
+ );
+ const showLocale =
+ localizationEnabled &&
+ hasCustomLabel &&
+ onTranslationsChange !== undefined;
- const onMouseOut = useCallback(() => {
- setIsHovered(false);
- }, []);
+ const [allLocales, setAllLocales] = useState<LocaleInfo[]>([]);
+ const [defaultLocale, setDefaultLocale] = useState('');
+ const userLocale: string = useSelector(
+ (state: { common: { locale: string } }) => state.common.locale,
+ );
+ const [activeLocale, setActiveLocale] = useState(DEFAULT_LOCALE_KEY);
+ const [localesLoaded, setLocalesLoaded] = useState(false);
+
+ const translationsRef = useRef(translations);
+ translationsRef.current = translations;
+
+ useEffect(() => {
+ if (!showLocale) return;
+ SupersetClient.get({
+ endpoint: '/api/v1/localization/available_locales',
+ }).then(
+ response => {
+ const { locales, default_locale } = response.json.result;
+ setAllLocales(locales);
+ setDefaultLocale(default_locale);
+ setLocalesLoaded(true);
+ },
+ err => logging.error('Failed to fetch available locales', err),
+ );
+ }, [showLocale]);
+
+ useEffect(() => {
+ if (!localesLoaded || !userLocale) return;
+ const labelTranslations = translationsRef.current?.label;
+ if (labelTranslations?.[userLocale]) {
+ setActiveLocale(userLocale);
+ } else {
+ setActiveLocale(DEFAULT_LOCALE_KEY);
+ }
+ }, [localesLoaded, userLocale]);
- const onClick = useCallback(() => {
- setIsEditMode(true);
+ const isLocaleMode = activeLocale !== DEFAULT_LOCALE_KEY;
+ const localeReady = showLocale && allLocales.length > 0;
+
+ const dropdownOpenRef = useRef(false);
+ const inputRef = useRef<InputRef>(null);
+
+ const handleDropdownOpenChange = useCallback((open: boolean) => {
+ if (open) {
+ dropdownOpenRef.current = true;
+ } else {
+ requestAnimationFrame(() => {
+ dropdownOpenRef.current = false;
+ inputRef.current?.focus();
+ });
+ }
}, []);
+ const onMouseOver = useCallback(() => setIsHovered(true), []);
+ const onMouseOut = useCallback(() => setIsHovered(false), []);
+ const onClick = useCallback(() => setIsEditMode(true), []);
+
const onBlur = useCallback(() => {
+ if (dropdownOpenRef.current) return;
setIsEditMode(false);
}, []);
const onInputBlur = useCallback(
- e => {
+ (e: React.FocusEvent<HTMLInputElement>) => {
+ if (dropdownOpenRef.current) return;
if (e.target.value === '') {
onChange(e);
}
- onBlur();
+ setIsEditMode(false);
+ },
+ [onChange],
+ );
+
+ const handleTranslationValue = useCallback(
+ (value: string) => {
+ onTranslationsChange?.({
+ ...translationsRef.current,
+ label: {
Review Comment:
**Suggestion:** When updating translations, the code spreads
`translationsRef.current` directly; if no translations exist yet this is
`undefined`, and spreading it will throw a runtime TypeError the first time a
translation is edited. Use a default empty object when
`translationsRef.current` is undefined so the spread is always safe. [type
error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ First-time column label translation edit throws runtime TypeError.
- ⚠️ Column localization UI breaks until user refreshes page.
```
</details>
```suggestion
...(translationsRef.current ?? {}),
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Ensure frontend is built with this PR code and feature flag
`ENABLE_CONTENT_LOCALIZATION` is enabled so
`isFeatureEnabled(FeatureFlag.EnableContentLocalization)` returns true in
`DndColumnSelectPopoverTitle`
(`superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx`).
2. Navigate to a view that renders `DndColumnSelectPopoverTitle` for a
column with a
custom label and localization enabled (parent passes `hasCustomLabel={true}`,
`onTranslationsChange={...}`, and either omits `translations` or passes it
as `undefined`,
which is the state for columns with no saved translations yet).
3. In the UI, click the column label to enter edit mode, use the inline
`LocaleSwitcher`
(rendered via `localeSwitcher` in the same file) to switch from `DEFAULT` to
a non-default
locale so that the locale-specific `<StyledTranslationInput>` is shown with
`onChange={handleTranslationValue}` (see final file state,
`StyledTranslationInput`
usage).
4. Type any character into the translation input; this calls
`handleTranslationValue`
which executes `onTranslationsChange?.({ ...translationsRef.current, ... })`
at lines
160–166 while `translationsRef.current` is still `undefined`, causing
`...translationsRef.current` to throw `TypeError: Cannot convert undefined
or null to
object` and breaking the React component for that control.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx
**Line:** 161:161
**Comment:**
*Type Error: When updating translations, the code spreads
`translationsRef.current` directly; if no translations exist yet this is
`undefined`, and spreading it will throw a runtime TypeError the first time a
translation is edited. Use a default empty object when
`translationsRef.current` is undefined so the spread is always safe.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=6b2e64b56d49b99dd781d78dcd389c937d3587a0080fbe5f17c9398fc7aecc75&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=6b2e64b56d49b99dd781d78dcd389c937d3587a0080fbe5f17c9398fc7aecc75&reaction=dislike'>👎</a>
##########
superset-frontend/src/components/TranslationEditor/LocaleSwitcher.tsx:
##########
@@ -0,0 +1,253 @@
+/**
+ * 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 { useCallback, useMemo, useState } from 'react';
+import type { MenuProps } from 'antd';
+import { t } from '@apache-superset/core';
+import { css, useTheme } from '@apache-superset/core/ui';
+import {
+ Badge,
+ Dropdown,
+ Icons,
+} from '@superset-ui/core/components';
+import type { Translations, LocaleInfo } from 'src/types/Localization';
+import { DEFAULT_LOCALE_KEY, countFieldTranslations } from './utils';
+
+export interface LocaleSwitcherProps {
+ /** Translation field key (e.g., 'dashboard_title', 'slice_name'). */
+ fieldName: string;
+ /** Current default (column) value — used for ✓ indicator on DEFAULT. */
+ defaultValue: string;
+ /** Full translations object for the entity. */
+ translations: Translations;
+ /** All configured locales from server (including default locale). */
+ allLocales: LocaleInfo[];
+ /** Server's default locale code (e.g., 'en'). */
+ defaultLocale: string;
+ /** Current user's UI locale code from session. */
+ userLocale: string;
+ /** Currently active locale. Controlled by parent. */
+ activeLocale: string;
+ /** Called when user picks a locale from the dropdown. */
+ onLocaleChange: (locale: string) => void;
+ /** Human-readable field label for accessibility (e.g., 'Dashboard Title').
*/
+ fieldLabel: string;
+ /** When false, renders as a read-only indicator without dropdown. Default
true. */
+ interactive?: boolean;
+ /** Called when the dropdown opens or closes. */
+ onDropdownOpenChange?: (open: boolean) => void;
+}
+
+/**
+ * Inline locale dropdown rendered as an Input suffix.
+ *
+ * Displays the currently active locale (DEFAULT or a specific language)
+ * with a badge showing translation count. Click opens a dropdown to
+ * switch between DEFAULT text and per-locale translations.
+ *
+ * Yellow highlight indicates the user's locale has no translation.
+ */
+export default function LocaleSwitcher({
+ fieldName,
+ defaultValue,
+ translations,
+ allLocales,
+ defaultLocale,
+ userLocale,
+ activeLocale,
+ onLocaleChange,
+ fieldLabel,
+ interactive = true,
+ onDropdownOpenChange,
+}: LocaleSwitcherProps) {
+ const theme = useTheme();
+
+ const fieldTranslations = translations[fieldName] ?? {};
+ const translationCount = countFieldTranslations(translations, fieldName);
+
+ const userLocaleHasTranslation =
+ userLocale === defaultLocale || Boolean(fieldTranslations[userLocale]);
Review Comment:
**Suggestion:** The warning highlight logic only checks for an exact
`userLocale` match in `fieldTranslations`, but `getLocalizedValue` falls back
from regional locales like `de-AT` to their base language `de`; this means the
LocaleSwitcher will incorrectly show a warning for users with regional locale
codes even when a base-language translation exists and is actually used. Update
`userLocaleHasTranslation` to consider the base language (substring before `-`)
when no exact match is present so the warning state matches the real
localization behavior. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Locale indicator shows warnings despite usable base-language
translation.
- ⚠️ Editors may believe translations missing for regional locales.
- ⚠️ Inaccurate visual signals reduce trust in localization tooling.
```
</details>
```suggestion
const userLocaleBase = userLocale.split('-')[0];
const userLocaleHasTranslation =
userLocale === defaultLocale ||
Boolean(
fieldTranslations[userLocale] ||
(userLocaleBase !== userLocale && fieldTranslations[userLocaleBase]),
);
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Configure content localization so that a field (e.g., dashboard title)
has a
translation only for the base language code, for example
`translations[fieldName] = { de:
'Umsatz-Dashboard' }`, and no entry for `de-AT`. This object is passed into
the
`LocaleSwitcher` component in
`superset-frontend/src/components/TranslationEditor/LocaleSwitcher.tsx:65-77`
via the
`translations` and `fieldName` props.
2. Ensure the application's default locale is `en` and the current user's UI
locale (prop
`userLocale`) is a regional variant like `de-AT`, while `allLocales`
contains both `de`
and `de-AT`. This results in `fieldTranslations` (computed at
`LocaleSwitcher.tsx:80`)
having a key for `de` but not for `de-AT`.
3. Render the editor UI that uses `LocaleSwitcher` as the suffix for that
field's input
(e.g., dashboard title editor). When the component renders, code at
`LocaleSwitcher.tsx:83-85` computes `userLocaleHasTranslation` as `false`
because it only
checks `fieldTranslations[userLocale]` (`de-AT`) and does not consider
`fieldTranslations['de']`.
4. Observe that the component sets `isWarning = true` at
`LocaleSwitcher.tsx:85`, which
drives the warning color `suffixColor = theme.colorWarning` at
`LocaleSwitcher.tsx:171-173`, causing the locale indicator to appear as a
missing-translation warning even though the runtime localization logic
(which falls back
from `de-AT` to `de`) actually provides a translated value to the user.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/src/components/TranslationEditor/LocaleSwitcher.tsx
**Line:** 83:84
**Comment:**
*Logic Error: The warning highlight logic only checks for an exact
`userLocale` match in `fieldTranslations`, but `getLocalizedValue` falls back
from regional locales like `de-AT` to their base language `de`; this means the
LocaleSwitcher will incorrectly show a warning for users with regional locale
codes even when a base-language translation exists and is actually used. Update
`userLocaleHasTranslation` to consider the base language (substring before `-`)
when no exact match is present so the warning state matches the real
localization behavior.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=38b71a217dc8b01955d78c8ed6adc749598825933f0fd441d6e091946576c7b5&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=38b71a217dc8b01955d78c8ed6adc749598825933f0fd441d6e091946576c7b5&reaction=dislike'>👎</a>
##########
superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts:
##########
@@ -223,6 +228,24 @@ export default function transformProps(
metricsB = [],
}: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
+ const localizedMetricLabelMap = buildLocalizedMetricLabelMap(
+ [...metrics, ...metricsB],
+ locale,
+ );
+
+ const localizedXAxisTitle =
+ getLocalizedFormDataValue(formData.translations, 'x_axis_title', locale) ??
+ xAxisTitle;
+ const localizedYAxisTitle =
+ getLocalizedFormDataValue(formData.translations, 'y_axis_title', locale) ??
+ yAxisTitle;
+ const localizedYAxisTitleSecondary =
+ getLocalizedFormDataValue(
+ formData.translations,
+ 'yAxisTitleSecondary',
Review Comment:
**Suggestion:** The secondary Y-axis title localization looks up
translations using the key `'yAxisTitleSecondary'`, but other fields use
snake_case keys that match control names (e.g., `'x_axis_title'`,
`'y_axis_title'`), so any translations stored under the expected
`'y_axis_title_secondary'` key will never be found and the secondary Y-axis
title will remain untranslated. Update the lookup key to the correct snake_case
value so localization works. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Secondary Y-axis titles stay untranslated in localized dashboards.
- ⚠️ Inconsistent localization behavior between primary and secondary axes.
```
</details>
```suggestion
'y_axis_title_secondary',
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Render a MixedTimeseries chart, which calls `transformProps` at
`superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts:124-231`
with `formData` including axis title translations under
`formData.translations`.
2. Note that other axis titles use snake_case translation keys:
`localizedXAxisTitle` uses
`'x_axis_title'` and `localizedYAxisTitle` uses `'y_axis_title'` at lines
236-241,
matching standard control keys.
3. Store a secondary Y-axis title translation under the consistent
snake_case key
`formData.translations['y_axis_title_secondary'][<locale>]` (the natural
pattern alongside
`'x_axis_title'` and `'y_axis_title'`).
4. When `transformProps` runs, `localizedYAxisTitleSecondary` is computed at
lines 242-247
using the camelCase key `'yAxisTitleSecondary'`, so
`getLocalizedFormDataValue` returns
`undefined` and the fallback `yAxisTitleSecondary` is used; at lines 697-714
where
`yAxis[1].name` is set to `localizedYAxisTitleSecondary`, the rendered
secondary Y-axis
title remains unlocalized.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
**Line:** 245:245
**Comment:**
*Logic Error: The secondary Y-axis title localization looks up
translations using the key `'yAxisTitleSecondary'`, but other fields use
snake_case keys that match control names (e.g., `'x_axis_title'`,
`'y_axis_title'`), so any translations stored under the expected
`'y_axis_title_secondary'` key will never be found and the secondary Y-axis
title will remain untranslated. Update the lookup key to the correct snake_case
value so localization works.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=38177dd9263e0c73c297707b8642e8a70c7d36736c1f0c1ef6768c00802aa30b&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=38177dd9263e0c73c297707b8642e8a70c7d36736c1f0c1ef6768c00802aa30b&reaction=dislike'>👎</a>
##########
superset/localization/schema_mixin.py:
##########
@@ -0,0 +1,93 @@
+# 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.
+"""
+Marshmallow mixin for translations field.
+
+Provides translations field with feature flag validation and XSS sanitization.
+Used by all schemas that accept user-provided translations (Post, Put, Copy).
+"""
+
+from __future__ import annotations
+
+from typing import Any
+
+from marshmallow import ValidationError, fields, post_load, validates_schema
+
+from superset import is_feature_enabled
+from superset.localization.sanitization import sanitize_translations
+from superset.localization.validation import validate_translations
+
+
+class TranslatableSchemaMixin:
+ """
+ Mixin for Marshmallow schemas that accept a ``translations`` field.
+
+ Adds:
+ - ``translations`` dict field (optional, nullable)
+ - ``@validates_schema``: feature flag check + structure validation
+ - ``@post_load``: XSS sanitization of translation values
+
+ Usage::
+
+ class MySchema(TranslatableSchemaMixin, Schema):
+ name = fields.String()
+ """
+
+ translations = fields.Dict(
+ metadata={"description": "Translations dict for content localization"},
+ allow_none=True,
+ )
+
+ @validates_schema
+ def validate_translations_data(
+ self, data: dict[str, Any], **kwargs: Any
+ ) -> None:
+ """
+ Validate translations field: feature flag and structure.
+
+ Checks:
+ 1. Feature flag ENABLE_CONTENT_LOCALIZATION must be enabled
+ 2. Structure must be valid: {field: {locale: value}}
+
+ Raises:
+ ValidationError: If feature disabled or structure invalid.
+ """
+ if "translations" not in data or data["translations"] is None:
+ return
+
+ if not is_feature_enabled("ENABLE_CONTENT_LOCALIZATION"):
+ raise ValidationError(
+ "Content localization is not enabled. "
+ "Set ENABLE_CONTENT_LOCALIZATION=True to use translations.",
+ field_name="translations",
+ )
+
+ validate_translations(data["translations"])
+
+ @post_load
+ def sanitize_translations_xss(
+ self, data: dict[str, Any], **kwargs: Any
+ ) -> dict[str, Any]:
+ """
+ Strip HTML from translation values to prevent XSS.
+
+ Sanitization runs after validation. All HTML tags are removed,
+ storing plain text that React will escape when rendering.
+ """
+ if "translations" in data:
Review Comment:
**Suggestion:** When the request payload includes `"translations": null`,
Marshmallow will deserialize this to `data["translations"] = None`, and the
`post_load` hook unconditionally passes this None value into
`sanitize_translations`, which is likely implemented to operate on a dict and
can raise a TypeError; adding a None check keeps behavior consistent with the
validator and prevents crashes. [type error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ Dashboard or chart save fails when translations sent as null.
- ⚠️ Feature-flagged localization endpoints less robust to client payloads.
```
</details>
```suggestion
if "translations" in data and data["translations"] is not None:
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. A Marshmallow schema responsible for dashboard/chart/filter create/update
mixes in
`TranslatableSchemaMixin` from `superset/localization/schema_mixin.py`.
2. A client of that API endpoint sends a JSON payload with `"translations":
null`
(explicitly clearing translations), which Marshmallow deserializes to
`data["translations"] = None`.
3. During `.load()`, the `@validates_schema` method
`validate_translations_data` at
`superset/localization/schema_mixin.py:55-80` sees `data["translations"] is
None` and
returns early, treating this as valid.
4. The `@post_load` hook `sanitize_translations_xss` at
`superset/localization/schema_mixin.py:81-93` then executes, hits `if
"translations" in
data:`, and calls `sanitize_translations(data["translations"])` with `None`;
if
`sanitize_translations` expects a dict of translations (as implied by
`validate_translations` and the mixin docstring), this results in a runtime
`TypeError`,
causing the API request to fail.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/localization/schema_mixin.py
**Line:** 91:91
**Comment:**
*Type Error: When the request payload includes `"translations": null`,
Marshmallow will deserialize this to `data["translations"] = None`, and the
`post_load` hook unconditionally passes this None value into
`sanitize_translations`, which is likely implemented to operate on a dict and
can raise a TypeError; adding a None check keeps behavior consistent with the
validator and prevents crashes.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=7405a664f1c6771f8c3d492e0025c8c607a62421569e6a5a6da262a8357ea2c3&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37790&comment_hash=7405a664f1c6771f8c3d492e0025c8c607a62421569e6a5a6da262a8357ea2c3&reaction=dislike'>👎</a>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]