ripped out dataset context

This commit is contained in:
Nate Kelley 2025-03-12 14:10:45 -06:00
parent aa77f00bc7
commit 6f188111c6
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
21 changed files with 66 additions and 276 deletions

View File

@ -124,3 +124,9 @@ export const useDeleteDataset = () => {
onSuccess
});
};
export const useIndividualDataset = ({ datasetId }: { datasetId: string }) => {
const dataset = useGetDatasetMetadata(datasetId);
const datasetData = useGetDatasetData(datasetId);
return { dataset, datasetData };
};

View File

@ -7,6 +7,7 @@ import {
updateDatasource
} from './requests';
import { queryKeys } from '@/api/query_keys';
import { useBusterNotifications } from '@/context/BusterNotifications';
export const useListDatasources = () => {
return useQuery({
@ -25,8 +26,20 @@ export const useGetDatasource = (id: string | undefined) => {
export const useDeleteDatasource = () => {
const queryClient = useQueryClient();
const { openConfirmModal } = useBusterNotifications();
const mutationFn = async (dataSourceId: string) => {
await openConfirmModal({
title: 'Delete Data Source',
content: 'Are you sure you want to delete this data source?',
onOk: async () => {
await deleteDatasource(dataSourceId);
}
});
};
return useMutation({
mutationFn: deleteDatasource,
mutationFn,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: queryKeys.datasourceGetList.queryKey

View File

@ -3,13 +3,15 @@ import type { DataSource, DataSourceListItem } from '@/api/asset_interfaces/data
export const datasourceGetList = queryOptions<DataSourceListItem[]>({
queryKey: ['datasources', 'list'] as const,
staleTime: 30 * 1000 // 30 seconds
staleTime: 30 * 1000, // 30 seconds,
initialData: [],
initialDataUpdatedAt: 0
});
export const datasourceGet = (id: string) =>
queryOptions<DataSource>({
queryKey: ['datasources', 'get', id] as const,
staleTime: 60 * 1000 // 60 seconds
staleTime: 10 * 1000 // 10 seconds
});
export const datasourceQueryKeys = {

View File

@ -1,12 +1,11 @@
'use client';
import { useIndividualDataset } from '@/context/Datasets';
import { useSelectedLayoutSegment } from 'next/navigation';
import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react';
import { DatasetApps } from './config';
import { createContext, useContextSelector } from 'use-context-selector';
import { useDebounce, useMemoizedFn } from '@/hooks';
import { useDeployDataset } from '@/api/buster_rest';
import { useDeployDataset, useIndividualDataset } from '@/api/buster_rest';
export const useDatasetPageContext = ({ datasetId }: { datasetId: string }) => {
const segments = useSelectedLayoutSegment() as DatasetApps;

View File

@ -4,23 +4,17 @@ import type { DataSource } from '@/api/asset_interfaces';
import { PulseLoader } from '@/components/ui/loaders';
import { AppDataSourceIcon } from '@/components/ui/icons/AppDataSourceIcons';
import { formatDate } from '@/lib';
import { Button } from '@/components/ui/buttons';
import { Dropdown, DropdownItems } from '@/components/ui/dropdown';
import { Separator } from '@/components/ui/seperator';
import React from 'react';
import { DataSourceFormContent } from './_DatasourceFormContent';
import { Title, Text } from '@/components/ui/typography';
import {
useDataSourceIndividual,
useDataSourceIndividualContextSelector
} from '@/context/DataSources';
import { Trash } from '@/components/ui/icons';
import { useDeleteDatasource, useGetDatasource } from '@/api/buster_rest/datasource';
export const DatasourceForm: React.FC<{ datasourceId: string }> = ({ datasourceId }) => {
const { dataSource } = useDataSourceIndividual(datasourceId);
const loadingDataSource = !dataSource?.id;
const { data: dataSource, isFetched: isFetchedDataSource } = useGetDatasource(datasourceId);
if (loadingDataSource) {
if (!isFetchedDataSource || !dataSource) {
return <SkeletonLoader />;
}
@ -59,9 +53,7 @@ const DataSourceFormHeader: React.FC<{ dataSource: DataSource }> = ({ dataSource
};
const DataSourceFormStatus: React.FC<{ dataSource: DataSource }> = ({ dataSource }) => {
const onDeleteDataSource = useDataSourceIndividualContextSelector(
(state) => state.onDeleteDataSource
);
const { mutateAsync: onDeleteDataSource } = useDeleteDatasource();
const dropdownItems: DropdownItems = [
{

View File

@ -2,10 +2,9 @@
import type { DataSource, DataSourceTypes } from '@/api/asset_interfaces';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { useDataSourceIndividualContextSelector } from '@/context/DataSources/DataSourceIndividualProvider';
import { BusterRoutes } from '@/routes';
import { useMemoizedFn } from '@/hooks';
import React, { useState } from 'react';
import React from 'react';
import { PostgresForm } from './_forms/PostgresForm';
import { DatasourceCreateCredentials } from '@/api/request_interfaces/datasources';
import { MySqlForm } from './_forms/MySqlForm';
@ -16,6 +15,7 @@ import { DataBricksForm } from './_forms/DataBricksForm';
import { useConfetti } from '@/hooks/useConfetti';
import { SqlServerForm } from './_forms/SqlServerForm';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { useCreateDatasource, useUpdateDatasource } from '@/api/buster_rest/datasource';
const FormRecord: Record<
DataSourceTypes,
@ -45,24 +45,19 @@ export const DataSourceFormContent: React.FC<{
type: DataSourceTypes;
}> = ({ dataSource, type, useConnection = false }) => {
const SelectedForm = FormRecord[type];
const onUpdateDataSource = useDataSourceIndividualContextSelector(
(state) => state.onUpdateDataSource
);
const onCreateDataSource = useDataSourceIndividualContextSelector(
(state) => state.onCreateDataSource
);
const { mutateAsync: onUpdateDataSource, isPending: isUpdatingDataSource } =
useUpdateDatasource();
const { mutateAsync: onCreateDataSource, isPending: isCreatingDataSource } =
useCreateDatasource();
const onChangePage = useAppLayoutContextSelector((s) => s.onChangePage);
const { openConfirmModal } = useBusterNotifications();
const { fireConfetti } = useConfetti();
const [submitting, setSubmitting] = useState(false);
const onSubmit = useMemoizedFn(async (credentials: DatasourceCreateCredentials) => {
setSubmitting(true);
try {
const name = credentials.datasource_name;
if (!useConnection) {
if (!useConnection && !isUpdatingDataSource && !isCreatingDataSource) {
await onUpdateDataSource({
id: dataSource!.id,
name,
@ -92,8 +87,6 @@ export const DataSourceFormContent: React.FC<{
} catch (error) {
// TODO: handle error
}
setSubmitting(false);
});
return (
@ -105,7 +98,7 @@ export const DataSourceFormContent: React.FC<{
{SelectedForm && (
<SelectedForm
dataSource={dataSource}
submitting={submitting}
submitting={isUpdatingDataSource || isCreatingDataSource}
onSubmit={onSubmit}
useConnection={useConnection}
/>

View File

@ -1,9 +1,5 @@
'use client';
import {
useDataSourceIndividualContextSelector,
useDataSourceListContextSelector
} from '@/context/DataSources';
import React from 'react';
import { AppDataSourceIcon } from '@/components/ui/icons/AppDataSourceIcons';
import type { DataSourceListItem } from '@/api/asset_interfaces';
@ -17,14 +13,12 @@ import { Button } from '@/components/ui/buttons';
import { Dropdown, DropdownItems } from '@/components/ui/dropdown';
import { Plus, Dots, Trash } from '@/components/ui/icons';
import { cn } from '@/lib/classMerge';
import { useDeleteDatasource, useListDatasources } from '@/api/buster_rest/datasource';
export const DatasourceList: React.FC = () => {
const isAdmin = useUserConfigContextSelector((x) => x.isAdmin);
const dataSourcesList = useDataSourceListContextSelector((x) => x.dataSourcesList) || [];
const isFetchedDatasourcesList = useDataSourceListContextSelector(
(state) => state.isFetchedDatasourcesList
);
const { data: dataSourcesList, isFetched: isFetchedDatasourcesList } = useListDatasources();
const { mutateAsync: onDeleteDataSource } = useDeleteDatasource();
const onChangePage = useAppLayoutContextSelector((s) => s.onChangePage);
const hasDataSources = dataSourcesList.length > 0 && !isFetchedDatasourcesList;
@ -35,7 +29,7 @@ export const DatasourceList: React.FC = () => {
{!isFetchedDatasourcesList ? (
<SkeletonLoader />
) : hasDataSources ? (
<DataSourceItems sources={dataSourcesList} />
<DataSourceItems sources={dataSourcesList} onDeleteDataSource={onDeleteDataSource} />
) : (
<SettingsEmptyState
showButton={isAdmin}
@ -72,11 +66,14 @@ const AddSourceHeader: React.FC<{ isAdmin: boolean }> = ({ isAdmin }) => {
);
};
const DataSourceItems: React.FC<{ sources: DataSourceListItem[] }> = ({ sources }) => {
const DataSourceItems: React.FC<{
sources: DataSourceListItem[];
onDeleteDataSource: (dataSourceId: string) => Promise<void>;
}> = ({ sources, onDeleteDataSource }) => {
return (
<div className="flex flex-col space-y-4">
{sources.map((source) => {
return <ListItem key={source.id} source={source} />;
return <ListItem key={source.id} source={source} onDeleteDataSource={onDeleteDataSource} />;
})}
</div>
);
@ -84,9 +81,8 @@ const DataSourceItems: React.FC<{ sources: DataSourceListItem[] }> = ({ sources
const ListItem: React.FC<{
source: DataSourceListItem;
}> = ({ source }) => {
const onDeleteDataSource = useDataSourceIndividualContextSelector((x) => x.onDeleteDataSource);
onDeleteDataSource: (dataSourceId: string) => Promise<void>;
}> = ({ source, onDeleteDataSource }) => {
const dropdownItems: DropdownItems = [
{
label: 'Delete',

View File

@ -8,7 +8,7 @@ import { BusterRoutes, createBusterRoute } from '@/routes';
import { useRouter } from 'next/navigation';
import { AppModal } from '@/components/ui/modal';
import { Text } from '@/components/ui/typography';
import { useDataSourceListContextSelector } from '@/context/DataSources';
import { useListDatasources } from '@/api/buster_rest/datasource';
const headerConfig = {
title: 'Create a dataset',
@ -27,7 +27,7 @@ export const NewDatasetModal: React.FC<{
const [selectedDatasource, setSelectedDatasource] = React.useState<string | null>(
datasourceId || null
);
const refetchDatasourcesList = useDataSourceListContextSelector((x) => x.refetchDatasourcesList);
const { refetch: refetchDatasourcesList } = useListDatasources();
const [datasetName, setDatasetName] = React.useState<string>('');
const disableSubmit = !selectedDatasource || !datasetName;
@ -109,8 +109,7 @@ const SelectDataSourceDropdown: React.FC<{
selectedDatasource: string | null;
}> = React.memo(({ setSelectedDatasource, selectedDatasource }) => {
const router = useRouter();
const dataSourcesList = useDataSourceListContextSelector((x) => x.dataSourcesList);
const refetchDatasourcesList = useDataSourceListContextSelector((x) => x.refetchDatasourcesList);
const { data: dataSourcesList } = useListDatasources();
const selectOptions: SelectItem[] = useMemo(() => {
return (dataSourcesList || []).map((dataSource) => ({
@ -128,7 +127,6 @@ const SelectDataSourceDropdown: React.FC<{
});
useMount(() => {
refetchDatasourcesList();
router.prefetch(
createBusterRoute({
route: BusterRoutes.APP_DATASETS_ID_OVERVIEW,

View File

@ -3,11 +3,9 @@ import { BusterWebSocketProvider } from './BusterWebSocket';
import { SupabaseContextProvider } from './Supabase/SupabaseContextProvider';
import { UseSupabaseContextType } from './Supabase/getSupabaseServerContext';
import { BusterReactQueryProvider } from './BusterReactQuery/BusterReactQueryAndApi';
import { DatasetProviders } from './Datasets';
import { AppLayoutProvider } from './BusterAppLayout';
import { BusterDashboardProvider } from './Dashboards/DashboardProvider';
import { BusterUserConfigProvider } from './Users/UserConfigProvider';
import { DataSourceProvider } from './DataSources';
import { BusterSQLProvider } from './SQL/useSQLProvider';
import { BusterTermsProvider } from './Terms/BusterTermsProvider';
import { BusterSearchProvider } from './Search';
@ -38,24 +36,20 @@ export const AppProviders: React.FC<
<BusterUserConfigProvider userInfo={userInfo}>
<BusterAssetsProvider>
<BusterSearchProvider>
<DataSourceProvider>
<DatasetProviders>
<BusterMetricsProvider>
<BusterDashboardProvider>
<BusterSQLProvider>
<BusterTermsProvider>
<BusterChatProvider>
<BusterPosthogProvider>
{children}
<RoutePrefetcher />
</BusterPosthogProvider>
</BusterChatProvider>
</BusterTermsProvider>
</BusterSQLProvider>
</BusterDashboardProvider>
</BusterMetricsProvider>
</DatasetProviders>
</DataSourceProvider>
<BusterMetricsProvider>
<BusterDashboardProvider>
<BusterSQLProvider>
<BusterTermsProvider>
<BusterChatProvider>
<BusterPosthogProvider>
{children}
<RoutePrefetcher />
</BusterPosthogProvider>
</BusterChatProvider>
</BusterTermsProvider>
</BusterSQLProvider>
</BusterDashboardProvider>
</BusterMetricsProvider>
</BusterSearchProvider>
</BusterAssetsProvider>
</BusterUserConfigProvider>

View File

@ -1,35 +0,0 @@
'use client';
import { createContext, useContextSelector } from 'use-context-selector';
import React from 'react';
import { useDatasourceCreate } from './useDatasourceCreate';
import { useDatasourceUpdate } from './useDatasourceUpdate';
export const useDataSourceIndividualProvider = () => {
const datasourceCreate = useDatasourceCreate();
const datasourceUpdate = useDatasourceUpdate();
return {
...datasourceCreate,
...datasourceUpdate
};
};
const DataSourceIndividualContext = createContext<
ReturnType<typeof useDataSourceIndividualProvider>
>({} as ReturnType<typeof useDataSourceIndividualProvider>);
const DataSourceIndividualProvider = ({ children }: { children: React.ReactNode }) => {
const dataSourceParams = useDataSourceIndividualProvider();
return (
<DataSourceIndividualContext.Provider value={dataSourceParams}>
{children}
</DataSourceIndividualContext.Provider>
);
};
export { DataSourceIndividualProvider };
export const useDataSourceIndividualContextSelector = <T,>(
selector: (state: ReturnType<typeof useDataSourceIndividualProvider>) => T
) => useContextSelector(DataSourceIndividualContext, selector);

View File

@ -1,5 +0,0 @@
import { useDataSourceIndividual } from './useDataSourceIndividual';
export * from './DataSourceIndividualProvider';
export { useDataSourceIndividual };

View File

@ -1,10 +0,0 @@
import { useGetDatasource } from '@/api/buster_rest/datasource';
export const useDataSourceIndividual = (id: string) => {
const { data: dataSource, isFetched: isFetchedDataSource } = useGetDatasource(id);
return {
dataSource,
isFetchedDataSource
};
};

View File

@ -1,50 +0,0 @@
import { useSocketQueryMutation } from '@/api/buster_socket_query';
import { queryKeys } from '@/api/query_keys';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { BusterRoutes } from '@/routes';
import { useMemoizedFn } from '@/hooks';
import { useQueryClient } from '@tanstack/react-query';
import { useCreateDatasource, useDeleteDatasource } from '@/api/buster_rest/datasource';
export const useDatasourceCreate = () => {
const { openConfirmModal } = useBusterNotifications();
const queryClient = useQueryClient();
const onChangePage = useAppLayoutContextSelector((s) => s.onChangePage);
const { mutateAsync: onCreateDataSource } = useCreateDatasource();
// const { mutateAsync: deleteDataSourceMutation } = useSocketQueryMutation({
// emitEvent: '/data_sources/delete',
// responseEvent: '/data_sources/delete:deleteDataSource',
// options: queryKeys.datasourceGetList,
// preCallback: (currentData, variables) => {
// return currentData?.filter((d) => d.id !== variables.id) || [];
// }
// });
const { mutateAsync: deleteDataSourceMutation } = useDeleteDatasource();
//DATA SOURCES INDIVIDUAL
const onDeleteDataSource = useMemoizedFn(async (dataSourceId: string, goToPage = true) => {
await openConfirmModal({
title: 'Delete Data Source',
content: 'Are you sure you want to delete this data source?',
onOk: async () => {
await deleteDataSourceMutation(dataSourceId);
}
}).then(() => {
if (goToPage) {
onChangePage({
route: BusterRoutes.SETTINGS_DATASOURCES
});
}
});
});
return {
onCreateDataSource,
onDeleteDataSource
};
};

View File

@ -1,9 +0,0 @@
import { useUpdateDatasource } from '@/api/buster_rest/datasource';
export const useDatasourceUpdate = () => {
const { mutateAsync: onUpdateDataSource } = useUpdateDatasource();
return {
onUpdateDataSource
};
};

View File

@ -1,13 +0,0 @@
import React, { PropsWithChildren } from 'react';
import { DataSourceListProvider } from './useDataSourceList';
import { DataSourceIndividualProvider } from './DataSourceIndividualProvider';
export const DataSourceProvider: React.FC<PropsWithChildren> = React.memo(({ children }) => {
return (
<DataSourceListProvider>
<DataSourceIndividualProvider>{children}</DataSourceIndividualProvider>
</DataSourceListProvider>
);
});
DataSourceProvider.displayName = 'DataSourceProvider';

View File

@ -1,12 +0,0 @@
import { useDataSourceListContextSelector } from './useDataSourceList';
import {
useDataSourceIndividualContextSelector,
useDataSourceIndividual
} from './DataSourceIndividualProvider';
export * from './DataSourceProvider';
export {
useDataSourceListContextSelector,
useDataSourceIndividual,
useDataSourceIndividualContextSelector
};

View File

@ -1,38 +0,0 @@
'use client';
import React from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
import { useListDatasources } from '@/api/buster_rest/datasource';
export const useDataSourceList = () => {
const {
data: dataSourcesList,
isFetched: isFetchedDatasourcesList,
refetch: refetchDatasourcesList
} = useListDatasources();
return {
dataSourcesList,
isFetchedDatasourcesList,
refetchDatasourcesList
};
};
const DataSourceListContext = createContext<ReturnType<typeof useDataSourceList>>(
{} as ReturnType<typeof useDataSourceList>
);
export const DataSourceListProvider = ({ children }: { children: React.ReactNode }) => {
const { dataSourcesList, isFetchedDatasourcesList, refetchDatasourcesList } = useDataSourceList();
return (
<DataSourceListContext.Provider
value={{ dataSourcesList, isFetchedDatasourcesList, refetchDatasourcesList }}>
{children}
</DataSourceListContext.Provider>
);
};
export const useDataSourceListContextSelector = <T,>(
selector: (state: ReturnType<typeof useDataSourceList>) => T
) => useContextSelector(DataSourceListContext, selector);

View File

@ -1,22 +0,0 @@
'use client';
import React, { PropsWithChildren } from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
export const useDatasets = () => {
return {};
};
const BusterDatasets = createContext<ReturnType<typeof useDatasets>>(
{} as ReturnType<typeof useDatasets>
);
export const DatasetProviders: React.FC<PropsWithChildren> = ({ children }) => {
const Datasets = useDatasets();
return <BusterDatasets.Provider value={Datasets}>{children}</BusterDatasets.Provider>;
};
export const useDatasetContextSelector = <T,>(
selector: (state: ReturnType<typeof useDatasets>) => T
) => useContextSelector(BusterDatasets, selector);

View File

@ -1,2 +0,0 @@
export * from './DatasetProvider';
export * from './useIndividualDatasetHook';

View File

@ -1,7 +0,0 @@
import { useGetDatasetData, useGetDatasetMetadata } from '@/api/buster_rest/datasets';
export const useIndividualDataset = ({ datasetId }: { datasetId: string }) => {
const dataset = useGetDatasetMetadata(datasetId);
const datasetData = useGetDatasetData(datasetId);
return { dataset, datasetData };
};

View File

@ -7,7 +7,7 @@ import { BusterRoutes } from '@/routes';
import { AppSegmented, SegmentedItem } from '@/components/ui/segmented';
import { AppTooltip } from '@/components/ui/tooltip';
import { NewDatasetModal } from '@/components/features/modal/NewDatasetModal';
import { useIndividualDataset } from '@/context/Datasets';
import { useIndividualDataset } from '@/api/buster_rest';
import { useHotkeys } from 'react-hotkeys-hook';
import { useUserConfigContextSelector } from '@/context/Users';
import { useMemoizedFn } from '@/hooks';