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

Add in block_suggestion interactivity handler support #1645

Merged
merged 9 commits into from Nov 11, 2022

Conversation

hello-ashleyintech
Copy link
Contributor

@hello-ashleyintech hello-ashleyintech commented Nov 2, 2022

Summary

This PR mirrors Fil's work in slackapi/deno-slack-sdk#116 to add interactivity support for block_suggestions in Bolt JS. This PR accomplishes this through adding a blockSuggestion function that can be chained from a SlackFunction object, for example:

const requestApprovalFunc = new SlackFunction(ApprovalFunction.id, notifyApprover);

requestApprovalFunc
  .blockSuggestion({ action_id: "ext_select_input" }, blockSuggestionHandler)
  .action('approve_request', approveActionHandler)
  .action({ action_id: /deny_*.+/ }, denyActionHandler)

This PR also mirrors a lot of Sarah's work in #1567!

Testing

  1. Pull down this branch ah-add-block-suggestions
  2. In a new tab or Terminal window, create a new Bolt JS test app using Bolt JS Request Time Off template - slack create -t slack-samples/bolt-js-request-time-off
  3. Create a symlink - run npm link in the bolt-js repo
  4. Point symlink to @slack/bolt package in your test app - run npm link @slack/bolt in your test app. Now the code from this branch will be available on your test app! ✨
  5. Create a trigger for your test app: slack create trigger --trigger-def "triggers/link-shortcut.json"
  6. In your test app's listeners/functions/request-approval.js file, add the following to the Block Kit within the notifyApprover function's call to client.chat.postMessage:
        {
          type: "input",
          block_id: "ext_select_block",
          optional: true,
          element: {
            type: "external_select",
            action_id: "ext_select_input",
            placeholder: {
              type: "plain_text",
              text: "Inspire",
            },
          },
          label: {
            type: "plain_text",
            text: "Inspirational Quote",
          },
        },
  1. Add in a blockSuggestionHandler function into request-approval.js:
async function blockSuggestionHandler ({ ack, body, client }) {
  console.log('BLOCK SUGGESTION HANDLER, ', JSON.stringify(body, null, 2));

  // Fetch an inspirational quote
  const apiResp = await fetch("https://api.quotable.io/quotes");
  const quotes = await apiResp.json();

  let suggestions = [];

  quotes.results.forEach(quote => {
    if (quote.content.toLowerCase().includes(body.value)) {
      suggestions.push({value: `${quote._id}`, text: {type:"plain_text", text: quote.content.slice(0,70)}})
    }
  })

  console.log('Returning', suggestions.length, 'quotes');

  const opts = {
    "options": suggestions
  }

  await ack(opts);
}
  1. Test the blockSuggestion function by adding any of the following to the requestApprovalFunc at the bottom of the file like so:
requestApprovalFunc
  .blockSuggestion({ action_id: "ext_select_input" }, blockSuggestionHandler)
  .action('approve_request', approveActionHandler) // Support Regex
  .action({ action_id: /deny_*.+/ }, denyActionHandler) // Support constraint object
// Other parameter combos to try:
  .blockSuggestion({ block_id: "ext_select_block", action_id: "ext_select_input" }, blockSuggestionHandler)
  .blockSuggestion({ type: "block_suggestion" }, blockSuggestionHandler)
  .blockSuggestion({ action_id: /ext_*.+/ }, blockSuggestionHandler)
  .blockSuggestion("ext_select_input", blockSuggestionHandler)

Then, run the trigger. You will receive a message from the application and you will be able to search for quotes using the suggestion dropdown. To pull up valid results, you can type in keywords and or let. View the demo below:
block-suggestions-gif-better-lol

Open TODOs for this PR (other than addressing feedback)

  • Add in unit tests
  • Make sure block_suggestions schema looks right and update any existing unit tests that don't align with this schema

TODOs after this PR

  • Add in a blockActions function support to create consistency with the blockSuggestion naming schema

Requirements (place an x in each [ ])

@hello-ashleyintech hello-ashleyintech added the enhancement M-T: A feature request for new functionality label Nov 2, 2022
@hello-ashleyintech hello-ashleyintech self-assigned this Nov 2, 2022
src/App.ts Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Nov 2, 2022

Codecov Report

❗ No coverage uploaded for pull request base (next-gen@f83ac89). Click here to learn what that means.
The diff coverage is n/a.

@@             Coverage Diff             @@
##             next-gen    #1645   +/-   ##
===========================================
  Coverage            ?   80.70%           
===========================================
  Files               ?       20           
  Lines               ?     1778           
  Branches            ?      505           
