UNPKG

@finos/legend-extension-dsl-data-quality

Version:
232 lines (202 loc) 7.65 kB
/** * Copyright (c) 2026-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 { PanelDisplayState } from '@finos/legend-art'; import type { DataQualityRelationValidationConfigurationState } from './DataQualityRelationValidationConfigurationState.js'; import { DataQualityRelationValidationState } from './DataQualityRelationValidationState.js'; import { ActionState, assertErrorThrown, hashValue, type GeneratorFn, } from '@finos/legend-shared'; import { action, computed, flow, makeObservable, observable } from 'mobx'; import { getDataQualityPureGraphManagerExtension } from '../../graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.js'; import { buildExecutionParameterValues } from '@finos/legend-query-builder'; import { DataQualityRelationValidation } from '../../graph-manager/index.js'; import type { RawLambda } from '@finos/legend-graph'; export enum SuggestedValidationsFilter { ALL, NEW, MODIFICATIONS, } export enum SuggestionType { NEW = 'NEW', EDIT = 'EDIT', APPLIED = 'APPLIED', } export class SuggestedValidationsState { readonly parentState: DataQualityRelationValidationConfigurationState; readonly suggestionPanelState: PanelDisplayState; readonly fetchState = ActionState.create(); suggestedValidations: DataQualityRelationValidationState[] = []; filter: SuggestedValidationsFilter = SuggestedValidationsFilter.ALL; constructor(parentState: DataQualityRelationValidationConfigurationState) { makeObservable(this, { suggestedValidations: observable, filter: observable, onClickSuggestValidations: action, onFilterChange: action, fetchValidationSuggestions: flow, existingValidationsByName: computed, selectedSuggestions: computed, filteredSuggestions: computed, }); this.parentState = parentState; this.suggestionPanelState = new PanelDisplayState({ initial: 0, default: 300, snap: 100, }); } get existingValidationsByName(): Map<string, string> { return new Map( this.parentState.validationStates.map((s) => [ s.relationValidation.name, hashValue(s.lambdaString), ]), ); } get filteredSuggestions(): DataQualityRelationValidationState[] { return this.suggestedValidations.filter((s) => { const type = this.getSuggestionType(s); switch (this.filter) { case SuggestedValidationsFilter.NEW: return type === SuggestionType.NEW; case SuggestedValidationsFilter.MODIFICATIONS: return type === SuggestionType.EDIT; default: return type === SuggestionType.NEW || type === SuggestionType.EDIT; } }); } get selectedSuggestions(): DataQualityRelationValidationState[] { return this.filteredSuggestions.filter((e) => e.isSelected); } getSuggestionType( suggestion: DataQualityRelationValidationState, ): SuggestionType { const name = suggestion.relationValidation.name; const existingHash = this.existingValidationsByName.get(name); // TODO: name does not exist already treated as new rule probably needs to change if exact assertion exist, because then the suggestion is unecessary? if (existingHash === undefined) { return SuggestionType.NEW; } return existingHash !== hashValue(suggestion.lambdaString) ? SuggestionType.EDIT : SuggestionType.APPLIED; } onClickSuggestValidations = () => { if (!this.suggestionPanelState.isOpen) { this.suggestionPanelState.toggle(); } if (!this.fetchState.isInProgress) { this.fetchValidationSuggestions(); } }; onFilterChange = (newFilter: SuggestedValidationsFilter) => { this.filter = newFilter; }; *fetchValidationSuggestions(): GeneratorFn<void> { this.fetchState.inProgress(); try { // Parse the response into proper RawLambda objects const packagePath = this.parentState.validationElement.path; const model = this.parentState.editorStore.graphManagerState.graph; const extension = getDataQualityPureGraphManagerExtension( this.parentState.editorStore.graphManagerState.graphManager, ); const options = { lambdaParameterValues: buildExecutionParameterValues( this.parentState.parametersState.parameterStates, this.parentState.editorStore.graphManagerState, ), }; const result = (yield extension.fetchValidationSuggestions( model, packagePath, options, )) as DataQualityRelationValidation[]; const suggestions: DataQualityRelationValidation[] = result.map( (suggestion) => { const lambda = this.parentState.editorStore.graphManagerState.graphManager.buildRawValueSpecification( suggestion.assertion, this.parentState.editorStore.graphManagerState.graph, ) as RawLambda; const validation = new DataQualityRelationValidation( suggestion.name, lambda, ); if (suggestion.type) { validation.type = suggestion.type; } return validation; }, ); // Create validation states const suggestedValidations: DataQualityRelationValidationState[] = suggestions.map( (validation) => new DataQualityRelationValidationState( validation, this.parentState.editorStore, ), ); // Convert to grammar strings for display const lambdas = new Map<string, RawLambda>(); suggestedValidations.forEach((validationState) => { lambdas.set( validationState.lambdaId, validationState.relationValidation.assertion, ); }); suggestedValidations.forEach((validationState) => { validationState.initializeWithColumns( this.parentState.relationTypeMetadata.columns, ); }); const isolatedLambdas = (yield this.parentState.editorStore.graphManagerState.graphManager.lambdasToPureCode( lambdas, )) as Map<string, string>; isolatedLambdas.forEach((grammarText, key) => { const validationState = suggestedValidations.find( (state) => state.lambdaId === key, ); if (validationState) { validationState.setLambdaString( validationState.extractLambdaString(grammarText), ); } }); suggestedValidations.forEach((validationState) => { if (validationState.dataQualityValidationLambdaFormState) { const description = validationState.dataQualityValidationLambdaFormState.getDescription(); validationState.relationValidation.description = description; } }); this.suggestedValidations = suggestedValidations; this.fetchState.pass(); } catch (error) { assertErrorThrown(error); this.fetchState.fail(); this.parentState.editorStore.applicationStore.notificationService.notifyError( error, ); } } }