From f4bc18d237413ac55e32ce16a23b3d2ab35a6976 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 14 Jan 2016 08:20:23 -0600 Subject: [PATCH] Refactor JIRA service to use gem --- Gemfile | 3 + app/controllers/concerns/service_params.rb | 2 +- app/models/project_services/jira_service.rb | 199 +++++++++--------- .../20160122231710_migrate_jira_to_gem.rb | 52 +++++ doc/integration/README.md | 55 ++--- doc/integration/img/jira_service_page.png | Bin 0 -> 45089 bytes doc/integration/jira.md | 147 ++++++++++++- 7 files changed, 313 insertions(+), 145 deletions(-) create mode 100644 db/migrate/20160122231710_migrate_jira_to_gem.rb create mode 100644 doc/integration/img/jira_service_page.png diff --git a/Gemfile b/Gemfile index 46245ab62d1..033bbe91296 100644 --- a/Gemfile +++ b/Gemfile @@ -161,6 +161,9 @@ gem 'connection_pool', '~> 2.0' # HipChat integration gem 'hipchat', '~> 1.5.0' +# JIRA integration +gem 'jira-ruby', '~> 0.1.17' + # Flowdock integration gem 'gitlab-flowdock-git-hook', '~> 1.0.1' diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb index 4cb3be41064..c33d7eecb9f 100644 --- a/app/controllers/concerns/service_params.rb +++ b/app/controllers/concerns/service_params.rb @@ -18,7 +18,7 @@ module ServiceParams :add_pusher, :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification, - :jira_issue_transition_id] + :jira_issue_transition_id, :url, :project_key] # Parameters to ignore if no value is specified FILTER_BLANK_PARAMS = [:password] diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index f81b66fd219..580adcfba99 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -1,15 +1,35 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null +# +require 'jira' + class JiraService < IssueTrackerService include HTTParty - include Gitlab::Routing.url_helpers + include Gitlab::Application.routes.url_helpers DEFAULT_API_VERSION = 2 - prop_accessor :username, :password, :api_url, :jira_issue_transition_id, - :title, :description, :project_url, :issues_url, :new_issue_url + prop_accessor :username, :password, :url, :project_key, + :jira_issue_transition_id, :title, :description - validates :api_url, presence: true, url: true, if: :activated? - - before_validation :set_api_url, :set_jira_issue_transition_id + before_validation :set_jira_issue_transition_id before_update :reset_password @@ -20,14 +40,34 @@ class JiraService < IssueTrackerService def reset_password # don't reset the password if a new one is provided - if api_url_changed? && !password_touched? + if url_changed? && !password_touched? self.password = nil end end + def options + url = URI.parse(self.url) + { + :username => self.username, + :password => self.password, + :site => URI.join(url, '/').to_s, + :context_path => url.path, + :auth_type => :basic, + :read_timeout => 120, + :use_ssl => url.scheme == 'https' + } + end + + def client + @client ||= ::JIRA::Client.new(options) + end + + def jira_project + @jira_project ||= client.Project.find(project_key) + end + def help - 'Setting `project_url`, `issues_url` and `new_issue_url` will '\ - 'allow a user to easily navigate to the Jira issue tracker. See the '\ + 'See the ' \ '[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\ 'for details.' end @@ -53,12 +93,25 @@ class JiraService < IssueTrackerService end def fields - super.push( - { type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' }, + [ + { type: 'text', name: 'url', title: 'URL', placeholder: 'https://jira.example.com' }, + { type: 'text', name: 'project_key', placeholder: 'PROJ' }, { type: 'text', name: 'username', placeholder: '' }, { type: 'password', name: 'password', placeholder: '' }, { type: 'text', name: 'jira_issue_transition_id', placeholder: '2' } - ) + ] + end + + def project_url + "#{url}/issues/?jql=project=#{project_key}" + end + + def issues_url + "#{url}/browse/:id" + end + + def new_issue_url + "#{url}/secure/CreateIssue.jspa" end def execute(push, issue = nil) @@ -72,7 +125,7 @@ class JiraService < IssueTrackerService end def create_cross_reference_note(mentioned, noteable, author) - issue_name = mentioned.id + issue_key = mentioned.id project = self.project noteable_name = noteable.class.name.underscore.downcase noteable_id = if noteable.is_a?(Commit) @@ -94,53 +147,25 @@ class JiraService < IssueTrackerService }, entity: { name: noteable_name.humanize.downcase, - url: entity_url, - title: noteable.title + url: entity_url } } - add_comment(data, issue_name) + add_comment(data, issue_key) end def test_settings - return unless api_url.present? - result = JiraService.get( - jira_api_test_url, - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) + return unless api_utrl.present? + # Test settings by getting the project + jira_project - case result.code - when 201, 200 - Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.") - true - else - Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}") - false - end - rescue Errno::ECONNREFUSED => e - Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}." + rescue Errno::ECONNREFUSED, JIRA::HTTPError => e + Rails.logger.info "#{self.class.name} Test ERROR: #{url} - #{e.message}" false end private - def build_api_url_from_project_url - server = URI(project_url) - default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port]) - server_url = "#{server.scheme}://#{server.host}" - server_url.concat(":#{server.port}") unless default_ports - "#{server_url}/rest/api/#{DEFAULT_API_VERSION}" - rescue - "" # looks like project URL was not valid - end - - def set_api_url - self.api_url = build_api_url_from_project_url if self.api_url.blank? - end - def set_jira_issue_transition_id self.jira_issue_transition_id ||= "2" end @@ -149,7 +174,7 @@ class JiraService < IssueTrackerService commit_id = if entity.is_a?(Commit) entity.id elsif entity.is_a?(MergeRequest) - entity.diff_head_sha + entity.last_commit.id end commit_url = build_entity_url(:commit, commit_id) @@ -161,69 +186,47 @@ class JiraService < IssueTrackerService end def transition_issue(issue) - message = { - transition: { - id: jira_issue_transition_id - } - } - send_message(close_issue_url(issue.iid), message.to_json) + issue = client.Issue.find(issue.iid) + issue.transitions.build.save(transition: { id: jira_issue_transition_id }) end def add_issue_solved_comment(issue, commit_id, commit_url) - comment = { - body: "Issue solved with [#{commit_id}|#{commit_url}]." - } - - send_message(comment_url(issue.iid), comment.to_json) + comment = "Issue solved with [#{commit_id}|#{commit_url}]." + send_message(issue.iid, comment) end - def add_comment(data, issue_name) - url = comment_url(issue_name) + def add_comment(data, issue_key) user_name = data[:user][:name] user_url = data[:user][:url] entity_name = data[:entity][:name] entity_url = data[:entity][:url] - entity_title = data[:entity][:title] project_name = data[:project][:name] - message = { - body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'} - } + message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]." - unless existing_comment?(issue_name, message[:body]) - send_message(url, message.to_json) - end + # unless existing_comment?(issue_name, message[:body]) + send_message(issue_key, message) + # end end - def auth - require 'base64' - Base64.urlsafe_encode64("#{self.username}:#{self.password}") - end - - def send_message(url, message) + def send_message(issue_key, message) return unless api_url.present? - result = JiraService.post( - url, - body: message, - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) + issue = client.Issue.find(issue_key) + issue.comments.build.save!(body: message) - message = case result.code - when 201, 200, 204 - "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}." - when 401 - "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again." - else - "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}" - end + # message = case result.code + # when 201, 200, 204 + # "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}." + # when 401 + # "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again." + # else + # "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}" + # end Rails.logger.info(message) message rescue URI::InvalidURIError, Errno::ECONNREFUSED => e - Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}." + Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}" end def existing_comment?(issue_name, new_comment) @@ -267,16 +270,4 @@ class JiraService < IssueTrackerService ) ) end - - def close_issue_url(issue_name) - "#{self.api_url}/issue/#{issue_name}/transitions" - end - - def comment_url(issue_name) - "#{self.api_url}/issue/#{issue_name}/comment" - end - - def jira_api_test_url - "#{self.api_url}/myself" - end end diff --git a/db/migrate/20160122231710_migrate_jira_to_gem.rb b/db/migrate/20160122231710_migrate_jira_to_gem.rb new file mode 100644 index 00000000000..972aea323d9 --- /dev/null +++ b/db/migrate/20160122231710_migrate_jira_to_gem.rb @@ -0,0 +1,52 @@ +class MigrateJiraToGem < ActiveRecord::Migration + def change + reversible do |dir| + select_all("SELECT id, properties FROM services WHERE services.type IN ('JiraService')").each do |service| + id = service['id'] + properties = JSON.parse(service['properties']) + properties_was = properties.clone + + dir.up do + # Migrate `project_url` to `project_key` + # Ignore if `project_url` doesn't have jql project query with project key + if properties['project_url'].present? + jql = properties['project_url'].match('project=([A-Za-z]*)') + properties['project_key'] = jql.captures.first if jql + end + + # Migrate `api_url` to `url` + if properties['api_url'].present? + url = properties['api_url'].match('(.*)\/rest\/api') + properties['url'] = url.captures.first if url + end + + # Delete now unnecessary properties + properties.delete('api_url') + properties.delete('project_url') + properties.delete('new_issue_url') + properties.delete('issues_url') + end + + dir.down do + # Rebuild old properties based on sane defaults + if properties['url'].present? + properties['api_url'] = "#{properties['url']}/rest/api/2" + properties['project_url'] = + "#{properties['url']}/issues/?jql=project=#{properties['project_key']}" + properties['issues_url'] = "#{properties['url']}/browse/:id" + properties['new_issue_url'] = "#{properties['url']}/secure/CreateIssue.jspa" + end + + # Delete the new properties + properties.delete('url') + properties.delete('project_key') + end + + # Update changes properties + if properties != properties_was + execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}") + end + end + end + end +end diff --git a/doc/integration/README.md b/doc/integration/README.md index c2fd299db07..0fc0be4ffce 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -5,57 +5,36 @@ trackers and external authentication. See the documentation below for details on how to configure these services. -- [Jira](../project_services/jira.md) Integrate with the JIRA issue tracker +- [JIRA](jira.md) Integrate with the JIRA issue tracker - [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. - [LDAP](ldap.md) Set up sign in via LDAP -- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure +- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab, and Google via OAuth. - [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider - [CAS](cas.md) Configure GitLab to sign in using CAS +- [Slack](slack.md) Integrate with the Slack chat service - [OAuth2 provider](oauth_provider.md) OAuth2 application creation - [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages - [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users -- [Akismet](akismet.md) Configure Akismet to stop spam -- [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. -[jenkins]: http://docs.gitlab.com/ee/integration/jenkins.html - - ## Project services Integration with services such as Campfire, Flowdock, Gemnasium, HipChat, Pivotal Tracker, and Slack are available in the form of a [Project Service][]. +You can find these within GitLab in the Services page under Project Settings if +you are at least a master on the project. +Project Services are a bit like plugins in that they allow a lot of freedom in +adding functionality to GitLab. For example there is also a service that can +send an email every time someone pushes new commits. +Because GitLab is open source we can ship with the code and tests for all +plugins. This allows the community to keep the plugins up to date so that they +always work in newer GitLab versions. + +For an overview of what projects services are available without logging in, +please see the [project_services directory][projects-code]. + +[jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html [Project Service]: ../project_services/project_services.md - -## SSL certificate errors - -When trying to integrate GitLab with services that are using self-signed certificates, -it is very likely that SSL certificate errors will occur on different parts of the -application, most likely Sidekiq. There are 2 approaches you can take to solve this: - -1. Add the root certificate to the trusted chain of the OS. -1. If using Omnibus, you can add the certificate to GitLab's trusted certificates. - -**OS main trusted chain** - -This [resource](http://kb.kerio.com/product/kerio-connect/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html) -has all the information you need to add a certificate to the main trusted chain. - -This [answer](http://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu) -at SuperUser also has relevant information. - -**Omnibus Trusted Chain** - -It is enough to concatenate the certificate to the main trusted certificate: - -```bash -cat jira.pem >> /opt/gitlab/embedded/ssl/certs/cacert.pem -``` - -After that restart GitLab with: - -```bash -sudo gitlab-ctl restart -``` +[projects-code]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services diff --git a/doc/integration/img/jira_service_page.png b/doc/integration/img/jira_service_page.png new file mode 100644 index 0000000000000000000000000000000000000000..0cc160bebe2fda31497afd077b7cd0f5b0930dec GIT binary patch literal 45089 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 Services > JIRA**. + +Fill in the required details on the page as described in the table below. + +| Field | Description | +| ----- | ----------- | +| `URL` | The base URL to the JIRA project which is being linked to this GitLab project. Ex. https://jira.example.com | +| `Project key` | The short, all capital letter identifier for your JIRA project. | +| `Username` | The username of the user created in [configuring JIRA step](#configuring-jira). | +| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). | +| `Jira issue transition` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). By default, this ID is `2` (in the example image, this is `2` as well) | + +After saving the configuration, your GitLab project will be able to interact +with the linked JIRA project. + +![Jira service page](img/jira_service_page.png) + +--- + +#### GitLab 6.x-7.7 with JIRA v6.x + +_**Note:** GitLab versions 7.8 and up contain various integration improvements. +We strongly recommend upgrading._ + +In `gitlab.yml` enable the JIRA issue tracker section by +[uncommenting these lines][jira-gitlab-yml]. This will make sure that all +issues within GitLab are pointing to the JIRA issue tracker. + +After you set this, you will be able to close issues in JIRA by a commit in +GitLab. + +Go to your project's **Settings** page and fill in the project name for the +JIRA project: + +![Set the JIRA project name in GitLab to 'NEW'](img/jira_project_name.png) + +--- + +You can also enable the JIRA service that will allow you to interact with JIRA +issues. Go to the **Settings > Services > JIRA** and: + +1. Tick the active check box to enable the service +1. Supply the URL to JIRA server, for example http://jira.example.com +1. Supply the username of a user we created under `Configuring JIRA` section, + for example `gitlab` +1. Supply the password of the user +1. Optional: supply the JIRA API version, default is version `2` +1. Optional: supply the JIRA issue transition ID (issue transition to closed). + This is dependent on JIRA settings, default is `2` +1. Hit save + + +![Jira services page](img/jira_service.png) + +[services-templates]: ../project_services/services_templates.md +[jira-gitlab-yml]: https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115