codeant-ai-for-open-source[bot] commented on code in PR #36201:
URL: https://github.com/apache/superset/pull/36201#discussion_r2600469028


##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/controlPanel.ts:
##########
@@ -36,8 +41,27 @@ import {
   lineWidth,
   tooltipContents,
   tooltipTemplate,
+  jsFunctionControl,
 } from '../../utilities/Shared_DeckGL';
 import { dndGeojsonColumn } from '../../utilities/sharedDndControls';
+import { BLACK_COLOR } from '../../utilities/controls';
+
+const defaultLabelConfigGenerator = `() => ({
+  // Check the documentation at:
+  // 
https://deck.gl/docs/api-reference/layers/geojson-layer#pointtype-options-2
+  getText: f => f.properties.name,

Review Comment:
   **Suggestion:** The default label JS generator string uses `getText: f => 
f.properties.name` which will throw if `f` or `f.properties` is undefined when 
the generated function is executed; guard access with optional chaining or a 
fallback to avoid runtime TypeError when features lack a `properties` object. 
[null pointer]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     getText: f => (f && f.properties ? f.properties.name : ''),
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The suggested change fixes a real runtime hazard: the default JS snippet 
will be executed in user code (deck.gl callbacks) and can throw if f or 
f.properties is undefined. Making the getter resilient (optional chaining or a 
fallback) prevents TypeErrors when features lack properties. The improved code 
addresses the issue directly.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
   **Line:** 52:52
   **Comment:**
        *Null Pointer: The default label JS generator string uses `getText: f 
