jira-ruby-added-transitions 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +46 -0
  4. data/README.rdoc +327 -0
  5. data/Rakefile +28 -0
  6. data/example.rb +119 -0
  7. data/http-basic-example.rb +112 -0
  8. data/jira-ruby.gemspec +30 -0
  9. data/lib/jira.rb +31 -0
  10. data/lib/jira/base.rb +469 -0
  11. data/lib/jira/base_factory.rb +49 -0
  12. data/lib/jira/client.rb +151 -0
  13. data/lib/jira/has_many_proxy.rb +43 -0
  14. data/lib/jira/http_client.rb +41 -0
  15. data/lib/jira/http_error.rb +16 -0
  16. data/lib/jira/oauth_client.rb +84 -0
  17. data/lib/jira/railtie.rb +10 -0
  18. data/lib/jira/request_client.rb +18 -0
  19. data/lib/jira/resource/attachment.rb +12 -0
  20. data/lib/jira/resource/comment.rb +14 -0
  21. data/lib/jira/resource/component.rb +10 -0
  22. data/lib/jira/resource/issue.rb +72 -0
  23. data/lib/jira/resource/issuetype.rb +10 -0
  24. data/lib/jira/resource/priority.rb +10 -0
  25. data/lib/jira/resource/project.rb +30 -0
  26. data/lib/jira/resource/status.rb +10 -0
  27. data/lib/jira/resource/transition.rb +16 -0
  28. data/lib/jira/resource/user.rb +14 -0
  29. data/lib/jira/resource/version.rb +10 -0
  30. data/lib/jira/resource/worklog.rb +16 -0
  31. data/lib/jira/tasks.rb +0 -0
  32. data/lib/jira/version.rb +3 -0
  33. data/lib/tasks/generate.rake +18 -0
  34. data/spec/integration/attachment_spec.rb +23 -0
  35. data/spec/integration/comment_spec.rb +54 -0
  36. data/spec/integration/component_spec.rb +42 -0
  37. data/spec/integration/issue_spec.rb +94 -0
  38. data/spec/integration/issuetype_spec.rb +26 -0
  39. data/spec/integration/priority_spec.rb +27 -0
  40. data/spec/integration/project_spec.rb +56 -0
  41. data/spec/integration/status_spec.rb +27 -0
  42. data/spec/integration/user_spec.rb +25 -0
  43. data/spec/integration/version_spec.rb +43 -0
  44. data/spec/integration/worklog_spec.rb +55 -0
  45. data/spec/jira/base_factory_spec.rb +46 -0
  46. data/spec/jira/base_spec.rb +556 -0
  47. data/spec/jira/client_spec.rb +188 -0
  48. data/spec/jira/has_many_proxy_spec.rb +45 -0
  49. data/spec/jira/http_client_spec.rb +77 -0
  50. data/spec/jira/http_error_spec.rb +25 -0
  51. data/spec/jira/oauth_client_spec.rb +111 -0
  52. data/spec/jira/request_client_spec.rb +14 -0
  53. data/spec/jira/resource/attachment_spec.rb +20 -0
  54. data/spec/jira/resource/issue_spec.rb +83 -0
  55. data/spec/jira/resource/project_factory_spec.rb +13 -0
  56. data/spec/jira/resource/project_spec.rb +28 -0
  57. data/spec/jira/resource/worklog_spec.rb +24 -0
  58. data/spec/mock_responses/attachment/10000.json +20 -0
  59. data/spec/mock_responses/component.post.json +28 -0
  60. data/spec/mock_responses/component/10000.invalid.put.json +5 -0
  61. data/spec/mock_responses/component/10000.json +39 -0
  62. data/spec/mock_responses/component/10000.put.json +39 -0
  63. data/spec/mock_responses/issue.json +1108 -0
  64. data/spec/mock_responses/issue.post.json +5 -0
  65. data/spec/mock_responses/issue/10002.invalid.put.json +6 -0
  66. data/spec/mock_responses/issue/10002.json +126 -0
  67. data/spec/mock_responses/issue/10002.put.missing_field_update.json +6 -0
  68. data/spec/mock_responses/issue/10002/comment.json +65 -0
  69. data/spec/mock_responses/issue/10002/comment.post.json +29 -0
  70. data/spec/mock_responses/issue/10002/comment/10000.json +29 -0
  71. data/spec/mock_responses/issue/10002/comment/10000.put.json +29 -0
  72. data/spec/mock_responses/issue/10002/worklog.json +98 -0
  73. data/spec/mock_responses/issue/10002/worklog.post.json +30 -0
  74. data/spec/mock_responses/issue/10002/worklog/10000.json +31 -0
  75. data/spec/mock_responses/issue/10002/worklog/10000.put.json +30 -0
  76. data/spec/mock_responses/issuetype.json +42 -0
  77. data/spec/mock_responses/issuetype/5.json +8 -0
  78. data/spec/mock_responses/priority.json +42 -0
  79. data/spec/mock_responses/priority/1.json +8 -0
  80. data/spec/mock_responses/project.json +12 -0
  81. data/spec/mock_responses/project/SAMPLEPROJECT.issues.json +1108 -0
  82. data/spec/mock_responses/project/SAMPLEPROJECT.json +84 -0
  83. data/spec/mock_responses/status.json +37 -0
  84. data/spec/mock_responses/status/1.json +7 -0
  85. data/spec/mock_responses/user_username=admin.json +17 -0
  86. data/spec/mock_responses/version.post.json +7 -0
  87. data/spec/mock_responses/version/10000.invalid.put.json +5 -0
  88. data/spec/mock_responses/version/10000.json +11 -0
  89. data/spec/mock_responses/version/10000.put.json +7 -0
  90. data/spec/spec_helper.rb +22 -0
  91. data/spec/support/clients_helper.rb +16 -0
  92. data/spec/support/matchers/have_attributes.rb +11 -0
  93. data/spec/support/matchers/have_many.rb +9 -0
  94. data/spec/support/matchers/have_one.rb +5 -0
  95. data/spec/support/shared_examples/integration.rb +190 -0
  96. metadata +315 -0
