@finos/legend-extension-dsl-data-quality
Version:
Legend extension for Data Quality
197 lines • 9.71 kB
JavaScript
/**
* 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 { DATA_QUALITY_VALIDATION_LOGICAL_FUNCTIONS, DATA_QUALITY_VALIDATION_PROPERTY_GUARANTEE_FUNCTIONS, SUPPORTED_TYPES, } from '../constants/DataQualityConstants.js';
import { assertTrue, UnsupportedOperationError } from '@finos/legend-shared';
import { AbstractPropertyExpression, matchFunctionName, } from '@finos/legend-graph';
import { DataQualityLambdaParameterParser, ParsedFunctionExpression, } from './DataQualityLambdaParameterParser.js';
import { DataQualityValidationFunctionFactory } from './DataQualityValidationFunctionFactory.js';
import { observe_DataQualityValidationFilterCondition, observe_DataQualityValidationLogicalGroupFunction, observe_DataQualityValidationPropertyGuarantee, } from './DataQualityValidationFunctionObserver.js';
export class DataQualityLambdaParameterExtractorVisitor {
lambdaBody;
graph;
observerContext;
filterBody;
constructor(lambdaBody, graph, observerContext) {
this.lambdaBody = lambdaBody;
this.graph = graph;
this.observerContext = observerContext;
}
visitAssertion(func) {
const { parameters = [] } = this.lambdaBody;
if (parameters.length === 0) {
return;
}
parameters.forEach((param) => {
if (param._type === SUPPORTED_TYPES.CLASS_INSTANCE) {
func.parameters.columns =
DataQualityLambdaParameterParser.processColSpecArray(param, this.observerContext);
}
if (param._type === SUPPORTED_TYPES.VAR) {
func.parameters.variableDeclaration =
DataQualityLambdaParameterParser.processVariableDeclaration(param);
}
});
}
visitFilter(func) {
const { parameters = [] } = this.lambdaBody;
if (parameters.length === 0) {
return;
}
const error = new UnsupportedOperationError('Cannot process type');
const firstParam = parameters[0];
const secondParam = parameters[1];
if (firstParam?._type !== SUPPORTED_TYPES.VAR) {
throw error;
}
if (secondParam?._type === SUPPORTED_TYPES.LAMBDA && !secondParam.body) {
throw error;
}
if (!secondParam) {
throw error;
}
DataQualityLambdaParameterParser.validateLambdaParameter(secondParam);
func.parameters.variableDeclaration =
DataQualityLambdaParameterParser.processVariableDeclaration(firstParam);
const lambdaBody = secondParam.body;
if (!lambdaBody || lambdaBody.length === 0) {
throw error;
}
this.filterBody = lambdaBody[0];
if (!this.filterBody) {
throw error;
}
const factory = new DataQualityValidationFunctionFactory(this.graph, this.observerContext);
const functionName = this.filterBody.function ?? '';
if (this.isLogicalFunction(functionName)) {
func.parameters.lambda.body = factory.createLogicalFunction(functionName);
}
else {
func.parameters.lambda.body =
factory.createFilterChildFunction(functionName);
}
func.parameters.lambda.body.accept(this);
const lambdaParams = secondParam.parameters;
if (lambdaParams.length === 0) {
throw error;
}
const firstLambdaParam = lambdaParams[0];
if (!firstLambdaParam) {
throw error;
}
func.parameters.lambda.variableDeclaration =
DataQualityLambdaParameterParser.processVariableDeclaration(firstLambdaParam);
}
visitCustomHelper(func) {
const { parameters = [] } = this.lambdaBody;
func.parameters.otherParams = [];
parameters.forEach((param) => {
if (param._type === SUPPORTED_TYPES.CLASS_INSTANCE) {
const processedColumn = DataQualityLambdaParameterParser.processColSpec(param, this.observerContext);
func.parameters.column = processedColumn;
}
else if (param._type === SUPPORTED_TYPES.VAR) {
func.parameters.variableDeclaration =
DataQualityLambdaParameterParser.processVariableDeclaration(param);
}
else if (DataQualityLambdaParameterParser.isSupportedPrimitive(param._type)) {
func.parameters.otherParams.push(DataQualityLambdaParameterParser.processPrimitiveParameter(param, this.graph, this.observerContext));
}
else if (param._type === SUPPORTED_TYPES.COLLECTION) {
func.parameters.otherParams.push(DataQualityLambdaParameterParser.processCollectionParameter(param, this.graph, this.observerContext));
}
else {
throw new UnsupportedOperationError(`Cannot process type: ${param._type}`);
}
});
}
visitFilterCondition(func) {
assertTrue(this.filterBody !== undefined, 'Expected filter body to be present');
const { processedParameters: [firstParam, ...otherParams], } = DataQualityLambdaParameterParser.processFunctionParameter(this.filterBody, this.graph, this.observerContext);
func.parameters.otherParams = otherParams;
if (firstParam instanceof AbstractPropertyExpression) {
func.parameters.property = firstParam;
}
else if (firstParam instanceof ParsedFunctionExpression &&
matchFunctionName(firstParam.name, Object.values(DATA_QUALITY_VALIDATION_PROPERTY_GUARANTEE_FUNCTIONS))) {
func.parameters.property = new DataQualityValidationFunctionFactory(this.graph, this.observerContext)
.createPropertyGuaranteeFunction(firstParam.name)
.accept(this, {
propertyGuaranteeParameters: firstParam.processedParameters,
});
}
else {
throw new UnsupportedOperationError('Logical function cannot be used as property parameter in filter condition');
}
observe_DataQualityValidationFilterCondition(func);
}
visitPropertyGuarantee(func, context) {
const { propertyGuaranteeParameters } = context;
assertTrue(Boolean(propertyGuaranteeParameters), `Expected ${func.name} parameters to be present`);
assertTrue(propertyGuaranteeParameters.length === 1, `Expected ${func.name} parameters to be 1, got ${propertyGuaranteeParameters.length}`);
const propertyParam = propertyGuaranteeParameters[0];
if (propertyParam instanceof AbstractPropertyExpression) {
func.parameters.property = propertyParam;
return observe_DataQualityValidationPropertyGuarantee(func);
}
throw new UnsupportedOperationError('Expected property parameter to be an AbstractPropertyExpression');
}
visitLogicalGroup(func) {
assertTrue(Boolean(this.filterBody), 'Expected filter body to be present');
const filterBody = this.filterBody;
const { parameters = [] } = filterBody;
if (parameters.length < 2) {
throw new UnsupportedOperationError('Logical group must have at least 2 parameters (left and right)');
}
const factory = new DataQualityValidationFunctionFactory(this.graph, this.observerContext);
const leftParam = parameters[0];
if (!leftParam || leftParam._type !== SUPPORTED_TYPES.FUNCTION) {
throw new UnsupportedOperationError(`Expected function for left parameter, got ${leftParam?._type}`);
}
const leftFunctionName = leftParam.function ?? '';
if (this.isLogicalFunction(leftFunctionName)) {
func.parameters.left = factory.createLogicalFunction(leftFunctionName);
this.filterBody = leftParam;
func.parameters.left.accept(this);
}
else {
func.parameters.left =
factory.createFilterConditionFunction(leftFunctionName);
this.filterBody = leftParam;
func.parameters.left.accept(this);
}
const rightParam = parameters[1];
if (!rightParam || rightParam._type !== SUPPORTED_TYPES.FUNCTION) {
throw new UnsupportedOperationError(`Expected function for right parameter, got ${rightParam?._type}`);
}
const rightFunctionName = rightParam.function ?? '';
if (this.isLogicalFunction(rightFunctionName)) {
func.parameters.right = factory.createLogicalFunction(rightFunctionName);
this.filterBody = rightParam;
func.parameters.right.accept(this);
}
else {
func.parameters.right =
factory.createFilterConditionFunction(rightFunctionName);
this.filterBody = rightParam;
func.parameters.right.accept(this);
}
observe_DataQualityValidationLogicalGroupFunction(func);
}
isLogicalFunction(name = '') {
return matchFunctionName(name, Object.values(DATA_QUALITY_VALIDATION_LOGICAL_FUNCTIONS));
}
}
//# sourceMappingURL=DataQualityLambdaParameterExtractorVisitor.js.map