diff --git a/.gitignore b/.gitignore index ec2ce58..4b07193 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,8 @@ venv.bak/ # Java .classpath -.project \ No newline at end of file +.project + +# OS/X +.DS_Store +*.xlvals diff --git a/.travis.yml b/.travis.yml index 58abda4..dd6df8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,13 @@ language: java +jdk: openjdk8 before_install: - chmod +x gradlew deploy: provider: releases api_key: secure: KGvqBU3jlrqtmciLW0PM2Hm8XbcXbARcgQLHJpLWFPKe1fR3brRU0PVnZjxPDFEwN8ehNT4DdL8VehutvJZTELyF4xH2OMMLKKgdfBCjB6oJ3mqOBTzvKo3Vdq1JhVtL+4Uc6DASscPwRd06stC6UPGdMDMMRwROWf/VmvWRCb1tjNFM6/Vbx/JE4XESkACvDn8mopYNPRhwvQbiAJAd9tmlcBvrwLRJ7jdcoTMKDDhoCJnfarjvxIvrbTiTeb9dz81vFLA8h7pVTuoVwgomlAzKzXKA3r2Ub2zIa+3EhXbmfGKFGReG9rOJ57r7mxSCgZTP+xi3VcH35hJyHbAOhs+7aOIbxZ90G/tVknA0UtOFe9MIqVApjn3VCAuOki24xITSsDdFt5t1qpmWNGVXfO/1kXacGHcezhTM9qlMrKosKCkezkMAMzcKnwsSmZXP+IwlhgCKdbJsHrcHbBsPF1Crkav07daBUX8nTcnsHsZAYE76S9pMJi4TXyVe93zFUv+o4lM47QWrYVOeZc1zlFpzI8rlt+OF9dczRQJ78McYh0gbhDMA8GZ4nVyRfMq/N+cjWsnHSISgxEdKQBvIbPE86hgH44ckDGdbwJBY6LcDNLMJ/e6yCQ5OX5CFnUEHAuQj2nXkGq0KBTCHvB1RGL9hQCg/UyXUxjggYrRbC+o= - file: build/libs/xlr-slack-plugin-1.0.0.jar + file_glob: true + file: build/libs/* skip_cleanup: true on: tags: true diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c77270a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2019 XEBIALABS + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 0f31146..e35ff0e 100755 --- a/README.md +++ b/README.md @@ -41,7 +41,65 @@ The Slack notification task needs the next information: ![notification](images/notification.png) ---- +## CreateChannel task +This task creates a new channel in slack + +![CreateChannel](images/CreateChannel.png) + +## AddUsersToChannel +This task adds a list of users based on their email addresses + +![AddUsersToChannel](images/AddUsersToChannel.png) + +## postMessage +This task posts a message to a slack channel based on the channel's ID + +![postMessage](images/postMessage.png) + +## Adding Slack App +This plugin needs a **Slack** to communicate. You can create a custom app to to by doing the following: + +1. Got to [https://api.slack.com/apps](https://api.slack.com/apps) to start building your app + + ![YourApps](images/yourapps.png) + +2. **Create a Slack App** - provide *App Name* and *Workspace* + + ![createaslackapp](images/createaslackapp.png) + +3. **Add features and functionality** - *Incoming Webhooks* & *Permissions* + + a. **Incoming Webhooks** + + ![incomingwebhooks](images/incomingwebhooks.png) + + Keep track of the `Webhook URL`. This will be the URL for sending notifications. + + b. **Permissions** + + ![permissions](images/permissions1.png) + ![permissions](images/permissions2.png) + ![permissions](images/permissions3.png) + + Add **Slack Permissions** as follows: + - Access information about user's public channels `channels:read` + - Modify your private channels `channels:write` + - Post to specific channels in Slack `incomming-webhook` + - Add a bot user with the username *@* `chat:write:bot` + - Access your workspace's profile information `users:read` + - View email addresses of people on this workspace `users:read.email` + + Keep track of the `OAuth Token` and **Reinstall App** + + Use the `Webhook URL` and the `OAuth Token` to configure the connection in *XL Release* + + +--- ## References: * [Slack Rest API](https://api.slack.com/web) - +* [Slack Rest API->Create Channel](https://api.slack.com/methods/channels.create) +* [Slack Rest API->Create Conversation](https://api.slack.com/methods/conversations.create) +* [Slack Rest API->Channels Invite](https://api.slack.com/methods/channels.invite) +* [Slack Rest API->Users Info](https://api.slack.com/methods/users.info) +* [Slack Rest API->Chat Post Message](https://api.slack.com/methods/chat.postMessage) +* [Slack Rest API->lookup By Email](https://api.slack.com/methods/users.lookupByEmail) diff --git a/build.gradle b/build.gradle index d8dcbd0..22a0889 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,46 @@ // Plugins to use +import org.apache.tools.ant.filters.ReplaceTokens + plugins { - id "java" - id "com.github.hierynomus.license" version "0.13.0" + id "com.github.hierynomus.license" version "0.14.0" + id "nebula.release" version "6.0.0" + id "com.xebialabs.xl.docker" version "1.1.0" + id "com.github.hierynomus.jython" version "0.8.0" } -// License definition -license { - header rootProject.file('src/main/license/xebialabs_community.license') - strictCheck true +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'eclipse' +apply plugin: 'maven' + +xlDocker { + compileImage = 'xebialabsunsupported/xlr_dev_compile' + compileVersion = '9.0' + runImage = 'xebialabsunsupported/xlr_dev_run' + runVersion = '9.0' + runPortMapping = '15516:5516' +} + + +if (!project.hasProperty('release.scope')) { + project.ext['release.scope'] = 'patch' } -// Project version -version="1.0.2" +if (!project.hasProperty('release.useLastTag')) { + project.ext['release.useLastTag'] = true +} + +processResources.configure { + filter ReplaceTokens, tokens: [ + 'project.version': version.toString(), + 'project.name' : rootProject.name + ] +} + +license { + header rootProject.file('LICENSE.md') + excludes(["**/*.json", "**/echarts.min.js", "**/*.png"]) + strictCheck false + ext.year = Calendar.getInstance().get(Calendar.YEAR) + ext.name = 'XEBIALABS' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ca78035..7a3265e 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04c20df..f16d266 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon May 30 12:17:38 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/images/AddUsersToChannel.png b/images/AddUsersToChannel.png new file mode 100644 index 0000000..c28e83a Binary files /dev/null and b/images/AddUsersToChannel.png differ diff --git a/images/CreateChannel.png b/images/CreateChannel.png new file mode 100644 index 0000000..14aed45 Binary files /dev/null and b/images/CreateChannel.png differ diff --git a/images/createaslackapp.png b/images/createaslackapp.png new file mode 100644 index 0000000..510280d Binary files /dev/null and b/images/createaslackapp.png differ diff --git a/images/incomingwebhooks.png b/images/incomingwebhooks.png new file mode 100644 index 0000000..f2eb994 Binary files /dev/null and b/images/incomingwebhooks.png differ diff --git a/images/permissions1.png b/images/permissions1.png new file mode 100644 index 0000000..176a740 Binary files /dev/null and b/images/permissions1.png differ diff --git a/images/permissions2.png b/images/permissions2.png new file mode 100644 index 0000000..2392b13 Binary files /dev/null and b/images/permissions2.png differ diff --git a/images/permissions3.png b/images/permissions3.png new file mode 100644 index 0000000..7d5b97d Binary files /dev/null and b/images/permissions3.png differ diff --git a/images/postMessage.png b/images/postMessage.png new file mode 100644 index 0000000..36be555 Binary files /dev/null and b/images/postMessage.png differ diff --git a/images/yourapps.png b/images/yourapps.png new file mode 100644 index 0000000..eef46e4 Binary files /dev/null and b/images/yourapps.png differ diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 0787fb7..0000000 Binary files a/src/.DS_Store and /dev/null differ diff --git a/src/main/.DS_Store b/src/main/.DS_Store deleted file mode 100644 index 0e98056..0000000 Binary files a/src/main/.DS_Store and /dev/null differ diff --git a/src/main/license/xebialabs_community.license b/src/main/license/xebialabs_community.license deleted file mode 100644 index 55bcd45..0000000 --- a/src/main/license/xebialabs_community.license +++ /dev/null @@ -1,3 +0,0 @@ -THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS -FOR A PARTICULAR PURPOSE. THIS CODE AND INFORMATION ARE NOT SUPPORTED BY XEBIALABS. diff --git a/src/main/resources/slack/AddUsersToChannel.py b/src/main/resources/slack/AddUsersToChannel.py new file mode 100644 index 0000000..0493613 --- /dev/null +++ b/src/main/resources/slack/AddUsersToChannel.py @@ -0,0 +1,129 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import requests +import urllib2 +import urllib + +def addUserIdToChannel(server, channel, userId): + response = {'status': -1} + try: + query_args = {'channel': channel, "user": userId} + url = "%s/channels.invite" % ( server['api'] ) + encoded_args = urllib.urlencode(query_args) + url = "%s?%s" % (url, encoded_args) + token = server['clientToken'] + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + if( not data['ok'] ): + if( data['error'] == "cant_invite_self" ): + print "Oops, Invited myself" + return {'status': 0 } + if( data['error'] == "already_in_channel" ): + print "Oops, User is already in the channel" + return {'status': 0 } + if( data['error'] == "user_not_found" ): + print "Oops, User not found" + return {'status': 0 } + print "url = %s\n\n" % url + #print json.dumps(data, indent=4, sort_keys=True) + print "Error: %s " % data['error'] + return {'status': 100 } + except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!' % error.code + print 'Reason: %s' % error.read() + print "url = %s" % url + return {'status': 300 } + except urllib2.URLError as error: + print myResponse.info() + print 'Network error!' + print 'Reason: %s' % error.reason + print "url = %s" % url + return {'status': 400 } + return {'status': 0 } + +def getUserIdByEmail(server, userEmail): + try: + query_args = {'email': userEmail} + url = "%s/users.lookupByEmail" % ( server['api'] ) + encoded_args = urllib.urlencode(query_args) + url = "%s?%s" % (url, encoded_args) + #url = "%s/users.lookupByEmail?user=%s" % (server['api'], userEmail) + token = server['clientToken'] + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + #print json.dumps(data, indent=4, sort_keys=True) + if( not data['ok'] ): + print "url = %s\n\n" % url + print "Error: %s for userEmail %s\n" % (data['error'], userEmail) + return {'status': 100, 'userId': 0 } + except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!' % error.code + print 'Reason: %s' % error.read() + print "url = %s" % url + return {'status': 300, 'userId': 0 } + except urllib2.URLError as error: + print myResponse.info() + print 'Network error!' + print 'Reason: %s' % error.reason + print "url = %s" % url + return {'status': 400, 'userId': 0 } + return {'status': 0, 'userId': data['user']['id'] } + +# Initialize variables & check parameters +response = '' +url = "%s/channels.invite" % (server['api']) +token = server['clientToken'] +proxyUrl = server['proxyUrl'] +if not url.strip(): + print 'Error!' + print 'Server configuration url undefined\n' + sys.exit(100) +if not token.strip(): + print 'Error!' + print 'Server configuration user token undefined\n' + sys.exit(100) +if not channel.strip(): + print 'Error!' + print 'Parameter channel undefined\n' + sys.exit(200) + +# Set up proxy +# proxyUrl format: 'http:// username:password@proxyurl:proxyport' +if proxyUrl: + proxy = urllib2.ProxyHandler({'http': proxyUrl.strip(), 'https': proxyUrl.strip()}) + auth = urllib2.HTTPBasicAuthHandler() + opener = urllib2.build_opener(proxy, auth, urllib2.HTTPHandler) + urllib2.install_opener(opener) + +# Call Slack Incoming WebHook +#channel = re.sub("#|@", "", channel) +for user in users: + results = getUserIdByEmail(server, user) + if ( results['status'] > 0 ): + #print "ERROR = %s " % results['error'] + print "ERROR getUserIdByEmail for %s\n" % user + else: + results = addUserIdToChannel(server, channel, results['userId']) + if ( results['status'] > 0 ): + print "ERROR = %s " % results['error'] + sys.exit(results['status']) + +sys.exit(0) diff --git a/src/main/resources/slack/CreateChannel.py b/src/main/resources/slack/CreateChannel.py new file mode 100644 index 0000000..0e61183 --- /dev/null +++ b/src/main/resources/slack/CreateChannel.py @@ -0,0 +1,72 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import requests +import urllib2 +import re + +# Initialize variables & check parameters +response = '' +url = "%s/channels.create" % (server['api']) +token = server['clientToken'] +proxyUrl = server['proxyUrl'] +if not url.strip(): + print 'Error!' + print 'Server configuration url undefined\n' + sys.exit(100) +if not token.strip(): + print 'Error!' + print 'Server configuration user token undefined\n' + sys.exit(100) +if not channel.strip(): + print 'Error!' + print 'Parameter channel undefined\n' + sys.exit(200) + +# Set up proxy +# proxyUrl format: 'http:// username:password@proxyurl:proxyport' +if proxyUrl: + proxy = urllib2.ProxyHandler({'http': proxyUrl.strip(), 'https': proxyUrl.strip()}) + auth = urllib2.HTTPBasicAuthHandler() + opener = urllib2.build_opener(proxy, auth, urllib2.HTTPHandler) + urllib2.install_opener(opener) + +# Call Slack Incoming WebHook +channel = re.sub("#|@", "", channel) +url = "%s?name=%s&validate=true" % ( url, channel.strip() ) +try: + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + if( not data['ok'] ): + print "url = %s\n\n" % url + print json.dumps(data, indent=4, sort_keys=True) + print "Error: %s " % data['error'] + sys.exit(100) + channelId = data['channel']['id'] +except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!' % error.code + print 'Reason: %s' % error.read() + print "url = %s" % url + sys.exit(300) +except urllib2.URLError as error: + print myResponse.info() + print 'Network error!' + print 'Reason: %s' % error.reason + print "url = %s" % url + sys.exit(400) + +# Print output +print 'Slack channel %s created successfully' % channel +sys.exit(0) diff --git a/src/main/resources/slack/CreateConversation.py b/src/main/resources/slack/CreateConversation.py new file mode 100644 index 0000000..ed6bac5 --- /dev/null +++ b/src/main/resources/slack/CreateConversation.py @@ -0,0 +1,127 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import requests +import urllib2 +import re + +def getUserIdByEmail(server, userEmail): + try: + query_args = {'email': userEmail} + url = "%s/users.lookupByEmail" % ( server['api'] ) + encoded_args = urllib.urlencode(query_args) + url = "%s?%s" % (url, encoded_args) + #url = "%s/users.lookupByEmail?user=%s" % (server['api'], userEmail) + token = server['clientToken'] + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + print json.dumps(data, indent=4, sort_keys=True) + if( not data['ok'] ): + print "url = %s\n\n" % url + print "Error: %s " % data['error'] + return {'status': 100, 'userId': 0 } + except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!' % error.code + print 'Reason: %s' % error.read() + print "url = %s" % url + return {'status': 300, 'userId': 0 } + except urllib2.URLError as error: + print myResponse.info() + print 'Network error!' + print 'Reason: %s' % error.reason + print "url = %s" % url + return {'status': 400, 'userId': 0 } + return {'status': 0, 'userId': data['user']['id'] } + +# Initialize variables & check parameters +response = '' +url = "%s/conversations.create" % (server['api']) +token = server['clientToken'] +proxyUrl = server['proxyUrl'] +if not url.strip(): + print 'Error!' + print 'Server configuration url undefined\n' + sys.exit(100) +if not token.strip(): + print 'Error!' + print 'Server configuration user token undefined\n' + sys.exit(100) +if not channel.strip(): + print 'Error!' + print 'Parameter channel undefined\n' + sys.exit(200) + +# Set up proxy +# proxyUrl format: 'http:// username:password@proxyurl:proxyport' +if proxyUrl: + proxy = urllib2.ProxyHandler({'http': proxyUrl.strip(), 'https': proxyUrl.strip()}) + auth = urllib2.HTTPBasicAuthHandler() + opener = urllib2.build_opener(proxy, auth, urllib2.HTTPHandler) + urllib2.install_opener(opener) + +user_ids="" +numberOfUsers=0 +for user in users: + results = getUserIdByEmail(server, user) + if ( results['status'] > 0 ): + sys.exit(results['status']) + if numberOfUsers == 0: + user_ids = results['userId'] + else: + user_ids = "%s,%s" % (userList, results['userId']) + numberOfUsers = numberOfUsers + 1 + +if isPrivate: + is_private="true" +else: + is_private="false" + +# Call Slack Incoming WebHook +channel = re.sub("#|@", "", channel) +url = "%s?name=%s&is_private=%s&user_ids=%s" % ( url, channel.strip(), is_private, user_ids ) +try: + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + if( not data['ok'] ): + if( data['error'] == "user_not_found" ): + print "Oops, User not found" + return {'status': 0 } + print "url = %s\n\n" % url + print "```" + print json.dumps(data, indent=4, sort_keys=True) + print "```" + print "Error: %s " % data['error'] + sys.exit(100) + channelId = data['channel']['id'] +except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!\n\n' % error.code + print 'Reason: %s\n\n' % error.read() + print "url = %s\n\n" % url + sys.exit(300) +except urllib2.URLError as error: + print myResponse.info() + print 'Network error!\n\n' + print 'Reason: %s\n\n' % error.reason + print "url = %s\n\n" % url + sys.exit(400) + +# Print output +print '\n\nSlack channel %s created successfully\n\n' % channel +sys.exit(0) diff --git a/src/main/resources/slack/Notification.py b/src/main/resources/slack/Notification.py index eb3bdc4..398c793 100644 --- a/src/main/resources/slack/Notification.py +++ b/src/main/resources/slack/Notification.py @@ -1,7 +1,11 @@ # -# THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS -# FOR A PARTICULAR PURPOSE. THIS CODE AND INFORMATION ARE NOT SUPPORTED BY XEBIALABS. +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # import json @@ -58,7 +62,7 @@ def printData(url, data): if icon: postdata = {'channel': channel.strip(), 'username': user.strip(), 'icon_emoji': icon.strip(), 'text': message.strip(), 'mrkdwn': True} else: - postdata = {'channel': channel.strip(), 'username': user.strip(), 'text': message.strip(), 'mrkdwn': True} + postdata = {'channel': channel.strip(), 'username': user.strip(), 'text': message, 'mrkdwn': True} data = json.dumps(postdata) response = urllib2.urlopen(request, data) except urllib2.HTTPError as error: diff --git a/src/main/resources/slack/postMessage.py b/src/main/resources/slack/postMessage.py new file mode 100644 index 0000000..cdda2ed --- /dev/null +++ b/src/main/resources/slack/postMessage.py @@ -0,0 +1,66 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import requests +import urllib2 +import re + +# Initialize variables & check parameters +response = '' +token = server['clientToken'] +proxyUrl = server['proxyUrl'] +url = "%s/chat.postMessage" % ( server['api'] ) +if not url.strip(): + print 'Error!' + print 'Server configuration url undefined\n' + sys.exit(100) +if not token.strip(): + print 'Error!' + print 'Server configuration user token undefined\n' + sys.exit(100) +# Set up proxy +# proxyUrl format: 'http:// username:password@proxyurl:proxyport' +if proxyUrl: + proxy = urllib2.ProxyHandler({'http': proxyUrl.strip(), 'https': proxyUrl.strip()}) + auth = urllib2.HTTPBasicAuthHandler() + opener = urllib2.build_opener(proxy, auth, urllib2.HTTPHandler) + urllib2.install_opener(opener) + +# Call Slack Incoming WebHook +query_args = {'channel': channelId, "text": message, 'mrkdwn': True} +encoded_args = urllib.urlencode(query_args) +url = "%s?%s" % (url, encoded_args) +try: + request = urllib2.Request( url ) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') + request.add_header('Authorization', 'Bearer %s' % token) + myResponse = urllib2.urlopen(request) + data = json.load(myResponse) + if( not data['ok'] ): + print "url = %s\n\n" % url + print json.dumps(data, indent=4, sort_keys=True) + print "Error: %s " % data['error'] + sys.exit(100) +except urllib2.HTTPError as error: + print myResponse.info() + print 'HTTP %s error!' % error.code + print 'Reason: %s' % error.read() + print "url = %s" % url + sys.exit(300) +except urllib2.URLError as error: + print myResponse.info() + print 'Network error!' + print 'Reason: %s' % error.reason + print "url = %s" % url + sys.exit(400) + +# Print output +sys.exit(0) diff --git a/src/main/resources/synthetic.xml b/src/main/resources/synthetic.xml index 4822321..e36b2a8 100644 --- a/src/main/resources/synthetic.xml +++ b/src/main/resources/synthetic.xml @@ -1,34 +1,58 @@ - + - + + + - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/yaml/xebialabs.yaml b/src/test/resources/yaml/xebialabs.yaml new file mode 100644 index 0000000..33359b3 --- /dev/null +++ b/src/test/resources/yaml/xebialabs.yaml @@ -0,0 +1,15 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +apiVersion: xl/v1 +kind: Import +metadata: + imports: + - xebialabs/xlr-configuration.yaml + - xebialabs/xlr-template.yaml diff --git a/src/test/resources/yaml/xebialabs/xlr-configuration.yaml b/src/test/resources/yaml/xebialabs/xlr-configuration.yaml new file mode 100644 index 0000000..3bde079 --- /dev/null +++ b/src/test/resources/yaml/xebialabs/xlr-configuration.yaml @@ -0,0 +1,20 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +--- +apiVersion: xl-release/v1 +kind: Templates +spec: +- directory: slack + children: + - name: slackbot + type: slack.Server + url: !value "slack_url" + userName: Rick Broker Bot + clientToken: !value "slack_clientToken" diff --git a/src/test/resources/yaml/xebialabs/xlr-template.yaml b/src/test/resources/yaml/xebialabs/xlr-template.yaml new file mode 100644 index 0000000..f66d8fe --- /dev/null +++ b/src/test/resources/yaml/xebialabs/xlr-template.yaml @@ -0,0 +1,70 @@ +# +# Copyright 2019 XEBIALABS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +--- +apiVersion: xl-release/v1 +kind: Templates +spec: +- directory: slack + children: + - template: Slack_Test + scheduledStartDate: 2019-10-03T13:00:00Z + dueDate: 2019-10-06T15:45:32.129Z + phases: + - phase: Send Messages + tasks: + - name: Send Message + type: slack.Notification + server: slackbot + channel: '#general' + message: Hello world + - name: Verify Message + type: xlrelease.GateTask + owner: admin + color: '#0099CC' + - phase: New Channel + tasks: + - name: Create New Channel + type: slack.CreateChannel + server: slackbot + channel: '#this-is-a-test' + variableMapping: + pythonScript.channelId: ${channelId} + - name: Send Message to new channel + type: slack.postMessage + server: slackbot + channelId: ${channelId} + message: Hello to new channel + - name: Verify Channel and Messages + type: xlrelease.GateTask + owner: admin + color: '#08B153' + - phase: Add Users to Channel + tasks: + - name: Add Users + type: slack.AddUsersToChannel + server: slackbot + channel: ${channelId} + users: + - rick_broker@yahoo.com + - name: Send Messages to new Users + type: slack.postMessage + server: slackbot + channelId: ${channelId} + message: Added Users + - name: Verify users added and Messages + type: xlrelease.GateTask + owner: admin + color: '#FD8D10' + variables: + - type: xlrelease.StringVariable + key: channelId + requiresValue: false + showOnReleaseStart: false + riskProfile: Default risk profile