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

Scenario visualization loading improvements - part 1 - FE changes #7515

Open
wants to merge 10 commits into
base: staging
Choose a base branch
from
4 changes: 2 additions & 2 deletions designer/client/cypress/support/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ function visitProcess(processName: string) {
}

function visitNewProcess(name?: string, fixture?: string, category?: string) {
cy.intercept("GET", "/api/processes/*").as("fetch");
cy.intercept("POST", "/api/processValidation/*").as("fetch");
return cy.createTestProcess(name, fixture, category).then((processName) => {
return cy.visitProcess(processName);
});
}

function visitNewFragment(name?: string, fixture?: string, category?: string) {
cy.intercept("GET", "/api/processes/*").as("fetch");
cy.intercept("POST", "/api/processValidation/*").as("fetch");
return cy.createTestFragment(name, fixture, category).then((processName) => {
return cy.visitProcess(processName);
});
Expand Down
20 changes: 15 additions & 5 deletions designer/client/src/actions/nk/fetchVisualizationData.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
import { ThunkAction } from "../reduxTypes";
import { displayCurrentProcessVersion } from "./process";
import { displayTestCapabilities, fetchStickyNotesForScenario } 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: scenario });
dispatch({ type: "CORRECT_INVALID_SCENARIO", processDefinitionData });
});
dispatch(loadProcessToolbarsConfiguration(name));
dispatch(displayTestCapabilities(name, scenario.scenarioGraph));
dispatch(fetchStickyNotesForScenario(name, scenario.processVersionId));
HttpService.validateProcess(name, name, scenario.scenarioGraph).then(({ data }) =>
dispatch({ type: "VALIDATION_RESULT", validationResult: data }),
);
onSuccess();
return scenario;
} catch (error) {
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) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO Boolean(scenario.validationResult) is more readable

return Boolean(scenario.validationResult);
};

//fixme maybe return hasErrors flag from backend?
hasNeitherErrorsNorWarnings = (scenario: Scenario) => {
return this.hasNoErrors(scenario) && this.hasNoWarnings(scenario);
return this.isValidationResultPresent(scenario) && 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
11 changes: 8 additions & 3 deletions designer/client/src/components/tips/ValidTips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import React from "react";
import TestingMode from "../../assets/img/icons/testingMode.svg";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import ValidTip from "./ValidTip";
import { useTheme } from "@mui/material";
import { Box, CircularProgress, 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 && (
<Box display={"flex"} justifyContent={"center"} height={"100%"} alignItems={"center"}>
<CircularProgress />
</Box>
)}
{hasNeitherErrorsNorWarnings && (
<ValidTip icon={CheckCircleIcon} message={"Everything seems to be OK"} color={theme.palette.success.main} />
)}
Expand Down
4 changes: 2 additions & 2 deletions designer/client/src/components/toolbars/creator/ToolBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ 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;
return allComponentGroups.map(filterComponentsByLabel(filters)).filter((g) => g.components.length > 0);
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 @@ -284,6 +284,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
6 changes: 4 additions & 2 deletions 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 @@ -77,8 +78,9 @@ export const getTestParameters = createSelector(getGraph, (g) => g.testFormParam
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[]),
export const getStickyNotes = createSelector(
[getGraph, getStickyNotesSettings],
(g, settings) => (settings?.enabled ? 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
1 change: 1 addition & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
* For all - default `windowLength` is 1 hour
* For `aggregate-session` - default `endSessionCondition` is now false
* Improved scenario visualization loading time
* [#7453](https://github.com/TouK/nussknacker/pull/7453) optimized and rearranged API calls and GUI loading order
* [#7516](https://github.com/TouK/nussknacker/pull/7516) Scenario testing endpoints no longer perform full scenario compilation and validation
* [#7522](https://github.com/TouK/nussknacker/pull/7522) Improved fetching UI Components: faster resolving of fragments, optimized db query for fetching fragments
* [#7524](https://github.com/TouK/nussknacker/pull/7524) Add a possibility to choose a new valid value in node details when inconsistencies in parameter's definition were detected.
Expand Down