Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WorkflowStep can not save #982

Closed
Arxing opened this issue May 3, 2022 · 13 comments
Closed

WorkflowStep can not save #982

Arxing opened this issue May 3, 2022 · 13 comments
Labels
lang:kotlin needs info project:bolt question M-T: User needs support to use the project

Comments

@Arxing
Copy link

Arxing commented May 3, 2022

I follow this document and tried adding WorkflowStep to my Slack bot, WorkflowStep.edit works fine, but WorkflowStep.save fails anyway, I can't find the reason, can you help me?

Reproducible in:

val step = WorkflowStep.builder()
      .callbackId("copy_review")
      .edit { req, context ->
        context.configure(withBlocks {
          section {
            blockId("intro-section")
            plainText("text")
          }
          input {
            blockId("task_name_input")
            label("Task Name")
            element {
              plainTextInput {
                actionId("task_name")
              }
            }
          }
        })
        context.ack()
      }
      .save { req, context ->
        val inputs = buildMap {
          put("taskName", WorkflowSteps.stepInput {
            it.value(req.payload.view.state.findElement("task_name").value)
          })
        }
        val outputs = buildList {
          add(WorkflowSteps.stepOutput {
            it.name("taskName").type("text").label("Task Name")
          })
        }
        context.update(inputs, outputs)
        context.ack()
      }
      .execute { req, context ->
        context.ack()
      }
      .build()
    app.step(step)

The Slack SDK version

+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt:1.22.0 (n)
+--- com.slack.api:bolt-ktor:1.22.0 (n)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (n)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0 (n)
+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0
|    |    +--- com.slack.api:slack-api-model:1.22.0 (*)
|    \--- com.slack.api:slack-app-backend:1.22.0
|         +--- com.slack.api:slack-api-model:1.22.0 (*)
|         +--- com.slack.api:slack-api-client:1.22.0 (*)
+--- com.slack.api:bolt-ktor:1.22.0
|    +--- com.slack.api:slack-api-client:1.22.0 (*)
|    +--- com.slack.api:bolt:1.22.0 (*)
+--- com.slack.api:slack-api-model-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model:1.22.0 (*)
+--- com.slack.api:slack-api-client-kotlin-extension:1.22.0
|    +--- com.slack.api:slack-api-model-kotlin-extension:1.22.0 (*)

Java Runtime version

java version "11.0.12" 2021-07-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.12+8-LTS-237)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.12+8-LTS-237, mixed mode)

OS info

ProductName:    macOS
ProductVersion: 12.2.1
BuildVersion:   21D62
Darwin Kernel Version 21.3.0: Wed Jan  5 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64

image

@srajiang
Copy link
Member

srajiang commented May 3, 2022

Hi @Arxing - Are there any logs you can provide for what you see when the WorkflowStep.edit vs. .save is occurring?

@srajiang srajiang added question M-T: User needs support to use the project and removed untriaged labels May 3, 2022
@seratch
Copy link
Member

seratch commented May 3, 2022

Hi @Arxing, I've checked your code and found that the following line of code can throw exceptions (actually the code as-is does not compile for me).

it.value(req.payload.view.state.findElement("task_name").value)

I've revised the part this way:

it.value(stateValues["task_name_input"]?.get("task_name")?.value)

Here is the complete code and its build settings:

import com.slack.api.bolt.App
import com.slack.api.bolt.middleware.builtin.WorkflowStep
import com.slack.api.bolt.socket_mode.SocketModeApp
import com.slack.api.model.kotlin_extension.block.withBlocks
import com.slack.api.model.workflow.WorkflowSteps

