From ccdd3efe082861dd4dc2002c0f1a5ca9e58b1dfa Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Fri, 3 Jan 2025 18:53:17 -0500 Subject: [PATCH] enum -> enums, client function docs --- backend/windmill-api/openapi.yaml | 2 +- backend/windmill-api/src/slack_approvals.rs | 44 +++++++++--------- python-client/wmill/wmill/client.py | 51 +++++++++++++++++---- typescript-client/client.ts | 43 +++++++++++++++-- 4 files changed, 102 insertions(+), 38 deletions(-) diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index d0dff30bae4c9..f8594cda5e82b 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -6980,7 +6980,7 @@ paths: required: false schema: type: string - - name: dynamic_enum_json + - name: dynamic_enums_json in: query required: false schema: diff --git a/backend/windmill-api/src/slack_approvals.rs b/backend/windmill-api/src/slack_approvals.rs index bba141dc29d56..282702b17d594 100644 --- a/backend/windmill-api/src/slack_approvals.rs +++ b/backend/windmill-api/src/slack_approvals.rs @@ -160,7 +160,7 @@ pub struct QueryDefaultArgsJson { #[derive(Deserialize, Debug)] pub struct QueryDynamicEnumJson { - dynamic_enum_json: Option, + dynamic_enums_json: Option, } #[derive(Deserialize, Debug)] @@ -172,7 +172,7 @@ struct ModalActionValue { message: Option, flow_step_id: Option, default_args_json: Option, - dynamic_enum_json: Option, + dynamic_enums_json: Option, } #[derive(Deserialize, Debug)] @@ -233,19 +233,19 @@ pub async fn slack_app_callback_handler( ) })?; - let dynamic_enum_json: Option = parsed_value - .dynamic_enum_json + let dynamic_enums_json: Option = parsed_value + .dynamic_enums_json .as_deref() .map(|s| serde_json::from_str(s)) .transpose() .map_err(|_| { Error::BadRequest( - "Invalid JSON in dynamic_enum_json".to_string(), + "Invalid JSON in dynamic_enums_json".to_string(), ) })?; tracing::debug!("Default args json: {:#?}", default_args_json); - tracing::debug!("Dynamic enum json: {:#?}", dynamic_enum_json); + tracing::debug!("Dynamic enum json: {:#?}", dynamic_enums_json); open_modal_with_blocks( &client, @@ -260,7 +260,7 @@ pub async fn slack_app_callback_handler( flow_step_id, container, default_args_json.as_ref(), - dynamic_enum_json.as_ref(), + dynamic_enums_json.as_ref(), ) .await .map_err(|e| Error::BadRequest(e.to_string()))?; @@ -289,7 +289,7 @@ pub async fn request_slack_approval( Query(channel_id): Query, Query(flow_step_id): Query, Query(default_args_json): Query, - Query(dynamic_enum_json): Query, + Query(dynamic_enums_json): Query, ) -> Result { let slack_resource_path = slack_resource_path.slack_resource_path; let channel_id = channel_id.channel_id; @@ -315,7 +315,7 @@ pub async fn request_slack_approval( message.message.as_deref(), flow_step_id.as_str(), default_args_json.default_args_json.as_ref(), - dynamic_enum_json.dynamic_enum_json.as_ref(), + dynamic_enums_json.dynamic_enums_json.as_ref(), ) .await .map_err(|e| Error::BadRequest(e.to_string()))?; @@ -430,7 +430,7 @@ async fn transform_schemas( order: Option<&Vec>, required: Option<&Vec>, default_args_json: Option<&serde_json::Value>, - dynamic_enum_json: Option<&serde_json::Value>, + dynamic_enums_json: Option<&serde_json::Value>, ) -> Result { tracing::debug!("Resume urls: {:#?}", urls); @@ -448,10 +448,10 @@ async fn transform_schemas( let is_required = required.unwrap().contains(key); let default_value = default_args_json.and_then(|json| json.get(key).cloned()); - let dynamic_enum_value = dynamic_enum_json.and_then(|json| json.get(key).cloned()); + let dynamic_enums_value = dynamic_enums_json.and_then(|json| json.get(key).cloned()); let input_block = - create_input_block(key, schema, is_required, default_value, dynamic_enum_value); + create_input_block(key, schema, is_required, default_value, dynamic_enums_value); match input_block { serde_json::Value::Array(arr) => blocks.extend(arr), _ => blocks.push(input_block), @@ -468,7 +468,7 @@ fn create_input_block( schema: &ResumeFormField, required: bool, default_value: Option, - dynamic_enum_value: Option, + dynamic_enums_value: Option, ) -> serde_json::Value { let placeholder = schema .description @@ -614,7 +614,7 @@ fn create_input_block( // Handle enum type if let Some(enums) = &schema.r#enum { tracing::debug!("Enum type"); - let enums = dynamic_enum_value + let enums = dynamic_enums_value .as_ref() .and_then(|v| v.as_array()) .cloned() @@ -866,7 +866,7 @@ async fn send_slack_message( message: Option<&str>, flow_step_id: &str, default_args_json: Option<&serde_json::Value>, - dynamic_enum_json: Option<&serde_json::Value>, + dynamic_enums_json: Option<&serde_json::Value>, ) -> Result> { let url = "https://slack.com/api/chat.postMessage"; @@ -890,8 +890,8 @@ async fn send_slack_message( value["default_args_json"] = default_args_json.clone(); } - if let Some(dynamic_enum_json) = dynamic_enum_json { - value["dynamic_enum_json"] = dynamic_enum_json.clone(); + if let Some(dynamic_enums_json) = dynamic_enums_json { + value["dynamic_enums_json"] = dynamic_enums_json.clone(); } let payload = serde_json::json!({ @@ -956,7 +956,7 @@ async fn get_modal_blocks( resource_path: &str, container: Container, default_args_json: Option<&serde_json::Value>, - dynamic_enum_json: Option<&serde_json::Value>, + dynamic_enums_json: Option<&serde_json::Value>, ) -> Result, Error> { let res = get_resume_urls_internal( axum::Extension(db.clone()), @@ -1065,7 +1065,7 @@ async fn get_modal_blocks( Some(&inner_schema.schema.order), Some(&inner_schema.schema.required), default_args_json, - dynamic_enum_json, + dynamic_enums_json, ) .await?; @@ -1087,7 +1087,7 @@ async fn get_modal_blocks( None, None, default_args_json, - dynamic_enum_json, + dynamic_enums_json, ) .await?; return Ok(axum::Json(construct_payload( @@ -1156,7 +1156,7 @@ async fn open_modal_with_blocks( flow_step_id: Option<&str>, container: Container, default_args_json: Option<&serde_json::Value>, - dynamic_enum_json: Option<&serde_json::Value>, + dynamic_enums_json: Option<&serde_json::Value>, ) -> Result<(), Box> { let resume_id = rand::random::(); let blocks_json = match get_modal_blocks( @@ -1171,7 +1171,7 @@ async fn open_modal_with_blocks( resource_path, container, default_args_json, - dynamic_enum_json, + dynamic_enums_json, ) .await { diff --git a/python-client/wmill/wmill/client.py b/python-client/wmill/wmill/client.py index 47ef32ccb66cf..e0a3b7419574f 100644 --- a/python-client/wmill/wmill/client.py +++ b/python-client/wmill/wmill/client.py @@ -631,14 +631,45 @@ def request_interactive_slack_approval( message: str = None, approver: str = None, default_args_json: dict = None, - dynamic_enum_json: dict = None, + dynamic_enums_json: dict = None, ) -> None: """ - Request interactive Slack approval - :param slack_resource_path: Slack resource path - :param channel_id: Slack channel - :param message: Message to send to Slack - :param approver: Approver name + Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields. + + **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Suspend -> Form" functionality. + Learn more at: https://www.windmill.dev/docs/flows/flow_approval#form + + :param slack_resource_path: The path to the Slack resource in Windmill. + :type slack_resource_path: str + :param channel_id: The Slack channel ID where the approval request will be sent. + :type channel_id: str + :param message: Optional custom message to include in the Slack approval request. + :type message: str, optional + :param approver: Optional user ID or name of the approver for the request. + :type approver: str, optional + :param default_args_json: Optional dictionary defining or overriding the default arguments for form fields. + :type default_args_json: dict, optional + :param dynamic_enums_json: Optional dictionary overriding the enum default values of enum form fields. + :type dynamic_enums_json: dict, optional + + :raises Exception: If the function is not called within a flow or flow preview. + :raises Exception: If the required flow job or flow step environment variables are not set. + + :return: None + + **Usage Example:** + >>> client.request_interactive_slack_approval( + ... slack_resource_path="/u/alex/my_slack_resource", + ... channel_id="admins-slack-channel", + ... message="Please approve this request", + ... approver="approver123", + ... default_args_json={"key1": "value1", "key2": 42}, + ... dynamic_enums_json={"foo": ["choice1", "choice2"], "bar": ["optionA", "optionB"]}, + ... ) + + **Notes:** + - This function must be executed within a Windmill flow or flow preview. + - The function checks for required environment variables (`WM_FLOW_JOB_ID`, `WM_FLOW_STEP_ID`) to ensure it is run in the appropriate context. """ workspace = self.workspace flow_job_id = os.environ.get("WM_FLOW_JOB_ID") @@ -662,8 +693,8 @@ def request_interactive_slack_approval( params["flow_step_id"] = os.environ.get("WM_FLOW_STEP_ID") if default_args_json: params["default_args_json"] = json.dumps(default_args_json) - if dynamic_enum_json: - params["dynamic_enum_json"] = json.dumps(dynamic_enum_json) + if dynamic_enums_json: + params["dynamic_enums_json"] = json.dumps(dynamic_enums_json) self.get( f"/w/{workspace}/jobs/slack_approval/{os.environ.get('WM_JOB_ID', 'NO_JOB_ID')}", @@ -1026,7 +1057,7 @@ def request_interactive_slack_approval( message: str = None, approver: str = None, default_args_json: dict = None, - dynamic_enum_json: dict = None, + dynamic_enums_json: dict = None, ) -> None: return _client.request_interactive_slack_approval( slack_resource_path=slack_resource_path, @@ -1034,7 +1065,7 @@ def request_interactive_slack_approval( message=message, approver=approver, default_args_json=default_args_json, - dynamic_enum_json=dynamic_enum_json, + dynamic_enums_json=dynamic_enums_json, ) @init_global_client diff --git a/typescript-client/client.ts b/typescript-client/client.ts index 868ed17d2f409..04dc94461c4fa 100644 --- a/typescript-client/client.ts +++ b/typescript-client/client.ts @@ -853,16 +853,49 @@ interface SlackApprovalOptions { message?: string; approver?: string; defaultArgsJson?: Record; - dynamicEnumJson?: Record; + dynamicEnumsJson?: Record; } +/** + * Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields. + * + * **[Enterprise Edition Only]** To include form fields in the Slack approval request, go to **Advanced -> Suspend -> Form** + * and define a form. Learn more at [Windmill Documentation](https://www.windmill.dev/docs/flows/flow_approval#form). + * + * @param {Object} options - The configuration options for the Slack approval request. + * @param {string} options.slackResourcePath - The path to the Slack resource in Windmill. + * @param {string} options.channelId - The Slack channel ID where the approval request will be sent. + * @param {string} [options.message] - Optional custom message to include in the Slack approval request. + * @param {string} [options.approver] - Optional user ID or name of the approver for the request. + * @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field. + * @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field. + * + * @returns {Promise} Resolves when the Slack approval request is successfully sent. + * + * @throws {Error} If the function is not called within a flow or flow preview. + * @throws {Error} If the `JobService.getSlackApprovalPayload` call fails. + * + * **Usage Example:** + * ```typescript + * await requestInteractiveSlackApproval({ + * slackResourcePath: "/u/alex/my_slack_resource", + * channelId: "admins-slack-channel", + * message: "Please approve this request", + * approver: "approver123", + * defaultArgsJson: { key1: "value1", key2: 42 }, + * dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] }, + * }); + * ``` + * + * **Note:** This function requires execution within a Windmill flow or flow preview. + */ export async function requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, - dynamicEnumJson, + dynamicEnumsJson, }: SlackApprovalOptions): Promise { const workspace = getWorkspace(); const flowJobId = getEnv("WM_FLOW_JOB_ID"); @@ -886,7 +919,7 @@ export async function requestInteractiveSlackApproval({ channelId: string; flowStepId: string; defaultArgsJson?: string; - dynamicEnumJson?: string; + dynamicEnumsJson?: string; } = { slackResourcePath, channelId, @@ -904,8 +937,8 @@ export async function requestInteractiveSlackApproval({ params.defaultArgsJson = JSON.stringify(defaultArgsJson); } - if (dynamicEnumJson) { - params.dynamicEnumJson = JSON.stringify(dynamicEnumJson); + if (dynamicEnumsJson) { + params.dynamicEnumsJson = JSON.stringify(dynamicEnumsJson); } await JobService.getSlackApprovalPayload({