jira_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/jira_client.gemspec +27 -0
  8. data/lib/jira_client.rb +121 -0
  9. data/lib/jira_client/api/comments.rb +19 -0
  10. data/lib/jira_client/api/issues.rb +62 -0
  11. data/lib/jira_client/api/projects.rb +19 -0
  12. data/lib/jira_client/api/server.rb +15 -0
  13. data/lib/jira_client/api/statuses.rb +20 -0
  14. data/lib/jira_client/api/users.rb +18 -0
  15. data/lib/jira_client/api/utils.rb +25 -0
  16. data/lib/jira_client/api/worklogs.rb +26 -0
  17. data/lib/jira_client/base.rb +54 -0
  18. data/lib/jira_client/comment.rb +12 -0
  19. data/lib/jira_client/configuration.rb +27 -0
  20. data/lib/jira_client/core_ext/string.rb +7 -0
  21. data/lib/jira_client/error/bad_request.rb +5 -0
  22. data/lib/jira_client/error/configuration_error.rb +5 -0
  23. data/lib/jira_client/error/issue_error.rb +5 -0
  24. data/lib/jira_client/error/resource_not_found.rb +5 -0
  25. data/lib/jira_client/error/unauthorized.rb +5 -0
  26. data/lib/jira_client/issue.rb +26 -0
  27. data/lib/jira_client/project.rb +5 -0
  28. data/lib/jira_client/server_info.rb +11 -0
  29. data/lib/jira_client/status.rb +7 -0
  30. data/lib/jira_client/timetracking.rb +6 -0
  31. data/lib/jira_client/user.rb +10 -0
  32. data/lib/jira_client/version.rb +3 -0
  33. data/lib/jira_client/worklog.rb +10 -0
  34. data/spec/fixtures/admin.json +17 -0
  35. data/spec/fixtures/basic_issue.json +6 -0
  36. data/spec/fixtures/comment.json +23 -0
  37. data/spec/fixtures/comments.json +30 -0
  38. data/spec/fixtures/invalid_assignee.json +6 -0
  39. data/spec/fixtures/invalid_comment.json +6 -0
  40. data/spec/fixtures/invalid_jql.json +6 -0
  41. data/spec/fixtures/issue_with_comments.json +33 -0
  42. data/spec/fixtures/issue_with_description.json +10 -0
  43. data/spec/fixtures/issue_with_status.json +15 -0
  44. data/spec/fixtures/issue_with_timetracking.json +16 -0
  45. data/spec/fixtures/issue_with_worklogs.json +34 -0
  46. data/spec/fixtures/issues.json +28 -0
  47. data/spec/fixtures/my_certificate.pem +52 -0
  48. data/spec/fixtures/no_issues_found.json +6 -0
  49. data/spec/fixtures/project.json +10 -0
  50. data/spec/fixtures/projects.json +22 -0
  51. data/spec/fixtures/server_info.json +14 -0
  52. data/spec/fixtures/status.json +7 -0
  53. data/spec/fixtures/statuses.json +16 -0
  54. data/spec/fixtures/user_doesnt_exist.json +6 -0
  55. data/spec/fixtures/users.json +22 -0
  56. data/spec/fixtures/worklog.json +31 -0
  57. data/spec/jira_client/api/comments_spec.rb +59 -0
  58. data/spec/jira_client/api/issues_spec.rb +314 -0
  59. data/spec/jira_client/api/projects_spec.rb +55 -0
  60. data/spec/jira_client/api/server_spec.rb +31 -0
  61. data/spec/jira_client/api/statuses_spec.rb +69 -0
  62. data/spec/jira_client/api/users_spec.rb +56 -0
  63. data/spec/jira_client/api/worklogs_spec.rb +86 -0
  64. data/spec/jira_client/configuration_spec.rb +78 -0
  65. data/spec/jira_client_spec.rb +49 -0
  66. data/spec/spec_helper.rb +56 -0
  67. metadata +226 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ coverage
