Skip to content

Commit

Permalink
Stashed single scenario loading improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mgoworko committed Jan 28, 2025
1 parent 8096bed commit 6bc0258
Show file tree
Hide file tree
Showing 20 changed files with 336 additions and 84 deletions.
21 changes: 16 additions & 5 deletions designer/client/src/actions/nk/fetchVisualizationData.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { ThunkAction } from "../reduxTypes";
import { displayCurrentProcessVersion } from "./process";
import { displayTestCapabilities, fetchStickyNotesForScenario, fetchValidatedProcess } from "./process";
import { fetchProcessDefinition } from "./processDefinitionData";
import { loadProcessToolbarsConfiguration } from "./loadProcessToolbarsConfiguration";
import { ProcessName } from "../../components/Process/types";
import HttpService from "../../http/HttpService";

export function fetchVisualizationData(processName: ProcessName, onSuccess: () => void, onError: (error) => void): ThunkAction {
return async (dispatch) => {
try {
const scenario = await dispatch(displayCurrentProcessVersion(processName));
dispatch({ type: "PROCESS_FETCH" });

const response = await HttpService.fetchLatestProcessDetailsWithoutValidation(processName);
const scenario = response.data;
const { name, isFragment, processingType } = scenario;
await dispatch(loadProcessToolbarsConfiguration(name));
const processDefinitionData = await dispatch(fetchProcessDefinition(processingType, isFragment));
dispatch({ type: "CORRECT_INVALID_SCENARIO", processDefinitionData });
await dispatch(fetchProcessDefinition(processingType, isFragment)).then((processDefinitionData) => {
dispatch({ type: "DISPLAY_PROCESS", scenario: response.data });
dispatch({ type: "CORRECT_INVALID_SCENARIO", processDefinitionData });
});

dispatch(loadProcessToolbarsConfiguration(processName));
dispatch(displayTestCapabilities(processName, response.data.scenarioGraph));
dispatch(fetchStickyNotesForScenario(processName, response.data.processVersionId));
dispatch(fetchValidatedProcess(name));

onSuccess();
return scenario;
} catch (error) {
Expand Down
12 changes: 12 additions & 0 deletions designer/client/src/actions/nk/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ export function fetchProcessToDisplay(processName: ProcessName, versionId?: Proc
};
}

export function fetchValidatedProcess(processName: ProcessName, versionId?: ProcessVersionId): ThunkAction<Promise<Scenario>> {
return (dispatch) => {
return HttpService.fetchProcessDetails(processName, versionId).then((response) => {
dispatch({
type: "DISPLAY_PROCESS",
scenario: response.data,
});
return response.data;
});
};
}

export function loadProcessState(processName: ProcessName, processVersionId: number): ThunkAction {
return (dispatch) =>
HttpService.fetchProcessState(processName, processVersionId).then(({ data }) =>
Expand Down
6 changes: 5 additions & 1 deletion designer/client/src/common/ProcessUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ class ProcessUtils {
return isEmpty(scenario) ? false : !isEmpty(scenario.scenarioGraph.nodes);
};

isValidationResultPresent = (scenario: Scenario) => {
return !!scenario.validationResult;
};

//fixme maybe return hasErrors flag from backend?
hasNeitherErrorsNorWarnings = (scenario: Scenario) => {
return this.hasNoErrors(scenario) && this.hasNoWarnings(scenario);
return !!scenario.validationResult && this.hasNoErrors(scenario) && this.hasNoWarnings(scenario);
};

extractInvalidNodes = (invalidNodes: Pick<ValidationResult, "warnings">) => {
Expand Down
6 changes: 5 additions & 1 deletion designer/client/src/components/tips/Tips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ export default function Tips(props: ToolbarPanelProps): JSX.Element {
renderThumbVertical={(props) => <div key={uuid4()} {...props} />}
hideTracksWhenNotNeeded={true}
>
<ValidTips testing={!!testResults} hasNeitherErrorsNorWarnings={ProcessUtils.hasNeitherErrorsNorWarnings(scenario)} />
<ValidTips
loading={!ProcessUtils.isValidationResultPresent(scenario)}
testing={!!testResults}
hasNeitherErrorsNorWarnings={ProcessUtils.hasNeitherErrorsNorWarnings(scenario)}
/>
{!ProcessUtils.hasNoErrors(scenario) && <Errors errors={errors} showDetails={showDetails} scenario={scenario} />}
{!ProcessUtils.hasNoWarnings(scenario) && (
<Warnings
Expand Down
8 changes: 6 additions & 2 deletions designer/client/src/components/tips/ValidTips.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React from "react";
import TestingMode from "../../assets/img/icons/testingMode.svg";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import HourglassEmptyIcon from "@mui/icons-material/HourglassEmpty";
import ValidTip from "./ValidTip";
import { useTheme } from "@mui/material";

export default function ValidTips(props: { hasNeitherErrorsNorWarnings?: boolean; testing?: boolean }): JSX.Element {
const { hasNeitherErrorsNorWarnings, testing } = props;
export default function ValidTips(props: { loading: boolean; hasNeitherErrorsNorWarnings?: boolean; testing?: boolean }): JSX.Element {
const { loading, hasNeitherErrorsNorWarnings, testing } = props;
const theme = useTheme();

return (
<React.Fragment>
{loading && (
<ValidTip icon={HourglassEmptyIcon} message={"Verifying scenario and loading tips"} color={theme.palette.warning.main} />
)}
{hasNeitherErrorsNorWarnings && (
<ValidTip icon={CheckCircleIcon} message={"Everything seems to be OK"} color={theme.palette.success.main} />
)}
Expand Down
7 changes: 4 additions & 3 deletions designer/client/src/components/toolbars/creator/ToolBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,12 @@ export default function ToolBox(props: ToolBoxProps): JSX.Element {
const pristine = useSelector(isPristine);
const { t } = useTranslation();

const componentGroups: ComponentGroup[] = useMemo(() => processDefinitionData.componentGroups, [processDefinitionData]);
const componentGroups: ComponentGroup[] = useMemo(() => processDefinitionData.componentGroups ?? [], [processDefinitionData]);
const filters = useMemo(() => props.filter?.toLowerCase().split(/\s/).filter(Boolean), [props.filter]);
const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine, props, t]);
const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine]);
const groups = useMemo(() => {
const allComponentGroups = stickyNotesSettings.enabled ? concat(componentGroups, stickyNoteToolGroup) : componentGroups;
const stickyNotesEnabled = stickyNotesSettings ? stickyNotesSettings.enabled : false;
const allComponentGroups = stickyNotesEnabled ? concat(componentGroups, stickyNoteToolGroup) : componentGroups;
return allComponentGroups.map(filterComponentsByLabel(filters)).filter((g) => g.components.length > 0);
}, [componentGroups, filters, stickyNoteToolGroup, stickyNotesSettings]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { useDispatch, useSelector } from "react-redux";
import { disableToolTipsHighlight, enableToolTipsHighlight, loadProcessState } from "../../../../actions/nk";
import Icon from "../../../../assets/img/toolbarButtons/deploy.svg";
import HttpService from "../../../../http/HttpService";
import { getProcessName, hasError, isDeployPossible, isSaveDisabled } from "../../../../reducers/selectors/graph";
import {
getProcessName,
hasError,
isDeployPossible,
isSaveDisabled,
isValidationResultPresent,
} from "../../../../reducers/selectors/graph";
import { getCapabilities } from "../../../../reducers/selectors/other";
import { useWindows } from "../../../../windowManager";
import { WindowKind } from "../../../../windowManager";
Expand All @@ -19,11 +25,12 @@ export default function DeployButton(props: ToolbarButtonProps) {
const deployPossible = useSelector(isDeployPossible);
const saveDisabled = useSelector(isSaveDisabled);
const hasErrors = useSelector(hasError);
const validationResultPresent = useSelector(isValidationResultPresent);
const processName = useSelector(getProcessName);
const capabilities = useSelector(getCapabilities);
const { disabled, type } = props;

const available = !disabled && deployPossible && capabilities.deploy;
const available = validationResultPresent && !disabled && deployPossible && capabilities.deploy;
const { t } = useTranslation();
const deployToolTip = !capabilities.deploy
? t("panels.actions.deploy.tooltips.forbidden", "Deploy forbidden for current scenario.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { useDispatch, useSelector } from "react-redux";
import { loadProcessState } from "../../../../actions/nk";
import Icon from "../../../../assets/img/toolbarButtons/run-off-schedule.svg";
import HttpService from "../../../../http/HttpService";
import { getProcessName, isRunOffSchedulePossible, isRunOffScheduleVisible } from "../../../../reducers/selectors/graph";
import {
getProcessName,
isRunOffSchedulePossible,
isRunOffScheduleVisible,
isValidationResultPresent,
} from "../../../../reducers/selectors/graph";
import { getCapabilities } from "../../../../reducers/selectors/other";
import { useWindows, WindowKind } from "../../../../windowManager";
import { ToggleProcessActionModalData } from "../../../modals/DeployProcessDialog";
Expand All @@ -21,11 +26,12 @@ export default function RunOffScheduleButton(props: ToolbarButtonProps) {
const dispatch = useDispatch();
const { disabled, type } = props;
const scenarioState = useSelector((state: RootState) => getProcessState(state));
const validationResultPresent = useSelector(isValidationResultPresent);
const isVisible = useSelector(isRunOffScheduleVisible);
const isPossible = useSelector(isRunOffSchedulePossible);
const processName = useSelector(getProcessName);
const capabilities = useSelector(getCapabilities);
const available = !disabled && isPossible && capabilities.deploy;
const available = validationResultPresent && !disabled && isPossible && capabilities.deploy;

const { open } = useWindows();
const action = (name: ProcessName, versionId: ProcessVersionId, comment: string) =>
Expand Down
8 changes: 8 additions & 0 deletions designer/client/src/http/HttpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ class HttpService {
return api.get<Scenario>(url);
}

fetchLatestProcessDetailsWithoutValidation(processName: ProcessName, versionId?: ProcessVersionId): Promise<AxiosResponse<Scenario>> {
const id = encodeURIComponent(processName);
const url = versionId
? `/processes/${id}/${versionId}?skipValidateAndResolve=true`
: `/processes/${id}?skipValidateAndResolve=true`;
return api.get<Scenario>(url);
}

fetchProcessesStates() {
return api
.get<StatusesType>("/processes/status")
Expand Down
3 changes: 2 additions & 1 deletion designer/client/src/reducers/selectors/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const isLatestProcessVersion = createSelector(getScenario, (d) => d?.isLa
export const isFragment = createSelector(getScenario, (p) => p?.isFragment);
export const isArchived = createSelector(getScenario, (p) => p?.isArchived);
export const isPristine = (state: RootState): boolean => ProcessUtils.nothingToSave(state) && !isProcessRenamed(state);
export const isValidationResultPresent = createSelector(getScenario, (p) => ProcessUtils.isValidationResultPresent(p));
export const hasError = createSelector(getScenario, (p) => !ProcessUtils.hasNoErrors(p));
export const hasWarnings = createSelector(getScenario, (p) => !ProcessUtils.hasNoWarnings(p));
export const hasPropertiesErrors = createSelector(getScenario, (p) => !ProcessUtils.hasNoPropertiesErrors(p));
Expand Down Expand Up @@ -78,7 +79,7 @@ export const getTestResults = createSelector(getGraph, (g) => g.testResults);
export const getProcessCountsRefresh = createSelector(getGraph, (g) => g.processCountsRefresh || null);
export const getProcessCounts = createSelector(getGraph, (g): ProcessCounts => g.processCounts || ({} as ProcessCounts));
export const getStickyNotes = createSelector([getGraph, getStickyNotesSettings], (g, settings) =>
settings.enabled ? g.stickyNotes || ([] as StickyNote[]) : ([] as StickyNote[]),
(settings ? settings.enabled : false) ? g.stickyNotes || ([] as StickyNote[]) : ([] as StickyNote[]),
);
export const getShowRunProcessDetails = createSelector(
[getTestResults, getProcessCounts],
Expand Down
2 changes: 1 addition & 1 deletion designer/client/src/reducers/selectors/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getProcessDefinitionData = createDeepEqualSelector(
getSettings,
(s) => s.processDefinitionData || ({} as ProcessDefinitionData),
);
export const getComponentGroups = createSelector(getProcessDefinitionData, (p) => p.componentGroups || ({} as ComponentGroup[]));
export const getComponentGroups = createSelector(getProcessDefinitionData, (p) => p.componentGroups || []);
export const getCategories = createSelector(getLoggedUser, (u) => u.categories || []);
export const getWritableCategories = createSelector(getLoggedUser, getCategories, (user, categories) =>
categories.filter((c) => user.canWrite(c)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package pl.touk.nussknacker.ui.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;

// In Scala 2.12 it is impossible to create Caffeine cache with generic types for key and/or value using purely Scala code.
// The type inference does not work as expected. This util written in Java is needed to bypass that problem.
public class GenericCaffeineCache<K, V> {
private final Cache<K, V> cache;

public GenericCaffeineCache(Duration ttl) {
this.cache = Caffeine.newBuilder().expireAfterWrite(ttl).build();
}

public Cache<K, V> getCache() {
return this.cache;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ class TestingApiHttpService(
capabilities = scenarioTestService.getTestingCapabilities(
scenarioGraph,
scenarioWithDetails.processVersionUnsafe,
scenarioWithDetails.isFragment,
)
} yield capabilities
}
Expand All @@ -93,7 +92,6 @@ class TestingApiHttpService(
parametersDefinition = scenarioTestService.testUISourceParametersDefinition(
scenarioGraph,
scenarioWithDetails.processVersionUnsafe,
scenarioWithDetails.isFragment
)
} yield parametersDefinition
}
Expand Down
Loading

0 comments on commit 6bc0258

Please sign in to comment.