@finos/legend-extension-dsl-data-quality
Version:
Legend extension for Data Quality
172 lines • 13.3 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* 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 { useDrop } from 'react-dnd';
import { useCallback } from 'react';
import { getClassPropertyIcon } from '@finos/legend-lego/graph-editor';
import { DataQualityGraphFetchTreeNodeData, isConstraintsClassesTreeEmpty, removeNodeRecursively, } from './utils/DataQualityGraphFetchTreeUtil.js';
import { BlankPanelPlaceholder, CheckSquareIcon, ChevronDownIcon, ChevronRightIcon, clsx, PanelDropZone, PURE_UnknownElementTypeIcon, SquareIcon, TimesIcon, TreeView, ExclamationTriangleIcon, MinusSquareIcon, } from '@finos/legend-art';
import { dataQualityClassValidation_setDataQualityGraphFetchTree } from '../graph-manager/DSL_DataQuality_GraphModifierHelper.js';
import { Class, Enumeration, PropertyGraphFetchTree, RootGraphFetchTree, } from '@finos/legend-graph';
import { DATA_QUALITY_VALIDATION_TEST_ID } from './constants/DataQualityConstants.js';
import { flowResult } from 'mobx';
import { DataQualityStructuralValidationsPanel } from './DataQualityStructuralValidationsPanel.js';
import { QUERY_BUILDER_EXPLORER_TREE_DND_TYPE, } from '@finos/legend-query-builder';
export const getQueryBuilderExplorerTreeNodeSortRank = (node) => {
if (node.type instanceof Class) {
return 1;
}
else if (node.type instanceof Enumeration) {
return 2;
}
else {
return 3;
}
};
export const DataQualityConstraintsTreeNodeContainer = observer((props) => {
const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
const { dataQualityState, isReadOnly, removeNode } = innerProps;
const { dataQualityGraphFetchTreeState } = dataQualityState;
let property, type, subType;
if (node.tree instanceof PropertyGraphFetchTree) {
property = node.tree.property.value;
type = property.genericType.value.rawType;
subType = node.tree.subType?.value;
}
else if (node.tree instanceof RootGraphFetchTree) {
type = node.tree.class.value;
}
const isExpandable = Boolean(node.childrenIds.length);
const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : null;
const nodeTypeIcon = type ? (getClassPropertyIcon(type)) : (_jsx(PURE_UnknownElementTypeIcon, {}));
const showClassConstraintsSelectionCheckBox = !node.isReadOnly &&
node.type instanceof Class &&
node.constraints.length !== 0;
const noClassConstraintsSelected = node.type instanceof Class &&
node.constraints.length > 0 &&
node.constraints.every((constraint) => !constraint.isSelected);
const allClassConstraintsSelected = node.type instanceof Class &&
node.constraints.length > 0 &&
node.constraints.every((constraint) => constraint.isSelected);
const toggleExpandNode = () => onNodeSelect?.(node);
const deleteNode = () => removeNode?.(node);
const toggleChecked = (constraint) => {
dataQualityGraphFetchTreeState.updateNode(node, [constraint.constraint], !constraint.isSelected);
constraint.setIsSelected(!constraint.isSelected);
};
const checkBoxIcon = () => {
if (noClassConstraintsSelected) {
return _jsx(SquareIcon, {});
}
if (allClassConstraintsSelected) {
return _jsx(CheckSquareIcon, {});
}
return _jsx(MinusSquareIcon, {});
};
const toggleClassConstraintsSelection = () => {
const desiredConstraints = [];
let addConstraint = true;
if (!allClassConstraintsSelected) {
node.constraints.forEach((constraint) => {
desiredConstraints.push(constraint.constraint);
constraint.setIsSelected(true);
});
}
else {
addConstraint = false;
node.constraints.forEach((constraint) => {
desiredConstraints.push(constraint.constraint);
constraint.setIsSelected(false);
});
}
dataQualityGraphFetchTreeState.updateNode(node, desiredConstraints, addConstraint);
};
return (_jsxs("div", { className: "constraints-selection-node", children: [_jsxs("div", { className: "tree-view__node__container data-quality-validation-graph-fetch-tree__node__container", style: {
paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 2)}rem`,
display: 'flex',
}, children: [_jsxs("div", { className: "data-quality-validation-graph-fetch-tree__node__content", children: [_jsxs("div", { className: "data-quality-validation-graph-fetch-tree__node__icon", children: [showClassConstraintsSelectionCheckBox && (_jsx("div", { onClick: toggleClassConstraintsSelection, children: _jsx("button", { className: clsx('panel__content__form__section__toggler__btn', 'data-quality-validation-graph-fetch-tree__constraint__checkbox', {
'panel__content__form__section__toggler__btn--toggled': !noClassConstraintsSelected,
}), children: checkBoxIcon() }) })), nodeExpandIcon && (_jsx("div", { className: "data-quality-validation-graph-fetch-tree__expand-icon", onClick: toggleExpandNode, children: nodeExpandIcon })), _jsx("div", { className: "data-quality-validation-graph-fetch-tree__type-icon", onClick: toggleExpandNode, children: nodeTypeIcon })] }), _jsxs("div", { className: "tree-view__node__label data-quality-validation-graph-fetch-tree__node__label", onClick: toggleExpandNode, children: [node.label, subType && (_jsx("div", { className: "data-quality-validation-graph-fetch-tree__node__sub-type", children: _jsx("div", { className: "data-quality-validation-graph-fetch-tree__node__sub-type__label", children: subType.name }) })), _jsx("div", { className: "data-quality-validation-graph-fetch-tree__node__type", children: _jsx("div", { className: "data-quality-validation-graph-fetch-tree__node__type__label", children: type ? type.name : '' }) })] })] }), !node.isReadOnly ? (_jsx("div", { className: "data-quality-validation-graph-fetch-tree__node__actions", children: _jsx("button", { className: "data-quality-validation-graph-fetch-tree__node__action", title: "Remove", tabIndex: -1, onClick: deleteNode, disabled: isReadOnly, children: _jsx(TimesIcon, {}) }) })) : (_jsx("div", {}))] }), node.constraints.length ? (_jsx("div", { style: {
paddingLeft: `${(level + 2) * (stepPaddingInRem ?? 2)}rem`,
display: 'flex',
flexDirection: 'column',
}, children: node.constraints.map((constraint) => (_jsxs("div", { className: "data-quality-validation-graph-fetch-tree__node__label data-quality-validation-graph-fetch-tree__node__constraint", onClick: () => {
toggleChecked(constraint);
}, children: [_jsx("button", { className: clsx('panel__content__form__section__toggler__btn', 'data-quality-validation-graph-fetch-tree__constraint__checkbox', {
'panel__content__form__section__toggler__btn--toggled': constraint.isSelected,
}), children: constraint.isSelected ? _jsx(CheckSquareIcon, {}) : _jsx(SquareIcon, {}) }), _jsx("div", { className: "data-quality-validation-graph-fetch-tree__constraint__name", children: constraint.constraint.name }), _jsx("div", { className: "data-quality-validation-graph-fetch-tree__constraint__value", children: constraint.lambdaString })] }, constraint.lambdaId))) })) : (_jsx(_Fragment, {}))] }));
});
export const ConstraintsSelectionExplorer = observer((props) => {
const { constraintsClasses, dataQualityState, updateTreeData } = props;
const { applicationStore } = dataQualityState;
const getChildNodes = (node) => node.childrenIds
.map((id) => constraintsClasses.nodes.get(id))
.filter((_node) => _node instanceof DataQualityGraphFetchTreeNodeData)
.sort((a, b) => a.label.localeCompare(b.label))
.sort((a, b) => getQueryBuilderExplorerTreeNodeSortRank(b) -
getQueryBuilderExplorerTreeNodeSortRank(a));
const removeNode = (node) => {
removeNodeRecursively(constraintsClasses, node);
updateTreeData({ ...constraintsClasses });
dataQualityClassValidation_setDataQualityGraphFetchTree(dataQualityState.constraintsConfigurationElement, constraintsClasses.tree);
};
const showStructuralValidations = () => {
dataQualityState.setShowStructuralValidations(true);
flowResult(dataQualityState.fetchStructuralValidations()).catch(applicationStore.alertUnhandledError);
};
const disableSyncToDQ = Boolean(!dataQualityState.dataQualityGraphFetchTreeState.treeData);
return (_jsxs("div", { className: "data-quality-validation-graph-fetch-constraints-selection__config-group__content", children: [_jsxs("div", { className: "data-quality-validation-graph-fetch-constraints-selection__structural-attributes", children: [_jsx("div", { children: dataQualityState.areNestedConstraintsSelected && (_jsxs("div", { className: "data-quality-validation-graph-fetch-constraints-selection__validation", children: [_jsx("div", { className: "data-quality-validation-graph-fetch-constraints-selection__validation__icon", children: _jsx(ExclamationTriangleIcon, {}) }), _jsx("div", { className: "data-quality-validation-graph-fetch-constraints-selection__validation__msg", children: "Nested constraints execution is not yet supported" })] })) }), _jsx("button", { className: "btn--dark structure-validations-btn", onClick: showStructuralValidations, disabled: Boolean(disableSyncToDQ), tabIndex: -1, title: "Show Structural Attributes to be validated during run", children: "Show Structural Attributes" })] }), _jsx("div", { className: "data-quality-validation-graph-fetch-constraints-selection__config-group__item", children: _jsx(TreeView, { components: {
TreeNodeContainer: DataQualityConstraintsTreeNodeContainer,
}, className: "data-quality-validation-graph-fetch-tree__container__tree", treeData: constraintsClasses, getChildNodes: getChildNodes, innerProps: {
dataQualityState,
isReadOnly: false,
removeNode,
} }) }), _jsx(DataQualityStructuralValidationsPanel, { dataQualityState: dataQualityState })] }));
});
export const DataQualityConstraintsSelection = observer((props) => {
const { dataQualityState } = props;
const constraintsConfiguration = dataQualityState.constraintsConfigurationElement;
const dataQualityGraphFetchTreeState = dataQualityState.dataQualityGraphFetchTreeState;
const treeData = dataQualityGraphFetchTreeState.treeData;
const updateTreeData = (data) => {
dataQualityGraphFetchTreeState.setGraphFetchTree(data);
};
const handleDrop = useCallback((item) => {
dataQualityGraphFetchTreeState.addProperty(item.node, {
refreshTreeData: true,
});
if (dataQualityGraphFetchTreeState.treeData) {
dataQualityClassValidation_setDataQualityGraphFetchTree(constraintsConfiguration, dataQualityGraphFetchTreeState.treeData.tree);
}
}, [dataQualityGraphFetchTreeState, constraintsConfiguration]);
const [{ isDragOver }, dropConnector] = useDrop(() => ({
accept: [
QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.CLASS_PROPERTY,
QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
],
drop: (item, monitor) => {
if (!monitor.didDrop()) {
handleDrop(item);
} // prevent drop event propagation to accomondate for nested DnD
},
collect: (monitor) => ({
isDragOver: monitor.isOver({ shallow: true }),
}),
}), [handleDrop]);
return (_jsx("div", { "data-testid": DATA_QUALITY_VALIDATION_TEST_ID.DATA_QUALITY_VALIDATION_TREE, className: "constraints-selection-tab", children: _jsxs(PanelDropZone, { isDragOver: isDragOver, dropTargetConnector: dropConnector, contentClassName: "data-quality-validation-graph-fetch-panel", children: [(!treeData || isConstraintsClassesTreeEmpty(treeData)) && (_jsx(BlankPanelPlaceholder, { text: "Add a scope for constraints", tooltipText: "Drag and drop properties here" })), treeData && !isConstraintsClassesTreeEmpty(treeData) && (_jsx(ConstraintsSelectionExplorer, { constraintsClasses: treeData, dataQualityState: dataQualityState, updateTreeData: updateTreeData }))] }) }));
});
//# sourceMappingURL=DataQualityConstraintsSelection.js.map