@@ -0,0 +1,49 @@
1
+ module JIRA
2
+
3
+ # This is the base class for all the JIRA resource factory instances.
4
+ class BaseFactory
5
+
6
+ attr_reader :client
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Return the name of the class which this factory generates, i.e.
13
+ # JIRA::Resource::FooFactory creates JIRA::Resource::Foo instances.
14
+ def target_class
15
+ # Need to do a little bit of work here as Module.const_get doesn't work
16
+ # with nested class names, i.e. JIRA::Resource::Foo.
17
+ #
18
+ # So create a method chain from the class componenets. This code will
19
+ # unroll to:
20
+ # Module.const_get('JIRA').const_get('Resource').const_get('Foo')
21
+ #
22
+ target_class_name = self.class.name.sub(/Factory$/, '')
23
+ class_components = target_class_name.split('::')
24
+
25
+ class_components.inject(Module) do |mod, const_name|
26
+ mod.const_get(const_name)
27
+ end
28
+ end
29
+
30
+ def self.delegate_to_target_class(*method_names)
31
+ method_names.each do |method_name|
32
+ define_method method_name do |*args|
33
+ target_class.send(method_name, @client, *args)
34
+ end
35
+ end
36
+ end
37
+
38
+ # The priciple purpose of this class is to delegate methods to the corresponding
39
+ # non-factory class and automatically prepend the client argument to the argument
40
+ # list.
41
+ delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql
42
+
43
+ # This method needs special handling as it has a default argument value
44
+ def build(attrs={})
45
+ target_class.build(@client, attrs)
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,151 @@
1
+ require 'json'
2
+ require 'forwardable'
3
+
4
+ module JIRA
5
+
6
+ # This class is the main access point for all JIRA::Resource instances.
7
+ #
8
+ # The client must be initialized with an options hash containing
9
+ # configuration options. The available options are:
10
+ #
11
+ # :site => 'http://localhost:2990',
12
+ # :context_path => '/jira',
13
+ # :signature_method => 'RSA-SHA1',
14
+ # :request_token_path => "/plugins/servlet/oauth/request-token",
15
+ # :authorize_path => "/plugins/servlet/oauth/authorize",
16
+ # :access_token_path => "/plugins/servlet/oauth/access-token",
17
+ # :private_key_file => "rsakey.pem",
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
26
+ #
27
+ # See the JIRA::Base class methods for all of the available methods on these accessor
28
+ # objects.
29
+
30
+ class Client
31
+
32
+ extend Forwardable
33
+
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
39
+
40
+ # The configuration options for this client instance
41
+ attr_reader :options
42
+
43
+ def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token
44
+
45
+ DEFAULT_OPTIONS = {
46
+ :site => 'http://localhost:2990',
47
+ :context_path => '/jira',
48
+ :rest_base_path => "/rest/api/2",
49
+ :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
50
+ :use_ssl => true,
51
+ :auth_type => :oauth
52
+ }
53
+
54
+ def initialize(options={})
55
+ options = DEFAULT_OPTIONS.merge(options)
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
+ @options.freeze
68
+ end
69
+
70
+ def Project # :nodoc:
71
+ JIRA::Resource::ProjectFactory.new(self)
72
+ end
73
+
74
+ def Issue # :nodoc:
75
+ JIRA::Resource::IssueFactory.new(self)
76
+ end
77
+
78
+ def Component # :nodoc:
79
+ JIRA::Resource::ComponentFactory.new(self)
80
+ end
81
+
82
+ def User # :nodoc:
83
+ JIRA::Resource::UserFactory.new(self)
84
+ end
85
+
86
+ def Issuetype # :nodoc:
87
+ JIRA::Resource::IssuetypeFactory.new(self)
88
+ end
89
+
90
+ def Priority # :nodoc:
91
+ JIRA::Resource::PriorityFactory.new(self)
92
+ end
93
+
94
+ def Status # :nodoc:
95
+ JIRA::Resource::StatusFactory.new(self)
96
+ end
97
+
98
+ def Comment # :nodoc:
99
+ JIRA::Resource::CommentFactory.new(self)
100
+ end
101
+
102
+ def Attachment # :nodoc:
103
+ JIRA::Resource::AttachmentFactory.new(self)
104
+ end
105
+
106
+ def Worklog # :nodoc:
107
+ JIRA::Resource::WorklogFactory.new(self)
108
+ end
109
+
110
+ def Version # :nodoc:
111
+ JIRA::Resource::VersionFactory.new(self)
112
+ end
113
+
114
+ # HTTP methods without a body
115
+ def delete(path, headers = {})
116
+ request(:delete, path, nil, merge_default_headers(headers))
117
+ end
118
+
119
+ def get(path, headers = {})
120
+ request(:get, path, nil, merge_default_headers(headers))
121
+ end
122
+
123
+ def head(path, headers = {})
124
+ request(:head, path, nil, merge_default_headers(headers))
125
+ end
126
+
127
+ # HTTP methods with a body
128
+ def post(path, body = '', headers = {})
129
+ headers = {'Content-Type' => 'application/json'}.merge(headers)
130
+ request(:post, path, body, merge_default_headers(headers))
131
+ end
132
+
133
+ def put(path, body = '', headers = {})
134
+ headers = {'Content-Type' => 'application/json'}.merge(headers)
135
+ request(:put, path, body, merge_default_headers(headers))
136
+ end
137
+
138
+ # Sends the specified HTTP request to the REST API through the
139
+ # appropriate method (oauth, basic).
140
+ def request(http_method, path, body = '', headers={})
141
+ @request_client.request(http_method, path, body, headers)
142
+ end
143
+
144
+ protected
145
+
146
+ def merge_default_headers(headers)
147
+ {'Accept' => 'application/json'}.merge(headers)
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # Whenever a collection from a has_many relationship is accessed, an instance
3
+ # of this class is returned. This instance wraps the Array of instances in
4
+ # the collection with an extra build method, which allows new instances to be
5
+ # built on the collection with the correct properties.
6
+ #
7
+ # In practice, instances of this class behave exactly like an Array.
8
+ #
9
+ class JIRA::HasManyProxy
10
+
11
+ attr_reader :target_class, :parent
12
+ attr_accessor :collection
13
+
14
+ def initialize(parent, target_class, collection = [])
15
+ @parent = parent
16
+ @target_class = target_class
17
+ @collection = collection
18
+ end
19
+
20
+ # Builds an instance of this class with the correct parent.
21
+ # For example, issue.comments.build(attrs) will initialize a
22
+ # comment as follows:
23
+ #
24
+ # JIRA::Resource::Comment.new(issue.client,
25
+ # :attrs => attrs,
26
+ # :issue => issue)
27
+ def build(attrs = {})
28
+ resource = target_class.new(parent.client, :attrs => attrs, parent.to_sym => parent)
29
+ collection << resource
30
+ resource
31
+ end
32
+
33
+ # Forces an HTTP request to fetch all instances of the target class that
34
+ # are associated with the parent
35
+ def all
36
+ target_class.all(parent.client, parent.to_sym => parent)
37
+ end
38
+
39
+ # Delegate any missing methods to the collection that this proxy wraps
40
+ def method_missing(method_name, *args, &block)
41
+ collection.send(method_name, *args, &block)
42
+ end
43
+ end
@@ -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.to_s.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,16 @@
1
+ require 'forwardable'
2
+ module JIRA
3
+
4
+ class HTTPError < StandardError
5
+ extend Forwardable
6
+
7
+ def_instance_delegators :@response, :message, :code
8
+ attr_reader :response
9
+
10
+ def initialize(response)
11
+ @response = response
12
+ end
13
+
14
+ end
15
+
16
+ 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
@@ -0,0 +1,10 @@
1
+ require 'jira'
2
+ require 'rails'
3
+
4
+ module JIRA
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load 'tasks/generate.rake'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require 'oauth'
2
+ require 'json'
3
+ require 'net/https'
4
+
5
+ module JIRA
6
+ class RequestClient
7
+
8
+ # Returns the response if the request was successful (HTTP::2xx) and
9
+ # raises a JIRA::HTTPError if it was not successful, with the response
10
+ # attached.
11
+
12
+ def request(*args)
13
+ response = make_request(*args)
14
+ raise HTTPError.new(response) unless response.kind_of?(Net::HTTPSuccess)
15
+ response
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module JIRA
2
+ module Resource
3
+
4
+ class AttachmentFactory < JIRA::BaseFactory # :nodoc:
5
+ end
6
+
7
+ class Attachment < JIRA::Base
8
+ has_one :author, :class => JIRA::Resource::User
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module JIRA
2
+ module Resource
3
+
4
+ class CommentFactory < JIRA::BaseFactory # :nodoc:
5
+ end
6
+
7
+ class Comment < JIRA::Base
8
+ belongs_to :issue
9
+
10
+ nested_collections true
11
+ end
12
+
13
+ end
14
+ end