mirror of https://github.com/buster-so/buster.git
create dataset endpoints
This commit is contained in:
parent
3104cace3c
commit
2812df14a1
|
@ -78,28 +78,14 @@ export const prefetchGetDatasetMetadata = async (
|
||||||
|
|
||||||
export const useCreateDataset = () => {
|
export const useCreateDataset = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const mutationFn = useMemoizedFn(() => createDataset());
|
|
||||||
const onSuccess = useMemoizedFn((newDataset: unknown) => {
|
const onSuccess = useMemoizedFn(() => {
|
||||||
queryClient.setQueryData<BusterDatasetListItem[]>(['datasets', {}], (oldData) => {
|
queryClient.invalidateQueries({ queryKey: ['datasets', {}] });
|
||||||
// const newListItem: BusterDatasetListItem = {
|
|
||||||
// ...newDataset,
|
|
||||||
// name: newDataset.name,
|
|
||||||
// created_at: newDataset.created_at,
|
|
||||||
// updated_at: newDataset.updated_at,
|
|
||||||
// definition: newDataset.definition,
|
|
||||||
// owner: '',
|
|
||||||
// };
|
|
||||||
return oldData;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const onError = useMemoizedFn((error: any) => {
|
|
||||||
console.error('Failed to create dataset:', error);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return useCreateReactMutation({
|
return useCreateReactMutation({
|
||||||
mutationFn,
|
mutationFn: createDataset,
|
||||||
onSuccess,
|
onSuccess
|
||||||
onError
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,11 @@ export const getDatasetDataSample = async (datasetId: string): Promise<BusterDat
|
||||||
.then((res) => res.data);
|
.then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createDataset = async (): Promise<BusterDataset> => {
|
export const createDataset = async (params: {
|
||||||
return await mainApi.post<BusterDataset>(`/datasets`).then((res) => res.data);
|
name: string;
|
||||||
|
data_source_id: string;
|
||||||
|
}): Promise<BusterDataset> => {
|
||||||
|
return await mainApi.post<BusterDataset>(`/datasets`, params).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteDataset = async (datasetId: string): Promise<void> => {
|
export const deleteDataset = async (datasetId: string): Promise<void> => {
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
import React, { useLayoutEffect, useMemo, useState } from 'react';
|
import React, { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { Button, Select, SelectProps } from 'antd';
|
import { Input, Select, SelectProps } from 'antd';
|
||||||
import { useMemoizedFn, useMount } from 'ahooks';
|
import { useMemoizedFn, useMount } from 'ahooks';
|
||||||
import { useDatasetContextSelector } from '@/context/Datasets';
|
|
||||||
import { useDataSourceContextSelector } from '@/context/DataSources';
|
import { useDataSourceContextSelector } from '@/context/DataSources';
|
||||||
import {
|
import { useCreateDataset } from '@/api/busterv2/datasets';
|
||||||
BusterDatasetListItem,
|
|
||||||
useCreateDataset,
|
|
||||||
useGetDatasets,
|
|
||||||
useUpdateDataset
|
|
||||||
} from '@/api/busterv2/datasets';
|
|
||||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { AppModal, Text } from '@/components';
|
import { AppModal, Text } from '@/components';
|
||||||
import { useAntToken } from '@/styles/useAntToken';
|
|
||||||
import { BusterList, BusterListColumn, BusterListRow } from '@/components/list';
|
|
||||||
import { formatDate } from '@/utils/date';
|
|
||||||
import { timeout } from '@/utils';
|
|
||||||
|
|
||||||
const headerConfig = {
|
const headerConfig = {
|
||||||
title: 'Create a dataset',
|
title: 'Create a dataset',
|
||||||
|
@ -35,21 +25,22 @@ export const NewDatasetModal: React.FC<{
|
||||||
const forceInitDataSourceList = useDataSourceContextSelector(
|
const forceInitDataSourceList = useDataSourceContextSelector(
|
||||||
(state) => state.forceInitDataSourceList
|
(state) => state.forceInitDataSourceList
|
||||||
);
|
);
|
||||||
const { mutateAsync: createDataset } = useCreateDataset();
|
const { mutateAsync: createDataset, isPending: creatingDataset } = useCreateDataset();
|
||||||
const [creatingDataset, setCreatingDataset] = React.useState(false);
|
|
||||||
const [selectedDatasource, setSelectedDatasource] = React.useState<string | null>(
|
const [selectedDatasource, setSelectedDatasource] = React.useState<string | null>(
|
||||||
datasourceId || null
|
datasourceId || null
|
||||||
);
|
);
|
||||||
|
const [datasetName, setDatasetName] = React.useState<string>('');
|
||||||
|
|
||||||
const disableSubmit = !selectedDatasource;
|
const disableSubmit = !selectedDatasource || !datasetName;
|
||||||
|
|
||||||
const createNewDatasetPreflight = useMemoizedFn(async () => {
|
const createNewDatasetPreflight = useMemoizedFn(async () => {
|
||||||
if (creatingDataset || disableSubmit) return;
|
if (creatingDataset || disableSubmit || !selectedDatasource) return;
|
||||||
setCreatingDataset(true);
|
|
||||||
beforeCreate?.();
|
beforeCreate?.();
|
||||||
|
|
||||||
const res = await createDataset({
|
const res = await createDataset({
|
||||||
data_source_id: selectedDatasource!
|
data_source_id: selectedDatasource,
|
||||||
|
name: datasetName
|
||||||
});
|
});
|
||||||
if (res.id) {
|
if (res.id) {
|
||||||
onChangePage({
|
onChangePage({
|
||||||
|
@ -62,16 +53,13 @@ export const NewDatasetModal: React.FC<{
|
||||||
afterCreate?.();
|
afterCreate?.();
|
||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
|
||||||
setCreatingDataset(false);
|
|
||||||
}, 500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const onAddDataSourceClick = useMemoizedFn(() => {
|
const onAddDataSourceClick = useMemoizedFn(() => {
|
||||||
onClose();
|
|
||||||
setTimeout(() => {
|
|
||||||
router.push(createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD }));
|
router.push(createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD }));
|
||||||
}, 350);
|
setTimeout(() => {
|
||||||
|
onClose();
|
||||||
|
}, 450);
|
||||||
});
|
});
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -98,14 +86,18 @@ export const NewDatasetModal: React.FC<{
|
||||||
return (
|
return (
|
||||||
<AppModal open={open} onClose={onClose} header={headerConfig} footer={footerConfig}>
|
<AppModal open={open} onClose={onClose} header={headerConfig} footer={footerConfig}>
|
||||||
{open && (
|
{open && (
|
||||||
|
<div className="mt-2 flex flex-col gap-3">
|
||||||
|
<FormWrapper title="Dataset name">
|
||||||
|
<DatasetNameInput setDatasetName={setDatasetName} datasetName={datasetName} />
|
||||||
|
</FormWrapper>
|
||||||
|
|
||||||
|
<FormWrapper title="Datasource">
|
||||||
<SelectDataSourceDropdown
|
<SelectDataSourceDropdown
|
||||||
setSelectedDatasource={setSelectedDatasource}
|
setSelectedDatasource={setSelectedDatasource}
|
||||||
selectedDatasource={selectedDatasource}
|
selectedDatasource={selectedDatasource}
|
||||||
/>
|
/>
|
||||||
)}
|
</FormWrapper>
|
||||||
|
</div>
|
||||||
{open && selectedDatasource && (
|
|
||||||
<SelectFromExistingDataset selectedDatasource={selectedDatasource} />
|
|
||||||
)}
|
)}
|
||||||
</AppModal>
|
</AppModal>
|
||||||
);
|
);
|
||||||
|
@ -132,6 +124,10 @@ const SelectDataSourceDropdown: React.FC<{
|
||||||
return selectOptions.find((option) => option.value === selectedDatasource);
|
return selectOptions.find((option) => option.value === selectedDatasource);
|
||||||
}, [selectOptions, selectedDatasource]);
|
}, [selectOptions, selectedDatasource]);
|
||||||
|
|
||||||
|
const onSelect = useMemoizedFn((value: unknown) => {
|
||||||
|
setSelectedDatasource(value as string);
|
||||||
|
});
|
||||||
|
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
initDataSourceList();
|
initDataSourceList();
|
||||||
router.prefetch(
|
router.prefetch(
|
||||||
|
@ -149,115 +145,41 @@ const SelectDataSourceDropdown: React.FC<{
|
||||||
value={selectedOption}
|
value={selectedOption}
|
||||||
placeholder="Select datasources that this term pertains to"
|
placeholder="Select datasources that this term pertains to"
|
||||||
popupMatchSelectWidth={true}
|
popupMatchSelectWidth={true}
|
||||||
autoFocus={true}
|
onChange={onSelect}
|
||||||
onChange={(value) => {
|
|
||||||
setSelectedDatasource(value as unknown as string);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
SelectDataSourceDropdown.displayName = 'SelectDataSourceDropdown';
|
SelectDataSourceDropdown.displayName = 'SelectDataSourceDropdown';
|
||||||
|
|
||||||
const SelectFromExistingDataset: React.FC<{
|
const DatasetNameInput: React.FC<{
|
||||||
selectedDatasource: string;
|
setDatasetName: (name: string) => void;
|
||||||
}> = React.memo(({ selectedDatasource }) => {
|
datasetName: string;
|
||||||
const token = useAntToken();
|
}> = React.memo(
|
||||||
const { data: importedDatasets, isFetched: isFetchedDatasets } = useGetDatasets({
|
({ setDatasetName, datasetName }) => {
|
||||||
imported: true
|
|
||||||
});
|
|
||||||
const { mutateAsync: onUpdateDataset } = useUpdateDataset();
|
|
||||||
|
|
||||||
const onChangePage = useAppLayoutContextSelector((s) => s.onChangePage);
|
|
||||||
|
|
||||||
const [submittingId, setSubmittingId] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const columns: BusterListColumn[] = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Updated at',
|
|
||||||
dataIndex: 'updated_at',
|
|
||||||
render: (v) => formatDate({ date: v, format: 'lll' }),
|
|
||||||
width: 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'actions',
|
|
||||||
width: 100,
|
|
||||||
render: (_, record: BusterDatasetListItem) => (
|
|
||||||
<div className="flex items-center justify-end">
|
|
||||||
<Button
|
|
||||||
loading={submittingId === record.id}
|
|
||||||
type="default"
|
|
||||||
onClick={() => onSelectDataset(record.id)}>
|
|
||||||
Use dataset
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}, [submittingId]);
|
|
||||||
|
|
||||||
const rows: BusterListRow[] = useMemo(() => {
|
|
||||||
return importedDatasets.map((dataset) => ({
|
|
||||||
id: dataset.id,
|
|
||||||
data: dataset
|
|
||||||
}));
|
|
||||||
}, [importedDatasets]);
|
|
||||||
|
|
||||||
const onSelectDataset = useMemoizedFn(async (datasetId: string) => {
|
|
||||||
setSubmittingId(datasetId);
|
|
||||||
// try {
|
|
||||||
// await onUpdateDataset({
|
|
||||||
// id: datasetId,
|
|
||||||
// name: 'test'
|
|
||||||
// });
|
|
||||||
// await timeout(500);
|
|
||||||
// onChangePage({
|
|
||||||
// route: BusterRoutes.APP_DATASETS_ID,
|
|
||||||
// datasetId
|
|
||||||
// });
|
|
||||||
// } catch (error) {
|
|
||||||
// setSubmittingId(null);
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Input
|
||||||
className="mt-3 flex h-[250px] w-full flex-col"
|
autoFocus
|
||||||
style={{
|
defaultValue={datasetName}
|
||||||
border: `0.5px solid ${token.colorBorder}`,
|
placeholder="Enter a name for your dataset"
|
||||||
borderRadius: token.borderRadius
|
onChange={(e) => setDatasetName(e.target.value)}
|
||||||
}}>
|
|
||||||
<div
|
|
||||||
className="flex"
|
|
||||||
style={{
|
|
||||||
padding: 12,
|
|
||||||
background: token.controlItemBgActive,
|
|
||||||
borderBottom: `0.5px solid ${token.colorBorder}`
|
|
||||||
}}>
|
|
||||||
<Text size="sm">Use an existing table or view as a dataset</Text>
|
|
||||||
</div>
|
|
||||||
<div className="h-full w-full">
|
|
||||||
<BusterList
|
|
||||||
columns={columns}
|
|
||||||
rows={rows}
|
|
||||||
showHeader={false}
|
|
||||||
emptyState={
|
|
||||||
!isFetchedDatasets ? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Text>No datasets found</Text>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
() => true
|
||||||
|
);
|
||||||
|
DatasetNameInput.displayName = 'DatasetNameInput';
|
||||||
|
|
||||||
|
const FormWrapper: React.FC<{
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}> = React.memo(({ title, children }) => {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-[minmax(150px,auto)_1fr] gap-4">
|
||||||
|
<div>
|
||||||
|
<Text>{title}</Text>
|
||||||
</div>
|
</div>
|
||||||
|
<div>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
SelectFromExistingDataset.displayName = 'SelectFromExistingDataset';
|
FormWrapper.displayName = 'FormWrapper';
|
||||||
|
|
|
@ -125,9 +125,7 @@ export const AppCodeEditor = forwardRef<AppCodeEditorHandle, AppCodeEditorProps>
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (language === 'yaml') {
|
if (language === 'yaml') {
|
||||||
console.log('loading yaml');
|
|
||||||
await configureMonacoToUseYaml(monaco);
|
await configureMonacoToUseYaml(monaco);
|
||||||
console.log('yaml loaded');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
monaco.editor.defineTheme('github-light', GithubLightTheme);
|
monaco.editor.defineTheme('github-light', GithubLightTheme);
|
||||||
|
@ -149,14 +147,6 @@ export const AppCodeEditor = forwardRef<AppCodeEditorHandle, AppCodeEditorProps>
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
hasLoadedDynamicEditor = true;
|
hasLoadedDynamicEditor = true;
|
||||||
|
|
||||||
// Add this to see how Monaco is tokenizing the text
|
|
||||||
editor.onDidChangeModelContent(() => {
|
|
||||||
const model = editor.getModel();
|
|
||||||
if (model) {
|
|
||||||
console.log(monaco.editor.tokenize(model.getValue(), 'yaml'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue