Skip to content

Commit

Permalink
[NU-7164] Add basic parser for fragment input definitions V2 (#7228)
Browse files Browse the repository at this point in the history
* Revert "Revert "[NU-7164] Add basic parser for fragment input definitions (#7167)" (#7202)"

This reverts commit 383b4d5.

* [NU-7164] Adjust fields parameter list

* Revert "[NU-7164] Adjust fields parameter list"

This reverts commit d4a7ccc.

* add dicts bugfix

* adjust type

* correct validation rules

---------

Co-authored-by: Piotr Rudnicki <[email protected]>
  • Loading branch information
DeamonDev and Piotr Rudnicki authored Nov 25, 2024
1 parent fcebd61 commit 12daa1b
Show file tree
Hide file tree
Showing 21 changed files with 163 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { PopoverPosition } from "@mui/material/Popover/Popover";
import i18next from "i18next";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import ValidationLabels from "../../../../../modals/ValidationLabels";
import { useTypeOptions } from "../../../fragment-input-definition/FragmentInputDefinition";
import { EditorProps, ExtendedEditor } from "../Editor";
import "@glideapps/glide-data-grid/dist/index.css";
import { CellMenu, DeleteColumnMenuItem, DeleteRowMenuItem, ResetColumnWidthMenuItem } from "./CellMenu";
Expand All @@ -31,6 +30,10 @@ import { TypesMenu } from "./TypesMenu";
import { customRenderers } from "./customRenderers";
import { isDatePickerCell } from "./customCells";
import type { GetRowThemeCallback } from "@glideapps/glide-data-grid/src/internal/data-grid/render/data-grid-render.cells";
import { useSelector } from "react-redux";
import { getProcessDefinitionData } from "../../../../../../reducers/selectors/settings";
import ProcessUtils from "../../../../../../common/ProcessUtils";
import { find, head, orderBy } from "lodash";

const SUPPORTED_TYPES = [
"java.lang.String",
Expand Down Expand Up @@ -87,6 +90,27 @@ const emptySelection = {
rows: CompactSelection.empty(),
};

export function useTableEditorTypeOptions() {
const definitionData = useSelector(getProcessDefinitionData);

const typeOptions = useMemo(
() =>
definitionData?.classes?.map((type) => ({
value: type.refClazzName as SupportedType,
label: ProcessUtils.humanReadableType(type),
})),
[definitionData?.classes],
);

const orderedTypeOptions = useMemo(() => orderBy(typeOptions, (item) => [item.label, item.value], ["asc"]), [typeOptions]);

const defaultTypeOption = useMemo(() => find(typeOptions, { label: "String" }) || head(typeOptions), [typeOptions]);
return {
orderedTypeOptions,
defaultTypeOption,
};
}

export const Table = ({ expressionObj, onValueChange, className, fieldErrors }: EditorProps) => {
const tableDateContext = useTableState(expressionObj);
const [{ rows, columns }, dispatch, rawExpression] = tableDateContext;
Expand All @@ -97,7 +121,7 @@ export const Table = ({ expressionObj, onValueChange, className, fieldErrors }:
}
}, [expressionObj.expression, onValueChange, rawExpression]);

const { defaultTypeOption, orderedTypeOptions } = useTypeOptions<SupportedType>();
const { defaultTypeOption, orderedTypeOptions } = useTableEditorTypeOptions();
const supportedTypes = useMemo(() => orderedTypeOptions.filter(({ value }) => SUPPORTED_TYPES.includes(value)), [orderedTypeOptions]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ interface Props extends Omit<MapVariableProps<FragmentInputParameter>, "readOnly
isEditMode?: boolean;
}

export function useTypeOptions<Value = string>() {
export function useFragmentInputDefinitionTypeOptions() {
const definitionData = useSelector(getProcessDefinitionData);

const typeOptions = useMemo(
() =>
definitionData?.classes?.map((type) => ({
// TODO: Instead of using type assertion type, set refClazzName as a union of available clazzNames
value: type.refClazzName as Value,
value: type.display as string,
label: ProcessUtils.humanReadableType(type),
})),
[definitionData?.classes],
Expand All @@ -40,7 +40,7 @@ export default function FragmentInputDefinition(props: Props): JSX.Element {
const { node, setProperty, isEditMode, showValidation } = passProps;

const readOnly = !isEditMode;
const { orderedTypeOptions, defaultTypeOption } = useTypeOptions();
const { orderedTypeOptions, defaultTypeOption } = useFragmentInputDefinitionTypeOptions();

const addField = useCallback(() => {
addElement("parameters", getDefaultFields(defaultTypeOption.value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,15 @@ export function FixedValuesSetting({
return (
<>
{fixedValuesType === FixedValuesType.ValueInputWithDictEditor && (
<DictSelect typ={typ} name={name} dictId={dictId} readOnly={readOnly} onChange={onChange} path={path} errors={errors} />
<DictSelect
typ={{ ...typ, refClazzName: "java.lang." + typ.refClazzName } as ReturnedType}
name={name}
dictId={dictId}
readOnly={readOnly}
onChange={onChange}
path={path}
errors={errors}
/>
)}
{fixedValuesType === FixedValuesType.ValueInputWithFixedValuesProvided && (
<UserDefinedListInput
Expand All @@ -46,7 +54,7 @@ export function FixedValuesSetting({
onChange={onChange}
path={path}
errors={errors}
typ={typ}
typ={{ ...typ, refClazzName: "java.lang." + typ.refClazzName } as ReturnedType}
name={name}
initialValue={initialValue}
inputLabel={userDefinedListInputLabel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ object AlignedComponentsDefinitionProvider {
new BuiltInComponentsDefinitionsPreparer(designerModelData.modelData.componentsUiConfig),
new FragmentComponentDefinitionExtractor(
designerModelData.modelData.modelClassLoader.classLoader,
designerModelData.modelData.modelDefinitionWithClasses.classDefinitions.all,
designerModelData.modelData.componentsUiConfig.groupName,
designerModelData.modelData.determineDesignerWideId
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class DefinitionsServiceSpec extends AnyFunSuite with Matchers with PatientScala
new BuiltInComponentsDefinitionsPreparer(ComponentsUiConfigParser.parse(model.modelConfig)),
new FragmentComponentDefinitionExtractor(
getClass.getClassLoader,
model.modelDefinitionWithClasses.classDefinitions.all,
Some(_),
DesignerWideComponentId.default(processingType.stringify, _)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class EdgeTypesPreparerTest extends AnyFunSuite with Matchers with ValidatedValu
test("return edge types for fragment, filters, switches and components with multiple inputs") {
val sampleFragmentDef = new FragmentComponentDefinitionExtractor(
getClass.getClassLoader,
Set.empty,
Some(_),
DesignerWideComponentId.default(Streaming.stringify, _)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class ComponentGroupsPreparerSpec
new BuiltInComponentsDefinitionsPreparer(new ComponentsUiConfig(Map.empty, groupNameMapping)),
new FragmentComponentDefinitionExtractor(
getClass.getClassLoader,
Set.empty,
Some(_),
DesignerWideComponentId.default("Streaming", _)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class ComponentsUsageHelperTest extends AnyFunSuite with Matchers with TableDriv

val alignedComponentsDefinitionProvider = new AlignedComponentsDefinitionProvider(
new BuiltInComponentsDefinitionsPreparer(new ComponentsUiConfig(Map.empty, Map.empty)),
new FragmentComponentDefinitionExtractor(getClass.getClassLoader, Some(_), determineDesignerWideId),
new FragmentComponentDefinitionExtractor(getClass.getClassLoader, Set.empty, Some(_), determineDesignerWideId),
modelDefinition,
ProcessingMode.UnboundedStream
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import pl.touk.nussknacker.engine.api.process.{ComponentUseCase, ProcessConfigCr
import pl.touk.nussknacker.engine.api.{JobData, MetaData, ProcessListener, ProcessVersion}
import pl.touk.nussknacker.engine.compile._
import pl.touk.nussknacker.engine.compile.nodecompilation.LazyParameterCreationStrategy
import pl.touk.nussknacker.engine.definition.clazz.ClassDefinitionSet
import pl.touk.nussknacker.engine.definition.clazz.{ClassDefinition, ClassDefinitionSet}
import pl.touk.nussknacker.engine.definition.globalvariables.ExpressionConfigDefinition
import pl.touk.nussknacker.engine.definition.model.{ModelDefinition, ModelDefinitionWithClasses}
import pl.touk.nussknacker.engine.dict.DictServicesFactoryLoader
Expand Down Expand Up @@ -137,13 +137,18 @@ class FlinkProcessCompilerDataFactory(
modelDefinitionWithTypes.modelDefinition.expressionConfig,
modelDefinitionWithTypes.classDefinitions
)
val adjustedDefinitions = adjustDefinitions(modelDefinitionWithTypes.modelDefinition, definitionContext)
val adjustedDefinitions = adjustDefinitions(
modelDefinitionWithTypes.modelDefinition,
definitionContext,
modelDefinitionWithTypes.classDefinitions.all
)
(ModelDefinitionWithClasses(adjustedDefinitions), dictRegistry)
}

protected def adjustDefinitions(
originalModelDefinition: ModelDefinition,
definitionContext: ComponentDefinitionContext
definitionContext: ComponentDefinitionContext,
classDefinitions: Set[ClassDefinition]
): ModelDefinition = originalModelDefinition

private def loadDictRegistry(userCodeClassLoader: ClassLoader) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import pl.touk.nussknacker.engine.api.process.{ComponentUseCase, ProcessConfigCr
import pl.touk.nussknacker.engine.api.typed.ReturningType
import pl.touk.nussknacker.engine.api.typed.typing.{TypingResult, Unknown}
import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess
import pl.touk.nussknacker.engine.definition.clazz.ClassDefinition
import pl.touk.nussknacker.engine.definition.component.dynamic.DynamicComponentDefinitionWithImplementation
import pl.touk.nussknacker.engine.definition.component.methodbased.MethodBasedComponentDefinitionWithImplementation
import pl.touk.nussknacker.engine.definition.component.{
Expand Down Expand Up @@ -42,7 +43,8 @@ abstract class StubbedFlinkProcessCompilerDataFactory(

override protected def adjustDefinitions(
originalModelDefinition: ModelDefinition,
definitionContext: ComponentDefinitionContext
definitionContext: ComponentDefinitionContext,
classDefinitions: Set[ClassDefinition]
): ModelDefinition = {
val usedSourceIds = process.allStartNodes
.map(_.head.data)
Expand All @@ -61,7 +63,8 @@ abstract class StubbedFlinkProcessCompilerDataFactory(
}

val fragmentParametersDefinitionExtractor = new FragmentParametersDefinitionExtractor(
definitionContext.userCodeClassLoader
definitionContext.userCodeClassLoader,
classDefinitions
)
val fragmentSourceDefinitionPreparer = new StubbedFragmentSourceDefinitionPreparer(
fragmentParametersDefinitionExtractor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ object ProcessValidator {

val nodeCompiler = new NodeCompiler(
modelDefinition,
new FragmentParametersDefinitionExtractor(classLoader),
new FragmentParametersDefinitionExtractor(classLoader, definitionWithTypes.classDefinitions.all),
expressionCompiler,
classLoader,
Seq.empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object ProcessCompilerData {
// for testing environment it's important to take classloader from user jar
val nodeCompiler = new NodeCompiler(
definitionWithTypes.modelDefinition,
new FragmentParametersDefinitionExtractor(userCodeClassLoader),
new FragmentParametersDefinitionExtractor(userCodeClassLoader, definitionWithTypes.classDefinitions.all),
expressionCompiler,
userCodeClassLoader,
listeners,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pl.touk.nussknacker.engine.compile.nodecompilation
import cats.data.Validated.{Valid, invalid, invalidNel, valid}
import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.implicits.toTraverseOps
import org.apache.commons.lang3.ClassUtils
import pl.touk.nussknacker.engine.api.NodeId
import pl.touk.nussknacker.engine.api.context.ProcessCompilationError._
import pl.touk.nussknacker.engine.api.context.{PartSubGraphCompilationError, ProcessCompilationError, ValidationContext}
Expand All @@ -14,9 +15,12 @@ import pl.touk.nussknacker.engine.api.parameter.{
ValueInputWithDictEditor,
ValueInputWithFixedValuesProvided
}
import pl.touk.nussknacker.engine.api.typed.typing.{Typed, Unknown}
import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult, Unknown}
import pl.touk.nussknacker.engine.api.validation.Validations.validateVariableName
import pl.touk.nussknacker.engine.compile.ExpressionCompiler
import pl.touk.nussknacker.engine.compile.nodecompilation.FragmentParameterValidator.permittedTypesForEditors
import pl.touk.nussknacker.engine.definition.clazz.ClassDefinition
import pl.touk.nussknacker.engine.definition.fragment.FragmentParameterTypingParser
import pl.touk.nussknacker.engine.graph.expression.Expression
import pl.touk.nussknacker.engine.graph.expression.Expression.Language
import pl.touk.nussknacker.engine.graph.node.FragmentInputDefinition.{FragmentClazzRef, FragmentParameter}
Expand All @@ -29,14 +33,23 @@ import pl.touk.nussknacker.engine.graph.node.{
}
import pl.touk.nussknacker.engine.language.dictWithLabel.DictKeyWithLabelExpressionParser

import scala.util.Try

object FragmentParameterValidator {

val permittedTypesForEditors: List[FragmentClazzRef] = List(
FragmentClazzRef[java.lang.Boolean],
FragmentClazzRef[String],
FragmentClazzRef[java.lang.Long]
FragmentClazzRef[java.lang.Long],
FragmentClazzRef("String"),
FragmentClazzRef("Boolean"),
FragmentClazzRef("Long")
)

}

case class FragmentParameterValidator(classDefinitions: Set[ClassDefinition] = Set.empty) {

// This method doesn't fully validate valueEditor (see ValueEditorValidator.validateAndGetEditor comments)
def validateAgainstClazzRefAndGetEditor(
valueEditor: ParameterValueInput,
Expand Down Expand Up @@ -147,9 +160,11 @@ object FragmentParameterValidator {
validateNonEmptyDictId(dictId, fragmentParameter.name).andThen(_ =>
dictionaries.get(dictId) match {
case Some(dictDefinition) =>
val fragmentParameterTypingResult = fragmentParameter.typ
.toRuntimeClass(classLoader)
.map(Typed(_))
val fragmentParameterTypingParser = new FragmentParameterTypingParser(classLoader, classDefinitions)
val fragmentParameterTypingResult = fragmentParameterTypingParser
.parseClassNameToTypingResult(
fragmentParameter.typ.refClazzName
)
.getOrElse(Unknown)

val dictValueType = dictDefinition.valueType(dictId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import pl.touk.nussknacker.engine.api.parameter.ParameterName
import pl.touk.nussknacker.engine.api.process.{ComponentUseCase, Source}
import pl.touk.nussknacker.engine.api.typed.ReturningType
import pl.touk.nussknacker.engine.api.typed.typing.{TypingResult, Unknown}
import pl.touk.nussknacker.engine.compile.nodecompilation.FragmentParameterValidator.validateParameterNames
import pl.touk.nussknacker.engine.compile.nodecompilation.NodeCompiler.NodeCompilationResult
import pl.touk.nussknacker.engine.compile.{
ComponentExecutorFactory,
Expand Down Expand Up @@ -100,6 +99,8 @@ class NodeCompiler(
new DynamicNodeValidator(expressionCompiler, globalVariablesPreparer, parametersEvaluator)
private val builtInNodeCompiler = new BuiltInNodeCompiler(expressionCompiler)

private val fragmentParameterValidator = FragmentParameterValidator(fragmentDefinitionExtractor.classDefinitions)

def compileSource(
nodeData: SourceNodeData
)(implicit jobData: JobData, nodeId: NodeId): NodeCompilationResult[Source] = nodeData match {
Expand Down Expand Up @@ -165,7 +166,7 @@ class NodeCompiler(
)
}

val parameterNameValidation = validateParameterNames(parameterDefinitions.value)
val parameterNameValidation = fragmentParameterValidator.validateParameterNames(parameterDefinitions.value)

// by relying on name for the field names used on FE, we display the same errors under all fields with the
// duplicated name
Expand Down Expand Up @@ -195,7 +196,7 @@ class NodeCompiler(

val fixedValuesErrors = fragmentInputDefinition.parameters
.map { param =>
FragmentParameterValidator.validateFixedExpressionValues(
fragmentParameterValidator.validateFixedExpressionValues(
param,
validationContext,
expressionCompiler
Expand All @@ -206,7 +207,7 @@ class NodeCompiler(

val dictValueEditorErrors = fragmentInputDefinition.parameters
.map { param =>
FragmentParameterValidator.validateValueInputWithDictEditor(param, expressionConfig.dictionaries, classLoader)
fragmentParameterValidator.validateValueInputWithDictEditor(param, expressionConfig.dictionaries, classLoader)
}
.sequence
.map(_ => ())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ class NodeDataValidator(modelData: ModelData) {

private val compiler = new NodeCompiler(
modelData.modelDefinition,
new FragmentParametersDefinitionExtractor(modelData.modelClassLoader.classLoader),
new FragmentParametersDefinitionExtractor(
modelData.modelClassLoader.classLoader,
modelData.modelDefinitionWithClasses.classDefinitions.all
),
expressionCompiler,
modelData.modelClassLoader.classLoader,
Seq.empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import pl.touk.nussknacker.engine.api.component.{
}
import pl.touk.nussknacker.engine.api.{FragmentSpecificData, NodeId}
import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess
import pl.touk.nussknacker.engine.definition.clazz.ClassDefinition
import pl.touk.nussknacker.engine.definition.component.{
ComponentDefinitionWithImplementation,
ComponentImplementationInvoker
Expand All @@ -18,11 +19,12 @@ import pl.touk.nussknacker.engine.util.MetaDataExtractor

class FragmentComponentDefinitionExtractor(
classLoader: ClassLoader,
classDefinitions: Set[ClassDefinition],
translateGroupName: ComponentGroupName => Option[ComponentGroupName],
determineDesignerWideId: ComponentId => DesignerWideComponentId
) {

val parametersExtractor = new FragmentParametersDefinitionExtractor(classLoader)
val parametersExtractor = new FragmentParametersDefinitionExtractor(classLoader, classDefinitions)

def extractFragmentComponentDefinition(
fragment: CanonicalProcess,
Expand Down
Loading

0 comments on commit 12daa1b

Please sign in to comment.