This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch dbt-metricflow
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 25b06dbedbde56df324d7e145fcdbbb3cff21cbb
Author: Beto Dealmeida <[email protected]>
AuthorDate: Thu Jul 17 16:40:45 2025 -0400

    Checkpoint
---
 .../src/shared-controls/dndControls.tsx            | 67 +++++++++++++++++-----
 .../controls/SemanticLayerVerification.tsx         | 31 +++++++++-
 .../components/controls/withAsyncVerification.tsx  |  1 +
 3 files changed, 81 insertions(+), 18 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
index 120a8cbbd8..10001a954c 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
@@ -54,6 +54,12 @@ let createColumnsVerification: any = null;
 let createSemanticLayerOnChange: any = null;
 let SEMANTIC_LAYER_CONTROL_FIELDS: any = null;
 
+// Notification system for when utilities are set
+const enhancedControls: Array<{
+  controlName: string;
+  invalidateCache: () => void;
+}> = [];
+
 // Export function to set semantic layer utilities from main app
 export function setSemanticLayerUtilities(utilities: {
   withAsyncVerification: any;
@@ -69,6 +75,11 @@ export function setSemanticLayerUtilities(utilities: {
     createSemanticLayerOnChange,
     SEMANTIC_LAYER_CONTROL_FIELDS,
   } = utilities);
+  
+  // Notify all enhanced controls that utilities are now available
+  enhancedControls.forEach(control => {
+    control.invalidateCache();
+  });
 }
 
 /**
@@ -92,27 +103,52 @@ function enhanceControlWithSemanticLayer(
   controlName: string,
   verificationType: 'metrics' | 'columns',
 ) {
+  // Cache for the enhanced control type
+  let cachedEnhancedType: any = null;
+  let utilitiesWereAvailable = false;
+  
+  // Register with notification system
+  enhancedControls.push({
+    controlName,
+    invalidateCache: () => {
+      cachedEnhancedType = null;
+      utilitiesWereAvailable = false;
+    }
+  });
+  
   // Return a control that will be enhanced at runtime if utilities are 
available
   return {
     ...baseControl,
     // Override the type to use a function that checks for enhancement at 
runtime
     get type() {
-      if (withAsyncVerification) {
-        const verificationFn =
-          verificationType === 'metrics'
-            ? createMetricsVerification(controlName)
-            : createColumnsVerification(controlName);
-
-        return withAsyncVerification({
-          baseControl: baseControl.type,
-          verify: verificationFn,
-          onChange: createSemanticLayerOnChange(
-            controlName,
-            SEMANTIC_LAYER_CONTROL_FIELDS,
-          ),
-          showLoadingState: true,
-        });
+      // Check if utilities became available since last call
+      const utilitiesAvailableNow = !!withAsyncVerification;
+      
+      if (utilitiesAvailableNow) {
+        // If utilities just became available or we haven't cached yet, create 
enhanced control
+        if (!utilitiesWereAvailable || !cachedEnhancedType) {
+          const verificationFn =
+            verificationType === 'metrics'
+              ? createMetricsVerification(controlName)
+              : createColumnsVerification(controlName);
+          
+          cachedEnhancedType = withAsyncVerification({
+            baseControl: baseControl.type,
+            verify: verificationFn,
+            onChange: createSemanticLayerOnChange(
+              controlName,
+              SEMANTIC_LAYER_CONTROL_FIELDS,
+            ),
+            showLoadingState: true,
+          });
+          
+          utilitiesWereAvailable = true;
+        }
+        
+        return cachedEnhancedType;
       }
+      
+      utilitiesWereAvailable = false;
       return baseControl.type;
     },
     mapStateToProps: (state: any, controlState: any) => {
@@ -131,6 +167,7 @@ function enhanceControlWithSemanticLayer(
           ...originalProps,
           needAsyncVerification: needsVerification,
           form_data: state.form_data,
+          datasource: state.datasource, // Pass datasource to verification 
function
         };
       }
 
diff --git 
a/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
 
b/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
index 850be4e11a..ed5e24ef18 100644
--- 
a/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
+++ 
b/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
@@ -99,6 +99,9 @@ function supportsSemanticLayerVerification(datasource: 
Dataset): boolean {
   return Boolean(database.engine_information?.supports_dynamic_columns);
 }
 
+// Cache for API calls to prevent duplicates
+const apiCallCache = new Map<string, Promise<{ dimensions: string[]; metrics: 
string[] } | null>>();
+
 /**
  * Call the validation API
  */
@@ -112,19 +115,40 @@ async function callValidationAPI(
     return null;
   }
 
+  // Create cache key based on the request parameters
+  const cacheKey = JSON.stringify({
+    datasource_id: datasource.id,
+    dimensions: selectedDimensions.sort(),
+    metrics: selectedMetrics.sort(),
+  });
+
+  // Check if we already have a pending request for the same parameters
+  if (apiCallCache.has(cacheKey)) {
+    return apiCallCache.get(cacheKey)!;
+  }
+
   try {
-    const response = await SupersetClient.post({
+    const apiPromise = SupersetClient.post({
       endpoint: `/api/v1/database/${databaseId}/valid_metrics_and_dimensions/`,
       jsonPayload: {
         datasource_id: datasource.id,
         dimensions: selectedDimensions,
         metrics: selectedMetrics,
       },
-    });
+    }).then(response => response.json as { dimensions: string[]; metrics: 
string[] });
+
+    // Cache the promise
+    apiCallCache.set(cacheKey, apiPromise);
 
-    return response.json as { dimensions: string[]; metrics: string[] };
+    // Remove from cache after a short delay to allow for immediate duplicates
+    setTimeout(() => {
+      apiCallCache.delete(cacheKey);
+    }, 100);
+
+    return await apiPromise;
   } catch (error) {
     console.warn('Failed to fetch valid metrics and dimensions:', error);
+    apiCallCache.delete(cacheKey);
     return null;
   }
 }
@@ -233,6 +257,7 @@ export function createColumnsVerification(controlName?: 
string): AsyncVerify {
     const queryFields = collectQueryFields(updatedFormData || {});
 
     // Call validation API
+    
     const validationResult = await callValidationAPI(
       datasource as Dataset,
       queryFields.dimensions,
diff --git 
a/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx 
b/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
index 852ac70a30..5c45427247 100644
--- 
a/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
+++ 
b/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
@@ -146,6 +146,7 @@ export default function withAsyncVerification({
     const [isLoading, setIsLoading] = useState<boolean>(initialIsLoading);
     const { addWarningToast } = restProps.actions;
     const verificationTriggeredByChange = useRef(false);
+    
 
     // memoize `restProps`, so that verification only triggers when material
     // props are actually updated.

Reply via email to