jira-ruby 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -5,7 +5,7 @@ API version 2).
5
5
 
6
6
  == Example usage
7
7
 
8
- client = JIRA::Client.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+ client = JIRA::Client.new({:consumer_key => CONSUMER_KEY, :consumer_secret => CONSUMER_SECRET})
9
9
 
10
10
  project = client.Project.find('SAMPLEPROJECT')
11
11
 
@@ -70,9 +70,47 @@ key.
70
70
  After you have entered all the information click OK and ensure OAuth authentication is
71
71
  enabled.
72
72
 
73
+ == Configuring JIRA to use HTTP Basic Auth
74
+
75
+ Follow the same steps described above to set up a new Application Link in JIRA,
76
+ however there is no need to set up any "incoming authentication" as this
77
+ defaults to HTTP Basic Auth.
78
+
79
+ == Using the API Gem in a command line application
80
+
81
+ Using HTTP Basic Authentication, configure and connect a client to your instance
82
+ of JIRA.
83
+
84
+ require 'rubygems'
85
+ require 'pp'
86
+ require 'jira'
87
+
88
+ # Consider the use of :use_ssl and :ssl_verify_mode options if running locally
89
+ # for tests.
90
+
91
+ username = "myremoteuser"
92
+ password = "myuserspassword"
93
+
94
+ options = {
95
+ :username => username,
96
+ :password => password,
97
+ :site => 'http://localhost:8080/',
98
+ :context_path => '/myjira',
99
+ :auth_type => :basic
100
+ }
101
+
102
+ client = JIRA::Client.new(options)
103
+
104
+ # Show all projects
105
+ projects = client.Project.all
106
+
107
+ projects.each do |project|
108
+ puts "Project -> key: #{project.key}, name: #{project.name}"
109
+ end
110
+
73
111
  == Using the API Gem in your Rails application
74
112
 
75
- The gem requires the consumer key and public certificate file (which
113
+ Using oauth, the gem requires the consumer key and public certificate file (which
76
114
  are generated in their respective rake tasks) to initialize an access token for
77
115
  using the JIRA API.
78
116
 
@@ -95,17 +133,22 @@ errors are handled gracefully
95
133
  class ApplicationController < ActionController::Base
96
134
  protect_from_forgery
97
135
 
98
- rescue_from JIRA::Client::UninitializedAccessTokenError do
136
+ rescue_from JIRA::OauthClient::UninitializedAccessTokenError do
99
137
  redirect_to new_jira_session_url
100
138
  end
101
139
 
102
140
  private
103
141
 
104
142
  def get_jira_client
143
+
144
+ # add any extra configuration options for your instance of JIRA,
145
+ # e.g. :use_ssl, :ssl_verify_mode, :context_path, :site
105
146
  options = {
106
- :private_key_file => "rsakey.pem"
147
+ :private_key_file => "rsakey.pem",
148
+ :consumer_key => 'test'
107
149
  }
108
- @jira_client = JIRA::Client.new('test', '', options)
150
+
151
+ @jira_client = JIRA::Client.new(options)
109
152
 
110
153
  # Add AccessToken if authorised previously.
111
154
  if session[:jira_auth]
@@ -190,10 +233,11 @@ Here's the same example as a Sinatra application:
190
233
  :authorize_path => "/plugins/servlet/oauth/authorize",
191
234
  :access_token_path => "/plugins/servlet/oauth/access-token",
192
235
  :private_key_file => "rsakey.pem",
193
- :rest_base_path => "/rest/api/2"
236
+ :rest_base_path => "/rest/api/2",
237
+ :consumer_key => "jira-ruby-example"
194
238
  }
195
239
 
196
- @jira_client = JIRA::Client.new('jira-ruby-example', '', options)
240
+ @jira_client = JIRA::Client.new(options)
197
241
  @jira_client.consumer.http.set_debug_output($stderr)
198
242
 
199
243
  # Add AccessToken if authorised previously.
