jira-ruby 0.0.2
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.
- data/.gitignore +9 -0
- data/Gemfile +5 -0
- data/README.markdown +81 -0
- data/Rakefile +9 -0
- data/example.rb +66 -0
- data/jira-ruby.gemspec +28 -0
- data/lib/jira.rb +12 -0
- data/lib/jira/client.rb +105 -0
- data/lib/jira/resource/base.rb +148 -0
- data/lib/jira/resource/base_factory.rb +44 -0
- data/lib/jira/resource/component.rb +15 -0
- data/lib/jira/resource/http_error.rb +17 -0
- data/lib/jira/resource/issue.rb +15 -0
- data/lib/jira/resource/project.rb +9 -0
- data/lib/jira/tasks.rb +0 -0
- data/lib/jira/version.rb +3 -0
- data/lib/tasks/generate.rake +16 -0
- data/spec/integration/component_spec.rb +70 -0
- data/spec/integration/issue_spec.rb +72 -0
- data/spec/integration/project_spec.rb +48 -0
- data/spec/jira/client_spec.rb +157 -0
- data/spec/jira/resource/base_factory_spec.rb +36 -0
- data/spec/jira/resource/base_spec.rb +292 -0
- data/spec/jira/resource/http_error_spec.rb +25 -0
- data/spec/jira/resource/project_factory_spec.rb +13 -0
- data/spec/mock_responses/component.post.json +28 -0
- data/spec/mock_responses/component/10000.json +39 -0
- data/spec/mock_responses/component/10000.put.json +39 -0
- data/spec/mock_responses/issue.post.json +5 -0
- data/spec/mock_responses/issue/10002.json +114 -0
- data/spec/mock_responses/project.json +12 -0
- data/spec/mock_responses/project/SAMPLEPROJECT.json +70 -0
- data/spec/spec_helper.rb +26 -0
- metadata +148 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Jira
|
2
|
+
module Resource
|
3
|
+
|
4
|
+
# This is the base class for all the Jira resource factory instances.
|
5
|
+
class BaseFactory
|
6
|
+
|
7
|
+
attr_reader :client
|
8
|
+
|
9
|
+
def initialize(client)
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return the name of the class which this factory generates, i.e.
|
14
|
+
# Jira::Resource::FooFactory creates Jira::Resource::Foo instances.
|
15
|
+
def target_class
|
16
|
+
# Need to do a little bit of work here as Module.const_get doesn't work
|
17
|
+
# with nested class names, i.e. Jira::Resource::Foo.
|
18
|
+
#
|
19
|
+
# So create a method chain from the class componenets. This code will
|
20
|
+
# unroll to:
|
21
|
+
# Module.const_get('Jira').const_get('Resource').const_get('Foo')
|
22
|
+
#
|
23
|
+
target_class_name = self.class.name.sub(/Factory$/, '')
|
24
|
+
class_components = target_class_name.split('::')
|
25
|
+
|
26
|
+
class_components.inject(Module) do |mod, const_name|
|
27
|
+
mod.const_get(const_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def all
|
32
|
+
target_class.all(@client)
|
33
|
+
end
|
34
|
+
|
35
|
+
def find(key)
|
36
|
+
target_class.find(@client, key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build(attrs={})
|
40
|
+
target_class.build(@client, attrs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module Jira
|
3
|
+
module Resource
|
4
|
+
|
5
|
+
class HTTPError < StandardError
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
delegate [:message, :code] => :response
|
9
|
+
attr_reader :response
|
10
|
+
|
11
|
+
def initialize(response)
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/jira/tasks.rb
ADDED
File without changes
|
data/lib/jira/version.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
namespace :jira do
|
2
|
+
desc "Generate a consumer key for your application"
|
3
|
+
task :generate_consumer_key do
|
4
|
+
#FIXME SERIOUSLY. THIS IS NOT A REAL SOLUTION. I FEEL SO UNCLEAN.
|
5
|
+
system("for i in {1..10}; do echo $RANDOM$RANDOM$RANDOM; done | md5 | awk '{ print \"You can use this as your consumer key: \" $0 }'")
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Run the system call to generate a RSA public certificate"
|
9
|
+
task :generate_public_cert do
|
10
|
+
puts "Executing 'openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem'"
|
11
|
+
system("openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem")
|
12
|
+
puts "Done. The RSA-SHA1 private keyfile is in the current directory: \'rsakey.pem\'."
|
13
|
+
puts "You will need to copy the following certificate into your application link configuration in Jira:"
|
14
|
+
system("cat rsacert.pem")
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jira::Resource::Component do
|
4
|
+
|
5
|
+
|
6
|
+
let(:client) do
|
7
|
+
client = Jira::Client.new('foo', 'bar')
|
8
|
+
client.set_access_token('abc', '123')
|
9
|
+
client
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:expected_attributes) do
|
13
|
+
{
|
14
|
+
'self' => "http://localhost:2990/jira/rest/api/2/component/10000",
|
15
|
+
'id' => "10000",
|
16
|
+
'name' => "Cheesecake"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
stub_request(:get,
|
22
|
+
"http://localhost:2990/jira/rest/api/2/component/10000").
|
23
|
+
to_return(:body => get_mock_response('component/10000.json'))
|
24
|
+
stub_request(:delete,
|
25
|
+
"http://localhost:2990/jira/rest/api/2/component/10000").
|
26
|
+
to_return(:body => nil)
|
27
|
+
stub_request(:post,
|
28
|
+
"http://localhost:2990/jira/rest/api/2/component").
|
29
|
+
with(:body => '{"name":"Test component","project":"SAMPLEPROJECT"}').
|
30
|
+
to_return(:status => 201, :body => get_mock_response('component.post.json'))
|
31
|
+
stub_request(:put,
|
32
|
+
"http://localhost:2990/jira/rest/api/2/component/10000").
|
33
|
+
with(:body => '{"name":"Jammy"}').
|
34
|
+
to_return(:status => 200, :body => get_mock_response('component/10000.put.json'))
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should get a single component by id" do
|
38
|
+
component = client.Component.find(10000)
|
39
|
+
|
40
|
+
component.should have_attributes(expected_attributes)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "builds and fetches single component" do
|
44
|
+
component = client.Component.build('id' => 10000)
|
45
|
+
component.fetch
|
46
|
+
|
47
|
+
component.should have_attributes(expected_attributes)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "deletes a component" do
|
51
|
+
component = client.Component.build('id' => "10000")
|
52
|
+
component.delete.should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "saves a new component" do
|
56
|
+
component = client.Component.build
|
57
|
+
component.save({"name" => "Test component", "project" => "SAMPLEPROJECT"}).should be_true
|
58
|
+
component.id.should == "10001"
|
59
|
+
component.name.should == "Test component"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "saves an existing component" do
|
63
|
+
component = client.Component.build('id' => '10000')
|
64
|
+
component.fetch
|
65
|
+
component.save('name' => 'Jammy').should be_true
|
66
|
+
component.id.should == "10000"
|
67
|
+
component.name.should == "Jammy"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jira::Resource::Issue do
|
4
|
+
|
5
|
+
let(:client) do
|
6
|
+
client = Jira::Client.new('foo', 'bar')
|
7
|
+
client.set_access_token('abc', '123')
|
8
|
+
client
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:expected_attributes) do
|
12
|
+
{
|
13
|
+
'self' => "http://localhost:2990/jira/rest/api/2/issue/10002",
|
14
|
+
'key' => "SAMPLEPROJECT-1",
|
15
|
+
'expand' => "renderedFields,names,schema,transitions,editmeta,changelog"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
stub_request(:get,
|
21
|
+
"http://localhost:2990/jira/rest/api/2/issue/10002").
|
22
|
+
to_return(:body => get_mock_response('issue/10002.json'))
|
23
|
+
stub_request(:delete,
|
24
|
+
"http://localhost:2990/jira/rest/api/2/issue/10002").
|
25
|
+
to_return(:body => nil)
|
26
|
+
stub_request(:post, "http://localhost:2990/jira/rest/api/2/issue").
|
27
|
+
with(:body => '{"foo":"bar"}').
|
28
|
+
to_return(:body => get_mock_response('issue.post.json'))
|
29
|
+
stub_request(:put, "http://localhost:2990/jira/rest/api/2/issue/10002").
|
30
|
+
with(:body => '{"foo":"bar"}').
|
31
|
+
to_return(:body => nil)
|
32
|
+
stub_request(:get,
|
33
|
+
"http://localhost:2990/jira/rest/api/2/issue/99999").
|
34
|
+
to_return(:status => 404, :body => '{"errorMessages":["Issue Does Not Exist"],"errors": {}}')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should get a single issue by key" do
|
38
|
+
issue = client.Issue.find('10002')
|
39
|
+
|
40
|
+
issue.should have_attributes(expected_attributes)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should handle issue not found" do
|
44
|
+
lambda do
|
45
|
+
issue = client.Issue.find('99999')
|
46
|
+
end.should raise_exception(Jira::Resource::HTTPError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "builds and fetches single issue" do
|
50
|
+
issue = client.Issue.build('id' => '10002')
|
51
|
+
issue.fetch
|
52
|
+
|
53
|
+
issue.should have_attributes(expected_attributes)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "deletes an issue" do
|
57
|
+
issue = client.Issue.build('id' => "10002")
|
58
|
+
issue.delete.should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should save a new record" do
|
62
|
+
subject = described_class.new(client)
|
63
|
+
subject.save('foo' => 'bar').should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should save an existing record" do
|
67
|
+
subject = client.Issue.build('id' => '10002')
|
68
|
+
subject.fetch
|
69
|
+
subject.save('foo' => 'bar').should be_true
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jira::Resource::Project do
|
4
|
+
|
5
|
+
let(:client) do
|
6
|
+
client = Jira::Client.new('foo', 'bar')
|
7
|
+
client.set_access_token('abc', '123')
|
8
|
+
client
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:expected_attributes) do
|
12
|
+
{
|
13
|
+
'self' => "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT",
|
14
|
+
'key' => "SAMPLEPROJECT",
|
15
|
+
'name' => "Sample Project for Developing RoR RESTful API"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
stub_request(:get,
|
21
|
+
"http://localhost:2990/jira/rest/api/2/project").
|
22
|
+
to_return(:body => get_mock_response('project.json'))
|
23
|
+
stub_request(:get,
|
24
|
+
"http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT").
|
25
|
+
to_return(:body => get_mock_response('project/SAMPLEPROJECT.json'))
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should get all the projects" do
|
29
|
+
projects = client.Project.all
|
30
|
+
projects.length.should == 1
|
31
|
+
|
32
|
+
first = projects.first
|
33
|
+
first.should have_attributes(expected_attributes)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should get a single project by key" do
|
37
|
+
project = client.Project.find('SAMPLEPROJECT')
|
38
|
+
|
39
|
+
project.should have_attributes(expected_attributes)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "builds and fetches single project" do
|
43
|
+
project = client.Project.build('key' => 'SAMPLEPROJECT')
|
44
|
+
project.fetch
|
45
|
+
|
46
|
+
project.should have_attributes(expected_attributes)
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jira::Client do
|
4
|
+
|
5
|
+
subject {Jira::Client.new('foo','bar')}
|
6
|
+
|
7
|
+
let(:response) do
|
8
|
+
response = mock("response")
|
9
|
+
response.stub(:kind_of?).with(Net::HTTPSuccess).and_return(true)
|
10
|
+
response
|
11
|
+
end
|
12
|
+
|
13
|
+
it "creates an instance" do
|
14
|
+
subject.class.should == Jira::Client
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sets consumer key" do
|
18
|
+
subject.key.should == 'foo'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "sets consumer secret" do
|
22
|
+
subject.secret.should == 'bar'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "sets the default options" do
|
26
|
+
Jira::Client::DEFAULT_OPTIONS.each do |key, value|
|
27
|
+
subject.options[key].should == value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "allows the overriding of some options" do
|
32
|
+
# Check it overrides a given option ...
|
33
|
+
client = Jira::Client.new('foo', 'bar', :site => 'http://foo.com/')
|
34
|
+
client.options[:site].should == 'http://foo.com/'
|
35
|
+
|
36
|
+
# ... but leaves the rest intact
|
37
|
+
Jira::Client::DEFAULT_OPTIONS.keys.reject do |key|
|
38
|
+
key == :site
|
39
|
+
end.each do |key|
|
40
|
+
client.options[key].should == Jira::Client::DEFAULT_OPTIONS[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
Jira::Client::DEFAULT_OPTIONS[:site].should_not == 'http://foo.com/'
|
44
|
+
end
|
45
|
+
|
46
|
+
# To avoid having to validate options after initialisation, e.g. setting
|
47
|
+
# client.options[:invalid] = 'foo'
|
48
|
+
it "freezes the options" do
|
49
|
+
subject.options.should be_frozen
|
50
|
+
end
|
51
|
+
|
52
|
+
it "creates a Oauth::Consumer on initialize" do
|
53
|
+
subject.consumer.class.should == OAuth::Consumer
|
54
|
+
subject.consumer.key.should == subject.key
|
55
|
+
subject.consumer.secret.should == subject.secret
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns an OAuth request_token" do
|
59
|
+
# Cannot just check for method delegation as http connection will be attempted
|
60
|
+
request_token = OAuth::RequestToken.new(subject.consumer)
|
61
|
+
subject.consumer.stub(:get_request_token => request_token)
|
62
|
+
subject.get_request_token.should == request_token
|
63
|
+
end
|
64
|
+
|
65
|
+
it "is possible to set the request token" do
|
66
|
+
token = mock()
|
67
|
+
OAuth::RequestToken.should_receive(:new).with(subject.consumer, 'foo', 'bar').and_return(token)
|
68
|
+
|
69
|
+
request_token = subject.set_request_token('foo', 'bar')
|
70
|
+
|
71
|
+
request_token.should == token
|
72
|
+
subject.request_token.should == token
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "access token" do
|
76
|
+
|
77
|
+
it "initializes the access token" do
|
78
|
+
request_token = OAuth::RequestToken.new(subject.consumer)
|
79
|
+
subject.consumer.stub(:get_request_token => request_token)
|
80
|
+
mock_access_token = mock()
|
81
|
+
request_token.should_receive(:get_access_token).with(:oauth_verifier => 'abc123').and_return(mock_access_token)
|
82
|
+
subject.init_access_token(:oauth_verifier => 'abc123')
|
83
|
+
subject.access_token.should == mock_access_token
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises an exception when accessing without initialisation" do
|
87
|
+
lambda do
|
88
|
+
subject.access_token
|
89
|
+
end.should raise_exception(Jira::Client::UninitializedAccessTokenError, "init_access_token must be called before using the client")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "is possible to set the access token" do
|
93
|
+
token = mock()
|
94
|
+
OAuth::AccessToken.should_receive(:new).with(subject.consumer, 'foo', 'bar').and_return(token)
|
95
|
+
|
96
|
+
access_token = subject.set_access_token('foo', 'bar')
|
97
|
+
|
98
|
+
access_token.should == token
|
99
|
+
subject.access_token.should == token
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "http" do
|
105
|
+
|
106
|
+
it "responds to the http methods" do
|
107
|
+
mock_access_token = mock()
|
108
|
+
subject.stub(:access_token => mock_access_token)
|
109
|
+
[:delete, :get, :head].each do |method|
|
110
|
+
mock_access_token.should_receive(:request).with(method, '/path', {'Accept' => 'application/json'}).and_return(response)
|
111
|
+
subject.send(method, '/path')
|
112
|
+
end
|
113
|
+
[:post, :put].each do |method|
|
114
|
+
mock_access_token.should_receive(:request).with(method,
|
115
|
+
'/path', '',
|
116
|
+
{'Accept' => 'application/json', 'Content-Type' => 'application/json'}).and_return(response)
|
117
|
+
subject.send(method, '/path')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "performs a request" do
|
122
|
+
access_token = mock()
|
123
|
+
access_token.should_receive(:request).with(:get, '/foo').and_return(response)
|
124
|
+
subject.stub(:access_token => access_token)
|
125
|
+
subject.request(:get, '/foo')
|
126
|
+
end
|
127
|
+
|
128
|
+
it "raises an exception for non success responses" do
|
129
|
+
response = mock()
|
130
|
+
response.stub(:kind_of?).with(Net::HTTPSuccess).and_return(false)
|
131
|
+
access_token = mock()
|
132
|
+
access_token.should_receive(:request).with(:get, '/foo').and_return(response)
|
133
|
+
subject.stub(:access_token => access_token)
|
134
|
+
|
135
|
+
lambda do
|
136
|
+
subject.request(:get, '/foo')
|
137
|
+
end.should raise_exception(Jira::Resource::HTTPError)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "Resource Factories" do
|
143
|
+
|
144
|
+
it "gets all projects" do
|
145
|
+
Jira::Resource::Project.should_receive(:all).with(subject).and_return([])
|
146
|
+
subject.Project.all.should == []
|
147
|
+
end
|
148
|
+
|
149
|
+
it "finds a single project" do
|
150
|
+
find_result = mock()
|
151
|
+
Jira::Resource::Project.should_receive(:find).with(subject, '123').and_return(find_result)
|
152
|
+
subject.Project.find('123').should == find_result
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|