Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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\"}");
}
}
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