@@ -0,0 +1,106 @@
1
+ require 'rubygems'
2
+ require 'pp'
3
+ require 'jira'
4
+
5
+ if ARGV.length == 0
6
+ # If not passed any command line arguments, prompt the
7
+ # user for the username and password.
8
+ puts "Enter the username: "
9
+ username = gets.strip
10
+
11
+ puts "Enter the password: "
12
+ password = gets.strip
13
+ elsif ARGV.length == 2
14
+ username, password = ARGV[0], ARGV[1]
15
+ else
16
+ # Script must be passed 0 or 2 arguments
17
+ raise "Usage: #{$0} [ username password ]"
18
+ end
19
+
20
+ options = {
21
+ :username => username,
22
+ :password => password,
23
+ :site => 'http://localhost:8080/',
24
+ :context_path => '',
25
+ :auth_type => :basic,
26
+ :use_ssl => false
27
+ }
28
+
29
+ client = JIRA::Client.new(options)
30
+
31
+ # Show all projects
32
+ projects = client.Project.all
33
+
34
+ projects.each do |project|
35
+ puts "Project -> key: #{project.key}, name: #{project.name}"
36
+ end
37
+
38
+ # # Find a specific project by key
39
+ # # ------------------------------
40
+ # project = client.Project.find('SAMPLEPROJECT')
41
+ # pp project
42
+ # project.issues.each do |issue|
43
+ # puts "#{issue.id} - #{issue.fields['summary']}"
44
+ # end
45
+ #
46
+ # # List all Issues
47
+ # # ---------------
48
+ # client.Issue.all.each do |issue|
49
+ # puts "#{issue.id} - #{issue.fields['summary']}"
50
+ # end
51
+ #
52
+ # # Delete an issue
53
+ # # ---------------
54
+ # issue = client.Issue.find('SAMPLEPROJECT-2')
55
+ # if issue.delete
56
+ # puts "Delete of issue SAMPLEPROJECT-2 sucessful"
57
+ # else
58
+ # puts "Delete of issue SAMPLEPROJECT-2 failed"
59
+ # end
60
+ #
61
+ # # Create an issue
62
+ # # ---------------
63
+ # issue = client.Issue.build
64
+ # issue.save({"fields"=>{"summary"=>"blarg from in example.rb","project"=>{"id"=>"10001"},"issuetype"=>{"id"=>"3"}}})
65
+ # issue.fetch
66
+ # pp issue
67
+ #
68
+ # # Update an issue
69
+ # # ---------------
70
+ # issue = client.Issue.find("10002")
71
+ # issue.save({"fields"=>{"summary"=>"EVEN MOOOOOOARRR NINJAAAA!"}})
72
+ # pp issue
73
+ #
74
+ # # Find a user
75
+ # # -----------
76
+ # user = client.User.find('admin')
77
+ # pp user
78
+ #
79
+ # # Get all issue types
80
+ # # -------------------
81
+ # issuetypes = client.Issuetype.all
82
+ # pp issuetypes
83
+ #
84
+ # # Get a single issue type
85
+ # # -----------------------
86
+ # issuetype = client.Issuetype.find('5')
87
+ # pp issuetype
88
+ #
89
+ # # Get all comments for an issue
90
+ # # -----------------------------
91
+ # issue.comments.each do |comment|
92
+ # pp comment
93
+ # end
94
+ #
95
+ # # Build and Save a comment
96
+ # # ------------------------
97
+ # comment = issue.comments.build
98
+ # comment.save!(:body => "New comment from example script")
99
+ #
100
+ # # Delete a comment from the collection
101
+ # # ------------------------------------
102
+ # issue.comments.last.delete
103
+ #
104
+ # # Update an existing comment
105
+ # # --------------------------
106
+ # issue.comments.first.save({"body" => "an updated comment frome example.rb"})
data/lib/jira.rb CHANGED
@@ -22,6 +22,9 @@ require 'jira/resource/comment'
22
22
  require 'jira/resource/worklog'