19
+ *.idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jira-client.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Matthew Williams
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # JiraClient::Client
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'jira-client'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install jira-client
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jira_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jira_client"
8
+ spec.version = JiraClient::VERSION
9
+ spec.authors = ["Matthew Williams"]
10
+ spec.email = ["m.williams@me.com"]
11
+ spec.description = %q{A Ruby client for the Jira 5 REST API}
12
+ spec.summary = %q{A Ruby client for the Jira 5 REST API...}
13
+ spec.homepage = "https://github.com/mrwillihog/jira_client"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_runtime_dependency "rest-client"
25
+ spec.add_runtime_dependency "json"
26
+ spec.add_runtime_dependency "chronic_duration"
27
+ end
@@ -0,0 +1,121 @@
1
+ require "rest-client"
2
+ require "json"
3
+
4
+ require "jira_client/base"
5
+ require "jira_client/comment"
6
+ require "jira_client/configuration"
7
+ require "jira_client/issue"
8
+ require "jira_client/project"
9
+ require "jira_client/status"
10
+ require "jira_client/timetracking"
11
+ require "jira_client/user"
12
+ require "jira_client/worklog"
13
+ require "jira_client/api/comments"
14
+ require "jira_client/api/issues"
15
+ require "jira_client/api/projects"
16
+ require "jira_client/api/server"
17
+ require "jira_client/api/statuses"
18
+ require "jira_client/api/users"
19
+ require "jira_client/api/worklogs"
20
+ require "jira_client/core_ext/string"
21
+ require "jira_client/error/configuration_error"
22
+ require "jira_client/error/bad_request"
23
+ require "jira_client/error/issue_error"
24
+ require "jira_client/error/resource_not_found"
25
+ require "jira_client/error/unauthorized"
26
+
27
+ module JiraClient
28
+ class << self
29
+ include JiraClient::API::Comments
30
+ include JiraClient::API::Issues
31
+ include JiraClient::API::Projects
32
+ include JiraClient::API::Server
33
+ include JiraClient::API::Statuses
34
+ include JiraClient::API::Users
35
+ include JiraClient::API::Worklogs
36
+
37
+ attr_accessor :configuration
38
+ def configure
39
+ self.configuration ||= Configuration.new
40
+ begin
41
+ yield(configuration)
42
+ rescue NoMethodError => e
43
+ raise JiraClient::Error::ConfigurationError, "Unrecognized configuration option provided #{e.message}"
44
+ end
45
+ end
46
+
47
+ def reset!
48
+ self.configuration = nil
49
+ @resource = nil
50
+ end
51
+
52
+ private
53
+
54
+ def get(path, params={})
55
+ request(:get, path, :params => params)
56
+ end
57
+
58
+ def post(path, params={})
59
+ request(:post, path, params.to_json)
60
+ end
61
+
62
+ def put(path, params={})
63
+ request(:put, path, params.to_json)
64
+ end
65
+
66
+ def request(method, path, params={})
67
+ begin
68
+ response = resource[URI.encode(path)].send(method.to_sym, params)
69
+ snake_case!(JSON.parse(response, :symbolize_names => true)) unless response.empty?
70
+ rescue RestClient::ResourceNotFound => e
71
+ raise_error JiraClient::Error::ResourceNotFound, e
72
+ rescue RestClient::BadRequest => e
73
+ raise_error JiraClient::Error::BadRequest, e
74
+ rescue RestClient::Unauthorized => e
75
+ raise_error JiraClient::Error::Unauthorized, e
76
+ end
77
+ end
78
+
79
+ def raise_error(error_type, message_response)
80
+ raise error_type, JSON.parse(message_response.response)["errorMessages"].join
81
+ end
82
+
83
+ # Returns a RestClient resource
84
+ def resource
85
+ raise JiraClient::Error::ConfigurationError, "No configuration found. Please run JiraClient.configure" if JiraClient.configuration.nil?
86
+ @resource ||= begin
87
+ url = JiraClient.configuration.full_url
88
+ options = {
89
+ :headers => {:content_type => :json, :accept => :json}
90
+ }
91
+
92
+ RestClient.proxy = JiraClient.configuration.proxy
93
+
94
+ unless JiraClient.configuration.certificate.nil?
95
+ options[:ssl_client_cert] = OpenSSL::X509::Certificate.new File.read(JiraClient.configuration.certificate)
96
+ options[:ssl_client_key] = OpenSSL::PKey::RSA.new File.read(JiraClient.configuration.certificate), JiraClient.configuration.certificate_passphrase
97
+ options[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
98
+ else
99
+ # RestClient will ignore basic auth parameters if nil
100
+ options[:user] = JiraClient.configuration.username
101
+ options[:password] = JiraClient.configuration.password
102
+ end
103
+
104
+ RestClient::Resource.new(url, options)
105
+ end
106
+ end
107
+
108
+ # Convert CamelCaseKeys to snake_case_keys
109
+ def snake_case!(value)
110
+ case value
111
+ when Array
112
+ value.map {|v| snake_case!(v)}
113
+ when Hash
114
+ Hash[value.map {|k, v| [k.to_s.underscore.to_sym, snake_case!(v)] }]
115
+ else
116
+ value
117
+ end
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,19 @@
1
+ require "jira_client/api/utils"
2
+
3
+ module JiraClient
4
+ module API
5
+ module Comments
6
+ include JiraClient::API::Utils
7
+
8
+ def find_issue_comments(key)
9
+ response = get("/issue/#{key}/comment")
10
+ response[:comments].map {|c| JiraClient::Comment.from_response c}
11
+ end
12
+
13
+ def comment_on_issue(key, message)
14
+ object_from_response(JiraClient::Comment, :post, "/issue/#{key}/comment", {:body => message})
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ require "jira_client/api/utils"
2
+
3
+ module JiraClient
4
+ module API
5
+ module Issues
6
+ include JiraClient::API::Utils
7
+
8
+ def find_issues(params={})
9
+ response = post("/search", params)
10
+ response[:issues].map {|i| JiraClient::Issue.from_response i}
11
+ end
12
+
13
+ def find_issue_by_key(key, params={})
14
+ fields = params[:fields].join "," if params.has_key? :fields
15
+ url = "/issue/#{key}"
16
+ url << "?fields=#{fields}" if fields
17
+ object_from_response(JiraClient::Issue, :get, url)
18
+ end
19
+
20
+ def assign_issue(key, username)
21
+ put("/issue/#{key}/assignee", :name => username)
22
+ end
23
+
24
+ def close_issue(key, params={})
25
+ transition_to(key, JiraClient::Status::CLOSE, params)
26
+ end
27
+
28
+ def reopen_issue(key, params={})
29
+ transition_to(key, JiraClient::Status::REOPEN, params)
30
+ end
31
+
32
+ def resolve_issue(key, params={})
33
+ transition_to(key, JiraClient::Status::RESOLVE, params)
34
+ end
35
+
36
+ def start_progress_on_issue(key, params={})
37
+ transition_to(key, JiraClient::Status::START_PROGRESS, params)
38
+ end
39
+
40
+ private
41
+
42
+ def transition_to(key, transition, params={})
43
+ opts = {:transition => {:id => transition}}
44
+ if params.has_key? :as
45
+ opts[:fields] ||= {}
46
+ opts[:fields][:resolution] = {:name => params[:as]}
47
+ end
48
+ opts[:update] = comment_hash(params[:comment])
49
+
50
+ # Remove keys with nil values
51
+ opts.reject! {|k,v| v.nil?}
52
+ post("/issue/#{key}/transitions", opts)
53
+ end
54
+
55
+ def comment_hash(comment)
56
+ return unless comment
57
+ {:comment => [{:add => {:body => comment}}]}
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,19 @@
1
+ require "jira_client/api/utils"
2
+ require "jira_client/project"
3
+
4
+ module JiraClient
5
+ module API
6
+ module Projects
7
+ include JiraClient::API::Utils
8
+
9
+ def find_projects
10
+ objects_from_response(JiraClient::Project, :get, '/project')
11
+ end
12
+
13
+ def find_project_by_key(key)
14
+ object_from_response(JiraClient::Project, :get, "/project/#{key.to_s}")
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ require "jira_client/api/utils"
2
+ require "jira_client/server_info"
3
+
4
+ module JiraClient
5
+ module API
6
+ module Server
7
+ include JiraClient::API::Utils
8
+
9
+ def server_info
10
+ object_from_response(JiraClient::ServerInfo, :get, "/serverInfo")
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ require 'jira_client/api/utils'
2
+ require "jira_client/status"
3
+
4
+ module JiraClient
5
+ module API
6
+ module Statuses
7
+ include JiraClient::API::Utils
8
+
9
+ def find_statuses
10
+ objects_from_response(JiraClient::Status, :get, '/status')
11
+ end
12
+
13
+ def find_status_by_id(id)
14
+ object_from_response(JiraClient::Status, :get, "/status/#{id}")
15
+ end
16
+ alias :find_status_by_name :find_status_by_id
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require "jira_client/api/utils"
2
+
3
+ module JiraClient
4
+ module API
5
+ module Users
6
+ include JiraClient::API::Utils
7
+
8
+ def find_users(username)
9
+ objects_from_response(JiraClient::User, :get, "/user/search", :username => username)
10
+ end
11
+
12
+ def find_user_by_username(username)
13
+ object_from_response(JiraClient::User, :get, "/user", :username => username)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module JiraClient
2
+ module API
3
+ module Utils
4
+
5
+ private
6
+
7
+ def objects_from_response(klass, request_method, path, options={})
8
+ response = send(request_method.to_sym, path, options)
9
+ objects_from_array(klass, response)
10
+ end
11
+
12
+ def objects_from_array(klass, array)
13
+ array.map do |element|
14
+ klass.from_response(element)
15
+ end
16
+ end
17
+
18
+ def object_from_response(klass, request_method, path, options={})
19
+ response = send(request_method.to_sym, path, options)
20
+ klass.from_response(response)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ require "chronic_duration"
2
+
3
+ module JiraClient
4
+ module API
5
+ module Worklogs
6
+
7
+ def create_worklog(key, time, opts={})
8
+ url = "/issue/#{key}/worklog"
9
+ url += "?adjustEstimate=new&newEstimate=#{opts[:remaining_estimate]}" if opts.has_key? :remaining_estimate
10
+ url += "?adjustEstimate=manual&reduceBy=#{opts[:reduce_estimate]}" if opts.has_key? :reduce_estimate
11
+ time_in_seconds = ChronicDuration.parse(time)
12
+ params = {
13
+ :timeSpentSeconds => time_in_seconds
14
+ }
15
+ params[:comment] = opts[:comment] if opts.has_key? :comment
16
+ post(url, params)
17
+ end
18
+
19
+ def find_issue_worklogs(key)
20
+ response = get("/issue/#{key}/worklog")
21
+ response[:worklogs].map {|w| JiraClient::Worklog.from_response w}
22
+ end
23
+
24
+ end
25
+ end
26
+ end