/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed 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 { observer } from 'mobx-react-lite'; import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; import { LegendQueryDataCubeSource, RawLegendQueryDataCubeSource, } from '../../stores/model/LegendQueryDataCubeSource.js'; import { LakehouseConsumerDataCubeSource } from '../../stores/model/LakehouseConsumerDataCubeSource.js'; import { useLegendDataCubeApplicationStore } from '../LegendDataCubeFrameworkProvider.js'; import { EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl, EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCViewUrl, EXTERNAL_APPLICATION_NAVIGATION__generateStudioViewUrl, EXTERNAL_APPLICATION_NAVIGATION__generateLakehouseViewUrl, EXTERNAL_APPLICATION_NAVIGATION__generateLakehouseAdHocViewUrl, } from '../../__lib__/LegendDataCubeNavigation.js'; import { DataCubeIcon } from '@finos/legend-art'; import { _defaultPrimitiveTypeValue, _elementPtr, _primitiveValue, _property, AlertType, FormAlert, FormButton, isPrimitiveType, UserDefinedFunctionDataCubeSource, } from '@finos/legend-data-cube'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { type DepotServerClient, DepotScope, StoreProjectData, } from '@finos/legend-server-depot'; import { deepClone, guaranteeIsString, guaranteeNonNullable, guaranteeType, isNonNullable, returnUndefOnError, } from '@finos/legend-shared'; import { type LightQuery, type PureProtocolProcessorPlugin, type V1_Enumeration, type V1_PureModelContext, type V1_PureModelContextData, type V1_Variable, PRIMITIVE_TYPE, V1_CORE_SYSTEM_MODELS, V1_deserializePureModelContext, V1_deserializePureModelContextData, V1_LegendSDLC, V1_observe_ValueSpecification, V1_PackageableType, V1_PureModelContextPointer, V1_serializeValueSpecification, V1_ValueSpecification, } from '@finos/legend-graph'; import { isValidV1_ValueSpecification, V1_BasicValueSpecificationEditor, } from '@finos/legend-query-builder'; import { LegendDataCubeBuilderState } from '../../stores/builder/LegendDataCubeBuilderStore.js'; import { fetchV1Enumeration, isVariableEnumerationType, } from '../../stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.js'; import { generateGAVCoordinates } from '@finos/legend-storage'; const handleFetchEnumerations = async ( enumerationVariables: V1_Variable[], query: LightQuery, systemModel: V1_PureModelContextData, depotServerClient: DepotServerClient, plugins: PureProtocolProcessorPlugin[], updateCallback: (val: { [name: string]: V1_Enumeration }) => void, ) => { await Promise.all( enumerationVariables.map(async (variable) => { const packageableType = guaranteeType( variable.genericType?.rawType, V1_PackageableType, 'Can only edit parameters with packageable type', ); const enumeration = await fetchV1Enumeration( packageableType.fullPath, query, systemModel, depotServerClient, plugins, ); return { variable, enumeration }; }), ).then((response) => { const result = response.reduce( (acc, { variable, enumeration }) => { acc[variable.name] = enumeration; return acc; }, {} as { [name: string]: V1_Enumeration }, ); updateCallback(result); }); }; const LegendQuerySourceViewer = observer( (props: { source: LegendQueryDataCubeSource }) => { const { source } = props; const store = useLegendDataCubeBuilderStore(); const application = useLegendDataCubeApplicationStore(); const systemModel = useMemo( () => V1_deserializePureModelContextData(V1_CORE_SYSTEM_MODELS), [], ); const [params, setParams] = useState< { variable: V1_Variable; valueSpec: V1_ValueSpecification }[] >(source.parameterValues.map(deepClone)); const [enumerations, setEnumerations] = useState<{ [name: string]: V1_Enumeration; }>({}); const [isUpdatingParameters, setIsUpdatingParameters] = useState(false); // If caching is enabled on the grid, we disable editing the query parameters. // User will need to disable caching to be able to edit parameters. const isCachingEnabled = store.builder?.dataCube?.isCachingEnabled() ?? false; const _handleFetchEnumerations = useCallback(async () => { const enumerationVariables = source.parameterValues .map((parameter) => source.lambda.parameters.find( (_param) => parameter.variable.name === _param.name, ), ) .filter(isNonNullable) .filter(isVariableEnumerationType); await handleFetchEnumerations( enumerationVariables, source.info, systemModel, store.depotServerClient, application.pluginManager.getPureProtocolProcessorPlugins(), setEnumerations, ); }, [ application.pluginManager, source, store.depotServerClient, systemModel, ]); useEffect(() => { // eslint-disable-next-line no-void void void _handleFetchEnumerations(); }, [_handleFetchEnumerations]); const updateParameterValue = ( name: string, valueSpec: V1_ValueSpecification, ): void => { setParams( params.map((param) => { if (param.variable.name === name) { param.valueSpec = valueSpec; } return param; }), ); }; const updateBuilderWithNewSpecification = async () => { // Verify that all params are valid if ( params.some( (param) => !isValidV1_ValueSpecification( param.valueSpec, param.variable.multiplicity, ), ) ) { return; } setIsUpdatingParameters(true); try { // Create the new raw source with new parameter values const newRawSource = new RawLegendQueryDataCubeSource(); newRawSource.queryId = source.info.id; newRawSource.parameterValues = params.map((param) => { const parameterVariable = source.lambda.parameters.find( (_param) => _param.name === param.variable.name, ); return [ JSON.stringify( V1_serializeValueSpecification( guaranteeNonNullable(parameterVariable), application.pluginManager.getPureProtocolProcessorPlugins(), ), ), JSON.stringify( V1_serializeValueSpecification( guaranteeType(param.valueSpec, V1_ValueSpecification), application.pluginManager.getPureProtocolProcessorPlugins(), ), ), ]; }); const newRawSourceData = RawLegendQueryDataCubeSource.serialization.toJson(newRawSource); // Process the new raw source and create the new specification const newSource = await store.engine.processSource(newRawSourceData); const newSpecification = await store.engine.generateBaseSpecification( RawLegendQueryDataCubeSource.serialization.toJson(newRawSource), newSource, ); newSpecification.configuration = store.builder?.initialSpecification.configuration; // Update the builder with a new state containing the new specification // We pass in the existing persistent data cube so we don't lose the // saved state of the data cube, if it exists. store.setBuilder( new LegendDataCubeBuilderState( store, newSpecification, store.builder?.persistentDataCube, ), ); store.sourceViewerDisplay.close(); } finally { setIsUpdatingParameters(false); } }; const link = application.config.queryApplicationUrl ? EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl( application.config.queryApplicationUrl, source.info.id, ) : undefined; const queryHasParameters = params.length > 0; return (