23
23
  require 'jira/resource/issue'
24
24
 
25
+ require 'jira/request_client'
26
+ require 'jira/oauth_client'
27
+ require 'jira/http_client'
25
28
  require 'jira/client'
26
29
 
27
30
  require 'jira/railtie' if defined?(Rails)
data/lib/jira/client.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'oauth'
2
1
  require 'json'
3
2
  require 'forwardable'
4
3
 
@@ -6,9 +5,8 @@ module JIRA
6
5
 
7
6
  # This class is the main access point for all JIRA::Resource instances.
8
7
  #
9
- # The client must be initialized with a consumer_key and consumer secret,
10
- # and an optional hash of extra configuration options. The available options
11
- # are:
8
+ # The client must be initialized with an options hash containing
9
+ # configuration options. The available options are:
12
10
  #
13
11
  # :site => 'http://localhost:2990',
14
12
  # :context_path => '/jira',
@@ -17,55 +15,56 @@ module JIRA
17
15
  # :authorize_path => "/plugins/servlet/oauth/authorize",
18
16
  # :access_token_path => "/plugins/servlet/oauth/access-token",
19
17
  # :private_key_file => "rsakey.pem",
20
- # :rest_base_path => "/rest/api/2"
21
- #
18
+ # :rest_base_path => "/rest/api/2",
19
+ # :consumer_key => nil,
20
+ # :consumer_secret => nil,
21
+ # :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
22
+ # :use_ssl => true,
23
+ # :username => nil,
24
+ # :password => nil,
25
+ # :auth_type => :oauth
22
26
  #
23
27
  # See the JIRA::Base class methods for all of the available methods on these accessor
24
28
  # objects.
25
- #
29
+
26
30
  class Client
27
31
 
28
32
  extend Forwardable
29
33
 
30
- # This exception is thrown when the client is used before the OAuth access token
31
- # has been initialized.
32
- class UninitializedAccessTokenError < StandardError
33
- def message
34
- "init_access_token must be called before using the client"
35
- end
36
- end
37
-
38
- # The OAuth::Consumer instance used by this client
39
- attr_accessor :consumer
34
+ # The OAuth::Consumer instance returned by the OauthClient
35
+ #
36
+ # The authenticated client instance returned by the respective client type
37
+ # (Oauth, Basic)
38
+ attr_accessor :consumer, :request_client
40
39
 
41
40
  # The configuration options for this client instance
42
41
  attr_reader :options
43
42
 
44
- def_instance_delegators :@consumer, :key, :secret, :get_request_token
43
+ def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token
45
44
 
46
45
  DEFAULT_OPTIONS = {
47
46
  :site => 'http://localhost:2990',
48
47
  :context_path => '/jira',
49
- :signature_method => 'RSA-SHA1',
50
- :request_token_path => "/plugins/servlet/oauth/request-token",
51
- :authorize_path => "/plugins/servlet/oauth/authorize",
52
- :access_token_path => "/plugins/servlet/oauth/access-token",
53
- :private_key_file => "rsakey.pem",
54
- :rest_base_path => "/rest/api/2"
48
+ :rest_base_path => "/rest/api/2",
49
+ :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
50
+ :use_ssl => true,
51
+ :auth_type => :oauth
55
52
  }
56
53
 
57
- def initialize(consumer_key, consumer_secret, options={})
54
+ def initialize(options={})
58
55
  options = DEFAULT_OPTIONS.merge(options)
59
-
60
- # prepend the context path to all authorization and rest paths
61
- options[:request_token_path] = options[:context_path] + options[:request_token_path]
62
- options[:authorize_path] = options[:context_path] + options[:authorize_path]
63
- options[:access_token_path] = options[:context_path] + options[:access_token_path]
64
- options[:rest_base_path] = options[:context_path] + options[:rest_base_path]
65
-
66
56
  @options = options
