-
Notifications
You must be signed in to change notification settings - Fork 94
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
base: staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be a final case class, serializable to JSON. Now it is a map, serializable to Json object. So adding any new field beside the map of raw params would break compatibility |
||
|
||
val empty: NodesDeploymentData = NodesDeploymentData(Map.empty) | ||
|
||
implicit val nodesDeploymentDataEncoder: Encoder[NodesDeploymentData] = Encoder | ||
|
@@ -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 |
---|---|---|
|
@@ -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 | ||
|
@@ -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 | ||
) extends SimpleParameterEditor | ||
|
||
@JsonCodec case class FixedValuesWithIconParameterEditor(possibleValues: List[FixedExpressionValueWithIcon]) | ||
extends SimpleParameterEditor | ||
|
@@ -100,3 +104,33 @@ object DualParameterEditor { | |
} | ||
|
||
} | ||
|
||
object FixedValuesParameterEditor { | ||
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()) | ||
} | ||
|
||
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] = { (c: HCursor) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion:
|
||
{ | ||
for { | ||
possibleValues <- c.downField("possibleValues").as[List[FixedExpressionValue]] | ||
modeOpt <- c.downField("mode").as[Option[String]] | ||
} yield { | ||
FixedValuesParameterEditor( | ||
possibleValues, | ||
modeOpt.map(FixedValuesEditorMode.fromName).getOrElse(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} | ||
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 | ||
|
@@ -49,6 +49,15 @@ trait TestWithParametersSupport[+T] { self: Source => | |
def parametersToTestData(params: Map[ParameterName, AnyRef]): T | ||
} | ||
|
||
/** | ||
* Used to define Source parameters for each activity | ||
* e.g. | ||
* {"DEPLOY": { "parametername": ...parameter configuration... } | ||
*/ | ||
trait WithActivityParameters { self: Source => | ||
def activityParametersDefinition: Map[String, Map[String, ParameterConfig]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be a good idea to define enum (sealed trait) with all possible scenario actions in the API module and make it the key of this map. Right now if we change the name of some scenario action, the change will not be enforceable by compiler in all implementations of this trait. |
||
} | ||
|
||
/** | ||
* [[pl.touk.nussknacker.engine.api.process.SourceFactory]] has to have method annotated with [[pl.touk.nussknacker.engine.api.MethodToInvoke]] | ||
* that returns [[pl.touk.nussknacker.engine.api.process.Source]] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { styled, TextField, TextFieldProps } from "@mui/material"; | ||
import React from "react"; | ||
|
||
export const ActivityCommentTextField = styled((props: TextFieldProps) => ( | ||
<TextField | ||
fullWidth | ||
multiline | ||
minRows={1} | ||
maxRows={3} | ||
InputLabelProps={{ shrink: true }} | ||
variant="outlined" | ||
label="Comment" | ||
{...props} | ||
/> | ||
))({ | ||
flexDirection: "column", | ||
".MuiFormLabel-root": { | ||
margin: 0, | ||
flexDirection: "column", | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { useSelector } from "react-redux"; | ||
import { getProcessName } from "../../reducers/selectors/graph"; | ||
import { Typography } from "@mui/material"; | ||
import React from "react"; | ||
import ProcessDialogWarnings from "./ProcessDialogWarnings"; | ||
|
||
interface Props { | ||
title: string; | ||
displayWarnings?: boolean; | ||
} | ||
|
||
export function ActivityHeader(props: Props): JSX.Element { | ||
const processName = useSelector(getProcessName); | ||
return ( | ||
<> | ||
<Typography | ||
variant={"body2"} | ||
sx={{ width: "100%", "::after": { content: "':'" }, fontWeight: 600, fontSize: "16px", lineHeight: "24px" }} | ||
> | ||
{props.title} | ||
</Typography> | ||
<Typography variant={"body2"} sx={{ width: "100%", fontWeight: 600, fontSize: "20px", lineHeight: "30px" }}> | ||
{processName} | ||
</Typography> | ||
{props.displayWarnings && <ProcessDialogWarnings />} | ||
</> | ||
); | ||
} |
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 { ActivityNodeParameters, ActivityParameterConfig } from "../../types/activity"; | ||
import { NodesDeploymentData } from "../../http/HttpService"; | ||
import { NodeValidationError } from "../../types"; | ||
import { default as EditableEditor } from "../graph/node-modal/editors/EditableEditor"; | ||
|
||
interface Props { | ||
nodeName: string; | ||
propertyName: string; | ||
propertyConfig: ActivityParameterConfig; | ||
nodesData: NodesDeploymentData; | ||
onChange: <K extends keyof ActivityNodeParameters["parameters"]>( | ||
nodeId: string, | ||
property: K, | ||
newValue: ActivityNodeParameters["parameters"][K], | ||
defaultValue?: ActivityNodeParameters["parameters"][K], | ||
) => void; | ||
errors: NodeValidationError[]; | ||
} | ||
|
||
export function ActivityProperty(props: Props): JSX.Element { | ||
const { nodeName, propertyName, propertyConfig, errors, nodesData, onChange } = props; | ||
|
||
const current = nodesData[nodeName][propertyName] || ""; | ||
const expressionObj = { expression: current, value: current, language: ExpressionLang.String }; | ||
const onValueChange = useCallback((newValue) => onChange(nodeName, propertyName, newValue), [onChange, nodeName, propertyName]); | ||
|
||
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> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this apply method required? In code itself it is not required, because there is default value for hintText, but maybe I'm missing sth related to some integrations/Flink/Java.