fun main() {
  System.setProperty("org.slf4j.simpleLogger.log.com.slack.api", "debug")
  val app = App()

  val step = WorkflowStep.builder()
    .callbackId("socket-mode-step")
    .edit { _, context ->
      context.configure(withBlocks {
        section {
          blockId("intro-section")
          plainText("text")
        }
        input {
          blockId("task_name_input")
          label("Task Name")
          element {
            plainTextInput {
              actionId("task_name")
            }
          }
        }
      })
      context.ack()
    }
    .save { req, context ->
      val stateValues = req.payload.view.state.values
      val inputs = buildMap {
        // This part used to be failing
        put("taskName", WorkflowSteps.stepInput {
          it.value(stateValues["task_name_input"]?.get("task_name")?.value)
        })
      }
      val outputs = buildList {
        add(WorkflowSteps.stepOutput {
          it.name("taskName").type("text").label("Task Name")
        })
      }
      context.update(inputs, outputs)
      context.ack()
    }
    .execute { _, context ->
      context.ack()
    }
    .build()
  app.step(step)

  SocketModeApp(app).start()
}
plugins {
  id("org.jetbrains.kotlin.jvm") version "1.6.21"
  id("application")
}
repositories {
  mavenCentral()
}
dependencies {
  implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
  implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
  implementation("com.slack.api:bolt-socket-mode:1.22.0")
  implementation("com.slack.api:slack-api-model-kotlin-extension:1.22.0")
  implementation("com.slack.api:slack-api-client-kotlin-extension:1.22.0")
  implementation("javax.websocket:javax.websocket-api:1.1")
  implementation("org.glassfish.tyrus.bundles:tyrus-standalone-client:1.17")
  implementation('org.slf4j:slf4j-simple:1.7.36') // or logback-classic
}
application {
  mainClassName = "MyAppKt" // add "Kt" suffix for main function source file
}

I hope this helps. Can you close this issue if my response was helpful?

@Arxing
Copy link
Author

Arxing commented May 4, 2022

sorry, findElement() is my extension function

fun ViewState.findElementOrNull(id: String): ViewState.Value? {
  return values.flatMap { (_, stateTree) ->
    stateTree.entries
  }.find { (elementId, _) ->
    elementId == id
  }?.value
}

fun ViewState.findElement(id: String): ViewState.Value = findElementOrNull(id).notNull()

It is expected to return the ViewState.Value of the specified element

@Arxing
Copy link
Author

Arxing commented May 4, 2022

Here is the log that triggers Workflow.save after I add step in WorkflowBuilder and click "save"

2022-05-04 11:09:22.970 [eventLoopGroupProxy-4-1] DEBUG App.runMiddleware():1017 - Applying a middleware (name: com.slack.api.bolt.middleware.builtin.SSLCheck)
2022-05-04 11:09:22.970 [eventLoopGroupProxy-4-1] DEBUG App.runMiddleware():1017 - Applying a middleware (name: com.slack.api.bolt.middleware.builtin.RequestVerification)
2022-05-04 11:09:22.970 [eventLoopGroupProxy-4-1] DEBUG SlackSignature$Verifier.isValid():150 - Request verification (timestamp: 1651633762, body: payload=*****)
2022-05-04 11:09:22.971 [eventLoopGroupProxy-4-1] DEBUG App.runMiddleware():1017 - Applying a middleware (name: com.slack.api.bolt.middleware.builtin.SingleTeamAuthorization)
2022-05-04 11:09:22.971 [eventLoopGroupProxy-4-1] DEBUG App.runMiddleware():1017 - Applying a middleware (name: com.slack.api.bolt.middleware.builtin.IgnoringSelfEvents)
2022-05-04 11:09:22.971 [eventLoopGroupProxy-4-1] DEBUG App.runMiddleware():1017 - Applying a middleware (name: com.slack.api.bolt.middleware.builtin.WorkflowStep)
2022-05-04 11:09:22.971 [eventLoopGroupProxy-4-1] DEBUG WorkflowStep.apply():51 - The WorkflowStep middleware started (request type: WorkflowStepSave)
2022-05-04 11:09:23.569 [eventLoopGroupProxy-4-1] DEBUG DetailedLoggingListener.accept():80 - 
[Request URL]
POST https://slack.com/api/workflows.updateStep
[Specified Request Headers]
Authorization: (redacted)
User-Agent: Java-Slack-SDK; slack-api-client/1.22.0; Java HotSpot(TM) 64-Bit Server VM/11.0.12; Mac OS X/11.2; bolt/1.22.0;
[Request Body]
workflow_step_edit_id=3469107548566.2193215711.e43211b2e5dc4224331364fcc259b868&inputs=%7B%22taskName%22%3A%7B%22value%22%3A%22123%22%7D%7D&outputs=%5B%7B%22name%22%3A%22taskName%22%2C%22type%22%3A%22text%22%2C%22label%22%3A%22Task%20Name%22%7D%5D

Content-Type: application/x-www-form-urlencoded
Content Length: 247

