From 900384c74be31725e9998ba3b0cc94f9e18c42da Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Wed, 31 Jan 2024 16:47:52 +0100 Subject: [PATCH 01/15] init --- .../components/toolbars/status/ProcessInfo.tsx | 10 +++++++++- .../status/buttons/CustomActionButton.tsx | 7 ++++++- .../helpers/customActionDisplayabilityResolver.ts | 15 +++++++++++++++ designer/client/src/types/scenarioGraph.ts | 5 +++++ .../restmodel/definition/package.scala | 12 ++++++++++++ .../engine/api/deployment/CustomAction.scala | 6 ++++++ report_prn_nu_styczen_2024.txt | 0 7 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 designer/client/src/helpers/customActionDisplayabilityResolver.ts create mode 100644 report_prn_nu_styczen_2024.txt diff --git a/designer/client/src/components/toolbars/status/ProcessInfo.tsx b/designer/client/src/components/toolbars/status/ProcessInfo.tsx index a5bf62e00a5..7e2e31f3cc7 100644 --- a/designer/client/src/components/toolbars/status/ProcessInfo.tsx +++ b/designer/client/src/components/toolbars/status/ProcessInfo.tsx @@ -3,7 +3,12 @@ import i18next from "i18next"; import { SwitchTransition } from "react-transition-group"; import { useSelector } from "react-redux"; import { RootState } from "../../../reducers"; -import { getScenario, getProcessUnsavedNewName, isProcessRenamed } from "../../../reducers/selectors/graph"; +import { + getScenario, + getProcessUnsavedNewName, + isProcessRenamed, + getProcessVersionId +} from "../../../reducers/selectors/graph"; import { getProcessState } from "../../../reducers/selectors/scenarioState"; import { getCustomActions } from "../../../reducers/selectors/settings"; import { CssFade } from "../../CssFade"; @@ -28,6 +33,9 @@ const ProcessInfo = memo(({ id, buttonsVariant, children }: ToolbarPanelProps) = const unsavedNewName = useSelector((state: RootState) => getProcessUnsavedNewName(state)); const processState = useSelector((state: RootState) => getProcessState(state)); const customActions = useSelector((state: RootState) => getCustomActions(state)); + const versionId = useSelector(getProcessVersionId); + + console.log(versionId); const description = ProcessStateUtils.getStateDescription(scenario, processState); const transitionKey = ProcessStateUtils.getTransitionKey(scenario, processState); diff --git a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx index ec7080d5e43..683810fdb3e 100644 --- a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx +++ b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx @@ -9,6 +9,9 @@ import { ToolbarButton } from "../../../toolbarComponents/toolbarButtons"; import { ToolbarButtonProps } from "../../types"; import UrlIcon from "../../../UrlIcon"; import { FallbackProps } from "react-error-boundary"; +import {useSelector} from "react-redux"; +import {getProcessVersionId} from "../../../../reducers/selectors/graph"; +import {resolveCustomActionDisplayability} from "../../../../helpers/customActionDisplayabilityResolver"; type CustomActionProps = { action: CustomAction; @@ -28,7 +31,9 @@ export default function CustomActionButton(props: CustomActionProps) { ); const statusName = processStatus?.name; - const available = !disabled && action.allowedStateStatusNames.includes(statusName); + const available = !disabled && + action.allowedStateStatusNames.includes(statusName) && + resolveCustomActionDisplayability(action.displayPolicy); const toolTip = available ? null diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts new file mode 100644 index 00000000000..12ff813c3f1 --- /dev/null +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -0,0 +1,15 @@ +import {CustomActionDisplayPolicy} from "../types"; +import {useSelector} from "react-redux"; +import {getProcessVersionId} from "../reducers/selectors/graph"; + +export function resolveCustomActionDisplayability(displayPolicy: CustomActionDisplayPolicy){ + const versionId = useSelector(getProcessVersionId); + + switch(displayPolicy) { + case CustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed: + //TODO + return true; + default: + break; + } +} diff --git a/designer/client/src/types/scenarioGraph.ts b/designer/client/src/types/scenarioGraph.ts index 619c5a3147a..45466ba9593 100644 --- a/designer/client/src/types/scenarioGraph.ts +++ b/designer/client/src/types/scenarioGraph.ts @@ -25,9 +25,14 @@ export type ProcessAdditionalFields = { metaDataType: string; }; +export enum CustomActionDisplayPolicy { + CurrentlyViewedProcessVersionIsDeployed +} + export type CustomAction = { name: string; allowedStateStatusNames: Array; + displayPolicy?: CustomActionDisplayPolicy; icon?: string; parameters?: Array; }; diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 1259a32a193..98367fba631 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -6,6 +6,7 @@ import io.circe.{Decoder, Encoder} import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} import pl.touk.nussknacker.engine.api.definition.ParameterEditor +import pl.touk.nussknacker.engine.api.deployment import pl.touk.nussknacker.engine.api.deployment.CustomAction import pl.touk.nussknacker.engine.api.typed.typing.TypingResult import pl.touk.nussknacker.engine.graph.EdgeType @@ -135,6 +136,13 @@ package object definition { def apply(action: CustomAction): UICustomAction = UICustomAction( name = action.name, allowedStateStatusNames = action.allowedStateStatusNames, + displayPolicy = action.displayPolicy match { + case Some(displayPolicy) => + displayPolicy match { + case deployment.CurrentlyViewedProcessVersionIsDeployed => Some(CurrentlyViewedProcessVersionIsDeployed) + } + case None => None + }, icon = action.icon, parameters = action.parameters.map(p => UICustomActionParameter(p.name, p.editor)) ) @@ -144,10 +152,14 @@ package object definition { @JsonCodec final case class UICustomAction( name: String, allowedStateStatusNames: List[String], + displayPolicy: Option[UICustomActionDisplayPolicy], icon: Option[URI], parameters: List[UICustomActionParameter] ) @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) + sealed trait UICustomActionDisplayPolicy + @JsonCodec private case object CurrentlyViewedProcessVersionIsDeployed extends UICustomActionDisplayPolicy + } diff --git a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala index aaff6ef8d3b..ae6c5267493 100644 --- a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala +++ b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala @@ -22,6 +22,7 @@ case class CustomAction( name: String, // We cannot use "engine.api.deployment.StateStatus" because it can be implemented as a class containing nonconstant attributes allowedStateStatusNames: List[String], + displayPolicy: Option[CustomActionDisplayPolicy] = None, parameters: List[CustomActionParameter] = Nil, icon: Option[URI] = None ) @@ -56,3 +57,8 @@ case class CustomActionNonExisting(request: CustomActionRequest) extends CustomA } case class CustomActionForbidden(request: CustomActionRequest, msg: String) extends CustomActionError + +sealed trait CustomActionDisplayPolicy + +// TODO: Add more +case object CurrentlyViewedProcessVersionIsDeployed extends CustomActionDisplayPolicy diff --git a/report_prn_nu_styczen_2024.txt b/report_prn_nu_styczen_2024.txt new file mode 100644 index 00000000000..e69de29bb2d From 8511475fe900e1d5ac4ab675ae812a329fce6b56 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Wed, 31 Jan 2024 16:49:26 +0100 Subject: [PATCH 02/15] remove redundant file --- report_prn_nu_styczen_2024.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 report_prn_nu_styczen_2024.txt diff --git a/report_prn_nu_styczen_2024.txt b/report_prn_nu_styczen_2024.txt deleted file mode 100644 index e69de29bb2d..00000000000 From 5a440b9471b7e5199268db24bfa76772e2e93b75 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 11:59:43 +0100 Subject: [PATCH 03/15] add backend logic --- designer/client/src/http/HttpService.ts | 6 ++++++ .../nussknacker/ui/api/ProcessesResources.scala | 6 ++++++ .../nussknacker/ui/process/ProcessService.scala | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/designer/client/src/http/HttpService.ts b/designer/client/src/http/HttpService.ts index 98c1a280f12..ef14aefbf35 100644 --- a/designer/client/src/http/HttpService.ts +++ b/designer/client/src/http/HttpService.ts @@ -268,6 +268,12 @@ class HttpService { .then((res) => res.data.filter(({ actionType }) => actionType === "DEPLOY").map(({ performedAt }) => performedAt)); } + fetchLastlyDeployedVersionId(processName: string) { + return api.get<{ + versionId: number | null; + }>(`/processes/${encodeURIComponent(processName)}/lastlyDeployedVersionId`); + } + deploy(processName: string, comment?: string): Promise<{ isSuccess: boolean }> { return api .post(`/processManagement/deploy/${encodeURIComponent(processName)}`, comment) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala index 79659aad693..7e9584894b4 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala @@ -133,6 +133,12 @@ class ProcessesResources( processService.getProcessActions(processId.id) } } + } ~ path("processes" / ProcessNameSegment / "lastlyDeployedVersionId") { processName => + processId(processName) { processId => + complete { + processService.getLastlyDeployedVersionId(processId.id) + } + } } ~ path("processes" / ProcessNameSegment) { processName => processId(processName) { processId => (delete & canWrite(processId)) { diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index 72e84ab8747..3bdcab3e2af 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -91,6 +91,7 @@ object ProcessService { case class ValidateAndResolve(includeValidationNodeResults: Boolean = true) extends ValidationMode } + @JsonCodec case class GetLastlyDeployedVersionId(versionId: Option[Long]) } trait ProcessService { @@ -134,6 +135,7 @@ trait ProcessService { def getProcessActions(id: ProcessId): Future[List[ProcessAction]] + def getLastlyDeployedVersionId(id: ProcessId): Future[GetLastlyDeployedVersionId] } /** @@ -497,4 +499,19 @@ class DBProcessService( throw ProcessValidationError("Scenario category not found.") } + override def getLastlyDeployedVersionId(id: ProcessId): Future[GetLastlyDeployedVersionId] = { + // actions are sorted by timestamp in DESC order, thanks to that the optimalization below works + val maybeLastProcessActionsDB = processActionRepository.getFinishedProcessActions(id, None).map(_.headOption) + val lastlyDeployedVersionIdDB = maybeLastProcessActionsDB + .map(_.flatMap { + case ProcessAction(_, _, processVersionId, _, _, _, ProcessActionType.Deploy, _, _, _, _, _) => + Some(processVersionId.value) + case _ => + None + }) + .map(GetLastlyDeployedVersionId) + + dbioRunner.runInTransaction(lastlyDeployedVersionIdDB) + } + } From f2ea1dee0a4f3f286a5db3336475d20d492bf5d1 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 12:52:05 +0100 Subject: [PATCH 04/15] add frontend code --- .../status/buttons/CustomActionButton.tsx | 19 ++++++++++++------ .../customActionDisplayabilityResolver.ts | 20 +++++++++++++------ designer/client/src/http/HttpService.ts | 8 +++++--- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx index 683810fdb3e..4970948b6f5 100644 --- a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx +++ b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx @@ -1,4 +1,4 @@ -import React, { ComponentType } from "react"; +import React, {ComponentType, useEffect, useState} from "react"; import { useTranslation } from "react-i18next"; import DefaultIcon from "../../../../assets/img/toolbarButtons/custom_action.svg"; import { CustomAction } from "../../../../types"; @@ -21,6 +21,7 @@ type CustomActionProps = { export default function CustomActionButton(props: CustomActionProps) { const { action, processStatus, disabled } = props; + const [isAvailable, setIsAvailable] = useState(false); const { t } = useTranslation(); @@ -30,12 +31,18 @@ export default function CustomActionButton(props: CustomActionProps) { ); + useEffect(() => { + const resolveDisplayability = async () => { + const res = await resolveCustomActionDisplayability(action.displayPolicy); + setIsAvailable(!disabled && + action.allowedStateStatusNames.includes(statusName) && res); + } + resolveDisplayability(); + }, []); + const statusName = processStatus?.name; - const available = !disabled && - action.allowedStateStatusNames.includes(statusName) && - resolveCustomActionDisplayability(action.displayPolicy); - const toolTip = available + const toolTip = isAvailable ? null : t("panels.actions.custom-action.tooltips.disabled", "Disabled for {{statusName}} status.", { statusName }); @@ -44,7 +51,7 @@ export default function CustomActionButton(props: CustomActionProps) { open({ diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 12ff813c3f1..5b3bc4c5f36 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -1,15 +1,23 @@ import {CustomActionDisplayPolicy} from "../types"; import {useSelector} from "react-redux"; -import {getProcessVersionId} from "../reducers/selectors/graph"; +import {getProcessName, getProcessVersionId} from "../reducers/selectors/graph"; +import HttpService from "../http/HttpService"; -export function resolveCustomActionDisplayability(displayPolicy: CustomActionDisplayPolicy){ - const versionId = useSelector(getProcessVersionId); +export async function resolveCustomActionDisplayability(displayPolicy: CustomActionDisplayPolicy){ + const processName = useSelector(getProcessName); + const processVersionId = useSelector(getProcessVersionId); switch(displayPolicy) { case CustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed: - //TODO - return true; + try { + const response = await HttpService.fetchLastlyDeployedVersionId(processName); + const data = response.data; + return data.versionId === processVersionId; + } catch (error) { + console.error("Error fetching lastly deployed version ID:", error); + return false; + } default: - break; + return false; } } diff --git a/designer/client/src/http/HttpService.ts b/designer/client/src/http/HttpService.ts index ef14aefbf35..1e40d70a1e2 100644 --- a/designer/client/src/http/HttpService.ts +++ b/designer/client/src/http/HttpService.ts @@ -61,6 +61,10 @@ export interface AppBuildInfo { processingType: any; } +export type LastlyDeployedVersionId = { + versionId: number | null; +} + export type ComponentActionType = { id: string; title: string; @@ -269,9 +273,7 @@ class HttpService { } fetchLastlyDeployedVersionId(processName: string) { - return api.get<{ - versionId: number | null; - }>(`/processes/${encodeURIComponent(processName)}/lastlyDeployedVersionId`); + return api.get(`/processes/${encodeURIComponent(processName)}/lastlyDeployedVersionId`); } deploy(processName: string, comment?: string): Promise<{ isSuccess: boolean }> { From 50c888e127d46c8bcca53b4a4dc9c3c3c571ad57 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 13:54:50 +0100 Subject: [PATCH 05/15] refactor --- .../customActionDisplayabilityResolver.ts | 2 +- .../restmodel/definition/package.scala | 21 ++++++++++++++++--- .../ui/process/ProcessService.scala | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 5b3bc4c5f36..08670864aca 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -14,7 +14,7 @@ export async function resolveCustomActionDisplayability(displayPolicy: CustomAct const data = response.data; return data.versionId === processVersionId; } catch (error) { - console.error("Error fetching lastly deployed version ID:", error); + console.error("Error while fetching lastly deployed version ID:", error); return false; } default: diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 98367fba631..dd74bda865a 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -1,7 +1,12 @@ package pl.touk.nussknacker.restmodel import io.circe.generic.JsonCodec -import io.circe.generic.extras.semiauto.{deriveConfiguredDecoder, deriveConfiguredEncoder} +import io.circe.generic.extras.semiauto.{ + deriveConfiguredDecoder, + deriveConfiguredEncoder, + deriveEnumerationDecoder, + deriveEnumerationEncoder +} import io.circe.{Decoder, Encoder} import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} @@ -139,7 +144,8 @@ package object definition { displayPolicy = action.displayPolicy match { case Some(displayPolicy) => displayPolicy match { - case deployment.CurrentlyViewedProcessVersionIsDeployed => Some(CurrentlyViewedProcessVersionIsDeployed) + case deployment.CurrentlyViewedProcessVersionIsDeployed => + Some(UICustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed) } case None => None }, @@ -160,6 +166,15 @@ package object definition { @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) sealed trait UICustomActionDisplayPolicy - @JsonCodec private case object CurrentlyViewedProcessVersionIsDeployed extends UICustomActionDisplayPolicy + + object UICustomActionDisplayPolicy { + case object CurrentlyViewedProcessVersionIsDeployed extends UICustomActionDisplayPolicy + + implicit val customActionDisplayPolicyEncoder: Encoder[UICustomActionDisplayPolicy] = + deriveEnumerationEncoder[UICustomActionDisplayPolicy] + + implicit val customActionDisplayPolicyDecoder: Decoder[UICustomActionDisplayPolicy] = + deriveEnumerationDecoder[UICustomActionDisplayPolicy] + } } diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index 3bdcab3e2af..89afc61a731 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -509,7 +509,7 @@ class DBProcessService( case _ => None }) - .map(GetLastlyDeployedVersionId) + .map(GetLastlyDeployedVersionId.apply) dbioRunner.runInTransaction(lastlyDeployedVersionIdDB) } From eabdbfa9d28254bd998cab7a47c1736239818e9f Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 16:29:17 +0100 Subject: [PATCH 06/15] refactor --- .../helpers/customActionDisplayabilityResolver.ts | 4 ++-- designer/client/src/http/HttpService.ts | 6 +++--- .../nussknacker/restmodel/definition/package.scala | 11 +++-------- .../touk/nussknacker/ui/api/ProcessesResources.scala | 4 ++-- .../touk/nussknacker/ui/process/ProcessService.scala | 12 ++++++------ 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 08670864aca..8925982e12d 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -10,11 +10,11 @@ export async function resolveCustomActionDisplayability(displayPolicy: CustomAct switch(displayPolicy) { case CustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed: try { - const response = await HttpService.fetchLastlyDeployedVersionId(processName); + const response = await HttpService.fetchLastDeployedVersionId(processName); const data = response.data; return data.versionId === processVersionId; } catch (error) { - console.error("Error while fetching lastly deployed version ID:", error); + console.error("Error while fetching last deployed version ID:", error); return false; } default: diff --git a/designer/client/src/http/HttpService.ts b/designer/client/src/http/HttpService.ts index 1e40d70a1e2..e9970e829ca 100644 --- a/designer/client/src/http/HttpService.ts +++ b/designer/client/src/http/HttpService.ts @@ -61,7 +61,7 @@ export interface AppBuildInfo { processingType: any; } -export type LastlyDeployedVersionId = { +export type LastDeployedVersionId = { versionId: number | null; } @@ -272,8 +272,8 @@ class HttpService { .then((res) => res.data.filter(({ actionType }) => actionType === "DEPLOY").map(({ performedAt }) => performedAt)); } - fetchLastlyDeployedVersionId(processName: string) { - return api.get(`/processes/${encodeURIComponent(processName)}/lastlyDeployedVersionId`); + fetchLastDeployedVersionId(processName: string) { + return api.get(`/processes/${encodeURIComponent(processName)}/lastDeployedVersionId`); } deploy(processName: string, comment?: string): Promise<{ isSuccess: boolean }> { diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index dd74bda865a..41bd46d50c0 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -12,7 +12,7 @@ import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} import pl.touk.nussknacker.engine.api.definition.ParameterEditor import pl.touk.nussknacker.engine.api.deployment -import pl.touk.nussknacker.engine.api.deployment.CustomAction +import pl.touk.nussknacker.engine.api.deployment.{CurrentlyViewedProcessVersionIsDeployed, CustomAction} import pl.touk.nussknacker.engine.api.typed.typing.TypingResult import pl.touk.nussknacker.engine.graph.EdgeType import pl.touk.nussknacker.engine.graph.evaluatedparam.{Parameter => NodeParameter} @@ -141,13 +141,8 @@ package object definition { def apply(action: CustomAction): UICustomAction = UICustomAction( name = action.name, allowedStateStatusNames = action.allowedStateStatusNames, - displayPolicy = action.displayPolicy match { - case Some(displayPolicy) => - displayPolicy match { - case deployment.CurrentlyViewedProcessVersionIsDeployed => - Some(UICustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed) - } - case None => None + displayPolicy = action.displayPolicy.map { case CurrentlyViewedProcessVersionIsDeployed => + UICustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed }, icon = action.icon, parameters = action.parameters.map(p => UICustomActionParameter(p.name, p.editor)) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala index 7e9584894b4..f1fc36a5e57 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala @@ -133,10 +133,10 @@ class ProcessesResources( processService.getProcessActions(processId.id) } } - } ~ path("processes" / ProcessNameSegment / "lastlyDeployedVersionId") { processName => + } ~ path("processes" / ProcessNameSegment / "lastDeployedVersionId") { processName => processId(processName) { processId => complete { - processService.getLastlyDeployedVersionId(processId.id) + processService.getLastDeployedVersionId(processId.id) } } } ~ path("processes" / ProcessNameSegment) { processName => diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index e6e85a85c8a..df3f74bba65 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -91,7 +91,7 @@ object ProcessService { case class ValidateAndResolve(includeValidationNodeResults: Boolean = true) extends ValidationMode } - @JsonCodec case class GetLastlyDeployedVersionId(versionId: Option[Long]) + @JsonCodec case class LastDeployedVersionId(versionId: Option[Long]) } trait ProcessService { @@ -135,7 +135,7 @@ trait ProcessService { def getProcessActions(id: ProcessId): Future[List[ProcessAction]] - def getLastlyDeployedVersionId(id: ProcessId): Future[GetLastlyDeployedVersionId] + def getLastDeployedVersionId(id: ProcessId): Future[LastDeployedVersionId] } /** @@ -499,19 +499,19 @@ class DBProcessService( throw ProcessValidationError("Scenario category not found.") } - override def getLastlyDeployedVersionId(id: ProcessId): Future[GetLastlyDeployedVersionId] = { + override def getLastDeployedVersionId(id: ProcessId): Future[LastDeployedVersionId] = { // actions are sorted by timestamp in DESC order, thanks to that the optimalization below works val maybeLastProcessActionsDB = processActionRepository.getFinishedProcessActions(id, None).map(_.headOption) - val lastlyDeployedVersionIdDB = maybeLastProcessActionsDB + val lastDeployedVersionIdDB = maybeLastProcessActionsDB .map(_.flatMap { case ProcessAction(_, _, processVersionId, _, _, _, ProcessActionType.Deploy, _, _, _, _, _) => Some(processVersionId.value) case _ => None }) - .map(GetLastlyDeployedVersionId.apply) + .map(LastDeployedVersionId.apply) - dbioRunner.runInTransaction(lastlyDeployedVersionIdDB) + dbioRunner.runInTransaction(lastDeployedVersionIdDB) } } From e1b6633e9fc3b94e88b6a23078221fe75fe9e7a0 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 16:36:20 +0100 Subject: [PATCH 07/15] remove console log --- .../src/components/toolbars/status/ProcessInfo.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/designer/client/src/components/toolbars/status/ProcessInfo.tsx b/designer/client/src/components/toolbars/status/ProcessInfo.tsx index 7e2e31f3cc7..5d5e9b64090 100644 --- a/designer/client/src/components/toolbars/status/ProcessInfo.tsx +++ b/designer/client/src/components/toolbars/status/ProcessInfo.tsx @@ -3,12 +3,7 @@ import i18next from "i18next"; import { SwitchTransition } from "react-transition-group"; import { useSelector } from "react-redux"; import { RootState } from "../../../reducers"; -import { - getScenario, - getProcessUnsavedNewName, - isProcessRenamed, - getProcessVersionId -} from "../../../reducers/selectors/graph"; +import { getScenario, getProcessUnsavedNewName, isProcessRenamed, getProcessVersionId } from "../../../reducers/selectors/graph"; import { getProcessState } from "../../../reducers/selectors/scenarioState"; import { getCustomActions } from "../../../reducers/selectors/settings"; import { CssFade } from "../../CssFade"; @@ -35,8 +30,6 @@ const ProcessInfo = memo(({ id, buttonsVariant, children }: ToolbarPanelProps) = const customActions = useSelector((state: RootState) => getCustomActions(state)); const versionId = useSelector(getProcessVersionId); - console.log(versionId); - const description = ProcessStateUtils.getStateDescription(scenario, processState); const transitionKey = ProcessStateUtils.getTransitionKey(scenario, processState); // TODO: better styling of process info toolbar in case of many custom actions From 1ab847411c03d30cccf032394db8be3f9c18b625 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 17:14:18 +0100 Subject: [PATCH 08/15] refactor --- .../status/buttons/CustomActionButton.tsx | 17 +++++------------ .../customActionDisplayabilityResolver.ts | 18 ++++++++---------- designer/client/src/http/HttpService.ts | 8 -------- .../ui/api/ProcessesResources.scala | 6 ------ .../ui/process/ProcessService.scala | 18 ------------------ 5 files changed, 13 insertions(+), 54 deletions(-) diff --git a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx index 4970948b6f5..e2d80c2363b 100644 --- a/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx +++ b/designer/client/src/components/toolbars/status/buttons/CustomActionButton.tsx @@ -21,7 +21,6 @@ type CustomActionProps = { export default function CustomActionButton(props: CustomActionProps) { const { action, processStatus, disabled } = props; - const [isAvailable, setIsAvailable] = useState(false); const { t } = useTranslation(); @@ -31,18 +30,12 @@ export default function CustomActionButton(props: CustomActionProps) { ); - useEffect(() => { - const resolveDisplayability = async () => { - const res = await resolveCustomActionDisplayability(action.displayPolicy); - setIsAvailable(!disabled && - action.allowedStateStatusNames.includes(statusName) && res); - } - resolveDisplayability(); - }, []); - const statusName = processStatus?.name; + const available = !disabled && + action.allowedStateStatusNames.includes(statusName) && + resolveCustomActionDisplayability(action.displayPolicy); - const toolTip = isAvailable + const toolTip = available ? null : t("panels.actions.custom-action.tooltips.disabled", "Disabled for {{statusName}} status.", { statusName }); @@ -51,7 +44,7 @@ export default function CustomActionButton(props: CustomActionProps) { open({ diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 8925982e12d..76c1022c143 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -1,20 +1,18 @@ import {CustomActionDisplayPolicy} from "../types"; import {useSelector} from "react-redux"; -import {getProcessName, getProcessVersionId} from "../reducers/selectors/graph"; -import HttpService from "../http/HttpService"; +import {getProcessName, getProcessVersionId, getScenario} from "../reducers/selectors/graph"; +import {ActionType} from "../components/Process/types"; -export async function resolveCustomActionDisplayability(displayPolicy: CustomActionDisplayPolicy){ - const processName = useSelector(getProcessName); +export function resolveCustomActionDisplayability(displayPolicy: CustomActionDisplayPolicy){ const processVersionId = useSelector(getProcessVersionId); + const scenario = useSelector(getScenario); switch(displayPolicy) { case CustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed: - try { - const response = await HttpService.fetchLastDeployedVersionId(processName); - const data = response.data; - return data.versionId === processVersionId; - } catch (error) { - console.error("Error while fetching last deployed version ID:", error); + if (scenario.lastDeployedAction !== null) { + return scenario.lastDeployedAction.processVersionId === processVersionId && + scenario.lastDeployedAction.actionType == ActionType.Deploy + } else { return false; } default: diff --git a/designer/client/src/http/HttpService.ts b/designer/client/src/http/HttpService.ts index e9970e829ca..98c1a280f12 100644 --- a/designer/client/src/http/HttpService.ts +++ b/designer/client/src/http/HttpService.ts @@ -61,10 +61,6 @@ export interface AppBuildInfo { processingType: any; } -export type LastDeployedVersionId = { - versionId: number | null; -} - export type ComponentActionType = { id: string; title: string; @@ -272,10 +268,6 @@ class HttpService { .then((res) => res.data.filter(({ actionType }) => actionType === "DEPLOY").map(({ performedAt }) => performedAt)); } - fetchLastDeployedVersionId(processName: string) { - return api.get(`/processes/${encodeURIComponent(processName)}/lastDeployedVersionId`); - } - deploy(processName: string, comment?: string): Promise<{ isSuccess: boolean }> { return api .post(`/processManagement/deploy/${encodeURIComponent(processName)}`, comment) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala index f1fc36a5e57..79659aad693 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ProcessesResources.scala @@ -133,12 +133,6 @@ class ProcessesResources( processService.getProcessActions(processId.id) } } - } ~ path("processes" / ProcessNameSegment / "lastDeployedVersionId") { processName => - processId(processName) { processId => - complete { - processService.getLastDeployedVersionId(processId.id) - } - } } ~ path("processes" / ProcessNameSegment) { processName => processId(processName) { processId => (delete & canWrite(processId)) { diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index df3f74bba65..ac37ac24711 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -91,7 +91,6 @@ object ProcessService { case class ValidateAndResolve(includeValidationNodeResults: Boolean = true) extends ValidationMode } - @JsonCodec case class LastDeployedVersionId(versionId: Option[Long]) } trait ProcessService { @@ -134,8 +133,6 @@ trait ProcessService { ): Future[ScenarioGraphWithValidationResult] def getProcessActions(id: ProcessId): Future[List[ProcessAction]] - - def getLastDeployedVersionId(id: ProcessId): Future[LastDeployedVersionId] } /** @@ -499,19 +496,4 @@ class DBProcessService( throw ProcessValidationError("Scenario category not found.") } - override def getLastDeployedVersionId(id: ProcessId): Future[LastDeployedVersionId] = { - // actions are sorted by timestamp in DESC order, thanks to that the optimalization below works - val maybeLastProcessActionsDB = processActionRepository.getFinishedProcessActions(id, None).map(_.headOption) - val lastDeployedVersionIdDB = maybeLastProcessActionsDB - .map(_.flatMap { - case ProcessAction(_, _, processVersionId, _, _, _, ProcessActionType.Deploy, _, _, _, _, _) => - Some(processVersionId.value) - case _ => - None - }) - .map(LastDeployedVersionId.apply) - - dbioRunner.runInTransaction(lastDeployedVersionIdDB) - } - } From 97a1cb5c1027e56b794e46298ebd8ec6e996c659 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Thu, 1 Feb 2024 17:16:09 +0100 Subject: [PATCH 09/15] restore blank line --- .../scala/pl/touk/nussknacker/ui/process/ProcessService.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index ac37ac24711..6f0f4aa3324 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -133,6 +133,7 @@ trait ProcessService { ): Future[ScenarioGraphWithValidationResult] def getProcessActions(id: ProcessId): Future[List[ProcessAction]] + } /** From 8e2918c2befd962f99a9857763f7a17ad8b6d9b1 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Fri, 2 Feb 2024 11:57:31 +0100 Subject: [PATCH 10/15] refactor BE --- .../restmodel/definition/package.scala | 46 ++++++++++---- .../engine/api/deployment/CustomAction.scala | 60 ++++++++++++++++++- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 41bd46d50c0..a9f6a1e6ac2 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -12,7 +12,14 @@ import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} import pl.touk.nussknacker.engine.api.definition.ParameterEditor import pl.touk.nussknacker.engine.api.deployment -import pl.touk.nussknacker.engine.api.deployment.{CurrentlyViewedProcessVersionIsDeployed, CustomAction} +import pl.touk.nussknacker.engine.api.deployment.{ + CustomAction, + CustomActionDisplayConditionalPolicy, + CustomActionDisplayPolicy, + CustomActionDisplaySimplePolicy, + NodeExpr, + StatusExpr +} import pl.touk.nussknacker.engine.api.typed.typing.TypingResult import pl.touk.nussknacker.engine.graph.EdgeType import pl.touk.nussknacker.engine.graph.evaluatedparam.{Parameter => NodeParameter} @@ -141,9 +148,7 @@ package object definition { def apply(action: CustomAction): UICustomAction = UICustomAction( name = action.name, allowedStateStatusNames = action.allowedStateStatusNames, - displayPolicy = action.displayPolicy.map { case CurrentlyViewedProcessVersionIsDeployed => - UICustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed - }, + displayPolicy = action.displayPolicy.map(UICustomActionDisplayPolicy.fromCustomActionDisplayPolicy), icon = action.icon, parameters = action.parameters.map(p => UICustomActionParameter(p.name, p.editor)) ) @@ -161,15 +166,36 @@ package object definition { @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) sealed trait UICustomActionDisplayPolicy + sealed trait UICustomActionDisplayPolicyExpr + @JsonCodec case class UIStatusExpr(status: String) extends UICustomActionDisplayPolicyExpr + @JsonCodec case class UINodeExpr(node: String) extends UICustomActionDisplayPolicyExpr + + @JsonCodec case class UICustomActionDisplaySimplePolicy( + version: Long, + operator: String, + expr: UICustomActionDisplayPolicyExpr + ) extends UICustomActionDisplayPolicy + + @JsonCodec case class UICustomActionDisplayConditionalPolicy( + condition: String, + operands: List[UICustomActionDisplayPolicy] + ) extends UICustomActionDisplayPolicy + + private object UICustomActionDisplayPolicy { + + def fromCustomActionDisplayPolicy(displayPolicy: CustomActionDisplayPolicy): UICustomActionDisplayPolicy = + displayPolicy match { + case CustomActionDisplaySimplePolicy(version, operator, NodeExpr(node)) => + UICustomActionDisplaySimplePolicy(version, operator, UINodeExpr(node)) - object UICustomActionDisplayPolicy { - case object CurrentlyViewedProcessVersionIsDeployed extends UICustomActionDisplayPolicy + case CustomActionDisplaySimplePolicy(version, operator, StatusExpr(status)) => + UICustomActionDisplaySimplePolicy(version, operator, UIStatusExpr(status)) - implicit val customActionDisplayPolicyEncoder: Encoder[UICustomActionDisplayPolicy] = - deriveEnumerationEncoder[UICustomActionDisplayPolicy] + case CustomActionDisplayConditionalPolicy(condition, operands) => + val uiOperands = operands.map(fromCustomActionDisplayPolicy) + UICustomActionDisplayConditionalPolicy(condition, uiOperands) + } - implicit val customActionDisplayPolicyDecoder: Decoder[UICustomActionDisplayPolicy] = - deriveEnumerationDecoder[UICustomActionDisplayPolicy] } } diff --git a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala index 9b8469d7958..cb42ce294c9 100644 --- a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala +++ b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/CustomAction.scala @@ -1,9 +1,11 @@ package pl.touk.nussknacker.engine.api.deployment import pl.touk.nussknacker.engine.api.ProcessVersion +import pl.touk.nussknacker.engine.api.context.ProcessCompilationError.CustomParameterValidationError import pl.touk.nussknacker.engine.api.definition.ParameterEditor import pl.touk.nussknacker.engine.deployment.User +import scala.util.{Failure, Success, Try} import java.net.URI /* @@ -35,6 +37,60 @@ case class CustomActionRequest(name: String, processVersion: ProcessVersion, use case class CustomActionResult(req: CustomActionRequest, msg: String) sealed trait CustomActionDisplayPolicy +sealed trait CustomActionDisplayPolicyExpr +case class StatusExpr(status: String) extends CustomActionDisplayPolicyExpr +case class NodeExpr(node: String) extends CustomActionDisplayPolicyExpr +case class CustomActionDisplaySimplePolicy(version: Long, operator: String, expr: CustomActionDisplayPolicyExpr) + extends CustomActionDisplayPolicy +case class CustomActionDisplayConditionalPolicy(condition: String, operands: List[CustomActionDisplayPolicy]) + extends CustomActionDisplayPolicy +class CustomActionDisplayPolicyError(msg: String) extends IllegalArgumentException(msg) -// TODO: Add more -case object CurrentlyViewedProcessVersionIsDeployed extends CustomActionDisplayPolicy +object CustomActionDisplayPolicy { + + class CustomActionDisplayPolicyBuilder private ( + private val version: Option[Long] = None, + private val operator: Option[String] = None, + private val expr: Option[CustomActionDisplayPolicyExpr] = None, + private val condition: Option[String] = None, + private val operands: List[CustomActionDisplayPolicy] = List() + ) { + def withVersion(version: Long): CustomActionDisplayPolicyBuilder = + new CustomActionDisplayPolicyBuilder(version = Some(version), operator, expr, condition, operands) + + def withOperator(operator: String): CustomActionDisplayPolicyBuilder = + if (operator == "is" || operator == "contains") { + new CustomActionDisplayPolicyBuilder(version, operator = Some(operator), expr, condition, operands) + } else { + throw new CustomActionDisplayPolicyError("Operator must be one of ['is', 'contains']") + } + + def withExpr(expr: CustomActionDisplayPolicyExpr): CustomActionDisplayPolicyBuilder = + new CustomActionDisplayPolicyBuilder(version, operator, expr = Some(expr), condition, operands) + + def withCondition(condition: String): CustomActionDisplayPolicyBuilder = + if (condition == "OR" || condition == "AND") { + new CustomActionDisplayPolicyBuilder(version, operator, expr, condition = Some(condition), operands) + } else { + throw new CustomActionDisplayPolicyError("Operator must be one of ['OR', 'AND']") + } + + def withOperands(operands: CustomActionDisplayPolicy*): CustomActionDisplayPolicyBuilder = + new CustomActionDisplayPolicyBuilder(version, operator, expr, condition, operands = operands.toList) + + def buildSimplePolicy(): CustomActionDisplaySimplePolicy = + CustomActionDisplaySimplePolicy( + version.getOrElse(throw new CustomActionDisplayPolicyError("version not set")), + operator.getOrElse(throw new CustomActionDisplayPolicyError("operator not set")), + expr.getOrElse(throw new CustomActionDisplayPolicyError("expr not set")) + ) + + def buildConditionalPolicy(): CustomActionDisplayConditionalPolicy = + CustomActionDisplayConditionalPolicy( + condition.getOrElse(throw new CustomActionDisplayPolicyError("condition not set")), + if (operands.nonEmpty) operands else throw new CustomActionDisplayPolicyError("operands list is empty") + ) + + } + +} From 88754422a6d93a296ae097d3ccdc93d84688e463 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Fri, 2 Feb 2024 12:42:37 +0100 Subject: [PATCH 11/15] add FE refactor --- .../customActionDisplayabilityResolver.ts | 13 +++----- designer/client/src/types/scenarioGraph.ts | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 76c1022c143..6c4fd05a365 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -7,14 +7,11 @@ export function resolveCustomActionDisplayability(displayPolicy: CustomActionDis const processVersionId = useSelector(getProcessVersionId); const scenario = useSelector(getScenario); - switch(displayPolicy) { - case CustomActionDisplayPolicy.CurrentlyViewedProcessVersionIsDeployed: - if (scenario.lastDeployedAction !== null) { - return scenario.lastDeployedAction.processVersionId === processVersionId && - scenario.lastDeployedAction.actionType == ActionType.Deploy - } else { - return false; - } + switch(displayPolicy.type) { + case "UICustomActionDisplaySimplePolicy": + return false; + case "UICustomActionDisplayConditionalPolicy": + return false; default: return false; } diff --git a/designer/client/src/types/scenarioGraph.ts b/designer/client/src/types/scenarioGraph.ts index 45466ba9593..c8e5543bf1e 100644 --- a/designer/client/src/types/scenarioGraph.ts +++ b/designer/client/src/types/scenarioGraph.ts @@ -25,9 +25,35 @@ export type ProcessAdditionalFields = { metaDataType: string; }; -export enum CustomActionDisplayPolicy { - CurrentlyViewedProcessVersionIsDeployed -} +export type StatusExpr = { + type: 'UIStatusExpr'; + status: string; +}; + +export type NodeExpr = { + type: 'UINodeExpr'; + node: string; +}; + +type CustomActionDisplayPolicyExpr = StatusExpr | NodeExpr; + +// CustomActionDisplayPolicy ADT +export type CustomActionDisplaySimplePolicy = { + type: 'UICustomActionDisplaySimplePolicy'; + version: number; + operator: string; + expr: CustomActionDisplayPolicyExpr; +}; + +export type CustomActionDisplayConditionalPolicy = { + type: 'UICustomActionDisplayConditionalPolicy'; + condition: string; + operands: CustomActionDisplayPolicy[]; +}; + +export type CustomActionDisplayPolicy = + | CustomActionDisplaySimplePolicy + | CustomActionDisplayConditionalPolicy; export type CustomAction = { name: string; From 375d8b59c7300dfa32af4acd70b9882f7e38ccaf Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Fri, 2 Feb 2024 13:01:19 +0100 Subject: [PATCH 12/15] add implicits for sealed trait --- .../customActionDisplayabilityResolver.ts | 1 + .../restmodel/definition/package.scala | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/designer/client/src/helpers/customActionDisplayabilityResolver.ts b/designer/client/src/helpers/customActionDisplayabilityResolver.ts index 6c4fd05a365..b9e94ac75ba 100644 --- a/designer/client/src/helpers/customActionDisplayabilityResolver.ts +++ b/designer/client/src/helpers/customActionDisplayabilityResolver.ts @@ -9,6 +9,7 @@ export function resolveCustomActionDisplayability(displayPolicy: CustomActionDis switch(displayPolicy.type) { case "UICustomActionDisplaySimplePolicy": + const { version, operator, expr } = displayPolicy; return false; case "UICustomActionDisplayConditionalPolicy": return false; diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index a9f6a1e6ac2..318bce18109 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -7,6 +7,7 @@ import io.circe.generic.extras.semiauto.{ deriveEnumerationDecoder, deriveEnumerationEncoder } +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} @@ -155,14 +156,6 @@ package object definition { } - @JsonCodec final case class UICustomAction( - name: String, - allowedStateStatusNames: List[String], - displayPolicy: Option[UICustomActionDisplayPolicy], - icon: Option[URI], - parameters: List[UICustomActionParameter] - ) - @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) sealed trait UICustomActionDisplayPolicy @@ -181,7 +174,10 @@ package object definition { operands: List[UICustomActionDisplayPolicy] ) extends UICustomActionDisplayPolicy - private object UICustomActionDisplayPolicy { + object UICustomActionDisplayPolicy { + + implicit val exprEncoder: Encoder[UICustomActionDisplayPolicyExpr] = deriveEncoder + implicit val exprDecoder: Decoder[UICustomActionDisplayPolicyExpr] = deriveDecoder def fromCustomActionDisplayPolicy(displayPolicy: CustomActionDisplayPolicy): UICustomActionDisplayPolicy = displayPolicy match { @@ -198,4 +194,12 @@ package object definition { } + @JsonCodec final case class UICustomAction( + name: String, + allowedStateStatusNames: List[String], + displayPolicy: Option[UICustomActionDisplayPolicy], + icon: Option[URI], + parameters: List[UICustomActionParameter] + ) + } From e1df125ffb754dfd107fd023d30a4c52ae95a8e0 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Fri, 2 Feb 2024 16:32:13 +0100 Subject: [PATCH 13/15] add missing codecs --- .../pl/touk/nussknacker/restmodel/definition/package.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 318bce18109..2b64b7d8194 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -179,6 +179,9 @@ package object definition { implicit val exprEncoder: Encoder[UICustomActionDisplayPolicyExpr] = deriveEncoder implicit val exprDecoder: Decoder[UICustomActionDisplayPolicyExpr] = deriveDecoder + implicit val policyEncoder: Encoder[UICustomActionDisplayPolicy] = deriveEncoder + implicit val policyDecoder: Decoder[UICustomActionDisplayPolicy] = deriveDecoder + def fromCustomActionDisplayPolicy(displayPolicy: CustomActionDisplayPolicy): UICustomActionDisplayPolicy = displayPolicy match { case CustomActionDisplaySimplePolicy(version, operator, NodeExpr(node)) => From 5f241ef1f1c67eaafeaee1a3dc75b43ddc64f00b Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Mon, 5 Feb 2024 09:38:47 +0100 Subject: [PATCH 14/15] refactor --- .../restmodel/definition/package.scala | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 2b64b7d8194..906459d79fa 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -159,26 +159,8 @@ package object definition { @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) sealed trait UICustomActionDisplayPolicy - sealed trait UICustomActionDisplayPolicyExpr - @JsonCodec case class UIStatusExpr(status: String) extends UICustomActionDisplayPolicyExpr - @JsonCodec case class UINodeExpr(node: String) extends UICustomActionDisplayPolicyExpr - - @JsonCodec case class UICustomActionDisplaySimplePolicy( - version: Long, - operator: String, - expr: UICustomActionDisplayPolicyExpr - ) extends UICustomActionDisplayPolicy - - @JsonCodec case class UICustomActionDisplayConditionalPolicy( - condition: String, - operands: List[UICustomActionDisplayPolicy] - ) extends UICustomActionDisplayPolicy object UICustomActionDisplayPolicy { - - implicit val exprEncoder: Encoder[UICustomActionDisplayPolicyExpr] = deriveEncoder - implicit val exprDecoder: Decoder[UICustomActionDisplayPolicyExpr] = deriveDecoder - implicit val policyEncoder: Encoder[UICustomActionDisplayPolicy] = deriveEncoder implicit val policyDecoder: Decoder[UICustomActionDisplayPolicy] = deriveDecoder @@ -197,6 +179,27 @@ package object definition { } + sealed trait UICustomActionDisplayPolicyExpr + + object UICustomActionPolicyExpr { + implicit val exprEncoder: Encoder[UICustomActionDisplayPolicyExpr] = deriveEncoder + implicit val exprDecoder: Decoder[UICustomActionDisplayPolicyExpr] = deriveDecoder + } + + @JsonCodec case class UIStatusExpr(status: String) extends UICustomActionDisplayPolicyExpr + @JsonCodec case class UINodeExpr(node: String) extends UICustomActionDisplayPolicyExpr + + @JsonCodec case class UICustomActionDisplaySimplePolicy( + version: Long, + operator: String, + expr: UICustomActionDisplayPolicyExpr + ) extends UICustomActionDisplayPolicy + + @JsonCodec case class UICustomActionDisplayConditionalPolicy( + condition: String, + operands: List[UICustomActionDisplayPolicy] + ) extends UICustomActionDisplayPolicy + @JsonCodec final case class UICustomAction( name: String, allowedStateStatusNames: List[String], From bb77e36814598bba2101a73511f8011799eb0f21 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Mon, 5 Feb 2024 10:43:06 +0100 Subject: [PATCH 15/15] refactor --- .../restmodel/definition/package.scala | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala index 906459d79fa..2ff9418bd87 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/definition/package.scala @@ -1,18 +1,10 @@ package pl.touk.nussknacker.restmodel import io.circe.generic.JsonCodec -import io.circe.generic.extras.semiauto.{ - deriveConfiguredDecoder, - deriveConfiguredEncoder, - deriveEnumerationDecoder, - deriveEnumerationEncoder -} -import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.generic.extras.semiauto.{deriveConfiguredDecoder, deriveConfiguredEncoder} import io.circe.{Decoder, Encoder} -import pl.touk.nussknacker.engine.api.component.ComponentType.ComponentType import pl.touk.nussknacker.engine.api.component.{ComponentGroupName, ComponentId} import pl.touk.nussknacker.engine.api.definition.ParameterEditor -import pl.touk.nussknacker.engine.api.deployment import pl.touk.nussknacker.engine.api.deployment.{ CustomAction, CustomActionDisplayConditionalPolicy, @@ -26,6 +18,7 @@ import pl.touk.nussknacker.engine.graph.EdgeType import pl.touk.nussknacker.engine.graph.evaluatedparam.{Parameter => NodeParameter} import pl.touk.nussknacker.engine.graph.expression.Expression import pl.touk.nussknacker.engine.graph.node.NodeData +import io.circe.generic.extras.{Configuration, ConfiguredJsonCodec} import java.net.URI @@ -156,13 +149,24 @@ package object definition { } + implicit val configuration: Configuration = Configuration.default.withDefaults.withDiscriminator("type") + @JsonCodec final case class UICustomActionParameter(name: String, editor: ParameterEditor) - sealed trait UICustomActionDisplayPolicy + @ConfiguredJsonCodec sealed trait UICustomActionDisplayPolicyExpr + + @JsonCodec case class UIStatusExpr(status: String) extends UICustomActionDisplayPolicyExpr + @JsonCodec case class UINodeExpr(node: String) extends UICustomActionDisplayPolicyExpr + + @ConfiguredJsonCodec sealed trait UICustomActionDisplayPolicy + + @JsonCodec case class UICustomActionDisplaySimplePolicy( + version: Long, + operator: String, + expr: UICustomActionDisplayPolicyExpr + ) extends UICustomActionDisplayPolicy object UICustomActionDisplayPolicy { - implicit val policyEncoder: Encoder[UICustomActionDisplayPolicy] = deriveEncoder - implicit val policyDecoder: Decoder[UICustomActionDisplayPolicy] = deriveDecoder def fromCustomActionDisplayPolicy(displayPolicy: CustomActionDisplayPolicy): UICustomActionDisplayPolicy = displayPolicy match { @@ -179,22 +183,6 @@ package object definition { } - sealed trait UICustomActionDisplayPolicyExpr - - object UICustomActionPolicyExpr { - implicit val exprEncoder: Encoder[UICustomActionDisplayPolicyExpr] = deriveEncoder - implicit val exprDecoder: Decoder[UICustomActionDisplayPolicyExpr] = deriveDecoder - } - - @JsonCodec case class UIStatusExpr(status: String) extends UICustomActionDisplayPolicyExpr - @JsonCodec case class UINodeExpr(node: String) extends UICustomActionDisplayPolicyExpr - - @JsonCodec case class UICustomActionDisplaySimplePolicy( - version: Long, - operator: String, - expr: UICustomActionDisplayPolicyExpr - ) extends UICustomActionDisplayPolicy - @JsonCodec case class UICustomActionDisplayConditionalPolicy( condition: String, operands: List[UICustomActionDisplayPolicy]