-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #282 from bento-platform/feat/dataset-workflow-input
[15] feat: implement new workflow system
- Loading branch information
Showing
26 changed files
with
769 additions
and
693 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,130 +1,157 @@ | ||
import React, {useCallback, useMemo, useState} from "react"; | ||
import { useSelector, useDispatch } from "react-redux"; | ||
import { Button, Col, Row, Table, Typography } from "antd"; | ||
|
||
import PropTypes from "prop-types"; | ||
|
||
import { Button, Col, Dropdown, Icon, Menu, Row, Table, Typography } from "antd"; | ||
|
||
import { useWorkflows } from "../../hooks"; | ||
import { useStartIngestionFlow } from "../manager/workflowCommon"; | ||
import { datasetPropTypesShape, projectPropTypesShape } from "../../propTypes"; | ||
import { clearDatasetDataType } from "../../modules/metadata/actions"; | ||
import { fetchDatasetDataTypesSummariesIfPossible } from "../../modules/datasets/actions"; | ||
|
||
import genericConfirm from "../ConfirmationModal"; | ||
import DataTypeSummaryModal from "./datatype/DataTypeSummaryModal"; | ||
import { nop } from "../../utils/misc"; | ||
|
||
const NA_TEXT = <span style={{ color: "#999", fontStyle: "italic" }}>N/A</span>; | ||
|
||
const DatasetDataTypes = React.memo( | ||
({isPrivate, project, dataset, onDatasetIngest}) => { | ||
const dispatch = useDispatch(); | ||
const datasetDataTypes = useSelector((state) => Object.values( | ||
state.datasetDataTypes.itemsByID[dataset.identifier]?.itemsByID ?? {})); | ||
const datasetSummaries = useSelector((state) => state.datasetSummaries.itemsByID[dataset.identifier]); | ||
const isFetchingDataset = useSelector( | ||
(state) => state.datasetDataTypes.itemsByID[dataset.identifier]?.isFetching); | ||
|
||
const [datatypeSummaryVisible, setDatatypeSummaryVisible] = useState(false); | ||
const [selectedDataType, setSelectedDataType] = useState(null); | ||
|
||
const selectedSummary = datasetSummaries?.data?.[selectedDataType?.id] ?? {}; | ||
|
||
const handleClearDataType = useCallback((dataType) => { | ||
genericConfirm({ | ||
title: `Are you sure you want to delete the "${dataType.label || dataType.id}" data type?`, | ||
content: "Deleting this means all instances of this data type contained in the dataset " + | ||
"will be deleted permanently, and will no longer be available for exploration.", | ||
onOk: async () => { | ||
await dispatch(clearDatasetDataType(dataset.identifier, dataType.id)); | ||
await dispatch(fetchDatasetDataTypesSummariesIfPossible(dataset.identifier)); | ||
}, | ||
}); | ||
}, [dispatch, dataset]); | ||
const DatasetDataTypes = React.memo(({ isPrivate, project, dataset }) => { | ||
const dispatch = useDispatch(); | ||
const datasetDataTypes = useSelector((state) => Object.values( | ||
state.datasetDataTypes.itemsByID[dataset.identifier]?.itemsByID ?? {})); | ||
const datasetSummaries = useSelector((state) => state.datasetSummaries.itemsByID[dataset.identifier]); | ||
const isFetchingDataset = useSelector( | ||
(state) => state.datasetDataTypes.itemsByID[dataset.identifier]?.isFetching); | ||
|
||
const showDataTypeSummary = useCallback((dataType) => { | ||
setSelectedDataType(dataType); | ||
setDatatypeSummaryVisible(true); | ||
}, []); | ||
const { workflowsByType } = useWorkflows(); | ||
const ingestionWorkflows = workflowsByType.ingestion.items; | ||
|
||
const dataTypesColumns = useMemo(() => [ | ||
{ | ||
title: "Name", | ||
key: "label", | ||
render: (dt) => | ||
isPrivate ? ( | ||
<a onClick={() => showDataTypeSummary(dt)}> | ||
{dt.label ?? NA_TEXT} | ||
</a> | ||
) : dt.label ?? NA_TEXT, | ||
defaultSortOrder: "ascend", | ||
sorter: (a, b) => a.label.localeCompare(b.label), | ||
const [datatypeSummaryVisible, setDatatypeSummaryVisible] = useState(false); | ||
const [selectedDataType, setSelectedDataType] = useState(null); | ||
|
||
const selectedSummary = datasetSummaries?.data?.[selectedDataType?.id] ?? {}; | ||
|
||
const handleClearDataType = useCallback((dataType) => { | ||
genericConfirm({ | ||
title: `Are you sure you want to delete the "${dataType.label || dataType.id}" data type?`, | ||
content: "Deleting this means all instances of this data type contained in the dataset " + | ||
"will be deleted permanently, and will no longer be available for exploration.", | ||
onOk: async () => { | ||
await dispatch(clearDatasetDataType(dataset.identifier, dataType.id)); | ||
await dispatch(fetchDatasetDataTypesSummariesIfPossible(dataset.identifier)); | ||
}, | ||
}); | ||
}, [dispatch, dataset]); | ||
|
||
const showDataTypeSummary = useCallback((dataType) => { | ||
setSelectedDataType(dataType); | ||
setDatatypeSummaryVisible(true); | ||
}, []); | ||
|
||
const startIngestionFlow = useStartIngestionFlow(); | ||
|
||
const dataTypesColumns = useMemo(() => [ | ||
{ | ||
title: "Name", | ||
key: "label", | ||
render: (dt) => | ||
isPrivate ? ( | ||
<a onClick={() => showDataTypeSummary(dt)}> | ||
{dt.label ?? NA_TEXT} | ||
</a> | ||
) : dt.label ?? NA_TEXT, | ||
defaultSortOrder: "ascend", | ||
sorter: (a, b) => a.label.localeCompare(b.label), | ||
}, | ||
{ | ||
title: "Count", | ||
dataIndex: "count", | ||
render: (c) => (c ?? NA_TEXT), | ||
}, | ||
...(isPrivate ? [ | ||
{ | ||
title: "Count", | ||
dataIndex: "count", | ||
render: (c) => (c ?? NA_TEXT), | ||
}, | ||
...(isPrivate ? [ | ||
{ | ||
title: "Actions", | ||
key: "actions", | ||
width: 230, | ||
render: (dt) => ( | ||
<Row gutter={10}> | ||
<Col span={12}> | ||
<Button | ||
icon="import" | ||
style={{ width: "100%" }} | ||
onClick={() => (onDatasetIngest || nop)(project, dataset, dt)} | ||
> | ||
Ingest | ||
</Button> | ||
</Col> | ||
<Col span={12}> | ||
<Button | ||
type="danger" | ||
icon="delete" | ||
disabled={ !dt.count || dt.count && dt.count === 0} | ||
onClick={() => handleClearDataType(dt)} | ||
style={{ width: "100%" }} | ||
> | ||
Clear | ||
title: "Actions", | ||
key: "actions", | ||
width: 240, | ||
render: (dt) => { | ||
const dtIngestionWorkflows = ingestionWorkflows | ||
.filter((wf) => wf.data_type === dt.id || (wf.tags ?? []).includes(dt.id)); | ||
const dtIngestionWorkflowsByID = Object.fromEntries( | ||
dtIngestionWorkflows.map((wf) => [wf.id, wf])); | ||
|
||
const ingestMenu = ( | ||
<Menu onClick={(i) => startIngestionFlow(dtIngestionWorkflowsByID[i.key], { | ||
// TODO: this requires that exactly this input is present, and may break in the future | ||
// in a bit of a non-obvious way. | ||
"project_dataset": `${project.identifier}:${dataset.identifier}`, | ||
})}> | ||
{dtIngestionWorkflows.map((wf) => (<Menu.Item key={wf.id}>{wf.name}</Menu.Item>))} | ||
</Menu> | ||
); | ||
|
||
const ingestDropdown = ( | ||
<Dropdown overlay={ingestMenu} trigger={["click"]}> | ||
<Button icon="import" style={{ width: "100%" }} disabled={!dtIngestionWorkflows.length}> | ||
Ingest <Icon type="down" /> | ||
</Button> | ||
</Col> | ||
</Row> | ||
), | ||
</Dropdown> | ||
); | ||
|
||
return ( | ||
<Row gutter={10}> | ||
<Col span={13}> | ||
{ingestDropdown} | ||
</Col> | ||
<Col span={11}> | ||
<Button | ||
type="danger" | ||
icon="delete" | ||
disabled={ !dt.count || dt.count && dt.count === 0} | ||
onClick={() => handleClearDataType(dt)} | ||
style={{ width: "100%" }} | ||
> | ||
Clear | ||
</Button> | ||
</Col> | ||
</Row> | ||
); | ||
}, | ||
] : null), | ||
], [isPrivate, project, dataset, onDatasetIngest]); | ||
|
||
const onDataTypeSummaryModalCancel = useCallback(() => setDatatypeSummaryVisible(false), []); | ||
|
||
return ( | ||
<> | ||
<DataTypeSummaryModal | ||
dataType={selectedDataType} | ||
summary={selectedSummary} | ||
visible={datatypeSummaryVisible} | ||
onCancel={onDataTypeSummaryModalCancel} | ||
/> | ||
|
||
<Typography.Title level={4}> | ||
Data Types | ||
</Typography.Title> | ||
|
||
<Table | ||
bordered | ||
dataSource={datasetDataTypes} | ||
rowKey="id" | ||
columns={dataTypesColumns} | ||
loading={isFetchingDataset} | ||
/> | ||
</> | ||
); | ||
}); | ||
}, | ||
] : null), | ||
], [isPrivate, project, dataset, ingestionWorkflows, startIngestionFlow]); | ||
|
||
const onDataTypeSummaryModalCancel = useCallback(() => setDatatypeSummaryVisible(false), []); | ||
|
||
return ( | ||
<> | ||
<DataTypeSummaryModal | ||
dataType={selectedDataType} | ||
summary={selectedSummary} | ||
visible={datatypeSummaryVisible} | ||
onCancel={onDataTypeSummaryModalCancel} | ||
/> | ||
|
||
<Typography.Title level={4}> | ||
Data Types | ||
</Typography.Title> | ||
|
||
<Table | ||
bordered | ||
size="middle" | ||
pagination={false} | ||
dataSource={datasetDataTypes} | ||
rowKey="id" | ||
columns={dataTypesColumns} | ||
loading={isFetchingDataset} | ||
/> | ||
</> | ||
); | ||
}); | ||
|
||
DatasetDataTypes.propTypes = { | ||
isPrivate: PropTypes.bool, | ||
project: projectPropTypesShape, | ||
dataset: datasetPropTypesShape, | ||
onDatasetIngest: PropTypes.func, | ||
}; | ||
|
||
export default DatasetDataTypes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.