Revert "Remove HipChat integration from GitLab"
This reverts commit a5378665a1
.
This commit is contained in:
parent
0de88f5799
commit
cd9ae6bb82
27 changed files with 953 additions and 48 deletions
|
@ -268,6 +268,7 @@ Rails/Presence:
|
|||
- 'app/models/clusters/platforms/kubernetes.rb'
|
||||
- 'app/models/concerns/mentionable.rb'
|
||||
- 'app/models/concerns/token_authenticatable.rb'
|
||||
- 'app/models/project_services/hipchat_service.rb'
|
||||
- 'app/models/project_services/irker_service.rb'
|
||||
- 'app/models/project_services/jira_service.rb'
|
||||
- 'app/models/project_services/kubernetes_service.rb'
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -201,6 +201,9 @@ gem 'connection_pool', '~> 2.0'
|
|||
# Discord integration
|
||||
gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
|
||||
|
||||
# HipChat integration
|
||||
gem 'hipchat', '~> 1.5.0'
|
||||
|
||||
# JIRA integration
|
||||
gem 'jira-ruby', '~> 1.4'
|
||||
|
||||
|
|
|
@ -366,6 +366,9 @@ GEM
|
|||
hashie (>= 3.0)
|
||||
health_check (2.6.0)
|
||||
rails (>= 4.0)
|
||||
hipchat (1.5.2)
|
||||
httparty
|
||||
mimemagic
|
||||
html-pipeline (2.8.4)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
|
@ -1040,6 +1043,7 @@ DEPENDENCIES
|
|||
hangouts-chat (~> 0.0.5)
|
||||
hashie-forbidden_attributes
|
||||
health_check (~> 2.6.0)
|
||||
hipchat (~> 1.5.0)
|
||||
html-pipeline (~> 2.8)
|
||||
html2text
|
||||
httparty (~> 0.16.4)
|
||||
|
|
|
@ -146,6 +146,7 @@ class Project < ApplicationRecord
|
|||
has_one :pipelines_email_service
|
||||
has_one :irker_service
|
||||
has_one :pivotaltracker_service
|
||||
has_one :hipchat_service
|
||||
has_one :flowdock_service
|
||||
has_one :assembla_service
|
||||
has_one :asana_service
|
||||
|
|
311
app/models/project_services/hipchat_service.rb
Normal file
311
app/models/project_services/hipchat_service.rb
Normal file
|
@ -0,0 +1,311 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HipchatService < Service
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
MAX_COMMITS = 3
|
||||
HIPCHAT_ALLOWED_TAGS = %w[
|
||||
a b i strong em br img pre code
|
||||
table th tr td caption colgroup col thead tbody tfoot
|
||||
ul ol li dl dt dd
|
||||
].freeze
|
||||
|
||||
prop_accessor :token, :room, :server, :color, :api_version
|
||||
boolean_accessor :notify_only_broken_pipelines, :notify
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def initialize_properties
|
||||
if properties.nil?
|
||||
self.properties = {}
|
||||
self.notify_only_broken_pipelines = true
|
||||
end
|
||||
end
|
||||
|
||||
def title
|
||||
'HipChat'
|
||||
end
|
||||
|
||||
def description
|
||||
'Private group chat and IM'
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'hipchat'
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'token', placeholder: 'Room token', required: true },
|
||||
{ type: 'text', name: 'room', placeholder: 'Room name or ID' },
|
||||
{ type: 'checkbox', name: 'notify' },
|
||||
{ type: 'select', name: 'color', choices: %w(yellow red green purple gray random) },
|
||||
{ type: 'text', name: 'api_version',
|
||||
placeholder: 'Leave blank for default (v2)' },
|
||||
{ type: 'text', name: 'server',
|
||||
placeholder: 'Leave blank for default. https://hipchat.example.com' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_pipelines' }
|
||||
]
|
||||
end
|
||||
|
||||
def self.supported_events
|
||||
%w(push issue confidential_issue merge_request note confidential_note tag_push pipeline)
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
message = create_message(data)
|
||||
return unless message.present?
|
||||
|
||||
gate[room].send('GitLab', message, message_options(data)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def test(data)
|
||||
begin
|
||||
result = execute(data)
|
||||
rescue StandardError => error
|
||||
return { success: false, result: error }
|
||||
end
|
||||
|
||||
{ success: true, result: result }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gate
|
||||
options = { api_version: api_version.present? ? api_version : 'v2' }
|
||||
options[:server_url] = server unless server.blank?
|
||||
@gate ||= HipChat::Client.new(token, options)
|
||||
end
|
||||
|
||||
def message_options(data = nil)
|
||||
{ notify: notify.present? && Gitlab::Utils.to_boolean(notify), color: message_color(data) }
|
||||
end
|
||||
|
||||
def create_message(data)
|
||||
object_kind = data[:object_kind]
|
||||
|
||||
case object_kind
|
||||
when "push", "tag_push"
|
||||
create_push_message(data)
|
||||
when "issue"
|
||||
create_issue_message(data) unless update?(data)
|
||||
when "merge_request"
|
||||
create_merge_request_message(data) unless update?(data)
|
||||
when "note"
|
||||
create_note_message(data)
|
||||
when "pipeline"
|
||||
create_pipeline_message(data) if should_pipeline_be_notified?(data)
|
||||
end
|
||||
end
|
||||
|
||||
def render_line(text)
|
||||
markdown(text.lines.first.chomp, pipeline: :single_line) if text
|
||||
end
|
||||
|
||||
def create_push_message(push)
|
||||
ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch'
|
||||
ref = Gitlab::Git.ref_name(push[:ref])
|
||||
|
||||
before = push[:before]
|
||||
after = push[:after]
|
||||
|
||||
message = []
|
||||
message << "#{push[:user_name]} "
|
||||
|
||||
if Gitlab::Git.blank_ref?(before)
|
||||
message << "pushed new #{ref_type} <a href=\""\
|
||||
"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
|
||||
" to #{project_link}\n"
|
||||
elsif Gitlab::Git.blank_ref?(after)
|
||||
message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
|
||||
else
|
||||
message << "pushed to #{ref_type} <a href=\""\
|
||||
"#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
|
||||
message << "of <a href=\"#{project.web_url}\">#{project.full_name.gsub!(/\s/, '')}</a> "
|
||||
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
|
||||
|
||||
push[:commits].take(MAX_COMMITS).each do |commit|
|
||||
message << "<br /> - #{render_line(commit[:message])} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)"
|
||||
end
|
||||
|
||||
if push[:commits].count > MAX_COMMITS
|
||||
message << "<br />... #{push[:commits].count - MAX_COMMITS} more commits"
|
||||
end
|
||||
end
|
||||
|
||||
message.join
|
||||
end
|
||||
|
||||
def markdown(text, options = {})
|
||||
return "" unless text
|
||||
|
||||
context = {
|
||||
project: project,
|
||||
pipeline: :email
|
||||
}
|
||||
|
||||
Banzai.render(text, context)
|
||||
|
||||
context.merge!(options)
|
||||
|
||||
html = Banzai.render_and_post_process(text, context)
|
||||
sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
|
||||
|
||||
sanitized_html.truncate(200, separator: ' ', omission: '...')
|
||||
end
|
||||
|
||||
def create_issue_message(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
title = render_line(obj_attr[:title])
|
||||
state = obj_attr[:state]
|
||||
issue_iid = obj_attr[:iid]
|
||||
issue_url = obj_attr[:url]
|
||||
description = obj_attr[:description]
|
||||
|
||||
issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
|
||||
|
||||
message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
|
||||
message << "<pre>#{markdown(description)}</pre>"
|
||||
|
||||
message.join
|
||||
end
|
||||
|
||||
def create_merge_request_message(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
merge_request_id = obj_attr[:iid]
|
||||
state = obj_attr[:state]
|
||||
description = obj_attr[:description]
|
||||
title = render_line(obj_attr[:title])
|
||||
|
||||
merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
|
||||
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
|
||||
message = ["#{user_name} #{state} #{merge_request_link} in " \
|
||||
"#{project_link}: <b>#{title}</b>"]
|
||||
|
||||
message << "<pre>#{markdown(description)}</pre>"
|
||||
message.join
|
||||
end
|
||||
|
||||
def format_title(title)
|
||||
"<b>#{render_line(title)}</b>"
|
||||
end
|
||||
|
||||
def create_note_message(data)
|
||||
data = HashWithIndifferentAccess.new(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = HashWithIndifferentAccess.new(data[:object_attributes])
|
||||
note = obj_attr[:note]
|
||||
note_url = obj_attr[:url]
|
||||
noteable_type = obj_attr[:noteable_type]
|
||||
commit_id = nil
|
||||
|
||||
case noteable_type
|
||||
when "Commit"
|
||||
commit_attr = HashWithIndifferentAccess.new(data[:commit])
|
||||
commit_id = commit_attr[:id]
|
||||
subject_desc = commit_id
|
||||
subject_desc = Commit.truncate_sha(subject_desc)
|
||||
subject_type = "commit"
|
||||
title = format_title(commit_attr[:message])
|
||||
when "Issue"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:issue])
|
||||
subject_id = subj_attr[:iid]
|
||||
subject_desc = "##{subject_id}"
|
||||
subject_type = "issue"
|
||||
title = format_title(subj_attr[:title])
|
||||
when "MergeRequest"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
|
||||
subject_id = subj_attr[:iid]
|
||||
subject_desc = "!#{subject_id}"
|
||||
subject_type = "merge request"
|
||||
title = format_title(subj_attr[:title])
|
||||
when "Snippet"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:snippet])
|
||||
subject_id = subj_attr[:id]
|
||||
subject_desc = "##{subject_id}"
|
||||
subject_type = "snippet"
|
||||
title = format_title(subj_attr[:title])
|
||||
end
|
||||
|
||||
subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
|
||||
message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
|
||||
message << title
|
||||
|
||||
message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
|
||||
message.join
|
||||
end
|
||||
|
||||
def create_pipeline_message(data)
|
||||
pipeline_attributes = data[:object_attributes]
|
||||
pipeline_id = pipeline_attributes[:id]
|
||||
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
|
||||
ref = pipeline_attributes[:ref]
|
||||
user_name = (data[:user] && data[:user][:name]) || 'API'
|
||||
status = pipeline_attributes[:status]
|
||||
duration = pipeline_attributes[:duration]
|
||||
|
||||
branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
|
||||
pipeline_url = "<a href=\"#{project_url}/pipelines/#{pipeline_id}\">##{pipeline_id}</a>"
|
||||
|
||||
"#{project_link}: Pipeline #{pipeline_url} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
|
||||
end
|
||||
|
||||
def message_color(data)
|
||||
pipeline_status_color(data) || color || 'yellow'
|
||||
end
|
||||
|
||||
def pipeline_status_color(data)
|
||||
return unless data && data[:object_kind] == 'pipeline'
|
||||
|
||||
case data[:object_attributes][:status]
|
||||
when 'success'
|
||||
'green'
|
||||
else
|
||||
'red'
|
||||
end
|
||||
end
|
||||
|
||||
def project_name
|
||||
project.full_name.gsub(/\s/, '')
|
||||
end
|
||||
|
||||
def project_url
|
||||
project.web_url
|
||||
end
|
||||
|
||||
def project_link
|
||||
"<a href=\"#{project_url}\">#{project_name}</a>"
|
||||
end
|
||||
|
||||
def update?(data)
|
||||
data[:object_attributes][:action] == 'update'
|
||||
end
|
||||
|
||||
def humanized_status(status)
|
||||
case status
|
||||
when 'success'
|
||||
'passed'
|
||||
else
|
||||
status
|
||||
end
|
||||
end
|
||||
|
||||
def should_pipeline_be_notified?(data)
|
||||
case data[:object_attributes][:status]
|
||||
when 'success'
|
||||
!notify_only_broken_pipelines?
|
||||
when 'failed'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -255,6 +255,7 @@ class Service < ApplicationRecord
|
|||
external_wiki
|
||||
flowdock
|
||||
hangouts_chat
|
||||
hipchat
|
||||
irker
|
||||
jira
|
||||
kubernetes
|
||||
|
|
14
config/initializers/hipchat_client_patch.rb
Normal file
14
config/initializers/hipchat_client_patch.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# This monkey patches the HTTParty used in https://github.com/hipchat/hipchat-rb.
|
||||
module HipChat
|
||||
class Client
|
||||
connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
|
||||
end
|
||||
|
||||
class Room
|
||||
connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
|
||||
end
|
||||
|
||||
class User
|
||||
connection_adapter ::Gitlab::ProxyHTTPConnectionAdapter
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddServicesTypeIndex < ActiveRecord::Migration[5.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :services, :type unless index_exists?(:services, :type)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index :services, :type if index_exists?(:services, :type)
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class RemoveHipchatServices < ActiveRecord::Migration[5.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute "DELETE FROM services WHERE type = 'HipchatService'"
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -1996,7 +1996,6 @@ ActiveRecord::Schema.define(version: 20190326164045) do
|
|||
t.boolean "confidential_note_events", default: true
|
||||
t.index ["project_id"], name: "index_services_on_project_id", using: :btree
|
||||
t.index ["template"], name: "index_services_on_template", using: :btree
|
||||
t.index ["type"], name: "index_services_on_type", using: :btree
|
||||
end
|
||||
|
||||
create_table "shards", force: :cascade do |t|
|
||||
|
|
|
@ -449,6 +449,45 @@ Get Hangouts Chat service settings for a project.
|
|||
GET /projects/:id/services/hangouts-chat
|
||||
```
|
||||
|
||||
## HipChat
|
||||
|
||||
Private group chat and IM
|
||||
|
||||
### Create/Edit HipChat service
|
||||
|
||||
Set HipChat service for a project.
|
||||
|
||||
```
|
||||
PUT /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `token` | string | true | Room token |
|
||||
| `color` | string | false | The room color |
|
||||
| `notify` | boolean | false | Enable notifications |
|
||||
| `room` | string | false |Room name or ID |
|
||||
| `api_version` | string | false | Leave blank for default (v2) |
|
||||
| `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. |
|
||||
|
||||
### Delete HipChat service
|
||||
|
||||
Delete HipChat service for a project.
|
||||
|
||||
```
|
||||
DELETE /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
### Get HipChat service settings
|
||||
|
||||
Get HipChat service settings for a project.
|
||||
|
||||
```
|
||||
GET /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
## Irker (IRC gateway)
|
||||
|
||||
Send IRC messages, on update, to a list of recipients through an Irker gateway.
|
||||
|
|
|
@ -29,8 +29,8 @@ See the documentation below for details on how to configure these services.
|
|||
|
||||
## Project services
|
||||
|
||||
Integration with services such as Campfire, Flowdock, Pivotal Tracker, and Slack
|
||||
are available in the form of a [Project Service][].
|
||||
Integration with services such as Campfire, Flowdock, HipChat,
|
||||
Pivotal Tracker, and Slack are available in the form of a [Project Service][].
|
||||
|
||||
[Project Service]: ../user/project/integrations/project_services.md
|
||||
|
||||
|
|
1
doc/project_services/hipchat.md
Normal file
1
doc/project_services/hipchat.md
Normal file
|
@ -0,0 +1 @@
|
|||
This document was moved to [user/project/integrations/hipchat.md](../user/project/integrations/hipchat.md).
|
|
@ -41,7 +41,7 @@ Objects (usually binary and large) created by a build process. These can include
|
|||
|
||||
### Atlassian
|
||||
|
||||
A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Confluence, Bamboo.
|
||||
A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo.
|
||||
|
||||
### Audit Log
|
||||
|
||||
|
|
|
@ -65,9 +65,7 @@ With GitLab Enterprise Edition, you can also:
|
|||
- View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html).
|
||||
- Leverage continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html).
|
||||
|
||||
You can also [integrate](project/integrations/project_services.md) GitLab with
|
||||
numerous third-party applications, such as Mattermost, Microsoft Teams, Trello,
|
||||
Slack, Bamboo CI, JIRA, and a lot more.
|
||||
You can also [integrate](project/integrations/project_services.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, JIRA, and a lot more.
|
||||
|
||||
## Projects
|
||||
|
||||
|
|
53
doc/user/project/integrations/hipchat.md
Normal file
53
doc/user/project/integrations/hipchat.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Atlassian HipChat
|
||||
|
||||
GitLab provides a way to send HipChat notifications upon a number of events,
|
||||
such as when a user pushes code, creates a branch or tag, adds a comment, and
|
||||
creates a merge request.
|
||||
|
||||
## Setup
|
||||
|
||||
GitLab requires the use of a HipChat v2 API token to work. v1 tokens are
|
||||
not supported at this time. Note the differences between v1 and v2 tokens:
|
||||
|
||||
HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
|
||||
token is allowed to send messages to *any* room.
|
||||
|
||||
HipChat v2 API has tokens that are can be created using the Integrations tab
|
||||
in the Group or Room admin page. By design, these are lightweight tokens that
|
||||
allow GitLab to send messages only to *one* room.
|
||||
|
||||
### Complete these steps in HipChat
|
||||
|
||||
1. Go to: <https://admin.hipchat.com/admin>
|
||||
1. Click on "Group Admin" -> "Integrations".
|
||||
1. Find "Build Your Own!" and click "Create".
|
||||
1. Select the desired room, name the integration "GitLab", and click "Create".
|
||||
1. In the "Send messages to this room by posting this URL" column, you should
|
||||
see a URL in the format:
|
||||
|
||||
```
|
||||
https://api.hipchat.com/v2/room/<room>/notification?auth_token=<token>
|
||||
```
|
||||
|
||||
HipChat is now ready to accept messages from GitLab. Next, set up the HipChat
|
||||
service in GitLab.
|
||||
|
||||
### Complete these steps in GitLab
|
||||
|
||||
1. Navigate to the project you want to configure for notifications.
|
||||
1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
|
||||
1. Click "HipChat".
|
||||
1. Select the "Active" checkbox.
|
||||
1. Insert the `token` field from the URL into the `Token` field on the Web page.
|
||||
1. Insert the `room` field from the URL into the `Room` field on the Web page.
|
||||
1. Save or optionally click "Test Settings".
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you do not see notifications, make sure you are using a HipChat v2 API
|
||||
token, not a v1 token.
|
||||
|
||||
Note that the v2 token is tied to a specific room. If you want to be able to
|
||||
specify arbitrary rooms, you can create an API token for a specific user in
|
||||
HipChat under "Account settings" and "API access". Use the `XXX` value under
|
||||
`auth_token=XXX`.
|
|
@ -36,6 +36,7 @@ Click on the service links to see further configuration instructions and details
|
|||
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
|
||||
| Flowdock | Flowdock is a collaboration web app for technical teams |
|
||||
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
|
||||
| [HipChat](hipchat.md) | Private group chat and IM |
|
||||
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
|
||||
| [JIRA](jira.md) | JIRA issue tracker |
|
||||
| JetBrains TeamCity CI | A continuous integration and build server |
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
|
@ -386,6 +387,44 @@ module API
|
|||
},
|
||||
chat_notification_events
|
||||
].flatten,
|
||||
'hipchat' => [
|
||||
{
|
||||
required: true,
|
||||
name: :token,
|
||||
type: String,
|
||||
desc: 'The room token'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :room,
|
||||
type: String,
|
||||
desc: 'The room name or ID'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :color,
|
||||
type: String,
|
||||
desc: 'The room color'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :notify,
|
||||
type: Boolean,
|
||||
desc: 'Enable notifications'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :api_version,
|
||||
type: String,
|
||||
desc: 'Leave blank for default (v2)'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :server,
|
||||
type: String,
|
||||
desc: 'Leave blank for default. https://hipchat.example.com'
|
||||
}
|
||||
],
|
||||
'irker' => [
|
||||
{
|
||||
required: true,
|
||||
|
@ -690,6 +729,7 @@ module API
|
|||
::ExternalWikiService,
|
||||
::FlowdockService,
|
||||
::HangoutsChatService,
|
||||
::HipchatService,
|
||||
::IrkerService,
|
||||
::JiraService,
|
||||
::KubernetesService,
|
||||
|
|
|
@ -62,4 +62,10 @@ FactoryBot.define do
|
|||
project_key: 'jira-key'
|
||||
)
|
||||
end
|
||||
|
||||
factory :hipchat_service do
|
||||
project
|
||||
type 'HipchatService'
|
||||
token 'test_token'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,11 +14,10 @@ describe 'Disable individual triggers' do
|
|||
end
|
||||
|
||||
context 'service has multiple supported events' do
|
||||
let(:service_name) { 'JIRA' }
|
||||
let(:service_name) { 'HipChat' }
|
||||
|
||||
it 'shows trigger checkboxes' do
|
||||
event_count = JiraService.supported_events.count
|
||||
expect(event_count).to be > 1
|
||||
event_count = HipchatService.supported_events.count
|
||||
|
||||
expect(page).to have_content "Trigger"
|
||||
expect(page).to have_css(checkbox_selector, count: event_count)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'User activates HipChat' do
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
visit(project_settings_integrations_path(project))
|
||||
|
||||
click_link('HipChat')
|
||||
end
|
||||
|
||||
context 'with standart settings' do
|
||||
it 'activates service' do
|
||||
check('Active')
|
||||
fill_in('Room', with: 'gitlab')
|
||||
fill_in('Token', with: 'verySecret')
|
||||
click_button('Save')
|
||||
|
||||
expect(page).to have_content('HipChat activated.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with custom settings' do
|
||||
it 'activates service' do
|
||||
check('Active')
|
||||
fill_in('Room', with: 'gitlab_custom')
|
||||
fill_in('Token', with: 'secretCustom')
|
||||
fill_in('Server', with: 'https://chat.example.com')
|
||||
click_button('Save')
|
||||
|
||||
expect(page).to have_content('HipChat activated.')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,7 @@ describe 'User views services' do
|
|||
it 'shows the list of available services' do
|
||||
expect(page).to have_content('Project services')
|
||||
expect(page).to have_content('Campfire')
|
||||
expect(page).to have_content('HipChat')
|
||||
expect(page).to have_content('Assembla')
|
||||
expect(page).to have_content('Pushover')
|
||||
expect(page).to have_content('Atlassian Bamboo')
|
||||
|
@ -21,7 +22,5 @@ describe 'User views services' do
|
|||
expect(page).to have_content('Asana')
|
||||
expect(page).to have_content('Irker (IRC gateway)')
|
||||
expect(page).to have_content('Packagist')
|
||||
expect(page).to have_content('Mattermost')
|
||||
expect(page).to have_content('Slack')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -223,6 +223,7 @@ project:
|
|||
- packagist_service
|
||||
- pivotaltracker_service
|
||||
- prometheus_service
|
||||
- hipchat_service
|
||||
- flowdock_service
|
||||
- assembla_service
|
||||
- asana_service
|
||||
|
|
|
@ -6794,6 +6794,28 @@
|
|||
"default": false,
|
||||
"wiki_page_events": true
|
||||
},
|
||||
{
|
||||
"id": 93,
|
||||
"title": "HipChat",
|
||||
"project_id": 5,
|
||||
"created_at": "2016-06-14T15:01:51.219Z",
|
||||
"updated_at": "2016-06-14T15:01:51.219Z",
|
||||
"active": false,
|
||||
"properties": {
|
||||
"notify_only_broken_pipelines": true
|
||||
},
|
||||
"template": false,
|
||||
"push_events": true,
|
||||
"issues_events": true,
|
||||
"merge_requests_events": true,
|
||||
"tag_push_events": true,
|
||||
"note_events": true,
|
||||
"pipeline_events": true,
|
||||
"type": "HipchatService",
|
||||
"category": "common",
|
||||
"default": false,
|
||||
"wiki_page_events": true
|
||||
},
|
||||
{
|
||||
"id": 91,
|
||||
"title": "Flowdock",
|
||||
|
|
408
spec/models/project_services/hipchat_service_spec.rb
Normal file
408
spec/models/project_services/hipchat_service_spec.rb
Normal file
|
@ -0,0 +1,408 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe HipchatService do
|
||||
describe "Associations" do
|
||||
it { is_expected.to belong_to :project }
|
||||
it { is_expected.to have_one :service_hook }
|
||||
end
|
||||
|
||||
describe 'Validations' do
|
||||
context 'when service is active' do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of(:token) }
|
||||
end
|
||||
|
||||
context 'when service is inactive' do
|
||||
before do
|
||||
subject.active = false
|
||||
end
|
||||
|
||||
it { is_expected.not_to validate_presence_of(:token) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "Execute" do
|
||||
let(:hipchat) { described_class.new }
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' }
|
||||
let(:project_name) { project.full_name.gsub(/\s/, '') }
|
||||
let(:token) { 'verySecret' }
|
||||
let(:server_url) { 'https://hipchat.example.com'}
|
||||
let(:push_sample_data) do
|
||||
Gitlab::DataBuilder::Push.build_sample(project, user)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(hipchat).to receive_messages(
|
||||
project_id: project.id,
|
||||
project: project,
|
||||
room: 123456,
|
||||
server: server_url,
|
||||
token: token
|
||||
)
|
||||
WebMock.stub_request(:post, api_url)
|
||||
end
|
||||
|
||||
it 'tests and return errors' do
|
||||
allow(hipchat).to receive(:execute).and_raise(StandardError, 'no such room')
|
||||
result = hipchat.test(push_sample_data)
|
||||
|
||||
expect(result[:success]).to be_falsey
|
||||
expect(result[:result].to_s).to eq('no such room')
|
||||
end
|
||||
|
||||
it 'uses v1 if version is provided' do
|
||||
allow(hipchat).to receive(:api_version).and_return('v1')
|
||||
expect(HipChat::Client).to receive(:new).with(
|
||||
token,
|
||||
api_version: 'v1',
|
||||
server_url: server_url
|
||||
).and_return(double(:hipchat_service).as_null_object)
|
||||
hipchat.execute(push_sample_data)
|
||||
end
|
||||
|
||||
it 'uses v2 as the version when nothing is provided' do
|
||||
allow(hipchat).to receive(:api_version).and_return('')
|
||||
expect(HipChat::Client).to receive(:new).with(
|
||||
token,
|
||||
api_version: 'v2',
|
||||
server_url: server_url
|
||||
).and_return(double(:hipchat_service).as_null_object)
|
||||
hipchat.execute(push_sample_data)
|
||||
end
|
||||
|
||||
context 'push events' do
|
||||
it "calls Hipchat API for push events" do
|
||||
hipchat.execute(push_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a push message" do
|
||||
message = hipchat.send(:create_push_message, push_sample_data)
|
||||
|
||||
push_sample_data[:object_attributes]
|
||||
branch = push_sample_data[:ref].gsub('refs/heads/', '')
|
||||
expect(message).to include("#{user.name} pushed to branch " \
|
||||
"<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'tag_push events' do
|
||||
let(:push_sample_data) do
|
||||
Gitlab::DataBuilder::Push.build(
|
||||
project,
|
||||
user,
|
||||
Gitlab::Git::BLANK_SHA,
|
||||
'1' * 40,
|
||||
'refs/tags/test',
|
||||
[])
|
||||
end
|
||||
|
||||
it "calls Hipchat API for tag push events" do
|
||||
hipchat.execute(push_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a tag push message" do
|
||||
message = hipchat.send(:create_push_message, push_sample_data)
|
||||
|
||||
push_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} pushed new tag " \
|
||||
"<a href=\"#{project.web_url}/commits/test\">test</a> to " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'issue events' do
|
||||
let(:issue) { create(:issue, title: 'Awesome issue', description: '**please** fix') }
|
||||
let(:issue_service) { Issues::CreateService.new(project, user) }
|
||||
let(:issues_sample_data) { issue_service.hook_data(issue, 'open') }
|
||||
|
||||
it "calls Hipchat API for issue events" do
|
||||
hipchat.execute(issues_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates an issue message" do
|
||||
message = hipchat.send(:create_issue_message, issues_sample_data)
|
||||
|
||||
obj_attr = issues_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} opened " \
|
||||
"<a href=\"#{obj_attr[:url]}\">issue ##{obj_attr["iid"]}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>Awesome issue</b>" \
|
||||
"<pre><strong>please</strong> fix</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'merge request events' do
|
||||
let(:merge_request) { create(:merge_request, description: '**please** fix', title: 'Awesome merge request', target_project: project, source_project: project) }
|
||||
let(:merge_service) { MergeRequests::CreateService.new(project, user) }
|
||||
let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') }
|
||||
|
||||
it "calls Hipchat API for merge requests events" do
|
||||
hipchat.execute(merge_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a merge request message" do
|
||||
message = hipchat.send(:create_merge_request_message,
|
||||
merge_sample_data)
|
||||
|
||||
obj_attr = merge_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} opened " \
|
||||
"<a href=\"#{obj_attr[:url]}\">merge request !#{obj_attr["iid"]}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>Awesome merge request</b>" \
|
||||
"<pre><strong>please</strong> fix</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context "Note events" do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository, creator: user) }
|
||||
|
||||
context 'when commit comment event triggered' do
|
||||
let(:commit_note) do
|
||||
create(:note_on_commit, author: user, project: project,
|
||||
commit_id: project.repository.commit.id,
|
||||
note: 'a comment on a commit')
|
||||
end
|
||||
|
||||
it "calls Hipchat API for commit comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(commit_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
commit_id = Commit.truncate_sha(data[:commit][:id])
|
||||
title = hipchat.send(:format_title, data[:commit][:message])
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"#{title}" \
|
||||
"<pre>a comment on a commit</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge request comment event triggered' do
|
||||
let(:merge_request) do
|
||||
create(:merge_request, source_project: project,
|
||||
target_project: project)
|
||||
end
|
||||
|
||||
let(:merge_request_note) do
|
||||
create(:note_on_merge_request, noteable: merge_request,
|
||||
project: project,
|
||||
note: "merge request **note**")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for merge request comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
merge_id = data[:merge_request]['iid']
|
||||
title = data[:merge_request]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>merge request <strong>note</strong></pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue comment event triggered' do
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let(:issue_note) do
|
||||
create(:note_on_issue, noteable: issue, project: project,
|
||||
note: "issue **note**")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for issue comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(issue_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
issue_id = data[:issue]['iid']
|
||||
title = data[:issue]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>issue <strong>note</strong></pre>")
|
||||
end
|
||||
|
||||
context 'with confidential issue' do
|
||||
before do
|
||||
issue.update!(confidential: true)
|
||||
end
|
||||
|
||||
it 'calls Hipchat API with issue comment' do
|
||||
data = Gitlab::DataBuilder::Note.build(issue_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
expect(message).to include("<pre>issue <strong>note</strong></pre>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when snippet comment event triggered' do
|
||||
let(:snippet) { create(:project_snippet, project: project) }
|
||||
let(:snippet_note) do
|
||||
create(:note_on_project_snippet, noteable: snippet,
|
||||
project: project,
|
||||
note: "snippet note")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for snippet comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(snippet_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
snippet_id = data[:snippet]['id']
|
||||
title = data[:snippet]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>snippet note</pre>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'pipeline events' do
|
||||
let(:pipeline) { create(:ci_empty_pipeline, user: create(:user)) }
|
||||
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
|
||||
|
||||
context 'for failed' do
|
||||
before do
|
||||
pipeline.drop
|
||||
end
|
||||
|
||||
it "calls Hipchat API" do
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a build message" do
|
||||
message = hipchat.__send__(:create_pipeline_message, data)
|
||||
|
||||
project_url = project.web_url
|
||||
project_name = project.full_name.gsub(/\s/, '')
|
||||
pipeline_attributes = data[:object_attributes]
|
||||
ref = pipeline_attributes[:ref]
|
||||
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
|
||||
duration = pipeline_attributes[:duration]
|
||||
user_name = data[:user][:name]
|
||||
|
||||
expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \
|
||||
"Pipeline <a href=\"#{project_url}/pipelines/#{pipeline.id}\">##{pipeline.id}</a> " \
|
||||
"of <a href=\"#{project_url}/commits/#{ref}\">#{ref}</a> #{ref_type} " \
|
||||
"by #{user_name} failed in #{duration} second(s)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for succeeded' do
|
||||
before do
|
||||
pipeline.succeed
|
||||
end
|
||||
|
||||
it "calls Hipchat API" do
|
||||
hipchat.notify_only_broken_pipelines = false
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "notifies only broken" do
|
||||
hipchat.notify_only_broken_pipelines = true
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).not_to have_requested(:post, api_url).once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#message_options" do
|
||||
it "is set to the defaults" do
|
||||
expect(hipchat.__send__(:message_options)).to eq({ notify: false, color: 'yellow' })
|
||||
end
|
||||
|
||||
it "sets notify to true" do
|
||||
allow(hipchat).to receive(:notify).and_return('1')
|
||||
|
||||
expect(hipchat.__send__(:message_options)).to eq({ notify: true, color: 'yellow' })
|
||||
end
|
||||
|
||||
it "sets the color" do
|
||||
allow(hipchat).to receive(:color).and_return('red')
|
||||
|
||||
expect(hipchat.__send__(:message_options)).to eq({ notify: false, color: 'red' })
|
||||
end
|
||||
|
||||
context 'with a successful build' do
|
||||
it 'uses the green color' do
|
||||
data = { object_kind: 'pipeline',
|
||||
object_attributes: { status: 'success' } }
|
||||
|
||||
expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'green' })
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a failed build' do
|
||||
it 'uses the red color' do
|
||||
data = { object_kind: 'pipeline',
|
||||
object_attributes: { status: 'failed' } }
|
||||
|
||||
expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'red' })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with UrlBlocker' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:hipchat) { described_class.new(project: project) }
|
||||
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||
|
||||
describe '#execute' do
|
||||
before do
|
||||
hipchat.server = 'http://localhost:9123'
|
||||
end
|
||||
|
||||
it 'raises UrlBlocker for localhost' do
|
||||
expect(Gitlab::UrlBlocker).to receive(:validate!).and_call_original
|
||||
expect { hipchat.execute(push_sample_data) }.to raise_error(Gitlab::HTTP::BlockedUrlError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -44,6 +44,7 @@ describe Project do
|
|||
it { is_expected.to have_one(:pipelines_email_service) }
|
||||
it { is_expected.to have_one(:irker_service) }
|
||||
it { is_expected.to have_one(:pivotaltracker_service) }
|
||||
it { is_expected.to have_one(:hipchat_service) }
|
||||
it { is_expected.to have_one(:flowdock_service) }
|
||||
it { is_expected.to have_one(:assembla_service) }
|
||||
it { is_expected.to have_one(:slack_slash_commands_service) }
|
||||
|
|
1
vendor/licenses.csv
vendored
1
vendor/licenses.csv
vendored
|
@ -520,6 +520,7 @@ hashie-forbidden_attributes,0.1.1,MIT
|
|||
he,1.1.1,MIT
|
||||
health_check,2.6.0,MIT
|
||||
highlight.js,9.13.1,New BSD
|
||||
hipchat,1.5.2,MIT
|
||||
hmac-drbg,1.0.1,MIT
|
||||
hoopy,0.1.4,MIT
|
||||
html-pipeline,2.8.4,MIT
|
||||
|
|
|
Loading…
Reference in a new issue