[Response Status]
200 
[Response Headers]
date: Wed, 04 May 2022 03:09:23 GMT
server: Apache
x-powered-by: HHVM/4.153.1
access-control-allow-origin: *
referrer-policy: no-referrer
x-slack-backend: r
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-headers: slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags
access-control-expose-headers: x-slack-req-id, retry-after
x-oauth-scopes: users:read,usergroups:read,users:read.email,users.profile:read,groups:read,channels:read,channels:history,chat:write,app_mentions:read,chat:write.customize,chat:write.public,incoming-webhook,commands,emoji:read,im:write,groups:write,mpim:write,workflow.steps:execute
x-accepted-oauth-scopes: workflow.steps:execute
expires: Mon, 26 Jul 1997 05:00:00 GMT
cache-control: private, no-cache, no-store, must-revalidate
pragma: no-cache
x-xss-protection: 0
x-content-type-options: nosniff
x-slack-req-id: e2eebbf0b8c28db0f2a49382a2f4a77c
vary: Accept-Encoding
content-type: application/json; charset=utf-8
x-envoy-upstream-service-time: 232
x-backend: main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow
x-server: slack-www-hhvm-main-iad-1hr0
x-slack-shared-secret-outcome: no-match
via: envoy-www-iad-r9ck, envoy-edge-nrt-ut7a
x-edge-backend: envoy-www
x-slack-edge-shared-secret-outcome: no-match
[Response Body]
{"ok":true}

2022-05-04 11:09:23.569 [eventLoopGroupProxy-4-1] DEBUG WorkflowStep.apply():119 - The WorkflowStep middleware completed (request type: WorkflowStepSave)
2022-05-04 11:09:23.569 [eventLoopGroupProxy-4-1] INFO  application.log():214 - 200 OK: POST - /slack/events

the screenshot
image

it just shows me the error "Sorry, there was a problem trying to save your step." and the dialog doesn't close, but can see in the background that the step have been added, is this a slack WorkflowBuilder bug? Or am I missing some details?

@seratch
Copy link
Member

seratch commented May 4, 2022

@Arxing I am still unsure about the cause of the issue that you are facing. Can you share your app's App Manifest (the YAML settings that you can find at https://api.slack.com/apps)? If your app does not enable Interactivity, the situation may arise.

@Arxing
Copy link
Author

Arxing commented May 4, 2022

Here is my app's App Manifest, I replaced some sensitive information with "*".

display_information:
  name: *
  description: *
  background_color: "#0f3663"
features:
  bot_user:
    display_name: *
    always_online: true
  shortcuts:
    - name: 刪除這則訊息
      type: message
      callback_id: deleteMessage
      description: 刪除這則訊息
    - name: Create a Schedule
      type: global
      callback_id: createSchedule
      description: 快速建立一個 Automation Schedule
  slash_commands:
    - command: /query-top-fatal-issues
      url: *
      description: 查詢 Fatal Crashlytics
      usage_hint: "平台 \b查詢天數 查詢筆數"
      should_escape: false
    - command: /query-top-non-fatal-issues
      url: *
      description: 查詢 Non-Fatal Crashlytics
      usage_hint: "平台 \b查詢天數 查詢筆數"
      should_escape: false
    - command: /gen-appbot-token
      url: *
      description: 取得 App Bot Token
      should_escape: false
    - command: /appbot-set-role
      url: *
      description: 設定使用者的角色
      should_escape: false
    - command: /appbot-admin
      url: *
      description: 管理員功能
      should_escape: false
    - command: /appbot-automation
      url: *
      description: APP 自動化指令
      should_escape: false
    - command: /appbot-test
      url: *
      description: 測試用指令
      should_escape: false
  workflow_steps:
    - name: 測試用 Step
      callback_id: copy_review
oauth_config:
  scopes:
    user:
      - chat:write
      - channels:history
      - reactions:read
      - users:read
      - users:read.email
      - channels:write
      - groups:write
      - mpim:write
      - im:write
    bot:
      - chat:write
      - app_mentions:read
      - chat:write.customize
      - chat:write.public
      - channels:read
      - commands
      - emoji:read
      - groups:read
      - incoming-webhook
      - users.profile:read
      - users:read
      - users:read.email
      - im:write
      - groups:write
      - mpim:write
      - workflow.steps:execute
settings:
  event_subscriptions:
    request_url: *
    bot_events:
      - workflow_step_execute
  interactivity:
    is_enabled: true
    request_url: *
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false

@seratch
Copy link
Member

seratch commented May 4, 2022

@Arxing Thanks for sharing the app's Manifest. The settings look great. Another possible cause would be a 3-second timeout. Your whole save function execution might take 3+ seconds for some reason. Can you modify your save method as below?

