Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NU-1806] Add action parameters #6860

Open
wants to merge 14 commits into
base: staging
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package pl.touk.nussknacker.engine.api.editor;

public enum FixedValuesEditorMode {
LIST, RADIO;

public static FixedValuesEditorMode fromName(String name) {
switch (name) {
case "LIST":
return LIST;
case "RADIO":
default:
return RADIO;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package pl.touk.nussknacker.engine.api.component

import io.circe.generic.extras.semiauto.{deriveUnwrappedDecoder, deriveUnwrappedEncoder}
import io.circe.{Decoder, Encoder}
import pl.touk.nussknacker.engine.api.NodeId
import pl.touk.nussknacker.engine.api.component.NodesDeploymentData.NodeDeploymentData

final case class NodesDeploymentData(dataByNodeId: Map[NodeId, NodeDeploymentData])

object NodesDeploymentData {

// Raw deployment parameters (name -> value) that are used as additional node configuration during deployment.
// Each node can be provided with dedicated set of parameters.
// TODO: consider replacing NodeDeploymentData with Json
type NodeDeploymentData = Map[String, String]
mgoworko marked this conversation as resolved.
Show resolved Hide resolved

val empty: NodesDeploymentData = NodesDeploymentData(Map.empty)

implicit val nodesDeploymentDataEncoder: Encoder[NodesDeploymentData] = Encoder
Expand All @@ -18,19 +23,3 @@ object NodesDeploymentData {
Decoder.decodeMap[NodeId, NodeDeploymentData].map(NodesDeploymentData(_))

}

sealed trait NodeDeploymentData

final case class SqlFilteringExpression(sqlExpression: String) extends NodeDeploymentData

object NodeDeploymentData {

implicit val nodeDeploymentDataEncoder: Encoder[NodeDeploymentData] =
deriveUnwrappedEncoder[SqlFilteringExpression].contramap { case sqlExpression: SqlFilteringExpression =>
sqlExpression
}

implicit val nodeDeploymentDataDecoder: Decoder[NodeDeploymentData] =
deriveUnwrappedDecoder[SqlFilteringExpression].map(identity)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package pl.touk.nussknacker.engine.api.definition

import io.circe.generic.JsonCodec
import io.circe.generic.extras.ConfiguredJsonCodec
import io.circe.{Decoder, Encoder, Json}
import io.circe.generic.semiauto.deriveEncoder
import io.circe.{Decoder, Encoder, HCursor, Json}
import pl.touk.nussknacker.engine.api.CirceUtil._
import pl.touk.nussknacker.engine.api.editor.FixedValuesEditorMode
import pl.touk.nussknacker.engine.api.editor.DualEditorMode

import java.time.temporal.ChronoUnit
Expand Down Expand Up @@ -73,8 +75,10 @@ object PeriodParameterEditor {
*/
case object CronParameterEditor extends SimpleParameterEditor

@JsonCodec case class FixedValuesParameterEditor(possibleValues: List[FixedExpressionValue])
extends SimpleParameterEditor
case class FixedValuesParameterEditor(
possibleValues: List[FixedExpressionValue],
mode: FixedValuesEditorMode = FixedValuesEditorMode.LIST
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we can remove default value here as we have a dedicated apply method for such case

) extends SimpleParameterEditor

@JsonCodec case class FixedValuesWithIconParameterEditor(possibleValues: List[FixedExpressionValueWithIcon])
extends SimpleParameterEditor
Expand All @@ -100,3 +104,28 @@ object DualParameterEditor {
}

}

object FixedValuesParameterEditor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move the object definition below the class definition

def apply(possibleValues: List[FixedExpressionValue]): FixedValuesParameterEditor =
FixedValuesParameterEditor(possibleValues, mode = FixedValuesEditorMode.LIST)

implicit val fixedValuesEditorModeEncoder: Encoder[FixedValuesEditorMode] = new Encoder[FixedValuesEditorMode] {
override def apply(a: FixedValuesEditorMode): Json = Encoder.encodeString(a.name())
}
Comment on lines +112 to +114
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can use Encoder.encodeString.contramap(_.name())


implicit val fixedValuesEditorModeDecoder: Decoder[FixedValuesEditorMode] =
Decoder.decodeString.emapTry(name => Try(FixedValuesEditorMode.fromName(name)))

implicit val fixedValuesParameterEditorEncoder: Encoder[FixedValuesParameterEditor] =
deriveEncoder[FixedValuesParameterEditor]

implicit val fixedValuesParameterEditorDecoder: Decoder[FixedValuesParameterEditor] =
Decoder
.forProduct2("possibleValues", "mode")(FixedValuesParameterEditor.apply)
.or(
Decoder.forProduct1("possibleValues")((p: List[FixedExpressionValue]) =>
FixedValuesParameterEditor(p, FixedValuesEditorMode.LIST)
)
)

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package pl.touk.nussknacker.engine.api.process

import pl.touk.nussknacker.engine.api.component.Component._
import pl.touk.nussknacker.engine.api.component.{Component, ProcessingMode}
import pl.touk.nussknacker.engine.api.component.{Component, ParameterConfig, ProcessingMode}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used

import pl.touk.nussknacker.engine.api.context.ContextTransformation
import pl.touk.nussknacker.engine.api.definition.{Parameter, WithExplicitTypesToExtract}
import pl.touk.nussknacker.engine.api.parameter.ParameterName
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions designer/client/src/actions/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ActionTypes =
| "PROCESS_RENAME"
| "EDIT_LABELS"
| "SHOW_METRICS"
| "UPDATE_ACTION_PARAMETERS"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, it seems that we can remove this action and fetch the action parameters in the DeployWithParametersDialog

| "UPDATE_TEST_CAPABILITIES"
| "UPDATE_TEST_FORM_PARAMETERS"
| "DISPLAY_PROCESS"
Expand Down
10 changes: 10 additions & 0 deletions designer/client/src/actions/nk/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ export function loadProcessState(processName: ProcessName, processVersionId: num
);
}

export function fetchActionParameters(processName: ProcessName, scenarioGraph: ScenarioGraph) {
return (dispatch) =>
HttpService.getActionParameters(processName, scenarioGraph).then(({ data }) => {
dispatch({
type: "UPDATE_ACTION_PARAMETERS",
actionParameters: data,
});
});
}

export function fetchTestFormParameters(processName: ProcessName, scenarioGraph: ScenarioGraph) {
return (dispatch) =>
HttpService.getTestFormParameters(processName, scenarioGraph).then(({ data }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ExpressionObj } from "./types";
import { isEmpty } from "lodash";
import { cx } from "@emotion/css";
import { selectStyled } from "../../../../../stylesheets/SelectStyled";
import { Stack, styled, Typography, useTheme } from "@mui/material";
import { FormControlLabel, Radio, RadioGroup, Stack, styled, Typography, useTheme } from "@mui/material";
import { ExtendedEditor } from "./Editor";
import { FieldError } from "../Validators";
import { FixedValuesOption } from "../../fragment-input-definition/item";
Expand Down Expand Up @@ -36,6 +36,11 @@ function getOptions(values: FixedValuesOption[]): Option[] {
}));
}

enum FixedValuesEditorMode {
LIST = "LIST",
RADIO = "RADIO",
}

export const FixedValuesEditor: ExtendedEditor<Props> = (props: Props) => {
const handleCurrentOption = (expressionObj: ExpressionObj, options: Option[]): Option => {
return (
Expand All @@ -46,6 +51,7 @@ export const FixedValuesEditor: ExtendedEditor<Props> = (props: Props) => {
};

const { expressionObj, readOnly, onValueChange, className, showValidation, editorConfig, fieldErrors } = props;
const mode = FixedValuesEditorMode[editorConfig.mode || "LIST"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for introducing editor mode concept instead of adding a new editor

const options = getOptions(editorConfig.possibleValues);
const currentOption = handleCurrentOption(expressionObj, options);
const theme = useTheme();
Expand All @@ -58,7 +64,16 @@ export const FixedValuesEditor: ExtendedEditor<Props> = (props: Props) => {

const { control, input, valueContainer, singleValue, menuPortal, menu, menuList, menuOption, indicatorSeparator, dropdownIndicator } =
selectStyled(theme);
return (
return mode == FixedValuesEditorMode.RADIO ? (
<div className={cx(className)}>
<RadioGroup value={currentOption.value} onChange={(event) => onValueChange(event.target.value)}>
{options.map((option: Option) => {
const label = option.value === "" ? `${option.label} (default)` : option.label;
return <FormControlLabel key={option.value} value={option.value} control={<Radio />} label={label} />;
})}
</RadioGroup>
</div>
) : (
<div className={cx(className)}>
<Creatable
value={currentOption}
Expand Down
49 changes: 49 additions & 0 deletions designer/client/src/components/modals/ActionParameter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ExpressionLang } from "../graph/node-modal/editors/expression/types";
import React, { useCallback } from "react";
import { FieldLabel } from "../graph/node-modal/FieldLabel";
import { getValidationErrorsForField } from "../graph/node-modal/editors/Validators";
import { ActionNodeParameters, ActionParameterConfig } from "../../types/action";
import { NodesDeploymentData } from "../../http/HttpService";
import { NodeValidationError } from "../../types";
import { default as EditableEditor } from "../graph/node-modal/editors/EditableEditor";

interface Props {
nodeName: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be nodeName or nodeId?

propertyName: string;
propertyConfig: ActionParameterConfig;
nodesData: NodesDeploymentData;
onChange: <K extends keyof ActionNodeParameters["parameters"]>(
nodeId: string,
property: K,
newValue: ActionNodeParameters["parameters"][K],
defaultValue?: ActionNodeParameters["parameters"][K],
) => void;
errors: NodeValidationError[];
}

export function ActionParameter(props: Props): JSX.Element {
const { nodeName, propertyName, propertyConfig, errors, nodesData, onChange } = props;

const current = nodesData[nodeName][propertyName] || "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's worth renaming it to currentNodePropertyName for readability sake?

const expressionObj = { expression: current, value: current, language: ExpressionLang.String };
const onValueChange = useCallback((newValue) => onChange(nodeName, propertyName, newValue), [onChange, nodeName, propertyName]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newValue type


return (
<EditableEditor
key={propertyName}
param={propertyConfig}
fieldLabel={propertyConfig.label || propertyName}
onValueChange={onValueChange}
expressionObj={expressionObj}
renderFieldLabel={() => (
<FieldLabel title={propertyConfig.label} label={propertyConfig.label} hintText={propertyConfig.hintText} />
)}
readOnly={false}
showSwitch={false}
showValidation={true}
//ScenarioProperties do not use any variables
variableTypes={{}}
fieldErrors={getValidationErrorsForField(errors, propertyName)}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { PropsWithChildren } from "react";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { Typography } from "@mui/material";
import AccordionDetails from "@mui/material/AccordionDetails";

interface Props {
nodeId: string;
}

export function AdvancedParametersSection({ children, nodeId }: PropsWithChildren<Props>): JSX.Element {
return (
<Accordion disableGutters elevation={0} sx={{ border: 0, "&::before": { display: "none" } }}>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={{ color: "inherit" }} />}
aria-controls={`${nodeId}-content`}
id={`${nodeId}-header`}
sx={{ flexDirection: "row-reverse", border: 0 }}
>
<Typography>{nodeId}</Typography>
</AccordionSummary>
<AccordionDetails>{children}</AccordionDetails>
</Accordion>
);
}
Loading
Loading