feat: workflows query in apps code split (#30424)
This commit is contained in:
parent
300dd76c54
commit
3a65d5a4ea
|
|
@ -32,6 +32,8 @@ export interface Plugin {
|
|||
responseType?: "TABLE" | "JSON";
|
||||
documentationLink?: string;
|
||||
generateCRUDPageComponent?: string;
|
||||
// We need to know if the plugin requires a datasource (Eg Workflows plugin does not require a datasource to create queries)
|
||||
requiresDatasource: boolean;
|
||||
}
|
||||
|
||||
export interface PluginFormPayload {
|
||||
|
|
@ -55,6 +57,9 @@ class PluginsApi extends Api {
|
|||
static defaultDynamicTriggerURL(datasourceId: string): string {
|
||||
return `/v1/datasources/${datasourceId}/trigger`;
|
||||
}
|
||||
static dynamicTriggerURLForInternalPlugins(pluginId: string): string {
|
||||
return `/${PluginsApi.url}/${pluginId}/trigger`;
|
||||
}
|
||||
static async fetchPlugins(
|
||||
workspaceId: string,
|
||||
): Promise<AxiosPromise<ApiResponse<Plugin[]>>> {
|
||||
|
|
|
|||
|
|
@ -1047,6 +1047,9 @@ export const selectFilesForExplorer = createSelector(
|
|||
group = isEmbeddedAIDataSource(file.config.datasource)
|
||||
? "AI Queries"
|
||||
: datasourceIdToNameMap[file.config.datasource.id] ?? "AI Queries";
|
||||
} else if (file.config.pluginType === PluginType.INTERNAL) {
|
||||
// TODO: Add a group for internal actions, currently only Workflow actions are internal
|
||||
group = "Workflows";
|
||||
} else {
|
||||
group = datasourceIdToNameMap[file.config.datasource.id];
|
||||
}
|
||||
|
|
@ -1059,7 +1062,6 @@ export const selectFilesForExplorer = createSelector(
|
|||
}, [] as Array<ExplorerFileEntity>);
|
||||
|
||||
const filesSortedByGroupName = sortBy(files, [
|
||||
(file) => file.entity.config?.isMainJSCollection,
|
||||
(file) => file.group?.toLowerCase(),
|
||||
(file) => file.entity.config?.name?.toLowerCase(),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -28,3 +28,5 @@ export const getCurrentWorkflowJSActions = (
|
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
state: AppState,
|
||||
): JSCollectionData[] => [];
|
||||
|
||||
export const getShowWorkflowFeature = () => false;
|
||||
|
|
|
|||
3
app/client/src/ce/utils/workflowHelpers.ts
Normal file
3
app/client/src/ce/utils/workflowHelpers.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const useWorkflowOptions = () => {
|
||||
return [];
|
||||
};
|
||||
|
|
@ -39,22 +39,26 @@ import type { Plugin } from "api/PluginApi";
|
|||
import { useModuleOptions } from "@appsmith/utils/moduleInstanceHelpers";
|
||||
import type { ActionParentEntityTypeInterface } from "@appsmith/entities/Engine/actionHelpers";
|
||||
import { createNewQueryBasedOnParentEntity } from "@appsmith/actions/helpers";
|
||||
import { useWorkflowOptions } from "@appsmith/utils/workflowHelpers";
|
||||
|
||||
export interface FilterFileOperationsProps {
|
||||
canCreateActions: boolean;
|
||||
query?: string;
|
||||
showModules?: boolean;
|
||||
showWorkflows?: boolean;
|
||||
}
|
||||
|
||||
export const useFilteredFileOperations = ({
|
||||
canCreateActions,
|
||||
query = "",
|
||||
showModules = true,
|
||||
showWorkflows = true,
|
||||
}: FilterFileOperationsProps) => {
|
||||
const { appWideDS = [], otherDS = [] } = useAppWideAndOtherDatasource();
|
||||
const plugins = useSelector(getPlugins);
|
||||
const moduleOptions = useModuleOptions();
|
||||
const showAppsmithAIQuery = useFeatureFlag(FEATURE_FLAG.ab_appsmith_ai_query);
|
||||
const workflowOptions = useWorkflowOptions();
|
||||
|
||||
// helper map for sorting based on recent usage
|
||||
const recentlyUsedDSMap = useRecentlyUsedDSMap();
|
||||
|
|
@ -84,6 +88,7 @@ export const useFilteredFileOperations = ({
|
|||
canCreateActions,
|
||||
canCreateDatasource,
|
||||
moduleOptions: showModules ? moduleOptions : [],
|
||||
workflowOptions: showWorkflows ? workflowOptions : [],
|
||||
plugins,
|
||||
recentlyUsedDSMap,
|
||||
query,
|
||||
|
|
@ -100,14 +105,16 @@ export const useFilteredAndSortedFileOperations = ({
|
|||
query,
|
||||
recentlyUsedDSMap = {},
|
||||
showAppsmithAIQuery = false,
|
||||
workflowOptions = [],
|
||||
}: {
|
||||
allDatasources?: Datasource[];
|
||||
canCreateActions?: boolean;
|
||||
canCreateDatasource?: boolean;
|
||||
moduleOptions?: ActionOperation[];
|
||||
plugins?: Plugin[];
|
||||
recentlyUsedDSMap?: Record<string, number>;
|
||||
query: string;
|
||||
recentlyUsedDSMap?: Record<string, number>;
|
||||
workflowOptions?: ActionOperation[];
|
||||
showAppsmithAIQuery?: boolean;
|
||||
}) => {
|
||||
const fileOperations: ActionOperation[] = [];
|
||||
|
|
@ -119,6 +126,11 @@ export const useFilteredAndSortedFileOperations = ({
|
|||
? [...actionOperations, appsmithAIActionOperation]
|
||||
: actionOperations;
|
||||
|
||||
// Add Workflow operations
|
||||
if (workflowOptions.length > 0) {
|
||||
workflowOptions.map((workflowOp) => fileOperations.push(workflowOp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around to get the rest api cloud image.
|
||||
* We don't have it store as a svg
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ export const defaultActionSettings: Record<PluginType, any> = {
|
|||
[PluginType.REMOTE]: saasActionSettingsConfig,
|
||||
[PluginType.JS]: [],
|
||||
[PluginType.AI]: saasActionSettingsConfig,
|
||||
[PluginType.INTERNAL]: saasActionSettingsConfig,
|
||||
};
|
||||
|
||||
export const defaultActionEditorConfigs: Record<PluginType, any> = {
|
||||
|
|
@ -180,6 +181,7 @@ export const defaultActionEditorConfigs: Record<PluginType, any> = {
|
|||
[PluginType.REMOTE]: [],
|
||||
[PluginType.JS]: [],
|
||||
[PluginType.AI]: [],
|
||||
[PluginType.INTERNAL]: [],
|
||||
};
|
||||
|
||||
export const defaultActionDependenciesConfig: Record<
|
||||
|
|
@ -192,6 +194,7 @@ export const defaultActionDependenciesConfig: Record<
|
|||
[PluginType.REMOTE]: {},
|
||||
[PluginType.JS]: {},
|
||||
[PluginType.AI]: {},
|
||||
[PluginType.INTERNAL]: {},
|
||||
};
|
||||
|
||||
export const defaultDatasourceFormButtonConfig: Record<PluginType, string[]> = {
|
||||
|
|
@ -201,4 +204,5 @@ export const defaultDatasourceFormButtonConfig: Record<PluginType, string[]> = {
|
|||
[PluginType.REMOTE]: apiActionDatasourceFormButtonConfig.REMOTE,
|
||||
[PluginType.JS]: [],
|
||||
[PluginType.AI]: apiActionDatasourceFormButtonConfig.AI,
|
||||
[PluginType.INTERNAL]: [],
|
||||
};
|
||||
|
|
|
|||
1
app/client/src/ee/utils/workflowHelpers.ts
Normal file
1
app/client/src/ee/utils/workflowHelpers.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "ce/utils/workflowHelpers";
|
||||
|
|
@ -14,6 +14,7 @@ export enum PluginType {
|
|||
JS = "JS",
|
||||
REMOTE = "REMOTE",
|
||||
AI = "AI",
|
||||
INTERNAL = "INTERNAL",
|
||||
}
|
||||
|
||||
export enum PluginPackageName {
|
||||
|
|
@ -30,6 +31,7 @@ export enum PluginPackageName {
|
|||
MS_SQL = "mssql-plugin",
|
||||
SNOWFLAKE = "snowflake-plugin",
|
||||
APPSMITH_AI = "appsmithai-plugin",
|
||||
WORKFLOW = "workflow-plugin",
|
||||
}
|
||||
|
||||
// more can be added subsequently.
|
||||
|
|
@ -191,6 +193,11 @@ export interface AIAction extends BaseAction {
|
|||
actionConfiguration: any;
|
||||
datasource: StoredDatasource;
|
||||
}
|
||||
export interface InternalAction extends BaseAction {
|
||||
pluginType: PluginType.INTERNAL;
|
||||
actionConfiguration: any;
|
||||
datasource: StoredDatasource;
|
||||
}
|
||||
|
||||
export interface EmbeddedApiAction extends BaseApiAction {
|
||||
datasource: EmbeddedRestDatasource;
|
||||
|
|
@ -231,7 +238,8 @@ export type Action =
|
|||
| QueryAction
|
||||
| SaaSAction
|
||||
| RemoteAction
|
||||
| AIAction;
|
||||
| AIAction
|
||||
| InternalAction;
|
||||
|
||||
export enum SlashCommand {
|
||||
NEW_API,
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ export const resolveActionURL = ({
|
|||
} else if (
|
||||
pluginType === PluginType.DB ||
|
||||
pluginType === PluginType.REMOTE ||
|
||||
pluginType === PluginType.AI
|
||||
pluginType === PluginType.AI ||
|
||||
pluginType === PluginType.INTERNAL
|
||||
) {
|
||||
return queryEditorIdURL({
|
||||
parentEntityId,
|
||||
|
|
@ -83,6 +84,7 @@ export const ACTION_PLUGIN_MAP: Array<ActionGroupConfig | undefined> = [
|
|||
PluginType.DB,
|
||||
PluginType.REMOTE,
|
||||
PluginType.AI,
|
||||
PluginType.INTERNAL,
|
||||
],
|
||||
icon: dbQueryIcon,
|
||||
key: generateReactKey(),
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import { getHasCreateActionPermission } from "@appsmith/utils/BusinessFeatures/p
|
|||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import { ActionParentEntityType } from "@appsmith/entities/Engine/actionHelpers";
|
||||
import { getShowWorkflowFeature } from "@appsmith/selectors/workflowSelectors";
|
||||
|
||||
const NoEntityFoundSvg = importSvg(
|
||||
async () => import("assets/svg/no_entities_found.svg"),
|
||||
|
|
@ -114,6 +115,8 @@ function EntityExplorer({ isActive }: { isActive: boolean }) {
|
|||
pagePermissions,
|
||||
);
|
||||
|
||||
const showWorkflows = useSelector(getShowWorkflowFeature);
|
||||
|
||||
const closeWalkthrough = useCallback(() => {
|
||||
if (isWalkthroughOpened && popFeature) {
|
||||
popFeature("EXPLORER_DATASOURCE_ADD");
|
||||
|
|
@ -165,6 +168,7 @@ function EntityExplorer({ isActive }: { isActive: boolean }) {
|
|||
editorId={applicationId}
|
||||
parentEntityId={pageId}
|
||||
parentEntityType={ActionParentEntityType.PAGE}
|
||||
showWorkflows={showWorkflows}
|
||||
>
|
||||
<Files />
|
||||
</FilesContextProvider>
|
||||
|
|
|
|||
|
|
@ -286,6 +286,15 @@ export function CurlIconV2() {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO (workflows): replace with actual workflow icon
|
||||
export function WorkflowIcon() {
|
||||
return (
|
||||
<EntityIcon>
|
||||
<Icon name="fork" size="lg" />
|
||||
</EntityIcon>
|
||||
);
|
||||
}
|
||||
|
||||
// height and width are set to 18px by default. This is to maintain the current icon sizes.
|
||||
// fontSize is set to 56% by default.
|
||||
export function JsFileIconV2(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ interface FilesContextContextProps {
|
|||
parentEntityId: string; // page, workflow or module
|
||||
parentEntityType: ActionParentEntityTypeInterface;
|
||||
showModules?: boolean;
|
||||
showWorkflows?: boolean;
|
||||
selectFilesForExplorer?: (state: any) => any;
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +52,7 @@ export const FilesContextProvider = ({
|
|||
parentEntityType,
|
||||
selectFilesForExplorer,
|
||||
showModules,
|
||||
showWorkflows,
|
||||
}: FilesContextProviderProps) => {
|
||||
const value = useMemo(() => {
|
||||
return {
|
||||
|
|
@ -61,6 +63,7 @@ export const FilesContextProvider = ({
|
|||
menuItems: menuItems || defaultMenuItems,
|
||||
selectFilesForExplorer,
|
||||
showModules,
|
||||
showWorkflows,
|
||||
};
|
||||
}, [
|
||||
canCreateActions,
|
||||
|
|
@ -68,6 +71,7 @@ export const FilesContextProvider = ({
|
|||
parentEntityType,
|
||||
menuItems,
|
||||
showModules,
|
||||
showWorkflows,
|
||||
selectFilesForExplorer,
|
||||
editorId,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ function Files() {
|
|||
parentEntityType,
|
||||
selectFilesForExplorer = default_selectFilesForExplorer,
|
||||
showModules = true,
|
||||
showWorkflows = true,
|
||||
} = context;
|
||||
|
||||
const files = useSelector(selectFilesForExplorer);
|
||||
|
|
@ -61,6 +62,7 @@ function Files() {
|
|||
query,
|
||||
canCreateActions,
|
||||
showModules,
|
||||
showWorkflows,
|
||||
});
|
||||
|
||||
const onCreate = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { FilesContextProvider } from "pages/Editor/Explorer/Files/FilesContextPr
|
|||
import { createMessage, EDITOR_PANE_TEXTS } from "@appsmith/constants/messages";
|
||||
import { EmptyState } from "../components/EmptyState";
|
||||
import { useQueryAdd } from "./hooks";
|
||||
import { getShowWorkflowFeature } from "@appsmith/selectors/workflowSelectors";
|
||||
|
||||
const ListQuery = () => {
|
||||
const pageId = useSelector(getCurrentPageId) as string;
|
||||
|
|
@ -33,6 +34,7 @@ const ListQuery = () => {
|
|||
const applicationId = useSelector(getCurrentApplicationId);
|
||||
|
||||
const addButtonClickHandler = useQueryAdd();
|
||||
const showWorkflows = useSelector(getShowWorkflowFeature);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
|
@ -77,6 +79,7 @@ const ListQuery = () => {
|
|||
editorId={applicationId}
|
||||
parentEntityId={pageId}
|
||||
parentEntityType={ActionParentEntityType.PAGE}
|
||||
showWorkflows={showWorkflows}
|
||||
>
|
||||
{items.map((file) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -354,7 +354,9 @@ export function EditorJSONtoForm(props: Props) {
|
|||
userWorkspacePermissions,
|
||||
);
|
||||
|
||||
const showSchema = useShowSchema(currentActionConfig?.pluginId || "");
|
||||
const showSchema =
|
||||
useShowSchema(currentActionConfig?.pluginId || "") &&
|
||||
!!plugin?.requiresDatasource;
|
||||
|
||||
const showRightPane =
|
||||
showSchema ||
|
||||
|
|
@ -617,9 +619,10 @@ export function EditorJSONtoForm(props: Props) {
|
|||
((hasDependencies || !!actionResponse) && !guidedTourEnabled) ||
|
||||
currentActionPluginName !== PluginName.SMTP;
|
||||
|
||||
// Datasource selection is hidden for Appsmith AI Plugin
|
||||
// TODO: @Diljit Remove this condition when knowledge retrieval for Appsmith AI is implemented
|
||||
const showDatasourceSelector = !isAppsmithAIPlugin(plugin?.packageName);
|
||||
// Datasource selection is hidden for Appsmith AI Plugin and for plugins that don't require datasource
|
||||
// TODO: @Diljit Remove this condition when knowledge retrieval for Appsmith AI is implemented (Only remove the AI Condition)
|
||||
const showDatasourceSelector =
|
||||
!isAppsmithAIPlugin(plugin?.packageName) && !!plugin?.requiresDatasource;
|
||||
|
||||
// when switching between different redux forms, make sure this redux form has been initialized before rendering anything.
|
||||
// the initialized prop below comes from redux-form.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import Disabler from "pages/common/Disabler";
|
|||
import ConvertToModuleInstanceCTA from "@appsmith/pages/Editor/EntityEditor/ConvertToModuleInstanceCTA";
|
||||
import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
|
||||
import ConvertEntityNotification from "@appsmith/pages/common/ConvertEntityNotification";
|
||||
import { PluginType } from "entities/Action";
|
||||
|
||||
type QueryEditorProps = RouteComponentProps<QueryEditorRouteParams>;
|
||||
|
||||
|
|
@ -83,12 +84,15 @@ function QueryEditor(props: QueryEditorProps) {
|
|||
name={action?.name || ""}
|
||||
pageId={pageId}
|
||||
/>
|
||||
<ConvertToModuleInstanceCTA
|
||||
canCreateModuleInstance={isCreatePermitted}
|
||||
canDeleteEntity={isDeletePermitted}
|
||||
entityId={action?.id || ""}
|
||||
moduleType={MODULE_TYPE.QUERY}
|
||||
/>
|
||||
{action?.pluginType !== PluginType.INTERNAL && (
|
||||
// Need to remove this check once workflow query is supported in module
|
||||
<ConvertToModuleInstanceCTA
|
||||
canCreateModuleInstance={isCreatePermitted}
|
||||
canDeleteEntity={isDeletePermitted}
|
||||
entityId={action?.id || ""}
|
||||
moduleType={MODULE_TYPE.QUERY}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
[
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type { Action, ActionConfig } from "entities/Action";
|
|||
import type { FormConfigType } from "components/formControls/BaseControl";
|
||||
import PluginsApi from "api/PluginApi";
|
||||
import type { ApiResponse } from "api/ApiResponses";
|
||||
import { getAction } from "@appsmith/selectors/entitiesSelector";
|
||||
import { getAction, getPlugin } from "@appsmith/selectors/entitiesSelector";
|
||||
import { getDataTreeActionConfigPath } from "entities/Action/actionProperties";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
|
||||
|
|
@ -29,6 +29,7 @@ import {
|
|||
} from "./helper";
|
||||
import type { DatasourceConfiguration } from "entities/Datasource";
|
||||
import { buffers } from "redux-saga";
|
||||
import type { Plugin } from "api/PluginApi";
|
||||
|
||||
export interface FormEvalActionPayload {
|
||||
formId: string;
|
||||
|
|
@ -166,8 +167,14 @@ function* fetchDynamicValueSaga(
|
|||
|
||||
dynamicFetchedValues.hasStarted = true;
|
||||
|
||||
const plugin: Plugin = yield select(getPlugin, pluginId);
|
||||
|
||||
let url = PluginsApi.defaultDynamicTriggerURL(datasourceId);
|
||||
|
||||
if (!!plugin && !plugin.requiresDatasource) {
|
||||
url = PluginsApi.dynamicTriggerURLForInternalPlugins(pluginId);
|
||||
}
|
||||
|
||||
if (
|
||||
"url" in evaluatedConfig &&
|
||||
!!evaluatedConfig.url &&
|
||||
|
|
@ -183,6 +190,7 @@ function* fetchDynamicValueSaga(
|
|||
let substitutedParameters = {};
|
||||
|
||||
const action: Action = yield select(getAction, actionId);
|
||||
const { workspaceId } = action;
|
||||
const dataTree: DataTree = yield select(getDataTree);
|
||||
|
||||
if (!!action) {
|
||||
|
|
@ -199,8 +207,9 @@ function* fetchDynamicValueSaga(
|
|||
const dataTreeActionConfigPath =
|
||||
getDataTreeActionConfigPath(dynamicBindingValue);
|
||||
// then we get the value of the current parameter from the evaluatedValues in the action object stored in the dataTree.
|
||||
// TODOD: Find a better way to pass the workspaceId
|
||||
const evaluatedValue = get(
|
||||
evalAction?.__evaluation__?.evaluatedValues,
|
||||
{ ...evalAction?.__evaluation__?.evaluatedValues, workspaceId },
|
||||
dataTreeActionConfigPath,
|
||||
);
|
||||
// if it exists, we store it in the substituted params object.
|
||||
|
|
|
|||
|
|
@ -289,6 +289,13 @@ function* formValueChangeSaga(
|
|||
) {
|
||||
currentEnvironment = Object.keys(datasourceStorages)[0];
|
||||
}
|
||||
let dsConfig = {
|
||||
url: "",
|
||||
};
|
||||
|
||||
if (plugin?.requiresDatasource) {
|
||||
dsConfig = datasourceStorages[currentEnvironment].datasourceConfiguration;
|
||||
}
|
||||
const postEvalActions =
|
||||
uiComponent === UIComponentTypes.UQIDbEditorForm
|
||||
? [
|
||||
|
|
@ -299,7 +306,7 @@ function* formValueChangeSaga(
|
|||
values.pluginId,
|
||||
field,
|
||||
hasRouteChanged,
|
||||
datasourceStorages[currentEnvironment]?.datasourceConfiguration,
|
||||
dsConfig,
|
||||
),
|
||||
]
|
||||
: [];
|
||||
|
|
@ -346,7 +353,14 @@ function* formValueChangeSaga(
|
|||
function* handleQueryCreatedSaga(actionPayload: ReduxAction<QueryAction>) {
|
||||
const { actionConfiguration, id, pageId, pluginId, pluginType } =
|
||||
actionPayload.payload;
|
||||
if (![PluginType.DB, PluginType.REMOTE, PluginType.AI].includes(pluginType))
|
||||
if (
|
||||
![
|
||||
PluginType.DB,
|
||||
PluginType.REMOTE,
|
||||
PluginType.AI,
|
||||
PluginType.INTERNAL,
|
||||
].includes(pluginType)
|
||||
)
|
||||
return;
|
||||
const pluginTemplates: Record<string, unknown> =
|
||||
yield select(getPluginTemplates);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user