/**
* 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 {
DataCube,
FormButton,
type DataCubeSettingValues,
DataCubePlaceholder,
DataCubeNativeMenuItem,
DataCubePlaceholderErrorDisplay,
type DataCubeMenuItem,
DataCubeSpecification,
DataCubeEvent,
DataCubeTitleBarMenuItems,
} from '@finos/legend-data-cube';
import {
useLegendDataCubeBuilderStore,
withLegendDataCubeBuilderStore,
} from './LegendDataCubeBuilderStoreProvider.js';
import { useParams } from '@finos/legend-application/browser';
import {
LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN,
type LegendDataCubeBuilderPathParams,
} from '../../__lib__/LegendDataCubeNavigation.js';
import { useEffect } from 'react';
import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js';
import type { LegendDataCubeBuilderStore } from '../../stores/builder/LegendDataCubeBuilderStore.js';
import { ReleaseViewer } from '@finos/legend-application';
import { isNonNullable } from '@finos/legend-shared';
import { LakehouseConsumerDataCubeSource } from '../../stores/model/LakehouseConsumerDataCubeSource.js';
import { LakehouseProducerDataCubeSource } from '../../stores/model/LakehouseProducerDataCubeSource.js';
import { useAuth, type AuthContextProps } from 'react-oidc-context';
const LegendDataCubeBuilderHeader = observer(() => {
const store = useLegendDataCubeBuilderStore();
return (
{store.application.pluginManager
.getApplicationPlugins()
.map((plugin) => plugin.builderInnerHeaderRenderer?.(store.builder))}
store.loader.display.open()}>
Load DataCube
store.creator.display.open()}
>
New DataCube
store.saverDisplay.open()}
>
Save DataCube
);
});
export const LegendDataCubeReleaseLogManager = observer(
(props: { showOnlyLatestNotes: boolean }) => {
const { showOnlyLatestNotes } = props;
const store = useLegendDataCubeBuilderStore();
let auth: AuthContextProps | undefined;
try {
auth = useAuth();
} catch {
auth = undefined;
}
const applicationStore = store.application;
const releaseService = applicationStore.releaseNotesService;
const releaseNotes =
(showOnlyLatestNotes
? releaseService.showableVersions()
: releaseService.releaseNotes) ?? [];
useEffect(() => {
if (auth === undefined || auth.isAuthenticated) {
applicationStore.releaseNotesService.updateViewedVersion();
}
}, [applicationStore, auth, auth?.isAuthenticated]);
return (
New features, enhancements and bug fixes that were released
{releaseNotes.map((e) => (
))}
);
},
);
export const LegendDataCubeAbout = observer(() => {
const store = useLegendDataCubeBuilderStore();
const releaseService = store.application.releaseNotesService;
const config = store.application.config;
return (
Environment:
{config.env}
Version:
{config.appVersion}
Revision:
{config.appVersionCommitId}
Build Time:
{config.appVersionBuildTime}
{releaseService.isConfigured && (
store.releaseLogDisplay.open()}
className="my-0.5 flex cursor-pointer font-bold text-sky-500 underline"
>
Details of Released Versions
)}
{config.engineQueryServerUrl !== undefined && (
)}
);
});
function generateMenuItems(store: LegendDataCubeBuilderStore) {
const application = store.application;
const builder = store.builder;
const persistentDataCube = builder?.persistentDataCube;
const isLakehouseSource =
store.builder?.source instanceof LakehouseConsumerDataCubeSource ||
store.builder?.source instanceof LakehouseProducerDataCubeSource;
const logMenuItem = (menuName: string) => {
store.engine.sendTelemetry(DataCubeEvent.SELECT_ITEM_TITLE_BAR, {
...store.engine.getDataFromSource(
builder?.dataCube?.getProcessedSource(),
),
menuName: menuName,
});
};
const docLinks = application.documentationService.links;
const menuItems: (DataCubeMenuItem | DataCubeNativeMenuItem)[] = builder
? [
...(builder.source
? [
{
label: DataCubeTitleBarMenuItems.VIEW_SOURCE,
action: () => {
const sourceViewerHeight =
store.application.pluginManager
.getApplicationPlugins()
.map((plugin) =>
plugin.getSourceViewerHeight?.(store.builder),
)
.filter(isNonNullable)[0] ?? 200;
store.sourceViewerDisplay.configuration.window.height =
Math.min(600, sourceViewerHeight);
store.sourceViewerDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.VIEW_SOURCE);
},
},
{
label: DataCubeTitleBarMenuItems.EDIT_SOURCE_QUERY,
action: () => {
store.queryEditorDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.EDIT_SOURCE_QUERY);
},
disabled: !isLakehouseSource,
},
]
: []),
...(persistentDataCube
? [
{
label: DataCubeTitleBarMenuItems.RESET_TO_LATEST_SAVE,
action: () => {
const latestSpecification =
DataCubeSpecification.serialization.fromJson(
persistentDataCube.content,
);
builder.dataCube
?.applySpecification(latestSpecification)
.catch((error) =>
store.alertService.alertUnhandledError(error),
);
logMenuItem(DataCubeTitleBarMenuItems.VIEW_SOURCE);
},
},
{
label: DataCubeTitleBarMenuItems.EDIT_QUERY,
action: () => {
store.codeEditorDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.EDIT_QUERY);
},
},
{
label: DataCubeTitleBarMenuItems.UPDATE_INFO,
action: () => {
// effectively, we open the save window to let user update the DataCube info, such as name, auto-enable caching, etc.
store.saverDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.UPDATE_INFO);
},
disabled:
!store.canCurrentUserManageDataCube(persistentDataCube),
},
{
label: DataCubeTitleBarMenuItems.DELETE_DATACUBE,
action: () => {
store.setDataCubeToDelete(builder.persistentDataCube);
store.deleteConfirmationDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.DELETE_DATACUBE);
},
disabled:
!store.canCurrentUserManageDataCube(persistentDataCube),
},
]
: []),
]
: [];
return [
...(menuItems.length
? [...menuItems, DataCubeNativeMenuItem.SEPARATOR]
: []),
...(docLinks?.length
? docLinks.map((link) => ({
label: link.label,
action: () => {
application.navigationService.navigator.visitAddress(link.url);
},
disabled: !link.url,
}))
: [
{
label: DataCubeTitleBarMenuItems.SEE_DOCUMENTATION,
action: () => {
const url = application.documentationService.url;
if (url) {
application.navigationService.navigator.visitAddress(
application.documentationService.url,
);
}
logMenuItem(DataCubeTitleBarMenuItems.SEE_DOCUMENTATION);
},
disabled: !application.documentationService.url,
},
]),
{
label: DataCubeTitleBarMenuItems.ABOUT,
action: () => {
store.aboutDisplay.open();
logMenuItem(DataCubeTitleBarMenuItems.ABOUT);
},
},
];
}
export const LegendDataCubeBuilder = withLegendDataCubeBuilderStore(
observer(() => {
const store = useLegendDataCubeBuilderStore();
const builder = store.builder;
const application = store.application;
const params = useParams();
const dataCubeId =
params[LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.DATA_CUBE_ID];
let auth: AuthContextProps | undefined;
try {
auth = useAuth();
} catch {
auth = undefined;
}
useEffect(() => {
application.navigationService.navigator.blockNavigation(
// Only block navigation in production, in development, we should have
// the flexibility to reload the page quickly
// eslint-disable-next-line no-process-env
[() => process.env.NODE_ENV === 'production'],
);
return (): void => {
application.navigationService.navigator.unblockNavigation();
};
}, [application]);
useEffect(() => {
store
.initialize()
.catch((error) => store.alertService.alertUnhandledError(error));
return () => {
store
.cleanUp()
.catch((error) => store.alertService.alertUnhandledError(error));
};
}, [store]);
useEffect(() => {
store
.loadDataCube(dataCubeId)
.catch((error) => store.alertService.alertUnhandledError(error));
}, [store, dataCubeId]);
useEffect(() => {
const releaseService = application.releaseNotesService;
const releaseNotes = releaseService.showableVersions();
const isOpen = releaseService.showCurrentReleaseModal;
if (releaseService.isConfigured && isOpen && releaseNotes?.length) {
store.releaseNotesDisplay.open();
} else if (auth === undefined || auth.isAuthenticated) {
releaseService.updateViewedVersion();
}
}, [application, store, auth, auth?.isAuthenticated]);
if (!store.initializeState.hasSucceeded) {
return (
}
menuItems={generateMenuItems(store)}
>
{store.initializeState.isInProgress && (
)}
{store.initializeState.hasFailed && (
)}
);
}
if (!builder) {
return (
}
menuItems={generateMenuItems(store)}
>
Create a new DataCube to start
store.creator.display.open()}
>
New DataCube
);
}
return (
,
getHeaderMenuItems: () => generateMenuItems(store),
settingsData: {
configurations: store.baseStore.settings,
values: application.settingService.getObjectValue(
LegendDataCubeSettingStorageKey.DATA_CUBE,
) as DataCubeSettingValues | undefined,
},
onSettingsChanged(event) {
application.settingService.persistValue(
LegendDataCubeSettingStorageKey.DATA_CUBE,
event.values,
);
},
enableCache: true,
}}
/>
);
}),
);