## Description This PR adds JS function data to autocompletion hints Fixes #15909 <img width="278" alt="Screenshot 2023-01-16 at 20 35 55" src="https://user-images.githubusercontent.com/46670083/212754461-68844350-5d23-4b50-af1f-675b7719dc49.png"> ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? 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: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test
938 lines
28 KiB
TypeScript
938 lines
28 KiB
TypeScript
import { AppState } from "@appsmith/reducers";
|
|
import {
|
|
ActionData,
|
|
ActionDataState,
|
|
} from "reducers/entityReducers/actionsReducer";
|
|
import { ActionResponse } from "api/ActionAPI";
|
|
import { createSelector } from "reselect";
|
|
import {
|
|
Datasource,
|
|
MockDatasource,
|
|
DatasourceStructure,
|
|
isEmbeddedRestDatasource,
|
|
} from "entities/Datasource";
|
|
import { Action, PluginType } from "entities/Action";
|
|
import { find, get, sortBy } from "lodash";
|
|
import ImageAlt from "assets/images/placeholder-image.svg";
|
|
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
|
import { AppStoreState } from "reducers/entityReducers/appReducer";
|
|
import { JSCollectionDataState } from "reducers/entityReducers/jsActionsReducer";
|
|
import { DefaultPlugin, GenerateCRUDEnabledPluginMap } from "api/PluginApi";
|
|
import { JSAction, JSCollection } from "entities/JSCollection";
|
|
import { APP_MODE } from "entities/App";
|
|
import { ExplorerFileEntity } from "@appsmith/pages/Editor/Explorer/helpers";
|
|
import { ActionValidationConfigMap } from "constants/PropertyControlConstants";
|
|
import { selectFeatureFlags } from "./usersSelectors";
|
|
import {
|
|
EvaluationError,
|
|
EVAL_ERROR_PATH,
|
|
PropertyEvaluationErrorType,
|
|
} from "utils/DynamicBindingUtils";
|
|
|
|
import { InstallState } from "reducers/uiReducers/libraryReducer";
|
|
import recommendedLibraries from "pages/Editor/Explorer/Libraries/recommendedLibraries";
|
|
import { TJSLibrary } from "workers/common/JSLibrary";
|
|
import { getEntityNameAndPropertyPath } from "@appsmith/workers/Evaluation/evaluationUtils";
|
|
|
|
export const getEntities = (state: AppState): AppState["entities"] =>
|
|
state.entities;
|
|
|
|
export const getDatasources = (state: AppState): Datasource[] => {
|
|
return state.entities.datasources.list;
|
|
};
|
|
|
|
export const getDatasourcesStructure = (
|
|
state: AppState,
|
|
): Record<string, DatasourceStructure> => {
|
|
return state.entities.datasources.structure;
|
|
};
|
|
|
|
export const getIsFetchingDatasourceStructure = (state: AppState): boolean => {
|
|
return state.entities.datasources.fetchingDatasourceStructure;
|
|
};
|
|
|
|
export const getMockDatasources = (state: AppState): MockDatasource[] => {
|
|
return state.entities.datasources.mockDatasourceList;
|
|
};
|
|
|
|
export const getDefaultPlugins = (state: AppState): DefaultPlugin[] =>
|
|
state.entities.plugins.defaultPluginList;
|
|
|
|
// Get plugin by id or package name
|
|
export const getDefaultPlugin = (
|
|
state: AppState,
|
|
pluginIdentifier: string,
|
|
): DefaultPlugin | undefined => {
|
|
return state.entities.plugins.defaultPluginList.find(
|
|
(plugin) =>
|
|
plugin.packageName === pluginIdentifier || plugin.id === pluginIdentifier,
|
|
);
|
|
};
|
|
|
|
export const getPluginIdsOfNames = (
|
|
state: AppState,
|
|
names: Array<string>,
|
|
): Array<string> | undefined => {
|
|
const plugins = state.entities.plugins.list.filter((plugin) =>
|
|
names.includes(plugin.name),
|
|
);
|
|
const pluginIds = plugins.map((plugin) => plugin.id);
|
|
|
|
if (!pluginIds.length) return undefined;
|
|
return pluginIds;
|
|
};
|
|
|
|
export const getPluginIdsOfPackageNames = (
|
|
state: AppState,
|
|
names: Array<string>,
|
|
): Array<string> | undefined => {
|
|
const plugins = state.entities.plugins.list.filter((plugin) =>
|
|
names.includes(plugin.packageName),
|
|
);
|
|
const pluginIds = plugins.map((plugin) => plugin.id);
|
|
|
|
if (!pluginIds.length) return undefined;
|
|
return pluginIds;
|
|
};
|
|
|
|
export const getPluginNameFromDatasourceId = (
|
|
state: AppState,
|
|
datasourceId: string,
|
|
): string | undefined => {
|
|
const datasource = state.entities.datasources.list.find(
|
|
(datasource) => datasource.id === datasourceId,
|
|
);
|
|
const plugin = state.entities.plugins.list.find(
|
|
(plugin) => plugin.id === datasource?.pluginId,
|
|
);
|
|
|
|
if (!plugin) return undefined;
|
|
return plugin.name;
|
|
};
|
|
|
|
export const getPluginPackageFromDatasourceId = (
|
|
state: AppState,
|
|
datasourceId: string,
|
|
): string | undefined => {
|
|
const datasource = state.entities.datasources.list.find(
|
|
(datasource) => datasource.id === datasourceId,
|
|
);
|
|
const plugin = state.entities.plugins.list.find(
|
|
(plugin) => plugin.id === datasource?.pluginId,
|
|
);
|
|
|
|
if (!plugin) return undefined;
|
|
return plugin.packageName;
|
|
};
|
|
|
|
export const getPluginNameFromId = (
|
|
state: AppState,
|
|
pluginId: string,
|
|
): string => {
|
|
const plugin = state.entities.plugins.list.find(
|
|
(plugin) => plugin.id === pluginId,
|
|
);
|
|
|
|
if (!plugin) return "";
|
|
return plugin.name;
|
|
};
|
|
|
|
export const getPluginTypeFromDatasourceId = (
|
|
state: AppState,
|
|
datasourceId: string,
|
|
): PluginType | undefined => {
|
|
const datasource = state.entities.datasources.list.find(
|
|
(datasource) => datasource.id === datasourceId,
|
|
);
|
|
const plugin = state.entities.plugins.list.find(
|
|
(plugin) => plugin.id === datasource?.pluginId,
|
|
);
|
|
|
|
if (!plugin) return undefined;
|
|
return plugin.type;
|
|
};
|
|
|
|
export const getPluginForm = (state: AppState, pluginId: string): any[] => {
|
|
return state.entities.plugins.formConfigs[pluginId];
|
|
};
|
|
export const getIsFetchingSinglePluginForm = (
|
|
state: AppState,
|
|
pluginId: string,
|
|
): boolean => {
|
|
return !!state.entities.plugins.fetchingSinglePluginForm[pluginId];
|
|
};
|
|
|
|
export const getIsExecutingDatasourceQuery = (state: AppState): boolean => {
|
|
return state.entities.datasources.executingDatasourceQuery;
|
|
};
|
|
|
|
export const getIsDatasourceTesting = (state: AppState): boolean => {
|
|
return state.entities.datasources.isTesting;
|
|
};
|
|
|
|
export const getEditorConfig = (state: AppState, pluginId: string): any[] => {
|
|
return state.entities.plugins.editorConfigs[pluginId];
|
|
};
|
|
|
|
export const getSettingConfig = (state: AppState, pluginId: string): any[] => {
|
|
return state.entities.plugins.settingConfigs[pluginId];
|
|
};
|
|
|
|
export const getDatasourceFormButtonConfig = (
|
|
state: AppState,
|
|
pluginId: string,
|
|
): string[] => {
|
|
return state.entities.plugins.datasourceFormButtonConfigs[pluginId];
|
|
};
|
|
|
|
export const getActions = (state: AppState): ActionDataState =>
|
|
state.entities.actions;
|
|
|
|
export const getJSCollections = (state: AppState): JSCollectionDataState =>
|
|
state.entities.jsActions;
|
|
|
|
export const getDatasource = (
|
|
state: AppState,
|
|
datasourceId: string,
|
|
): Datasource | undefined =>
|
|
state.entities.datasources.list.find(
|
|
(datasource) => datasource.id === datasourceId,
|
|
);
|
|
|
|
export const getDatasourceDrafts = (state: AppState) => {
|
|
return state.ui.datasourcePane.drafts;
|
|
};
|
|
|
|
export const getDatasourceDraft = (state: AppState, id: string) => {
|
|
const drafts = state.ui.datasourcePane.drafts;
|
|
if (id in drafts) return drafts[id];
|
|
return {};
|
|
};
|
|
|
|
export const getDatasourceActionRouteInfo = (state: AppState) => {
|
|
return state.ui.datasourcePane.actionRouteInfo;
|
|
};
|
|
|
|
export const getDatasourcesByPluginId = (
|
|
state: AppState,
|
|
id: string,
|
|
): Datasource[] => {
|
|
return state.entities.datasources.list.filter((d) => d.pluginId === id);
|
|
};
|
|
|
|
export const getPlugins = (state: AppState) => state.entities.plugins.list;
|
|
|
|
export const getPluginByPackageName = (state: AppState, name: string) =>
|
|
state.entities.plugins.list.find((p) => p.packageName === name);
|
|
|
|
export const getPluginEditorConfigs = (state: AppState) =>
|
|
state.entities.plugins.editorConfigs;
|
|
|
|
export const getPluginDependencyConfig = (state: AppState) =>
|
|
state.entities.plugins.dependencies;
|
|
|
|
export const getPluginSettingConfigs = (state: AppState, pluginId: string) =>
|
|
state.entities.plugins.settingConfigs[pluginId];
|
|
|
|
export const getDBPlugins = createSelector(getPlugins, (plugins) =>
|
|
plugins.filter((plugin) => plugin.type === PluginType.DB),
|
|
);
|
|
|
|
export const getDBAndRemotePlugins = createSelector(getPlugins, (plugins) =>
|
|
plugins.filter(
|
|
(plugin) =>
|
|
plugin.type === PluginType.DB || plugin.type === PluginType.REMOTE,
|
|
),
|
|
);
|
|
|
|
export const getUnconfiguredDatasources = (state: AppState) =>
|
|
state.entities.datasources.unconfiguredList ?? [];
|
|
|
|
export const getDatasourceByPluginId = (state: AppState, pluginId: string) =>
|
|
state.entities.datasources.list.filter((d) => d.pluginId === pluginId);
|
|
|
|
export const getDBDatasources = createSelector(
|
|
getDBPlugins,
|
|
getEntities,
|
|
(dbPlugins, entities) => {
|
|
const datasources = entities.datasources.list;
|
|
const dbPluginIds = dbPlugins.map((plugin) => plugin.id);
|
|
|
|
return datasources.filter((datasource) =>
|
|
dbPluginIds.includes(datasource.pluginId),
|
|
);
|
|
},
|
|
);
|
|
|
|
export const getDBAndRemoteDatasources = createSelector(
|
|
getDBAndRemotePlugins,
|
|
getEntities,
|
|
(plugins, entities) => {
|
|
const datasources = entities.datasources.list;
|
|
const pluginIds = plugins.map((plugin) => plugin.id);
|
|
|
|
return datasources.filter((datasource) =>
|
|
pluginIds.includes(datasource.pluginId),
|
|
);
|
|
},
|
|
);
|
|
|
|
export const getQueryName = (state: AppState, actionId: string): string => {
|
|
const action = state.entities.actions.find((action: ActionData) => {
|
|
return action.config.id === actionId;
|
|
});
|
|
|
|
return action?.config.name ?? "";
|
|
};
|
|
|
|
const getCurrentPageId = (state: AppState) =>
|
|
state.entities.pageList.currentPageId;
|
|
|
|
export const getDatasourcePlugins = createSelector(getPlugins, (plugins) => {
|
|
return plugins.filter((plugin) => plugin?.allowUserDatasources ?? true);
|
|
});
|
|
|
|
export const getPluginImages = createSelector(getPlugins, (plugins) => {
|
|
const pluginImages: Record<string, string> = {};
|
|
|
|
plugins.forEach((plugin) => {
|
|
pluginImages[plugin.id] = plugin?.iconLocation ?? ImageAlt;
|
|
});
|
|
|
|
return pluginImages;
|
|
});
|
|
|
|
export const getPluginNames = createSelector(getPlugins, (plugins) => {
|
|
const pluginNames: Record<string, string> = {};
|
|
|
|
plugins.forEach((plugin) => {
|
|
pluginNames[plugin.id] = plugin?.name;
|
|
});
|
|
|
|
return pluginNames;
|
|
});
|
|
|
|
export const getPluginTemplates = createSelector(getPlugins, (plugins) => {
|
|
const pluginTemplates: Record<string, any> = {};
|
|
|
|
plugins.forEach((plugin) => {
|
|
pluginTemplates[plugin.id] = plugin.templates;
|
|
});
|
|
|
|
return pluginTemplates;
|
|
});
|
|
|
|
export const getPluginResponseTypes = createSelector(getPlugins, (plugins) => {
|
|
const pluginResponseTypes: Record<string, any> = {};
|
|
|
|
plugins.forEach((plugin) => {
|
|
pluginResponseTypes[plugin.id] = plugin.responseType;
|
|
});
|
|
|
|
return pluginResponseTypes;
|
|
});
|
|
|
|
export const getPluginDocumentationLinks = createSelector(
|
|
getPlugins,
|
|
(plugins) => {
|
|
const pluginDocumentationLinks: Record<string, string | undefined> = {};
|
|
|
|
plugins.forEach((plugin) => {
|
|
pluginDocumentationLinks[plugin.id] = plugin.documentationLink;
|
|
});
|
|
|
|
return pluginDocumentationLinks;
|
|
},
|
|
);
|
|
|
|
export const getGenerateCRUDEnabledPluginMap = createSelector(
|
|
getPlugins,
|
|
(plugins) => {
|
|
const pluginIdGenerateCRUDPageEnabled: GenerateCRUDEnabledPluginMap = {};
|
|
plugins.map((plugin) => {
|
|
if (plugin.generateCRUDPageComponent) {
|
|
pluginIdGenerateCRUDPageEnabled[plugin.id] = plugin.packageName;
|
|
}
|
|
});
|
|
return pluginIdGenerateCRUDPageEnabled;
|
|
},
|
|
);
|
|
|
|
export const getActionsForCurrentPage = createSelector(
|
|
getCurrentPageId,
|
|
getActions,
|
|
(pageId, actions) => {
|
|
if (!pageId) return [];
|
|
return actions.filter((a) => a.config.pageId === pageId);
|
|
},
|
|
);
|
|
|
|
export const getJSCollectionsForCurrentPage = createSelector(
|
|
getCurrentPageId,
|
|
getJSCollections,
|
|
(pageId, actions) => {
|
|
if (!pageId) return [];
|
|
return actions.filter((a) => a.config.pageId === pageId);
|
|
},
|
|
);
|
|
|
|
export const getPlugin = (state: AppState, pluginId: string) => {
|
|
return state.entities.plugins.list.find((plugin) => plugin.id === pluginId);
|
|
};
|
|
|
|
export const getActionResponses = createSelector(getActions, (actions) => {
|
|
const responses: Record<string, ActionResponse | undefined> = {};
|
|
|
|
actions.forEach((a) => {
|
|
responses[a.config.id] = a.data;
|
|
});
|
|
return responses;
|
|
});
|
|
|
|
export const getAction = (
|
|
state: AppState,
|
|
actionId: string,
|
|
): Action | undefined => {
|
|
const action = find(state.entities.actions, (a) => a.config.id === actionId);
|
|
return action ? action.config : undefined;
|
|
};
|
|
|
|
export const getActionData = (
|
|
state: AppState,
|
|
actionId: string,
|
|
): ActionResponse | undefined => {
|
|
const action = find(state.entities.actions, (a) => a.config.id === actionId);
|
|
return action ? action.data : undefined;
|
|
};
|
|
|
|
export const getJSCollection = (
|
|
state: AppState,
|
|
actionId: string,
|
|
): JSCollection | undefined => {
|
|
const jsaction = find(
|
|
state.entities.jsActions,
|
|
(a) => a.config.id === actionId,
|
|
);
|
|
return jsaction ? jsaction.config : undefined;
|
|
};
|
|
|
|
export const getJSFunctionFromName = (state: AppState, name: string) => {
|
|
const {
|
|
entityName: collectionName,
|
|
propertyPath: functionName,
|
|
} = getEntityNameAndPropertyPath(name);
|
|
const jsCollection = find(
|
|
state.entities.jsActions,
|
|
(a) => a.config.name === collectionName,
|
|
);
|
|
if (jsCollection) {
|
|
return find(jsCollection.config.actions, (a) => a.name === functionName);
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
export function getCurrentPageNameByActionId(
|
|
state: AppState,
|
|
actionId: string,
|
|
): string {
|
|
const action = state.entities.actions.find((action) => {
|
|
return action.config.id === actionId;
|
|
});
|
|
const pageId = action ? action.config.pageId : "";
|
|
return getPageNameByPageId(state, pageId);
|
|
}
|
|
|
|
export function getCurrentPageNameByJSCollectionId(
|
|
state: AppState,
|
|
actionId: string,
|
|
): string {
|
|
const action = state.entities.jsActions.find((action) => {
|
|
return action.config.id === actionId;
|
|
});
|
|
const pageId = action ? action.config.pageId : "";
|
|
return getPageNameByPageId(state, pageId);
|
|
}
|
|
|
|
export function getPageNameByPageId(state: AppState, pageId: string): string {
|
|
const page = state.entities.pageList.pages.find(
|
|
(page) => page.pageId === pageId,
|
|
);
|
|
return page ? page.pageName : "";
|
|
}
|
|
|
|
const getQueryPaneSavingMap = (state: AppState) => state.ui.queryPane.isSaving;
|
|
const getApiPaneSavingMap = (state: AppState) => state.ui.apiPane.isSaving;
|
|
const getActionDirtyState = (state: AppState) => state.ui.apiPane.isDirty;
|
|
|
|
export const isActionSaving = (id: string) =>
|
|
createSelector(
|
|
[getQueryPaneSavingMap, getApiPaneSavingMap],
|
|
(querySavingMap, apiSavingsMap) => {
|
|
return (
|
|
(id in querySavingMap && querySavingMap[id]) ||
|
|
(id in apiSavingsMap && apiSavingsMap[id])
|
|
);
|
|
},
|
|
);
|
|
|
|
export const isActionDirty = (id: string) =>
|
|
createSelector([getActionDirtyState], (actionDirtyMap) => {
|
|
return id in actionDirtyMap && actionDirtyMap[id];
|
|
});
|
|
|
|
export const getAppData = (state: AppState) => state.entities.app;
|
|
|
|
export const getAppStoreData = (state: AppState): AppStoreState =>
|
|
state.entities.app.store;
|
|
|
|
export const getCanvasWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
|
state.entities.canvasWidgets;
|
|
|
|
export const getCanvasWidgetsStructure = (state: AppState) =>
|
|
state.entities.canvasWidgetsStructure;
|
|
|
|
const getPageWidgets = (state: AppState) => state.ui.pageWidgets;
|
|
export const getCurrentPageWidgets = createSelector(
|
|
getPageWidgets,
|
|
getCurrentPageId,
|
|
(widgetsByPage, currentPageId) =>
|
|
currentPageId ? widgetsByPage[currentPageId] : {},
|
|
);
|
|
|
|
export const getParentModalId = (
|
|
widget: any,
|
|
pageWidgets: Record<string, any>,
|
|
) => {
|
|
let parentModalId;
|
|
let { parentId } = widget;
|
|
let parentWidget = pageWidgets[parentId];
|
|
while (parentId && parentId !== MAIN_CONTAINER_WIDGET_ID) {
|
|
if (parentWidget?.type === "MODAL_WIDGET") {
|
|
parentModalId = parentId;
|
|
break;
|
|
}
|
|
parentId = parentWidget?.parentId;
|
|
parentWidget = pageWidgets[parentId];
|
|
}
|
|
return parentModalId;
|
|
};
|
|
|
|
export const getCanvasWidgetsWithParentId = createSelector(
|
|
getCanvasWidgets,
|
|
(canvasWidgets: CanvasWidgetsReduxState) => {
|
|
return Object.entries(canvasWidgets).reduce(
|
|
(res, [widgetId, widget]: any) => {
|
|
const parentModalId = getParentModalId(widget, canvasWidgets);
|
|
|
|
return {
|
|
...res,
|
|
[widgetId]: { ...widget, parentModalId },
|
|
};
|
|
},
|
|
{},
|
|
);
|
|
},
|
|
);
|
|
|
|
export const getAllWidgetsMap = createSelector(
|
|
getPageWidgets,
|
|
(widgetsByPage) => {
|
|
return Object.entries(widgetsByPage).reduce(
|
|
(res: any, [pageId, pageWidgets]: any) => {
|
|
const widgetsMap = Object.entries(pageWidgets).reduce(
|
|
(res, [widgetId, widget]: any) => {
|
|
const parentModalId = getParentModalId(widget, pageWidgets);
|
|
|
|
return {
|
|
...res,
|
|
[widgetId]: { ...widget, pageId, parentModalId },
|
|
};
|
|
},
|
|
{},
|
|
);
|
|
|
|
return {
|
|
...res,
|
|
...widgetsMap,
|
|
};
|
|
},
|
|
{},
|
|
);
|
|
},
|
|
);
|
|
|
|
export const getAllPageWidgets = createSelector(
|
|
getAllWidgetsMap,
|
|
(widgetsMap) => {
|
|
return Object.entries(widgetsMap).reduce((res: any[], [, widget]: any) => {
|
|
res.push(widget);
|
|
return res;
|
|
}, []);
|
|
},
|
|
);
|
|
|
|
export const getPageList = createSelector(
|
|
(state: AppState) => state.entities.pageList.pages,
|
|
(pages) => pages,
|
|
);
|
|
|
|
export const getPageListAsOptions = createSelector(
|
|
(state: AppState) => state.entities.pageList.pages,
|
|
(pages) =>
|
|
pages.map((page) => ({
|
|
label: page.pageName,
|
|
id: page.pageId,
|
|
value: `'${page.pageName}'`,
|
|
})),
|
|
);
|
|
|
|
export const getExistingPageNames = createSelector(
|
|
(state: AppState) => state.entities.pageList.pages,
|
|
(pages) => pages.map((page) => page.pageName),
|
|
);
|
|
|
|
export const getExistingWidgetNames = createSelector(
|
|
(state: AppState) => state.entities.canvasWidgets,
|
|
(widgets) => Object.values(widgets).map((widget) => widget.widgetName),
|
|
);
|
|
|
|
export const getExistingActionNames = createSelector(
|
|
(state: AppState) => state.entities.actions,
|
|
getCurrentPageId,
|
|
// editingEntityName is actually an id and not a name per say and it points to the id of an action being edited through the explorer.
|
|
(state: AppState) => state.ui.explorer.entity.editingEntityName,
|
|
(actions, currentPageId, editingEntityId) => {
|
|
// get the current action being edited
|
|
const editingAction =
|
|
editingEntityId &&
|
|
actions.filter(
|
|
(action: { config: { id: string } }) =>
|
|
action.config.id === editingEntityId,
|
|
);
|
|
|
|
// if the current action being edited is on the same page, filter the actions on the page and return their names.
|
|
// or if the there is no current action being edited (this happens when a widget, or any other entity is being edited), return the actions on the page.
|
|
if (
|
|
(editingAction &&
|
|
editingAction.length > 0 &&
|
|
editingAction[0].config.pageId === currentPageId) ||
|
|
(editingAction && editingAction.length < 1)
|
|
) {
|
|
return actions.map(
|
|
(actionItem: { config: { name: string; pageId: string } }) => {
|
|
if (actionItem.config.pageId === currentPageId) {
|
|
return actionItem.config.name;
|
|
}
|
|
return undefined;
|
|
},
|
|
);
|
|
} else {
|
|
// if current action being edited is on another page, filter the actions not on the page and return their names.
|
|
return actions.map(
|
|
(actionItem: { config: { name: string; pageId: string } }) => {
|
|
if (actionItem.config.pageId !== currentPageId) {
|
|
return actionItem.config.name;
|
|
}
|
|
return undefined;
|
|
},
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
export const getExistingJSCollectionNames = createSelector(
|
|
getJSCollections,
|
|
(jsActions) =>
|
|
jsActions.map((action: { config: { name: string } }) => action.config.name),
|
|
);
|
|
|
|
export const getAppMode = (state: AppState) => state.entities.app.mode;
|
|
|
|
export const widgetsMapWithParentModalId = (state: AppState) => {
|
|
const appMode = getAppMode(state);
|
|
return appMode === APP_MODE.EDIT
|
|
? getAllWidgetsMap(state)
|
|
: getCanvasWidgetsWithParentId(state);
|
|
};
|
|
|
|
export const getIsReconnectingDatasourcesModalOpen = (state: AppState) =>
|
|
state.entities.datasources.isReconnectingModalOpen;
|
|
|
|
export const getPageActions = (pageId = "") => {
|
|
return (state: AppState) => {
|
|
return state.entities.actions.filter((action) => {
|
|
return action.config.pageId == pageId;
|
|
});
|
|
};
|
|
};
|
|
|
|
export const selectDatasourceIdToNameMap = createSelector(
|
|
getDatasources,
|
|
(datasources) => {
|
|
return datasources.reduce((acc, datasource) => {
|
|
acc[datasource.id] = datasource.name;
|
|
return acc;
|
|
}, {} as Record<string, string>);
|
|
},
|
|
);
|
|
|
|
export const selectWidgetsForCurrentPage = createSelector(
|
|
(state: AppState) => state.ui.pageCanvasStructure,
|
|
getCurrentPageId,
|
|
(canvasStructure, pageId) => (pageId ? canvasStructure[pageId] : null),
|
|
);
|
|
|
|
export const selectAllPages = (state: AppState) => {
|
|
return state.entities.pageList.pages;
|
|
};
|
|
|
|
export const getIsListing = (state: AppState) => {
|
|
return state.entities.datasources.isListing;
|
|
};
|
|
|
|
export const getDatasourceLoading = (state: AppState) => {
|
|
return state.entities.datasources.loading;
|
|
};
|
|
|
|
export const selectFilesForExplorer = createSelector(
|
|
getActionsForCurrentPage,
|
|
getJSCollectionsForCurrentPage,
|
|
selectDatasourceIdToNameMap,
|
|
selectFeatureFlags,
|
|
(actions, jsActions, datasourceIdToNameMap, featureFlags) => {
|
|
const { JS_EDITOR: isJSEditorEnabled } = featureFlags;
|
|
const files = [...actions, ...(isJSEditorEnabled ? jsActions : [])].reduce(
|
|
(acc, file) => {
|
|
let group = "";
|
|
if (file.config.pluginType === PluginType.JS) {
|
|
group = "JS Objects";
|
|
} else if (file.config.pluginType === PluginType.API) {
|
|
group = isEmbeddedRestDatasource(file.config.datasource)
|
|
? "APIs"
|
|
: datasourceIdToNameMap[file.config.datasource.id] ?? "APIs";
|
|
} else {
|
|
group = datasourceIdToNameMap[file.config.datasource.id];
|
|
}
|
|
acc = acc.concat({
|
|
type: file.config.pluginType,
|
|
entity: file,
|
|
group,
|
|
});
|
|
return acc;
|
|
},
|
|
[] as Array<ExplorerFileEntity>,
|
|
);
|
|
|
|
const filesSortedByGroupName = sortBy(files, [
|
|
(file) => file.group?.toLowerCase(),
|
|
(file) => file.entity.config?.name?.toLowerCase(),
|
|
]);
|
|
const groupedFiles = filesSortedByGroupName.reduce(
|
|
(acc, file) => {
|
|
if (acc.group !== file.group) {
|
|
acc.files = acc.files.concat({
|
|
type: "group",
|
|
entity: {
|
|
name: file.group,
|
|
},
|
|
});
|
|
acc.group = file.group;
|
|
}
|
|
acc.files = acc.files.concat({
|
|
...file,
|
|
entity: { id: file.entity.config.id, name: file.entity.config.name },
|
|
});
|
|
return acc;
|
|
},
|
|
{
|
|
group: "" as any,
|
|
files: [] as any,
|
|
},
|
|
);
|
|
return groupedFiles.files;
|
|
},
|
|
);
|
|
|
|
export const getActionValidationConfig = (state: AppState, action: any) => {
|
|
const pluginId = action.pluginId;
|
|
return getActionValidationConfigFromPlugin(
|
|
state.entities.plugins.editorConfigs[pluginId],
|
|
{},
|
|
);
|
|
};
|
|
|
|
export const getAllActionValidationConfig = (state: AppState) => {
|
|
const allActions = state.entities.actions;
|
|
const allValidationConfigs: {
|
|
[actionId: string]: ActionValidationConfigMap;
|
|
} = {};
|
|
for (const action of allActions) {
|
|
const pluginId = action.config.pluginId;
|
|
let validationConfigs: ActionValidationConfigMap = {};
|
|
validationConfigs = getActionValidationConfigFromPlugin(
|
|
state.entities.plugins.editorConfigs[pluginId],
|
|
{},
|
|
);
|
|
allValidationConfigs[action.config.id] = validationConfigs;
|
|
}
|
|
return allValidationConfigs;
|
|
};
|
|
|
|
function getActionValidationConfigFromPlugin(
|
|
editorConfigs: any,
|
|
validationConfig: ActionValidationConfigMap,
|
|
): ActionValidationConfigMap {
|
|
let newValidationConfig: ActionValidationConfigMap = {
|
|
...validationConfig,
|
|
};
|
|
if (!editorConfigs || !editorConfigs.length) return {};
|
|
for (const editorConfig of editorConfigs) {
|
|
if (editorConfig.validationConfig) {
|
|
const configProperty = editorConfig.configProperty;
|
|
newValidationConfig[configProperty] = editorConfig.validationConfig;
|
|
}
|
|
|
|
if (editorConfig.children) {
|
|
const childrenValidationConfig = getActionValidationConfigFromPlugin(
|
|
editorConfig.children,
|
|
validationConfig,
|
|
);
|
|
newValidationConfig = Object.assign(
|
|
newValidationConfig,
|
|
childrenValidationConfig,
|
|
);
|
|
}
|
|
}
|
|
return newValidationConfig;
|
|
}
|
|
export const getJSActions = (
|
|
state: AppState,
|
|
JSCollectionId: string,
|
|
): JSAction[] => {
|
|
const jsCollection = state.entities.jsActions.find(
|
|
(jsCollectionData) => jsCollectionData.config.id === JSCollectionId,
|
|
);
|
|
|
|
return jsCollection?.config.actions
|
|
? sortBy(jsCollection?.config.actions, ["name"])
|
|
: [];
|
|
};
|
|
|
|
export const getActiveJSActionId = (
|
|
state: AppState,
|
|
jsCollectionId: string,
|
|
): string | null => {
|
|
const jsCollection = state.entities.jsActions.find(
|
|
(jsCollectionData) => jsCollectionData.config.id === jsCollectionId,
|
|
);
|
|
return jsCollection?.activeJSActionId ?? null;
|
|
};
|
|
|
|
export const getIsExecutingJSAction = (
|
|
state: AppState,
|
|
jsCollectionId: string,
|
|
actionId: string,
|
|
): boolean => {
|
|
const jsCollection = state.entities.jsActions.find(
|
|
(jsCollectionData) => jsCollectionData.config.id === jsCollectionId,
|
|
);
|
|
if (jsCollection?.isExecuting && jsCollection.isExecuting[actionId]) {
|
|
return jsCollection.isExecuting[actionId];
|
|
}
|
|
return false;
|
|
};
|
|
|
|
export const getJSCollectionParseErrors = (
|
|
state: AppState,
|
|
jsCollectionName: string,
|
|
) => {
|
|
const dataTree = state.evaluations.tree;
|
|
const allErrors = get(
|
|
dataTree,
|
|
`${jsCollectionName}.${EVAL_ERROR_PATH}.body`,
|
|
[],
|
|
) as EvaluationError[];
|
|
return allErrors.filter((error) => {
|
|
return error.errorType === PropertyEvaluationErrorType.PARSE;
|
|
});
|
|
};
|
|
|
|
export const getNumberOfEntitiesInCurrentPage = createSelector(
|
|
getCanvasWidgets,
|
|
getActionsForCurrentPage,
|
|
getJSCollectionsForCurrentPage,
|
|
(widgets, actions, jsCollections) => {
|
|
return (
|
|
Object.keys(widgets).length - 1 + actions.length + jsCollections.length
|
|
);
|
|
},
|
|
);
|
|
|
|
export const selectIsInstallerOpen = (state: AppState) =>
|
|
state.ui.libraries.isInstallerOpen;
|
|
export const selectInstallationStatus = (state: AppState) =>
|
|
state.ui.libraries.installationStatus;
|
|
export const selectInstalledLibraries = (state: AppState) =>
|
|
state.ui.libraries.installedLibraries;
|
|
export const selectStatusForURL = (url: string) =>
|
|
createSelector(selectInstallationStatus, (statusMap) => {
|
|
return statusMap[url];
|
|
});
|
|
export const selectIsLibraryInstalled = createSelector(
|
|
[selectInstalledLibraries, (_: AppState, url: string) => url],
|
|
(installedLibraries, url) => {
|
|
return !!installedLibraries.find((lib) => lib.url === url);
|
|
},
|
|
);
|
|
|
|
export const selectQueuedLibraries = createSelector(
|
|
selectInstallationStatus,
|
|
(statusMap) => {
|
|
return Object.keys(statusMap).filter(
|
|
(url) => statusMap[url] === InstallState.Queued,
|
|
);
|
|
},
|
|
);
|
|
|
|
export const selectLibrariesForExplorer = createSelector(
|
|
selectInstalledLibraries,
|
|
selectInstallationStatus,
|
|
(libs, libStatus) => {
|
|
const queuedInstalls = Object.keys(libStatus)
|
|
.filter((key) => libStatus[key] === InstallState.Queued)
|
|
.map((url) => {
|
|
const recommendedLibrary = recommendedLibraries.find(
|
|
(lib) => lib.url === url,
|
|
);
|
|
return {
|
|
name: recommendedLibrary?.name || url,
|
|
docsURL: recommendedLibrary?.url || url,
|
|
version: recommendedLibrary?.version || "",
|
|
url: recommendedLibrary?.url || url,
|
|
accessor: [],
|
|
} as TJSLibrary;
|
|
});
|
|
return [...queuedInstalls, ...libs];
|
|
},
|
|
);
|
|
|
|
export const getAllJSActionsData = (state: AppState) => {
|
|
const jsActionsData: Record<string, unknown> = {};
|
|
const jsCollections = state.entities.jsActions;
|
|
jsCollections.forEach((collection) => {
|
|
if (collection.data) {
|
|
Object.keys(collection.data).forEach((actionId) => {
|
|
const jsAction = getJSActions(state, collection.config.id).find(
|
|
(action) => action.id === actionId,
|
|
);
|
|
if (jsAction) {
|
|
jsActionsData[`${collection.config.name}.${jsAction.name}`] =
|
|
collection.data?.[actionId];
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return jsActionsData;
|
|
};
|