diff options
author | Sean McGivern <sean@gitlab.com> | 2019-04-09 16:06:36 +0300 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2019-04-10 14:58:18 +0300 |
commit | cd9ae6bb820ae8ec98cce38d958298e081860dab (patch) | |
tree | 5ae1b923e40d42392c6443e2308722bf670727f1 /app/models | |
parent | 0de88f57997ef1d60467464f15411dae0c7ee870 (diff) |
Revert "Remove HipChat integration from GitLab"
This reverts commit a5378665a1dc0b9c8dc3a4fa279a0eb78aac5aac.
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/project.rb | 1 | ||||
-rw-r--r-- | app/models/project_services/hipchat_service.rb | 311 | ||||
-rw-r--r-- | app/models/service.rb | 1 |
3 files changed, 313 insertions, 0 deletions
diff --git a/app/models/project.rb b/app/models/project.rb index 66fc83113ea..cb9b2a02eef 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb new file mode 100644 index 00000000000..a69b7b4c4b6 --- /dev/null +++ b/app/models/project_services/hipchat_service.rb @@ -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 diff --git a/app/models/service.rb b/app/models/service.rb index c6d5eb353dc..de549becf71 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -255,6 +255,7 @@ class Service < ApplicationRecord external_wiki flowdock hangouts_chat + hipchat irker jira kubernetes |