diff --git a/pom.xml b/pom.xml index 1ea7917..51a7594 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.katalon katalon-studio-slack-plugin - 1.0.6 + 1.0.7 bundle @@ -26,15 +26,14 @@ - com.github.seratch - jslack - 1.1.6 - - - org.slf4j - slf4j-api - - + org.apache.httpcomponents + httpclient + 4.5.7 + + + com.google.code.gson + gson + 2.8.5 diff --git a/src/main/java/com/katalon/plugin/slack/SlackEventListenerInitializer.java b/src/main/java/com/katalon/plugin/slack/SlackEventListenerInitializer.java index 7fef87a..f5b6a4f 100644 --- a/src/main/java/com/katalon/plugin/slack/SlackEventListenerInitializer.java +++ b/src/main/java/com/katalon/plugin/slack/SlackEventListenerInitializer.java @@ -1,14 +1,7 @@ package com.katalon.plugin.slack; -import java.io.IOException; - import org.osgi.service.event.Event; -import com.github.seratch.jslack.Slack; -import com.github.seratch.jslack.api.methods.SlackApiException; -import com.github.seratch.jslack.shortcut.Shortcut; -import com.github.seratch.jslack.shortcut.model.ApiToken; -import com.github.seratch.jslack.shortcut.model.ChannelName; import com.katalon.platform.api.event.EventListener; import com.katalon.platform.api.event.ExecutionEvent; import com.katalon.platform.api.exception.ResourceException; @@ -32,25 +25,23 @@ public void registerListener(EventListener listener) { if (ExecutionEvent.TEST_SUITE_FINISHED_EVENT.equals(event.getTopic())) { ExecutionEvent eventObject = (ExecutionEvent) event.getProperty("org.eclipse.e4.data"); - Slack slack = Slack.getInstance(); - ApiToken token = ApiToken.of(authToken); - Shortcut shortcut = slack.shortcut(token); TestSuiteExecutionContext testSuiteContext = (TestSuiteExecutionContext) eventObject .getExecutionContext(); TestSuiteStatusSummary testSuiteSummary = TestSuiteStatusSummary.of(testSuiteContext); System.out.println("Slack: Start sending summary message to channel: " + channel); - shortcut.postAsBot(ChannelName.of(channel), - "Summary execution result of test suite: " + testSuiteContext.getSourceId() - + "\nTotal test cases: " + Integer.toString(testSuiteSummary.getTotalTestCases()) - + "\nTotal passes: " + Integer.toString(testSuiteSummary.getTotalPasses()) - + "\nTotal failures: " + Integer.toString(testSuiteSummary.getTotalFailures()) - + "\nTotal errors: " + Integer.toString(testSuiteSummary.getTotalErrors()) - + "\nTotal skipped: " + Integer.toString(testSuiteSummary.getTotalSkipped())); + String message = "Summary execution result of test suite: " + testSuiteContext.getSourceId() + + "\nTotal test cases: " + Integer.toString(testSuiteSummary.getTotalTestCases()) + + "\nTotal passes: " + Integer.toString(testSuiteSummary.getTotalPasses()) + + "\nTotal failures: " + Integer.toString(testSuiteSummary.getTotalFailures()) + + "\nTotal errors: " + Integer.toString(testSuiteSummary.getTotalErrors()) + + "\nTotal skipped: " + Integer.toString(testSuiteSummary.getTotalSkipped()); + SlackUtil.sendMessage(authToken, channel, message); + System.out.println("Slack: Summary message has been successfully sent"); } - } catch (ResourceException | IOException | SlackApiException e) { - e.printStackTrace(System.out); + } catch (ResourceException | SlackException e) { + e.printStackTrace(System.err); } }); } diff --git a/src/main/java/com/katalon/plugin/slack/SlackException.java b/src/main/java/com/katalon/plugin/slack/SlackException.java new file mode 100644 index 0000000..c5f1645 --- /dev/null +++ b/src/main/java/com/katalon/plugin/slack/SlackException.java @@ -0,0 +1,18 @@ +package com.katalon.plugin.slack; + +public class SlackException extends Exception { + + private static final long serialVersionUID = 1171601532588814667L; + + public SlackException(String message) { + super(message, null); + } + + public SlackException(Throwable throwable) { + super("", throwable); + } + + public SlackException(String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/src/main/java/com/katalon/plugin/slack/SlackPreferencePage.java b/src/main/java/com/katalon/plugin/slack/SlackPreferencePage.java index d4dfa53..0383eb6 100644 --- a/src/main/java/com/katalon/plugin/slack/SlackPreferencePage.java +++ b/src/main/java/com/katalon/plugin/slack/SlackPreferencePage.java @@ -1,5 +1,6 @@ package com.katalon.plugin.slack; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.swt.SWT; @@ -15,10 +16,6 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; -import com.github.seratch.jslack.Slack; -import com.github.seratch.jslack.shortcut.Shortcut; -import com.github.seratch.jslack.shortcut.model.ApiToken; -import com.github.seratch.jslack.shortcut.model.ChannelName; import com.katalon.platform.api.exception.ResourceException; import com.katalon.platform.api.preference.PluginPreference; import com.katalon.platform.api.service.ApplicationManager; @@ -100,20 +97,19 @@ private void testSlackConnection(String token, String channel) { lblConnectionStatus.setText("Connecting..."); thread = new Thread(() -> { try { - Slack slack = Slack.getInstance(); - ApiToken apiToken = ApiToken.of(token); - Shortcut shortcut = slack.shortcut(apiToken); - shortcut.postAsBot(ChannelName.of(channel), "This is a test message from Katalon Studio using Slack Plugin"); + SlackUtil.sendMessage(token, channel, "This is a test message from Katalon Studio using Slack Plugin"); syncExec(() -> { lblConnectionStatus .setForeground(lblConnectionStatus.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN)); lblConnectionStatus.setText("Connection success"); }); } catch (Exception e) { + e.printStackTrace(System.err); syncExec(() -> { lblConnectionStatus .setForeground(lblConnectionStatus.getDisplay().getSystemColor(SWT.COLOR_DARK_RED)); - lblConnectionStatus.setText("Connection failed"); + lblConnectionStatus + .setText("Connection failed. Reason: " + StringUtils.defaultString(e.getMessage())); }); } finally { syncExec(() -> btnTestConnection.setEnabled(true)); diff --git a/src/main/java/com/katalon/plugin/slack/SlackUtil.java b/src/main/java/com/katalon/plugin/slack/SlackUtil.java new file mode 100644 index 0000000..b35f391 --- /dev/null +++ b/src/main/java/com/katalon/plugin/slack/SlackUtil.java @@ -0,0 +1,180 @@ +package com.katalon.plugin.slack; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import com.google.gson.stream.JsonReader; + +public class SlackUtil { + public static final String RES_IS_OK = "isOk"; + + public static final String RES_ERROR_MSG = "errorMsg"; + + /** + * Send message to Slack for Team Collaboration + * + * @param msg String message to send + * @throws SlackException + */ + public static void sendMessage(String token, String channel, String msg) throws SlackException { + try { + Map response = getResponseFromSendingMsg(buildSlackUri(token, channel, msg)); + + boolean isOk = (boolean) response.get(RES_IS_OK); + if (!isOk) { + String errorMsg = (String) response.get(RES_ERROR_MSG); + throw new SlackException(errorMsg); + } + } catch (IOException | URISyntaxException e) { + throw new SlackException(e); + } + } + + /** + * Slack URI builder which will encode user info and message into URI + * + * @param token the authentication token + * @param channel Channel name + * @param username Bot name + * @param asIssuedTokenUser if true, Bot name will be ignore. Otherwise, message will be sent using Bot name + * @param msg Message to send + * @return URI + * @throws URISyntaxException + * @throws Exception UnsupportedEncodingException + * @see UnsupportedEncodingException + * @see Create Slack auth token + */ + public static URI buildSlackUri(String token, String channel, String msg) throws URISyntaxException { + // scheme:[//[user:password@]host[:port]][/]path[?query][#fragment] + URIBuilder uriBuilder = new URIBuilder().setScheme("https") + .setHost("slack.com") + .setPath("/api/chat.postMessage") + .addParameter("token", token) + .addParameter("channel", channel); + uriBuilder.addParameter("text", msg); + return uriBuilder.build(); + } + + /** + * Get response data from sending message to Slack + * + * @param uri URI to send GET request + * @return A Map object with 2 keys SlackUtil.RES_IS_OK and SlackUtil.RES_ERROR_MSG + * @throws ClientProtocolException + * @throws IOException + */ + public static Map getResponseFromSendingMsg(URI uri) throws ClientProtocolException, IOException { + boolean isOk = false; + String errorMsg = null; + + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpGet httpget = new HttpGet(uri); + CloseableHttpResponse response = httpclient.execute(httpget); + try { + HttpEntity entity = response.getEntity(); + if (entity != null) { + InputStreamReader in = new InputStreamReader(entity.getContent()); + try { + JsonReader reader = new JsonReader(in); + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (StringUtils.equals(name, "ok")) { + isOk = reader.nextBoolean(); + } else if (StringUtils.equals(name, "error")) { + errorMsg = reader.nextString(); + } else { + reader.skipValue(); // avoid some unhandled events + } + } + reader.endObject(); + reader.close(); + } finally { + in.close(); + } + } + } finally { + response.close(); + } + + Map res = new HashMap(); + res.put(RES_IS_OK, isOk); + res.put(RES_ERROR_MSG, errorMsg); + return res; + } + + /** + * Slack Bold format text message + * + * @param msg String text message + * @return String Bold format text message + */ + public String fmtBold(String msg) { + return "*" + msg + "*"; + } + + /** + * Slack Italic format text message + * + * @param msg String text message + * @return String Italic format text message + */ + public String fmtItalic(String msg) { + return "_" + msg + "_"; + } + + /** + * Slack responded message status + */ + public static class SlackMsgStatus { + private static SlackMsgStatus _instance; + + private static Map msgMap; + + public static SlackMsgStatus getInstance() { + if (_instance == null) { + _instance = new SlackMsgStatus(); + } + return _instance; + } + + public SlackMsgStatus() { + msgMap = new HashMap(); + msgMap.put("channel_not_found", StringConstants.SLACK_ERROR_MSG_CHANNEL_NOT_FOUND); + msgMap.put("not_in_channel", StringConstants.SLACK_ERROR_MSG_NOT_IN_CHANNEL); + msgMap.put("is_archived", StringConstants.SLACK_ERROR_MSG_IS_ARCHIVED); + msgMap.put("msg_too_long", StringConstants.SLACK_ERROR_MSG_MSG_TOO_LONG); + msgMap.put("no_text", StringConstants.SLACK_ERROR_MSG_NO_TEXT); + msgMap.put("rate_limited", StringConstants.SLACK_ERROR_MSG_RATE_LIMITED); + msgMap.put("not_authed", StringConstants.SLACK_ERROR_MSG_NOT_AUTHED); + msgMap.put("invalid_auth", StringConstants.SLACK_ERROR_MSG_INVALID_AUTH); + msgMap.put("account_inactive", StringConstants.SLACK_ERROR_MSG_ACCOUNT_INACTIVE); + } + + public Map getMsgMap() { + return msgMap; + } + + public String getMsgDescription(String msgCode) { + if (getMsgMap().get(msgCode) == null) { + // No message description found + return msgCode; + } + return getMsgMap().get(msgCode); + } + } +} diff --git a/src/main/java/com/katalon/plugin/slack/StringConstants.java b/src/main/java/com/katalon/plugin/slack/StringConstants.java new file mode 100644 index 0000000..4493bac --- /dev/null +++ b/src/main/java/com/katalon/plugin/slack/StringConstants.java @@ -0,0 +1,14 @@ +package com.katalon.plugin.slack; + + +public class StringConstants { + public static final String SLACK_ERROR_MSG_CHANNEL_NOT_FOUND = "Invalid value passed for Channel/Group."; + public static final String SLACK_ERROR_MSG_NOT_IN_CHANNEL = "Unable to post user messages to a channel/group they are not in."; + public static final String SLACK_ERROR_MSG_IS_ARCHIVED = "Channel/Group has been archived."; + public static final String SLACK_ERROR_MSG_MSG_TOO_LONG = "Message text is too long."; + public static final String SLACK_ERROR_MSG_NO_TEXT = "No message text provided."; + public static final String SLACK_ERROR_MSG_RATE_LIMITED = "Application has posted too many messages."; + public static final String SLACK_ERROR_MSG_NOT_AUTHED = "No authentication token provided."; + public static final String SLACK_ERROR_MSG_INVALID_AUTH = "Invalid authentication token."; + public static final String SLACK_ERROR_MSG_ACCOUNT_INACTIVE = "Authentication token is for a deleted user or team."; +}