.save { req, context ->
  app.executorService().submit {
    // Run the following code asynchronously
    val stateValues = req.payload.view.state.values
    val inputs = buildMap {
      put("taskName", WorkflowSteps.stepInput {
        it.value(stateValues["task_name_input"]?.get("task_name")?.value)
      })
    }
    val outputs = buildList {
      add(WorkflowSteps.stepOutput {
        it.name("taskName").type("text").label("Task Name")
      })
    }
    context.update(inputs, outputs)
  }
  // Immediately acknowledge the request
  context.ack()
}

The context.update utility internally performs workflows.* API calls. Those APIs tend to be a bit slow. If the context.update takes a bit longer time in your use case, the approach should help. Also, if the code works for you, you can go with this workaround for production apps too.

@Arxing
Copy link
Author

Arxing commented May 4, 2022

I know there's a 3-second timeout.

I've tried calling Context.update() asynchronously and immediately returning Context.ack()(< 10ms), but still the same result. 🥲

@seratch
Copy link
Member

seratch commented May 4, 2022

@Arxing Thanks for your response. hmm, I don't have anything further to guess at this point.

I would suggest trying my example and seeing if there is any difference. Here is my App Manifest:

display_information:
  name: workflow-step-socket-mode-app
features:
  bot_user:
    display_name: workflow-step-socket-mode-app
    always_online: false
  workflow_steps:
    - name: socket-mode-step
      callback_id: socket-mode-step
oauth_config:
  scopes:
    bot:
      - workflow.steps:execute
settings:
  event_subscriptions:
    bot_events:
      - workflow_step_execute
  interactivity:
    is_enabled: true
  org_deploy_enabled: false
  socket_mode_enabled: true
  token_rotation_enabled: false

You can create a new app and then generate a new App-Level token with connections:write scope. You can install the app into your Slack workspace. Once you set SLACK_BOT_TOKEN and SLACK_APP_TOKEN env variables, the app should work for you.

If your issue can arise only with a specific app and/or a workspace, please contact our customer support agents for further help by checking the Slack server-side activity data. You can submit inquiries either by /feedback command in your Slack workspace or at https://my.slack.com/help/requests/new .

@Arxing
Copy link
Author

Arxing commented May 25, 2022

Hi @seratch I'm happy to tell you that I solved my problem! the problem is because I used ngrok to develop locally, when I deploy to the remote machine everything works fine!

But I immediately encountered another problem, I successfully added the step I wrote in my workflow, but when I am in workflowStepSave or workflowStepExecute I will receive /slack/challenge request, but there is no challenge in the request body, the request body I received looks like this:

{
  "token": "*",
  "team_id": "*",
  "api_app_id": "*",
  "event": {
    "type": "workflow_step_execute",
    "callback_id": "triggerAction",
    "workflow_step": {
      "workflow_step_execute_id": "3569568604502.2193215711.7d10728bace18d43446a83d38ff5e584",
      "workflow_id": "409291001966899534",
      "workflow_instance_id": "409292316780540782",
      "step_id": "fcef04d0-62e0-4c0d-b4c8-8bf064b42d96",
      "inputs": {
        "action": {
          "value": "v-a"
        }
      },
      "outputs": [
        {
          "name": "name-label",
          "type": "text",
          "label": "label"
        }
      ]
    },
    "event_ts": "1653486831.627975"
  },
  "type": "event_callback",
  "event_id": "Ev03HMU2N2QG",
  "event_time": 1653486831,
  "authorizations": [
    {
      "enterprise_id": null,
      "team_id": "*",
      "user_id": "*",
      "is_bot": true,
      "is_enterprise_install": false
    }
  ],
  "is_ext_shared_channel": false
}

and that workflowStepExecute is not executed, I don't know where the problem is

@seratch
Copy link
Member

seratch commented Jun 13, 2022

@Arxing You might miss the logs but I am sure that url_verification request was sent separately. Let me know if you have anything else that you need our help. Otherwise, we will close this issue in a few business days.

@seratch seratch closed this as completed Jun 21, 2022
@lhrolim
Copy link

lhrolim commented Apr 2, 2023

Hi, I am bumping into a similar issue @Arxing pointed out in regards to ngrok and although I could work on a similar manner to deploy to a stage environment, curious to understand better as developing locally would boost productivity drastically.

  1. What could be the reason behind that failure ?
  2. Is there any known workarounds to make it work ?

Thanks

@dsehgal-atlassian
Copy link

Hi @lhrolim @Arxing were you able to figure out the root cause? And If there's a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang:kotlin needs info project:bolt question M-T: User needs support to use the project
Projects
None yet
Development

No branches or pull requests

5 participants