57
+ @options[:rest_base_path] = @options[:context_path] + @options[:rest_base_path]
58
+
59
+ case options[:auth_type]
60
+ when :oauth
61
+ @request_client = OauthClient.new(@options)
62
+ @consumer = @request_client.consumer
63
+ when :basic
64
+ @request_client = HttpClient.new(@options)
65
+ end
66
+
67
67
  @options.freeze
68
- @consumer = OAuth::Consumer.new(consumer_key,consumer_secret,options)
69
68
  end
70
69
 
71
70
  def Project # :nodoc:
@@ -112,44 +111,17 @@ module JIRA
112
111
  JIRA::Resource::VersionFactory.new(self)
113
112
  end
114
113
 
115
- # Returns the current request token if it is set, else it creates
116
- # and sets a new token.
117
- def request_token
118
- @request_token ||= get_request_token
119
- end
120
-
121
- # Sets the request token from a given token and secret.
122
- def set_request_token(token, secret)
123
- @request_token = OAuth::RequestToken.new(@consumer, token, secret)
124
- end
125
-
126
- # Initialises and returns a new access token from the params hash
127
- # returned by the OAuth transaction.
128
- def init_access_token(params)
129
- @access_token = request_token.get_access_token(params)
130
- end
131
-
132
- # Sets the access token from a preexisting token and secret.
133
- def set_access_token(token, secret)
134
- @access_token = OAuth::AccessToken.new(@consumer, token, secret)
135
- end
136
-
137
- # Returns the current access token. Raises an
138
- # JIRA::Client::UninitializedAccessTokenError exception if it is not set.
139
- def access_token
140
- raise UninitializedAccessTokenError.new unless @access_token
141
- @access_token
142
- end
143
-
144
114
  # HTTP methods without a body
145
115
  def delete(path, headers = {})
146
- request(:delete, path, merge_default_headers(headers))
116
+ request(:delete, path, nil, merge_default_headers(headers))
147
117
  end
118
+
148
119
  def get(path, headers = {})
149
- request(:get, path, merge_default_headers(headers))
120
+ request(:get, path, nil, merge_default_headers(headers))
150
121
  end
122
+
151
123
  def head(path, headers = {})
152
- request(:head, path, merge_default_headers(headers))
124
+ request(:head, path, nil, merge_default_headers(headers))
153
125
  end
154
126
 
155
127
  # HTTP methods with a body
@@ -157,21 +129,16 @@ module JIRA
157
129
  headers = {'Content-Type' => 'application/json'}.merge(headers)
158
130
  request(:post, path, body, merge_default_headers(headers))
159
131
  end
132
+
160
133
  def put(path, body = '', headers = {})
161
134
  headers = {'Content-Type' => 'application/json'}.merge(headers)
162
135
  request(:put, path, body, merge_default_headers(headers))
163
136
  end
164
137
 
165
138
  # Sends the specified HTTP request to the REST API through the
166
- # OAuth token.
167
- #
168
- # Returns the response if the request was successful (HTTP::2xx) and
169
- # raises a JIRA::HTTPError if it was not successful, with the response
170
- # attached.
171
- def request(http_method, path, *arguments)
172
- response = access_token.request(http_method, path, *arguments)
173
- raise HTTPError.new(response) unless response.kind_of?(Net::HTTPSuccess)
174
- response
139
+ # appropriate method (oauth, basic).
140
+ def request(http_method, path, body = '', headers)
141
+ @request_client.request(http_method, path, body, headers)
175
142
  end
176
143
 
177
144
  protected
