Checkpoint

This commit is contained in:
Beto Dealmeida
2025-07-17 16:40:45 -04:00
parent b27d6dc9b6
commit 25b06dbedb
3 changed files with 81 additions and 18 deletions

View File

@@ -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
};
}

View File

@@ -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[] });
return response.json as { dimensions: string[]; metrics: string[] };
// Cache the promise
apiCallCache.set(cacheKey, apiPromise);
// 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,

View File

@@ -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.