/** * Copyright (c) 2025-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 { DataCubeCodeEditor, FormTextInput } from '@finos/legend-data-cube'; import { CustomSelectorInput } from '@finos/legend-art'; import { useAuth } from 'react-oidc-context'; import { assertErrorThrown, guaranteeNonNullable } from '@finos/legend-shared'; import { useEffect, type ReactNode } from 'react'; import type { LakehouseConsumerDataCubeSourceBuilderState } from '../../../stores/builder/source/LakehouseConsumerDataCubeSourceBuilderState.js'; import { useLegendDataCubeBuilderStore } from '../LegendDataCubeBuilderStoreProvider.js'; import { V1_EntitlementsLakehouseEnvironmentType, type V1_EntitlementsDataProductLite, } from '@finos/legend-graph'; export const LakehouseConsumerDataCubeSourceBuilder: React.FC<{ sourceBuilder: LakehouseConsumerDataCubeSourceBuilderState; }> = observer(({ sourceBuilder }) => { const auth = useAuth(); const store = useLegendDataCubeBuilderStore(); const renderDataProductLabel = ( dataProduct: V1_EntitlementsDataProductLite, ): ReactNode => { const title = dataProduct.title; const id = guaranteeNonNullable(dataProduct.id); // If title is empty, show id only. Otherwise show title (prominent) and id (muted) // Main text: slightly larger and normal weight. Subtext: grey and slightly smaller. return (
{title ?? id}
{title ? (
{id}
) : null}
); }; const renderDataProductMainText = ( dataProduct: V1_EntitlementsDataProductLite, ): ReactNode => { const title = dataProduct.title; const id = guaranteeNonNullable(dataProduct.id); // Ensure selected value is vertically centered inside the control by // making the label container full-height and centering its content. return (
{title ?? id}
); }; useEffect(() => { sourceBuilder.reset(); try { sourceBuilder.loadDataProducts(auth.user?.access_token); } catch (error) { assertErrorThrown(error); store.alertService.alertUnhandledError(error); } }, [sourceBuilder, auth, store]); return (
Mode
{ sourceBuilder.setEnvMode(newVal.value); sourceBuilder.resetDataProduct(); }} value={{ label: sourceBuilder.envMode === V1_EntitlementsLakehouseEnvironmentType.PRODUCTION ? 'Production' : sourceBuilder.envMode === V1_EntitlementsLakehouseEnvironmentType.PRODUCTION_PARALLEL ? 'Production (parallel)' : 'Development', value: sourceBuilder.envMode, }} placeholder="Choose mode" isClearable={true} escapeClearsValue={true} />
Data Product
{ const title = dataProduct.title; const id = guaranteeNonNullable(dataProduct.id); return { label: renderDataProductLabel(dataProduct), // include a searchable text so react-select filter can match id/title searchText: `${title} ${id}`.trim(), value: guaranteeNonNullable(dataProduct), }; })} filterOption={(option, rawInput) => { const input = rawInput.toLowerCase(); // option.data may contain our custom searchText const data = option.data as { searchText?: string } | undefined; // Prefer a non-empty searchText from data when available const searchTextSource = data?.searchText && data.searchText.trim() !== '' ? data.searchText.toLowerCase().trim() : ''.trim(); // If user hasn't typed anything, show all options if (input === '') { return true; } return searchTextSource.includes(input); }} disabled={ sourceBuilder.dataProductLoadingState.isInProgress || sourceBuilder.dataProductLoadingState.hasFailed } isLoading={sourceBuilder.dataProductLoadingState.isInProgress} onChange={( newValue: { label: ReactNode; searchText?: string; value: V1_EntitlementsDataProductLite; } | null, ) => { sourceBuilder.setSelectedDataProduct(newValue?.value); sourceBuilder .fetchAccessPoints() .catch((error) => store.alertService.alertUnhandledError(error), ); }} value={ sourceBuilder.selectedDataProduct ? { label: renderDataProductMainText( sourceBuilder.selectedDataProduct, ), searchText: `${sourceBuilder.selectedDataProduct.title ?? ''} ${sourceBuilder.selectedDataProduct.id}`.trim(), value: sourceBuilder.selectedDataProduct, } : null } placeholder={`Choose a Data Product`} isClearable={false} escapeClearsValue={true} />
{sourceBuilder.accessPoints.size > 0 && (
Access Point
({ label: title?.trim() ? title : id, value: id, }), )} disabled={false} isLoading={false} onChange={(newValue: { label: string; value: string } | null) => { const value = newValue?.value ?? ''; const label = newValue?.label ?? ''; sourceBuilder.setSelectedAccessPoint([value, label]); sourceBuilder.setWarehouse( sourceBuilder.DEFAULT_CONSUMER_WAREHOUSE, ); sourceBuilder .initializeQuery() .catch((error) => store.alertService.alertUnhandledError(error), ); }} value={ sourceBuilder.selectedAccessPoint ? { label: sourceBuilder.selectedAccessPoint[1], value: sourceBuilder.selectedAccessPoint[0], } : null } isClearable={false} escapeClearsValue={true} />
)} {sourceBuilder.selectedAccessPoint && (
Warehouse
{ sourceBuilder.setWarehouse(event.target.value); }} />
)} {sourceBuilder.warehouse && sourceBuilder.showQueryEditor && (
Query
)}
); });