jira-ruby-added-transitions 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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