## Description Evaluation split changes for EE. 1. RequiresLinting function has moved to common place - on EE extra checks will be added 2. DataTreeFactory - getActionsForCurrentPage changed to getCurrentActions -- which will be modified on EE to acomodate package actions 3. same as above for getJSCollectionsForCurrentPage --> changed to getCurrentJSCollections #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > > #### Type of change - Chore (housekeeping or task changes that don't impact user perception) > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
347 lines
10 KiB
TypeScript
347 lines
10 KiB
TypeScript
import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers";
|
|
import type { AppState } from "@appsmith/reducers";
|
|
import { createSelector } from "reselect";
|
|
import { getUserApplicationsWorkspaces } from "@appsmith/selectors/applicationSelectors";
|
|
import { getWidgets } from "sagas/selectors";
|
|
import {
|
|
getActionResponses,
|
|
getActions,
|
|
getCurrentActions,
|
|
getCanvasWidgets,
|
|
} from "@appsmith/selectors/entitiesSelector";
|
|
import { getLastSelectedWidget } from "./ui";
|
|
import { GuidedTourEntityNames } from "pages/Editor/GuidedTour/constants";
|
|
import type { SIGNPOSTING_STEP } from "pages/Editor/FirstTimeUserOnboarding/Utils";
|
|
import { isBoolean, intersection } from "lodash";
|
|
import { getEvaluationInverseDependencyMap } from "./dataTreeSelectors";
|
|
import { getNestedValue } from "pages/Editor/utils";
|
|
import { getDependenciesFromInverseDependencies } from "components/editorComponents/Debugger/helpers";
|
|
|
|
// Signposting selectors
|
|
|
|
export const getFirstTimeUserOnboardingApplicationIds = (state: AppState) => {
|
|
return state.ui.onBoarding.firstTimeUserOnboardingApplicationIds;
|
|
};
|
|
|
|
export const getFirstTimeUserOnboardingComplete = (state: AppState) => {
|
|
return state.ui.onBoarding.firstTimeUserOnboardingComplete;
|
|
};
|
|
|
|
export const getFirstTimeUserOnboardingModal = (state: AppState) =>
|
|
state.ui.onBoarding.showFirstTimeUserOnboardingModal;
|
|
|
|
export const getIsFirstTimeUserOnboardingEnabled = createSelector(
|
|
(state: AppState) => state.entities.pageList.applicationId,
|
|
getFirstTimeUserOnboardingApplicationIds,
|
|
(currentApplicationId, applicationIds) => {
|
|
return applicationIds.includes(currentApplicationId);
|
|
},
|
|
);
|
|
|
|
export const getInOnboardingWidgetSelection = (state: AppState) =>
|
|
state.ui.onBoarding.inOnboardingWidgetSelection;
|
|
|
|
export const getIsOnboardingWidgetSelection = (state: AppState) =>
|
|
state.ui.onBoarding.inOnboardingWidgetSelection;
|
|
|
|
export const getSignpostingStepState = (state: AppState) =>
|
|
state.ui.onBoarding.stepState;
|
|
export const getSignpostingStepStateByStep = createSelector(
|
|
getSignpostingStepState,
|
|
(_state: AppState, step: SIGNPOSTING_STEP) => step,
|
|
(stepState, step) => {
|
|
return stepState.find((state) => state.step === step);
|
|
},
|
|
);
|
|
export const getSignpostingUnreadSteps = createSelector(
|
|
getSignpostingStepState,
|
|
(stepState) => {
|
|
if (!stepState.length) return [];
|
|
return stepState.filter((state) => isBoolean(state.read) && !state.read);
|
|
},
|
|
);
|
|
export const getSignpostingSetOverlay = (state: AppState) =>
|
|
state.ui.onBoarding.setOverlay;
|
|
export const getSignpostingTooltipVisible = (state: AppState) =>
|
|
state.ui.onBoarding.showSignpostingTooltip;
|
|
export const getIsAnonymousDataPopupVisible = (state: AppState) =>
|
|
state.ui.onBoarding.showAnonymousDataPopup;
|
|
export const isWidgetActionConnectionPresent = createSelector(
|
|
getCanvasWidgets,
|
|
getCurrentActions,
|
|
getEvaluationInverseDependencyMap,
|
|
(widgets, actions, deps) => {
|
|
const actionLables = actions.map((action: any) => action.config.name);
|
|
|
|
let isBindingAvailable = !!Object.values(widgets).find((widget: any) => {
|
|
const depsConnections = getDependenciesFromInverseDependencies(
|
|
deps,
|
|
widget.widgetName,
|
|
);
|
|
return !!intersection(depsConnections?.directDependencies, actionLables)
|
|
.length;
|
|
});
|
|
|
|
if (!isBindingAvailable) {
|
|
isBindingAvailable = !!Object.values(widgets).find((widget: any) => {
|
|
return (
|
|
widget.dynamicTriggerPathList &&
|
|
!!widget.dynamicTriggerPathList.find((path: { key: string }) => {
|
|
return !!actionLables.find((label: string) => {
|
|
const snippet = getNestedValue(widget, path.key);
|
|
return snippet ? snippet.indexOf(`${label}.run`) > -1 : false;
|
|
});
|
|
})
|
|
);
|
|
});
|
|
}
|
|
return isBindingAvailable;
|
|
},
|
|
);
|
|
|
|
// Guided Tour selectors
|
|
export const isExploringSelector = (state: AppState) =>
|
|
state.ui.guidedTour.exploring;
|
|
export const inGuidedTour = (state: AppState) => state.ui.guidedTour.guidedTour;
|
|
export const getCurrentStep = (state: AppState) =>
|
|
state.ui.guidedTour.currentStep;
|
|
export const wasTableWidgetSelected = (state: AppState) =>
|
|
state.ui.guidedTour.tableWidgetWasSelected;
|
|
export const showEndTourDialogSelector = (state: AppState) =>
|
|
state.ui.guidedTour.showEndTourDialog;
|
|
export const showDeviatingDialogSelector = (state: AppState) =>
|
|
state.ui.guidedTour.showDeviatingDialog;
|
|
export const showPostCompletionMessage = (state: AppState) =>
|
|
state.ui.guidedTour.showPostCompletionMessage;
|
|
export const forceShowContentSelector = (state: AppState) =>
|
|
state.ui.guidedTour.forceShowContent;
|
|
|
|
export const getTableWidget = createSelector(getWidgets, (widgets) => {
|
|
return Object.values(widgets).find(
|
|
(widget) => widget.widgetName === "CustomersTable",
|
|
);
|
|
});
|
|
|
|
export const getQueryAction = createSelector(getActions, (actions) => {
|
|
return actions.find((action) => {
|
|
return action.config.name === "getCustomers";
|
|
});
|
|
});
|
|
|
|
export const isQueryLimitUpdated = createSelector(getQueryAction, (query) => {
|
|
if (query) {
|
|
let body = query.config.actionConfiguration.body;
|
|
if (body) {
|
|
// eslint-disable-next-line no-console
|
|
const regex = /SELECT \* FROM user_data ORDER BY id LIMIT 10;/gi;
|
|
// Replacing new line characters
|
|
body = body.replace(/(?:\r\n|\r|\n)/g, "");
|
|
// Replace sql comments
|
|
body = body.replace(/(\/\*[^*]*\*\/)|(\/\/[^*]*)|(--[^.].*)/gm, "");
|
|
return regex.test(body);
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
export const isQueryExecutionSuccessful = createSelector(
|
|
getActionResponses,
|
|
getQueryAction,
|
|
(responses, query) => {
|
|
if (query?.config.id && responses[query.config.id]) {
|
|
return responses[query.config.id]?.isExecutionSuccess;
|
|
}
|
|
},
|
|
);
|
|
|
|
export const isTableWidgetSelected = createSelector(
|
|
getTableWidget,
|
|
getLastSelectedWidget,
|
|
wasTableWidgetSelected,
|
|
(tableWidget, selectedWidgetId, tableWidgetWasSelected) => {
|
|
if (!tableWidgetWasSelected) {
|
|
return tableWidget?.widgetId === selectedWidgetId;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
);
|
|
|
|
export const tableWidgetHasBinding = createSelector(
|
|
getTableWidget,
|
|
(tableWidget) => {
|
|
if (tableWidget) {
|
|
if (tableWidget.tableData === `{{getCustomers.data}}`) {
|
|
return tableWidget.widgetId;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
},
|
|
);
|
|
|
|
export const containerWidgetAdded = createSelector(getWidgets, (widgets) => {
|
|
return !!Object.values(widgets).find(
|
|
(widget) => widget.type === "CONTAINER_WIDGET",
|
|
);
|
|
});
|
|
|
|
export const getHadReachedStep = (state: AppState) =>
|
|
state.ui.guidedTour.hadReachedStep;
|
|
|
|
export const isNameInputBoundSelector = createSelector(
|
|
getTableWidget,
|
|
getWidgets,
|
|
(tableWidget, widgets) => {
|
|
if (tableWidget) {
|
|
const widgetValues = Object.values(widgets);
|
|
const countryInput = widgetValues.find((widget) => {
|
|
if (widget.type === "INPUT_WIDGET_V2") {
|
|
return (
|
|
widget.defaultText ===
|
|
`{{${tableWidget.widgetName}.selectedRow.name}}`
|
|
);
|
|
}
|
|
return false;
|
|
});
|
|
|
|
if (countryInput) return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
);
|
|
|
|
// Get the id of NameInput
|
|
export const nameInputSelector = createSelector(getWidgets, (widgets) => {
|
|
const widgetValues = Object.values(widgets);
|
|
const nameInput = widgetValues.find((widget) => {
|
|
if (widget.type === "INPUT_WIDGET_V2") {
|
|
return widget.widgetName === "NameInput";
|
|
}
|
|
});
|
|
|
|
return nameInput ? nameInput.widgetId : "";
|
|
});
|
|
// Check if CountryInput is selected
|
|
export const countryInputSelector = createSelector(
|
|
getWidgets,
|
|
getLastSelectedWidget,
|
|
(widgets, selectedWidgetId) => {
|
|
const widgetValues = Object.values(widgets);
|
|
const countryInput = widgetValues.find((widget) => {
|
|
if (widget.type === "INPUT_WIDGET_V2") {
|
|
return widget.widgetName === "CountryInput";
|
|
}
|
|
});
|
|
|
|
return countryInput ? countryInput.widgetId === selectedWidgetId : false;
|
|
},
|
|
);
|
|
|
|
export const isCountryInputBound = createSelector(
|
|
getTableWidget,
|
|
getWidgets,
|
|
(tableWidget, widgets) => {
|
|
if (tableWidget) {
|
|
const widgetValues = Object.values(widgets);
|
|
const countryInput = widgetValues.find((widget) => {
|
|
if (widget.widgetName === GuidedTourEntityNames.COUNTRY_INPUT) {
|
|
return (
|
|
widget.defaultText ===
|
|
`{{${tableWidget.widgetName}.selectedRow.country}}`
|
|
);
|
|
}
|
|
return false;
|
|
});
|
|
|
|
if (countryInput) return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
);
|
|
|
|
export const isEmailInputBound = createSelector(
|
|
getTableWidget,
|
|
getWidgets,
|
|
(tableWidget, widgets) => {
|
|
if (tableWidget) {
|
|
const widgetValues = Object.values(widgets);
|
|
const countryInput = widgetValues.find((widget) => {
|
|
if (widget.widgetName === GuidedTourEntityNames.EMAIL_INPUT) {
|
|
return (
|
|
widget.defaultText ===
|
|
`{{${tableWidget.widgetName}.selectedRow.email}}`
|
|
);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
if (countryInput) return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
);
|
|
|
|
export const isButtonWidgetPresent = createSelector(getWidgets, (widgets) => {
|
|
const widgetValues = Object.values(widgets);
|
|
const buttonWidget = widgetValues.find((widget) => {
|
|
return widget.type === "BUTTON_WIDGET";
|
|
});
|
|
|
|
return !!buttonWidget;
|
|
});
|
|
|
|
export const buttonWidgetHasOnClickBinding = createSelector(
|
|
getWidgets,
|
|
(widgets) => {
|
|
const widgetValues = Object.values(widgets);
|
|
const buttonWidget = widgetValues.find((widget) => {
|
|
return (
|
|
widget.type === "BUTTON_WIDGET" &&
|
|
widget.onClick &&
|
|
widget.onClick.includes("{{updateCustomerInfo.run(")
|
|
);
|
|
});
|
|
|
|
return !!buttonWidget;
|
|
},
|
|
);
|
|
|
|
export const buttonWidgetHasOnSuccessBinding = createSelector(
|
|
getWidgets,
|
|
(widgets) => {
|
|
const widgetValues = Object.values(widgets);
|
|
const buttonWidget = widgetValues.find((widget) => {
|
|
return (
|
|
widget.type === "BUTTON_WIDGET" &&
|
|
widget.onClick &&
|
|
widget.onClick.includes("getCustomers.run()")
|
|
);
|
|
});
|
|
|
|
return !!buttonWidget;
|
|
},
|
|
);
|
|
|
|
export const showSuccessMessage = (state: AppState) =>
|
|
state.ui.guidedTour.showSuccessMessage;
|
|
export const showInfoMessageSelector = (state: AppState) =>
|
|
state.ui.guidedTour.showInfoMessage;
|
|
|
|
export const loading = (state: AppState) => state.ui.guidedTour.loading;
|
|
|
|
// To find an workspace where the user has permission to create an
|
|
// application
|
|
export const getOnboardingWorkspaces = createSelector(
|
|
getUserApplicationsWorkspaces,
|
|
(userWorkspaces) => {
|
|
return userWorkspaces.filter((userWorkspace) =>
|
|
hasCreateNewAppPermission(userWorkspace.workspace.userPermissions ?? []),
|
|
);
|
|
},
|
|
);
|