Skip to content

Commit

Permalink
Fix #719 Enable developers to customize the way to handle unmatched r…
Browse files Browse the repository at this point in the history
…equests
  • Loading branch information
seratch committed May 10, 2021
1 parent 2ab39d0 commit 3e0f899
Show file tree
Hide file tree
Showing 7 changed files with 439 additions and 55 deletions.
51 changes: 35 additions & 16 deletions bolt/src/main/java/com/slack/api/bolt/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.slack.api.bolt.service.builtin.FileInstallationService;
import com.slack.api.bolt.service.builtin.oauth.*;
import com.slack.api.bolt.service.builtin.oauth.default_impl.*;
import com.slack.api.bolt.util.ListenerCodeSuggestion;
import com.slack.api.methods.MethodsClient;
import com.slack.api.methods.SlackApiException;
import com.slack.api.methods.response.auth.AuthTestResponse;
Expand Down Expand Up @@ -993,7 +994,9 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No SlashCommandHandler registered for command: {}", request.getPayload().getCommand());
command = request.getPayload().getCommand();
log.warn("No SlashCommandHandler registered for command: {}\n{}",
command, ListenerCodeSuggestion.command(command));
break;
}
case Event: {
Expand All @@ -1007,7 +1010,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
EventsApiPayload<Event> payload = buildEventPayload(request);
return handler.apply(payload, request.getContext());
}
log.warn("No BoltEventHandler registered for event: {}", request.getEventTypeAndSubtype());
log.warn("No BoltEventHandler registered for event: {}\n{}",
request.getEventTypeAndSubtype(), ListenerCodeSuggestion.event(request.getEventTypeAndSubtype()));
break;
}
case UrlVerification: {
Expand All @@ -1029,7 +1033,9 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No AttachmentActionHandler registered for callback_id: {}", request.getPayload().getCallbackId());
callbackId = request.getPayload().getCallbackId();
log.warn("No AttachmentActionHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.attachmentAction(callbackId));
break;
}
case BlockAction: {
Expand All @@ -1048,7 +1054,9 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No BlockActionHandler registered for action_id: {}", actions.get(0).getActionId());
actionId = actions.get(0).getActionId();
log.warn("No BlockActionHandler registered for action_id: {}\n{}",
actionId, ListenerCodeSuggestion.blockAction(actionId));
} else {
for (BlockActionPayload.Action action : request.getPayload().getActions()) {
// Returned response values will be ignored
Expand All @@ -1070,7 +1078,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No BlockSuggestionHandler registered for action_id: {}", actionId);
log.warn("No BlockSuggestionHandler registered for action_id: {}\n{}",
actionId, ListenerCodeSuggestion.blockSuggestion(actionId));
break;
}
case GlobalShortcut: {
Expand All @@ -1084,7 +1093,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No GlobalShortcutHandler registered for callback_id: {}", callbackId);
log.warn("No GlobalShortcutHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.globalShortcut(callbackId));
break;
}
case MessageShortcut: {
Expand All @@ -1098,7 +1108,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No MessageShortcutHandler registered for callback_id: {}", callbackId);
log.warn("No MessageShortcutHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.messageShortcut(callbackId));
break;
}
case DialogSubmission: {
Expand All @@ -1112,7 +1123,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No DialogSubmissionHandler registered for callback_id: {}", callbackId);
log.warn("No DialogSubmissionHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.dialogSubmission(callbackId));
break;
}
case DialogCancellation: {
Expand All @@ -1126,7 +1138,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No DialogCancellationHandler registered for callback_id: {}", callbackId);
log.warn("No DialogCancellationHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.dialogCancellation(callbackId));
break;
}
case DialogSuggestion: {
Expand All @@ -1140,7 +1153,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No DialogSuggestionHandler registered for callback_id: {}", callbackId);
log.warn("No DialogSuggestionHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.dialogSuggestion(callbackId));
break;
}
case ViewSubmission: {
Expand All @@ -1154,7 +1168,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No ViewSubmissionHandler registered for callback_id: {}", callbackId);
log.warn("No ViewSubmissionHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.viewSubmission(callbackId));
break;
}
case ViewClosed: {
Expand All @@ -1168,7 +1183,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No ViewClosedHandler registered for callback_id: {}", callbackId);
log.warn("No ViewClosedHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.viewClosed(callbackId));
break;
}
case WorkflowStepEdit: {
Expand All @@ -1182,7 +1198,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No WorkflowStepEditHandler registered for callback_id: {}", callbackId);
log.warn("No WorkflowStepEditHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.WORKFLOW_STEP);
break;
}
case WorkflowStepSave: {
Expand All @@ -1196,7 +1213,8 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
}
}
}
log.warn("No WorkflowStepSaveHandler registered for callback_id: {}", callbackId);
log.warn("No WorkflowStepSaveHandler registered for callback_id: {}\n{}",
callbackId, ListenerCodeSuggestion.WORKFLOW_STEP);
break;
}
case WorkflowStepExecute: {
Expand All @@ -1221,12 +1239,13 @@ protected Response runHandler(Request slackRequest) throws IOException, SlackApi
EventsApiPayload<Event> payload = buildEventPayload(request);
return handler.apply(payload, request.getContext());
}
log.warn("No BoltEventHandler registered for event: {}", request.getEventTypeAndSubtype());
log.warn("No BoltEventHandler registered for event: {}\n{}",
request.getEventTypeAndSubtype(), ListenerCodeSuggestion.WORKFLOW_STEP);
break;
}
default:
}
return Response.json(404, "{\"error\":\"no handler found\"}");
return appConfig.getUnmatchedRequestHandler().handle(slackRequest);
} finally {
if (log.isDebugEnabled()) {
log.debug("The handler completed (request type: {})", slackRequest.getRequestType());
Expand Down
9 changes: 9 additions & 0 deletions bolt/src/main/java/com/slack/api/bolt/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.slack.api.Slack;
import com.slack.api.SlackConfig;
import com.slack.api.bolt.handler.UnmatchedRequestHandler;
import com.slack.api.bolt.handler.builtin.DefaultUnmatchedRequestHandler;
import com.slack.api.bolt.meta.BoltLibraryVersion;
import com.slack.api.bolt.service.builtin.oauth.view.OAuthInstallPageRenderer;
import com.slack.api.bolt.service.builtin.oauth.view.OAuthRedirectUriPageRenderer;
Expand Down Expand Up @@ -157,6 +159,13 @@ public void setOAuthCallbackEnabled(boolean enabled) {
private transient OAuthRedirectUriPageRenderer oAuthRedirectUriPageRenderer =
new OAuthDefaultRedirectUriPageRenderer();

/**
* Handles unmatched requests (default behavior is simply returning 404 Not Found).
*/
@Builder.Default
private transient UnmatchedRequestHandler unmatchedRequestHandler =
new DefaultUnmatchedRequestHandler();

// --------------------------

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.slack.api.bolt.handler;

import com.slack.api.bolt.request.Request;
import com.slack.api.bolt.response.Response;

@FunctionalInterface
public interface UnmatchedRequestHandler {

Response handle(Request<?> request);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.slack.api.bolt.handler.builtin;

import com.slack.api.bolt.request.Request;
import com.slack.api.bolt.response.Response;
import com.slack.api.bolt.handler.UnmatchedRequestHandler;

public class DefaultUnmatchedRequestHandler implements UnmatchedRequestHandler {
@Override
public Response handle(Request<?> request) {
return Response.json(404, "{\"error\":\"no handler found\"}");
}
}
142 changes: 142 additions & 0 deletions bolt/src/main/java/com/slack/api/bolt/util/ListenerCodeSuggestion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package com.slack.api.bolt.util;

public class ListenerCodeSuggestion {
private ListenerCodeSuggestion() {
}

public static final String COMMON_PREFIX = "---\n" +
"[Suggestion] You can handle this type of event with the following listener function:\n\n";

public static final String WORKFLOW_STEP = COMMON_PREFIX +
"WorkflowStep step = WorkflowStep.builder()\n" +
" .callbackId(\"copy_review\")\n" +
" .edit((req, ctx) -> { return ctx.ack(); })\n" +
" .save((req, ctx) -> { return ctx.ack(); })\n" +
" .execute((req, ctx) -> { return ctx.ack(); })\n" +
" .build();\n" +
"\n" +
"app.step(step);\n";

public static final String viewSubmission(String callbackId) {
return COMMON_PREFIX +
"app.viewSubmission(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Sent inputs: req.getPayload().getView().getState().getValues()\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String viewClosed(String callbackId) {
return COMMON_PREFIX +
"app.viewClosed(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String dialogSubmission(String callbackId) {
return COMMON_PREFIX +
"app.dialogSubmission(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String dialogSuggestion(String callbackId) {
return COMMON_PREFIX +
"app.dialogSubmission(\"" + callbackId + "\", (req, ctx) -> {\n" +
" List<Option> options = Arrays.asList(Option.builder().label(\"label\").value(\"value\").build());\n" +
" return ctx.ack(r -> r.options(options));\n" +
"});\n";
}

public static String dialogCancellation(String callbackId) {
return COMMON_PREFIX +
"app.dialogCancellation(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String command(String command) {
return COMMON_PREFIX +
"app.command(\"" + command + "\", (req, ctx) -> {\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String attachmentAction(String callbackId) {
return COMMON_PREFIX +
"app.attachmentAction(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String blockAction(String actionId) {
return COMMON_PREFIX +
"app.blockAction(\"" + actionId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String blockSuggestion(String actionId) {
return COMMON_PREFIX +
"app.blockSuggestion(\"" + actionId + "\", (req, ctx) -> {\n" +
" List<Option> options = Arrays.asList(Option.builder().text(plainText(\"label\")).value(\"v\").build());\n" +
" return ctx.ack(r -> r.options(options));\n" +
"});\n";
}

public static final String event(String eventTypeAndSubtype) {
String className = toEventClassName(eventTypeAndSubtype);
return COMMON_PREFIX +
"app.event(" + className + ".class, (payload, ctx) -> {\n" +
" return ctx.ack();\n" +
"});\n";
}

public static final String toEventClassName(String eventTypeAndSubtype) {
String eventType = eventTypeAndSubtype;
String[] elements = eventTypeAndSubtype.split(":");
if (elements.length == 2) {
// message events with subtype
eventType = elements[0] + "_" + elements[1]
.replaceFirst("_message", "")
.replaceFirst("message_", "");
}
if (eventType == null) {
return "";
}
StringBuilder sb = new StringBuilder();
char[] cs = eventType.toCharArray();
for (int i = 0; i < cs.length; i++) {
char c = cs[i];
if (i == 0) {
sb.append(Character.toUpperCase(c));
} else if (c == '_') {
i++;
sb.append(Character.toUpperCase(cs[i]));
} else {
sb.append(c);
}
}
return sb.toString() + "Event";
}

public static String globalShortcut(String callbackId) {
return COMMON_PREFIX +
"app.globalShortcut(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}

public static String messageShortcut(String callbackId) {
return COMMON_PREFIX +
"app.messageShortcut(\"" + callbackId + "\", (req, ctx) -> {\n" +
" // Do something where\n" +
" return ctx.ack();\n" +
"});\n";
}
}
Loading

0 comments on commit 3e0f899

Please sign in to comment.