@@ -0,0 +1,41 @@
1
+ require 'json'
2
+ require 'net/https'
3
+
4
+ module JIRA
5
+ class HttpClient < RequestClient
6
+
7
+ DEFAULT_OPTIONS = {
8
+ :username => '',
9
+ :password => ''
10
+ }
11
+
12
+ attr_reader :options
13
+
14
+ def initialize(options)
15
+ @options = DEFAULT_OPTIONS.merge(options)
16
+ end
17
+
18
+ def make_request(http_method, path, body='', headers)
19
+ request = Net::HTTP.const_get(http_method.capitalize).new(path, headers)
20
+ request.body = body unless body.nil?
21
+ request.basic_auth(@options[:username], @options[:password])
22
+ response = basic_auth_http_conn.request(request)
23
+ response
24
+ end
25
+
26
+ def basic_auth_http_conn
27
+ http_conn(uri)
28
+ end
29
+
30
+ def http_conn(uri)
31
+ http_conn = Net::HTTP.new(uri.host, uri.port)
32
+ http_conn.use_ssl = @options[:use_ssl]
33
+ http_conn.verify_mode = @options[:ssl_verify_mode]
34
+ http_conn
35
+ end
36
+
37
+ def uri
38
+ uri = URI.parse(@options[:site])
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,84 @@
1
+ require 'oauth'
2
+ require 'json'
3
+ require 'forwardable'
4
+
5
+ module JIRA
6
+ class OauthClient < RequestClient
7
+
8
+ DEFAULT_OPTIONS = {
9
+ :signature_method => 'RSA-SHA1',
10
+ :request_token_path => "/plugins/servlet/oauth/request-token",
11
+ :authorize_path => "/plugins/servlet/oauth/authorize",
12
+ :access_token_path => "/plugins/servlet/oauth/access-token",
13
+ :private_key_file => "rsakey.pem",
14
+ :consumer_key => nil,
15
+ :consumer_secret => nil
16
+ }
17
+
18
+ # This exception is thrown when the client is used before the OAuth access token
19
+ # has been initialized.
20
+ class UninitializedAccessTokenError < StandardError
21
+ def message
22
+ "init_access_token must be called before using the client"
23
+ end
24
+ end
25
+
26
+ extend Forwardable
27
+
28
+ attr_accessor :consumer
29
+ attr_reader :options
30
+
31
+ def_instance_delegators :@consumer, :key, :secret, :get_request_token
32
+
33
+ def initialize(options)
34
+ @options = DEFAULT_OPTIONS.merge(options)
35
+ @consumer = init_oauth_consumer(@options)
36
+ end
37
+
38
+ def init_oauth_consumer(options)
39
+ @options[:request_token_path] = @options[:context_path] + @options[:request_token_path]
40
+ @options[:authorize_path] = @options[:context_path] + @options[:authorize_path]
41
+ @options[:access_token_path] = @options[:context_path] + @options[:access_token_path]
42
+ OAuth::Consumer.new(@options[:consumer_key],@options[:consumer_secret],@options)
43
+ end
44
+
45
+ # Returns the current request token if it is set, else it creates
46
+ # and sets a new token.
47
+ def request_token
48
+ @request_token ||= get_request_token
49
+ end
50
+
51
+ # Sets the request token from a given token and secret.
52
+ def set_request_token(token, secret)
53
+ @request_token = OAuth::RequestToken.new(@consumer, token, secret)
54
+ end
55
+
56
+ # Initialises and returns a new access token from the params hash
57
+ # returned by the OAuth transaction.
58
+ def init_access_token(params)
59
+ @access_token = request_token.get_access_token(params)
60
+ end
61
+
62
+ # Sets the access token from a preexisting token and secret.
63
+ def set_access_token(token, secret)
64
+ @access_token = OAuth::AccessToken.new(@consumer, token, secret)
65
+ end
66
+
67
+ # Returns the current access token. Raises an
68
+ # JIRA::Client::UninitializedAccessTokenError exception if it is not set.
69
+ def access_token
70
+ raise UninitializedAccessTokenError.new unless @access_token
71
+ @access_token
72
+ end
73
+
74
+ def make_request(http_method, path, body='', headers)
75
+ case http_method
76
+ when :delete, :get, :head
77
+ response = access_token.send http_method, path, headers
78
+ when :post, :put
79
+ response = access_token.send http_method, path, body, headers
80
+ end
81
+ response
82
+ end
83
+ end
84
+ end