=> f.properties.name` which will throw if `f` or `f.properties` is undefined 
when the generated function is executed; guard access with optional chaining or 
a fallback to avoid runtime TypeError when features lack a `properties` object.
   
   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>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts:
##########
@@ -0,0 +1,121 @@
+/**
+ * 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 { SqlaFormData } from '@superset-ui/core';
+import {
+  computeGeoJsonTextOptionsFromJsOutput,
+  computeGeoJsonTextOptionsFromFormData,
+  computeGeoJsonIconOptionsFromJsOutput,
+  computeGeoJsonIconOptionsFromFormData,
+} from './Geojson';
+
+jest.mock('@deck.gl/react', () => ({
+  __esModule: true,
+  default: () => null,
+}));
+
+test('computeGeoJsonTextOptionsFromJsOutput returns an empty object for 
non-object input', () => {
+  expect(computeGeoJsonTextOptionsFromJsOutput(null)).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput(42)).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput([1, 2, 3])).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput('string')).toEqual({});
+});
+
+test('computeGeoJsonTextOptionsFromJsOutput extracts valid text options from 
the input object', () => {
+  const input = {
+    getText: 'name',
+    getTextColor: [1, 2, 3, 255],
+    invalidOption: true,
+  };
+  const expectedOutput = {
+    getText: 'name',
+    getTextColor: [1, 2, 3, 255],
+  };
+  expect(computeGeoJsonTextOptionsFromJsOutput(input)).toEqual(expectedOutput);
+});
+
+test('computeGeoJsonTextOptionsFromFormData computes text options based on 
form data', () => {
+  const formData: SqlaFormData = {
+    label_property_name: 'name',
+    label_color: { r: 1, g: 2, b: 3, a: 1 },
+    label_size: 123,
+    label_size_unit: 'pixels',
+    datasource: 'test_datasource',
+    viz_type: 'deck_geojson',
+  };
+
+  const expectedOutput = {
+    getText: expect.any(Function),
+    getTextColor: [1, 2, 3, 255],
+    getTextSize: 123,
+    textSizeUnits: 'pixels',
+  };
+
+  const actualOutput = computeGeoJsonTextOptionsFromFormData(formData);
+  expect(actualOutput).toEqual(expectedOutput);
+
+  const sampleFeature = { properties: { name: 'Test' } };
+  expect(actualOutput.getText(sampleFeature)).toBe('Test');
+});
+
+test('computeGeoJsonIconOptionsFromJsOutput returns an empty object for 
non-object input', () => {
+  expect(computeGeoJsonIconOptionsFromJsOutput(null)).toEqual({});
+  expect(computeGeoJsonIconOptionsFromJsOutput(42)).toEqual({});
+  expect(computeGeoJsonIconOptionsFromJsOutput([1, 2, 3])).toEqual({});
+  expect(computeGeoJsonIconOptionsFromJsOutput('string')).toEqual({});
+});
+
+test('computeGeoJsonIconOptionsFromJsOutput extracts valid icon options from 
the input object', () => {
+  const input = {
+    getIcon: 'icon_name',
+    getIconColor: [1, 2, 3, 255],
+    invalidOption: false,
+  };
+
+  const expectedOutput = {
+    getIcon: 'icon_name',
+    getIconColor: [1, 2, 3, 255],
+  };
+
+  expect(computeGeoJsonIconOptionsFromJsOutput(input)).toEqual(expectedOutput);
+});
+
+test('computeGeoJsonIconOptionsFromFormData computes icon options based on 
form data', () => {
+  const formData: SqlaFormData = {
+    icon_url: 'https://example.com/icon.png',
+    icon_size: 123,
+    icon_size_unit: 'pixels',
+    datasource: 'test_datasource',
+    viz_type: 'deck_geojson',
+  };
+
+  const expectedOutput = {
+    getIcon: expect.any(Function),
+    getIconSize: 123,
+    iconSizeUnits: 'pixels',
+  };
+
+  const actualOutput = computeGeoJsonIconOptionsFromFormData(formData);
+  expect(actualOutput).toEqual(expectedOutput);

Review Comment:
   **Suggestion:** Fragile equality assertion for icon options: asserting exact 
equality on the whole returned object will fail if the implementation returns 
additional properties (or different numeric sizing behavior); assert the actual 
object contains the expected subset instead of strict equality. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     expect(actualOutput).toEqual(expect.objectContaining(expectedOutput));
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Similar to the text options test, using 
expect.objectContaining(expectedOutput) reduces brittleness if the 
implementation returns extra harmless properties.
   This is a reasonable improvement for test maintainability and doesn't weaken 
checks for the keys we care about.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
   **Line:** 114:114
   **Comment:**
        *Possible Bug: Fragile equality assertion for icon options: asserting 
exact equality on the whole returned object will fail if the implementation 
returns additional properties (or different numeric sizing behavior); assert 
the actual object contains the expected subset instead of strict equality.
   
   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>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.ts:
##########
@@ -39,6 +39,7 @@ export function columnChoices(datasource: Dataset | 
QueryResponse | null) {
 }
 
 export const PRIMARY_COLOR = { r: 0, g: 122, b: 135, a: 1 };
+export const BLACK_COLOR = { r: 0, g: 0, b: 0, a: 1 };

Review Comment:
   **Suggestion:** Exported color object is mutable; because `BLACK_COLOR` is a 
shared object, other modules can accidentally modify its properties at runtime 
leading to subtle state/visualization bugs—freeze the object to make it 
immutable. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
   export const BLACK_COLOR = Object.freeze({ r: 0, g: 0, b: 0, a: 1 }) as 
const;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Freezing the object prevents accidental runtime mutation of a shared 
constant which can cause subtle UI/state bugs. The proposed Object.freeze(... ) 
as const is syntactically correct and reasonable here. Note that the change 
tightens the exported type to readonly, which may surface compile errors if any 
consumer mutates the object — that's a real safety win, not a functional bugfix 
but a protective change.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.ts
   **Line:** 42:42
   **Comment:**
        *Logic Error: Exported color object is mutable; because `BLACK_COLOR` 
is a shared object, other modules can accidentally modify its properties at 
runtime leading to subtle state/visualization bugs—freeze the object to make it 
immutable.
   
   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>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx:
##########
@@ -169,6 +279,38 @@ export const getLayer: GetLayerType<GeoJsonLayer> = 
function ({
     processedFeatures = jsFnMutator(features) as ProcessedFeature[];

Review Comment:
   **Suggestion:** The code passes the module-level `features` array directly 
into a user-provided mutator (`jsFnMutator(features)`), allowing the user 
function to mutate shared state and causing race conditions if multiple layers 
run concurrently; pass a defensive copy (shallow or deep depending on 
expectations) to avoid accidental mutation of module-level state. [race 
condition]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       // pass a copy to avoid mutation of module-level `features` and reduce 
race conditions
       const featuresCopy = features.slice();
       processedFeatures = jsFnMutator(featuresCopy) as ProcessedFeature[];
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Correct — handing the module-level features array directly to arbitrary user 
code lets that code mutate shared state (features) and can create surprising 
bugs or race conditions if multiple calls interleave. Passing a copy (at least 
a shallow copy) is a cheap and sensible defense that prevents accidental 
mutation of the module-level array.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
   **Line:** 279:279
   **Comment:**
        *Race Condition: The code passes the module-level `features` array 
directly into a user-provided mutator (`jsFnMutator(features)`), allowing the 
user function to mutate shared state and causing race conditions if multiple 
layers run concurrently; pass a defensive copy (shallow or deep depending on 
expectations) to avoid accidental mutation of module-level state.
   
   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>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts:
##########
@@ -0,0 +1,121 @@
+/**
+ * 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 { SqlaFormData } from '@superset-ui/core';
+import {
+  computeGeoJsonTextOptionsFromJsOutput,
+  computeGeoJsonTextOptionsFromFormData,
+  computeGeoJsonIconOptionsFromJsOutput,
+  computeGeoJsonIconOptionsFromFormData,
+} from './Geojson';
+
+jest.mock('@deck.gl/react', () => ({
+  __esModule: true,
+  default: () => null,
+}));
+
+test('computeGeoJsonTextOptionsFromJsOutput returns an empty object for 
non-object input', () => {
+  expect(computeGeoJsonTextOptionsFromJsOutput(null)).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput(42)).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput([1, 2, 3])).toEqual({});
+  expect(computeGeoJsonTextOptionsFromJsOutput('string')).toEqual({});
+});
+
+test('computeGeoJsonTextOptionsFromJsOutput extracts valid text options from 
the input object', () => {
+  const input = {
+    getText: 'name',
+    getTextColor: [1, 2, 3, 255],
+    invalidOption: true,
+  };
+  const expectedOutput = {
+    getText: 'name',
+    getTextColor: [1, 2, 3, 255],
+  };
+  expect(computeGeoJsonTextOptionsFromJsOutput(input)).toEqual(expectedOutput);
+});
+
+test('computeGeoJsonTextOptionsFromFormData computes text options based on 
form data', () => {
+  const formData: SqlaFormData = {
+    label_property_name: 'name',
+    label_color: { r: 1, g: 2, b: 3, a: 1 },
+    label_size: 123,
+    label_size_unit: 'pixels',
+    datasource: 'test_datasource',
+    viz_type: 'deck_geojson',
+  };
+
+  const expectedOutput = {
+    getText: expect.any(Function),
+    getTextColor: [1, 2, 3, 255],
+    getTextSize: 123,
+    textSizeUnits: 'pixels',
+  };
+
+  const actualOutput = computeGeoJsonTextOptionsFromFormData(formData);
+  expect(actualOutput).toEqual(expectedOutput);

Review Comment:
   **Suggestion:** Fragile equality assertion: using a strict object equality 
with an expected object that contains an asymmetric matcher 
(`expect.any(Function)`) will fail if the actual output contains additional 
valid properties; assert that the actual object contains the expected keys 
instead of requiring exact equality. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     expect(actualOutput).toEqual(expect.objectContaining(expectedOutput));
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The recommendation to use expect.objectContaining makes the test more 
resilient to harmless additions to the returned object.
   It's a legitimate improvement to reduce brittle tests and won't hide an 
actual regression in the asserted keys. The improved code directly replaces the 
two-line assertion and is syntactically correct.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
   **Line:** 70:70
   **Comment:**
        *Possible Bug: Fragile equality assertion: using a strict object 
equality with an expected object that contains an asymmetric matcher 
(`expect.any(Function)`) will fail if the actual output contains additional 
valid properties; assert that the actual object contains the expected keys 
instead of requiring exact equality.
   
   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>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx:
##########
@@ -169,6 +279,38 @@ export const getLayer: GetLayerType<GeoJsonLayer> = 
function ({
     processedFeatures = jsFnMutator(features) as ProcessedFeature[];
   }
 
+  let pointType = 'circle';
+  if (fd.enable_labels) {
+    pointType = `${pointType}+text`;
+  }
+  if (fd.enable_icons) {
+    pointType = `${pointType}+icon`;
+  }
+
+  let labelOpts: Partial<GeoJsonLayerProps> = {};
+  if (fd.enable_labels) {
+    if (fd.enable_label_javascript_mode) {
+      const generator = sandboxedEval(fd.label_javascript_config_generator);
+      if (typeof generator === 'function') {

Review Comment:
   **Suggestion:** Evaluating user-provided JavaScript with `sandboxedEval` and 
immediately calling `generator()` is executed without error handling; if the 
sandboxed evaluation or the user generator throws, the whole layer construction 
will throw and break rendering. Wrap `sandboxedEval` and `generator()` calls in 
a try/catch and fallback to a safe default (e.g. empty options) to prevent 
unhandled exceptions from user code. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
         try {
           const generator = 
sandboxedEval(fd.label_javascript_config_generator);
           if (typeof generator === 'function') {
             const out = generator();
             labelOpts = computeGeoJsonTextOptionsFromJsOutput(out);
           }
         } catch (e) {
           // eslint-disable-next-line no-console
           console.error('Failed to evaluate label javascript generator:', e);
           labelOpts = {};
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   This is a valid and practical improvement — sandboxedEval or the generator() 
invocation can throw and that would bubble up and break layer construction / 
rendering. Wrapping evaluation and invocation in try/catch and falling back to 
an empty options object avoids user code from taking down the layer. The 
suggested patch is straightforward and addresses a real runtime risk introduced 
by executing user-supplied code.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
   **Line:** 293:294
   **Comment:**
        *Possible Bug: Evaluating user-provided JavaScript with `sandboxedEval` 
and immediately calling `generator()` is executed without error handling; if 
the sandboxed evaluation or the user generator throws, the whole layer 
construction will throw and break rendering. Wrap `sandboxedEval` and 
`generator()` calls in a try/catch and fallback to a safe default (e.g. empty 
options) to prevent unhandled exceptions from user code.
   
   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>



-- 
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]

Reply via email to