feat: workflows query in apps code split (#30424)

This commit is contained in:
Ayush Pahwa 2024-01-22 19:50:22 +07:00 committed by GitHub
parent 300dd76c54
commit 3a65d5a4ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 109 additions and 18 deletions

View File

@ -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[]>>> {

View File

@ -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(),
]);

View File

@ -28,3 +28,5 @@ export const getCurrentWorkflowJSActions = (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
state: AppState,
): JSCollectionData[] => [];
export const getShowWorkflowFeature = () => false;

View File

@ -0,0 +1,3 @@
export const useWorkflowOptions = () => {
return [];
};

View File

@ -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

View File

@ -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]: [],
};

View File

@ -0,0 +1 @@
export * from "ce/utils/workflowHelpers";

View File

@ -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,

View File

@ -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(),

View File

@ -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>

View File

@ -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(

View File

@ -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,
]);

View File

@ -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(() => {

View File

@ -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 (

View File

@ -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.

View File

@ -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}
/>
)}
</>
),
[

View File

@ -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.

View File

@ -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);