===========================================
  Hits                ?     1435           
  Misses              ?      221           
  Partials            ?      122           

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@@ -36,36 +36,62 @@ export type KnownOptionsPayloadFromType<T extends string> = Extract<SlackOptions
*/
export interface BlockSuggestion extends StringIndexed {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely would love a double check on this schema and thoughts on the below. 👇

Right now the schema is a hybrid between Fil's schema (which I'm guessing might be part of a newer block_suggestions approach?) and the original Block Suggestions schema added to the options object, which also contains some legacy things (such as InteractiveMessage and DialogSuggestion), about a year and a half ago.

If we use just the new schema, we do get a few errors (ex: the enterprise_id property not being able to be accessed error thrown in App.ts that is called out above, as well as some issues with accessing certain properties in the unit tests where the new and old schemas deviate). This is causing concern for me that using just the new schema might accidentally wipe compatibility for how folks are currently using this with Bolt within .options(), if they are at all. Would love some thoughts on how to approach this, whether it's keeping it as this hybrid approach or trying to migrate everything to the new approach and mitigating any errors that pop up!

Copy link
Member

@srajiang srajiang Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment I lean against trying to consume these types, as internally in bolt-js we already have a model of block_suggestion which needs to be compatible with legacy and next-gen event payloads. I don't want to be using different types internally for blockSuggestion handling vs. .options block suggestion handling events because the underlying events aren't different between next-gen and today's block_suggestion (nor do we want them to drift too much).

It would also add additional dependency for us on deno_slack_sdk to be selectively consuming deno_slack_sdk function handler types. Currently we're keeping this dependency limited to manifest generation.

Would love to know @filmaj's thoughts on this though since sounds like this could be a candidate for the shared types work he's been prototyping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'original' Block Suggestions schema that @hello-ashleyintech links to looks no longer valid to me. I just played around with this PR and it worked great, but, the block suggestion payloads coming in look more like the type I wrote up in the deno-slack-sdk than the original schema in bolt-js.

As for how to go about that, I leave it to you. Whether you want to update the original schema in bolt or consume the deno one, your call. But, definitely, do some testing on your end to inspect the shape of that payload (in both enterprise and non-enterprise workspaces since the shape changes somewhat based on that. I have access to one that Jim set up, ping me if you want in on that).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to this: I ended up adding a new type for Block Suggestions in the next-gen interactivity scenario and left the old Block Suggestions type under options as is! this is to continue legacy support of the original BlockSuggestions while still enabling the next-gen work. See this comment for more context!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So therefore, depending on 1.0 vs. 2.0 context, the payload shapes differ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming! What a headache, though...

src/types/options/index.ts Outdated Show resolved Hide resolved
src/types/options/index.ts Outdated Show resolved Hide resolved
@hello-ashleyintech hello-ashleyintech marked this pull request as ready for review November 7, 2022 20:38
Copy link
Contributor

@filmaj filmaj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work! Worked like a charm for me!

I left some comments on changes, though. I think we do need to update the type of the block suggestion payload to conform to the new structure we seem to be dispatching from the backend.

src/App.ts Outdated Show resolved Hide resolved
src/SlackFunction.spec.ts Show resolved Hide resolved
src/SlackFunction.spec.ts Outdated Show resolved Hide resolved
@@ -36,36 +36,62 @@ export type KnownOptionsPayloadFromType<T extends string> = Extract<SlackOptions
*/
export interface BlockSuggestion extends StringIndexed {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'original' Block Suggestions schema that @hello-ashleyintech links to looks no longer valid to me. I just played around with this PR and it worked great, but, the block suggestion payloads coming in look more like the type I wrote up in the deno-slack-sdk than the original schema in bolt-js.

As for how to go about that, I leave it to you. Whether you want to update the original schema in bolt or consume the deno one, your call. But, definitely, do some testing on your end to inspect the shape of that payload (in both enterprise and non-enterprise workspaces since the shape changes somewhat based on that. I have access to one that Jim set up, ping me if you want in on that).

/**
* Block Suggestion payload model for next-gen interactivity
*/
export interface BlockSuggestionPayload extends StringIndexed {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the payload from Fil's PR and had to tweak it (make some params optional and add more params) a bit based on what I observed for block_suggestions payloads in both enterprise and regular workspaces. If anyone is testing this, would love confirmation that the payloads look similar! You can view example shapes of enterprise + non enterprise payloads I was getting in the corresponding test file, src/types/block-suggestion/index.spec.ts

Copy link
Member

@srajiang srajiang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work @ashleymcm, things are working nicely on my end on manual tests. A suggestions BlockSuggestionPayload type:

  • We can have BlockSuggestionPayload extend FunctionContext instead of explicitly naming those types to account for the function_data, bot_access_token and optional interactivity types.

I am okay to proceed with merging for now and followup with any improvements though!

@srajiang srajiang merged commit cd432d6 into next-gen Nov 11, 2022
@srajiang srajiang deleted the ah-add-block-suggestion branch November 11, 2022 18:59
srajiang pushed a commit that referenced this pull request Nov 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement M-T: A feature request for new functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants