/**
* 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 && (
)}
);
});