jira-ruby 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +259 -0
- data/Rakefile +9 -0
- data/example.rb +47 -4
- data/jira-ruby.gemspec +5 -3
- data/lib/jira.rb +21 -7
- data/lib/jira/base.rb +466 -0
- data/lib/jira/base_factory.rb +49 -0
- data/lib/jira/client.rb +79 -8
- data/lib/jira/has_many_proxy.rb +43 -0
- data/lib/jira/http_error.rb +16 -0
- data/lib/jira/resource/attachment.rb +12 -0
- data/lib/jira/resource/comment.rb +14 -0
- data/lib/jira/resource/component.rb +4 -9
- data/lib/jira/resource/issue.rb +49 -5
- data/lib/jira/resource/issuetype.rb +10 -0
- data/lib/jira/resource/priority.rb +10 -0
- data/lib/jira/resource/project.rb +24 -3
- data/lib/jira/resource/status.rb +10 -0
- data/lib/jira/resource/user.rb +14 -0
- data/lib/jira/resource/version.rb +10 -0
- data/lib/jira/resource/worklog.rb +16 -0
- data/lib/jira/version.rb +2 -2
- data/spec/integration/attachment_spec.rb +26 -0
- data/spec/integration/comment_spec.rb +55 -0
- data/spec/integration/component_spec.rb +25 -52
- data/spec/integration/issue_spec.rb +50 -47
- data/spec/integration/issuetype_spec.rb +27 -0
- data/spec/integration/priority_spec.rb +27 -0
- data/spec/integration/project_spec.rb +32 -24
- data/spec/integration/status_spec.rb +27 -0
- data/spec/integration/user_spec.rb +25 -0
- data/spec/integration/version_spec.rb +43 -0
- data/spec/integration/worklog_spec.rb +55 -0
- data/spec/jira/base_factory_spec.rb +46 -0
- data/spec/jira/base_spec.rb +555 -0
- data/spec/jira/client_spec.rb +12 -12
- data/spec/jira/has_many_proxy_spec.rb +45 -0
- data/spec/jira/{resource/http_error_spec.rb → http_error_spec.rb} +1 -1
- data/spec/jira/resource/attachment_spec.rb +20 -0
- data/spec/jira/resource/issue_spec.rb +83 -0
- data/spec/jira/resource/project_factory_spec.rb +3 -3
- data/spec/jira/resource/project_spec.rb +28 -0
- data/spec/jira/resource/worklog_spec.rb +24 -0
- data/spec/mock_responses/attachment/10000.json +20 -0
- data/spec/mock_responses/component/10000.invalid.put.json +5 -0
- data/spec/mock_responses/issue.json +1108 -0
- data/spec/mock_responses/issue/10002.invalid.put.json +6 -0
- data/spec/mock_responses/issue/10002.json +13 -1
- data/spec/mock_responses/issue/10002.put.missing_field_update.json +6 -0
- data/spec/mock_responses/issue/10002/comment.json +65 -0
- data/spec/mock_responses/issue/10002/comment.post.json +29 -0
- data/spec/mock_responses/issue/10002/comment/10000.json +29 -0
- data/spec/mock_responses/issue/10002/comment/10000.put.json +29 -0
- data/spec/mock_responses/issue/10002/worklog.json +98 -0
- data/spec/mock_responses/issue/10002/worklog.post.json +30 -0
- data/spec/mock_responses/issue/10002/worklog/10000.json +31 -0
- data/spec/mock_responses/issue/10002/worklog/10000.put.json +30 -0
- data/spec/mock_responses/issuetype.json +42 -0
- data/spec/mock_responses/issuetype/5.json +8 -0
- data/spec/mock_responses/priority.json +42 -0
- data/spec/mock_responses/priority/1.json +8 -0
- data/spec/mock_responses/project/SAMPLEPROJECT.issues.json +1108 -0
- data/spec/mock_responses/project/SAMPLEPROJECT.json +15 -1
- data/spec/mock_responses/status.json +37 -0
- data/spec/mock_responses/status/1.json +7 -0
- data/spec/mock_responses/user?username=admin.json +17 -0
- data/spec/mock_responses/version.post.json +7 -0
- data/spec/mock_responses/version/10000.invalid.put.json +5 -0
- data/spec/mock_responses/version/10000.json +11 -0
- data/spec/mock_responses/version/10000.put.json +7 -0
- data/spec/spec_helper.rb +7 -12
- data/spec/support/matchers/have_attributes.rb +11 -0
- data/spec/support/matchers/have_many.rb +9 -0
- data/spec/support/matchers/have_one.rb +5 -0
- data/spec/support/shared_examples/integration.rb +174 -0
- metadata +139 -24
- data/README.markdown +0 -81
- data/lib/jira/resource/base.rb +0 -148
- data/lib/jira/resource/base_factory.rb +0 -44
- data/lib/jira/resource/http_error.rb +0 -17
- data/spec/jira/resource/base_factory_spec.rb +0 -36
- data/spec/jira/resource/base_spec.rb +0 -292
data/README.markdown
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
Jira 5 API Gem
|
2
|
-
==============
|
3
|
-
|
4
|
-
Links to JIRA REST API documentation
|
5
|
-
------------------------------------
|
6
|
-
* [Overview](https://developer.atlassian.com/display/JIRADEV/JIRA+REST+APIs)
|
7
|
-
* [Reference](http://docs.atlassian.com/jira/REST/5.0-rc1/)
|
8
|
-
|
9
|
-
|
10
|
-
Setting up the JIRA SDK
|
11
|
-
-----------------------
|
12
|
-
On Mac OS,
|
13
|
-
|
14
|
-
brew install atlassian-plugin-sdk
|
15
|
-
|
16
|
-
Otherwise:
|
17
|
-
|
18
|
-
* Download the SDK from https://developer.atlassian.com/ (You will need
|
19
|
-
an Atlassian login for this)
|
20
|
-
* Unpack the dowloaded archive
|
21
|
-
* From within the archive directory, run:
|
22
|
-
|
23
|
-
./bin/atlas-run-standalone --product jira --version 5.0-rc2
|
24
|
-
|
25
|
-
Once this is running, you should be able to connect to
|
26
|
-
[http://localhost:2990/] and login to the JIRA admin system using `admin:admin`
|
27
|
-
|
28
|
-
You'll need to create a dummy project and probably some issues to test using
|
29
|
-
this library.
|
30
|
-
|
31
|
-
Configuring JIRA to use OAuth
|
32
|
-
-----------------------------
|
33
|
-
From the Jira API tutorial
|
34
|
-
|
35
|
-
> The first step is to register a new consumer in JIRA. This is done through
|
36
|
-
> the Application Links administration screens in JIRA. Create a new
|
37
|
-
> Application Link.
|
38
|
-
> [Administration/Plugins/Application Links](http://localhost:2990/jira/plugins/servlet/applinks/listApplicationLinks)
|
39
|
-
>
|
40
|
-
> When creating the Application Link use a placeholder URL or the correct URL
|
41
|
-
> to your client (e.g. `http://localhost:3000`), if your client can be reached
|
42
|
-
> via HTTP and choose the Generic Application type. After this Application Link
|
43
|
-
> has been created, edit the configuration and go to the incoming
|
44
|
-
> authentication configuration screen and select OAuth. Enter in this the
|
45
|
-
> public key and the consumer key which your client will use when making
|
46
|
-
> requests to JIRA.
|
47
|
-
|
48
|
-
This public key and consumer key will need to be generated by the Gem user, using OpenSSL
|
49
|
-
or similar to generate the public key and the provided rake task to generate the consumer
|
50
|
-
key.
|
51
|
-
|
52
|
-
> After you have entered all the information click OK and ensure OAuth authentication is
|
53
|
-
> enabled.
|
54
|
-
|
55
|
-
Using the API Gem in your application
|
56
|
-
-------------------------------------
|
57
|
-
The JiraApi gem requires the consumer key and public certificate file (which are generated in their respective rake tasks) to initialize an access token for using the Jira API. These two pieces of information are applied globally to your application, with separate JiraApi::Client objects created on a per-user basis.
|
58
|
-
|
59
|
-
An example initializer which sets the key and certificate filename in a pair of globals is shown below, myapp/config/initializers/jira\_api.rb:
|
60
|
-
|
61
|
-
$CONSUMER_KEY = 'cbaec507669c65979b6b6eefdb1c5bb0' #Your consumer key. Can be generated by rake jira_api:generate_public_cert
|
62
|
-
$PUBLIC_CERT_FILE = 'rsakey.pem' #Location of the Private Key File (generated by rake jira_api:generate_public_cert
|
63
|
-
|
64
|
-
This allows acces to the variables when a session is being set up.
|
65
|
-
The following sample controller shows how to set up and initialize an access token for a particular user session.
|
66
|
-
(Note that the callback url is defined in the Jira application link interface, and can be placed wherever suits you best in your application. The session#callback method is simply an example)
|
67
|
-
|
68
|
-
\#TODO cannot pass params hash straight into init\_access\_token method - errors with a missing parameter exception
|
69
|
-
|
70
|
-
class SessionsController < ApplicationController
|
71
|
-
def create
|
72
|
-
session[:client] = JiraApi::Client.new($CONSUMER_KEY, '', :private_key_file => $PUBLIC_CERT_FILE)
|
73
|
-
session[:request_token] = session[:client].request_token #Generate the request token
|
74
|
-
redirect_to session[:request_token].authorize_url #Redirect to Jira to authorize the token
|
75
|
-
end
|
76
|
-
|
77
|
-
def callback
|
78
|
-
session[:client].init_access_token(:oauth_verifier => params[:oauth_verifier]) #Initialize the access token
|
79
|
-
redirect_to root_url #Redirect to the desired page after initializing the access token
|
80
|
-
end
|
81
|
-
end
|
data/lib/jira/resource/base.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
module Jira
|
2
|
-
module Resource
|
3
|
-
|
4
|
-
class Base
|
5
|
-
|
6
|
-
attr_reader :client
|
7
|
-
attr_accessor :expanded, :deleted, :attrs
|
8
|
-
alias :expanded? :expanded
|
9
|
-
alias :deleted? :deleted
|
10
|
-
|
11
|
-
def initialize(client, options = {})
|
12
|
-
@client = client
|
13
|
-
@attrs = options[:attrs] || {}
|
14
|
-
@expanded = options[:expanded] || false
|
15
|
-
@deleted = false
|
16
|
-
end
|
17
|
-
|
18
|
-
# The class methods are never called directly, they are always
|
19
|
-
# invoked from a BaseFactory subclass instance.
|
20
|
-
def self.all(client)
|
21
|
-
response = client.get(rest_base_path(client))
|
22
|
-
json = parse_json(response.body)
|
23
|
-
json.map do |attrs|
|
24
|
-
self.new(client, :attrs => attrs)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.find(client, key)
|
29
|
-
instance = self.new(client)
|
30
|
-
instance.attrs[key_attribute.to_s] = key
|
31
|
-
instance.fetch
|
32
|
-
instance
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.build(client, attrs)
|
36
|
-
self.new(client, :attrs => attrs)
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.rest_base_path(client)
|
40
|
-
client.options[:rest_base_path] + '/' + self.endpoint_name
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.endpoint_name
|
44
|
-
self.name.split('::').last.downcase
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.key_attribute
|
48
|
-
:key
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.parse_json(string)
|
52
|
-
JSON.parse(string)
|
53
|
-
end
|
54
|
-
|
55
|
-
def respond_to?(method_name)
|
56
|
-
if attrs.keys.include? method_name.to_s
|
57
|
-
true
|
58
|
-
else
|
59
|
-
super(method_name)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def method_missing(method_name, *args, &block)
|
64
|
-
if attrs.keys.include? method_name.to_s
|
65
|
-
attrs[method_name.to_s]
|
66
|
-
else
|
67
|
-
super(method_name)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def rest_base_path
|
72
|
-
# Just proxy this to the class method
|
73
|
-
self.class.rest_base_path(client)
|
74
|
-
end
|
75
|
-
|
76
|
-
def fetch(reload = false)
|
77
|
-
return if expanded? && !reload
|
78
|
-
response = client.get(url)
|
79
|
-
set_attrs_from_response(response)
|
80
|
-
@expanded = true
|
81
|
-
end
|
82
|
-
|
83
|
-
def save(attrs)
|
84
|
-
http_method = new_record? ? :post : :put
|
85
|
-
response = client.send(http_method, url, attrs.to_json)
|
86
|
-
set_attrs(attrs, false)
|
87
|
-
set_attrs_from_response(response)
|
88
|
-
@expanded = false
|
89
|
-
true
|
90
|
-
end
|
91
|
-
|
92
|
-
def set_attrs_from_response(response)
|
93
|
-
unless response.body.nil? or response.body.length < 2
|
94
|
-
json = self.class.parse_json(response.body)
|
95
|
-
set_attrs(json)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Set the current attributes from a hash. If clobber is true, any existing
|
100
|
-
# hash values will be clobbered by the new hash, otherwise the hash will
|
101
|
-
# be deeply merged into attrs. The target paramater is for internal use only
|
102
|
-
# and should not be used.
|
103
|
-
def set_attrs(hash, clobber=true, target = nil)
|
104
|
-
target ||= @attrs
|
105
|
-
if clobber
|
106
|
-
target.merge!(hash)
|
107
|
-
hash
|
108
|
-
else
|
109
|
-
hash.each do |k, v|
|
110
|
-
if v.is_a?(Hash)
|
111
|
-
set_attrs(v, clobber, target[k])
|
112
|
-
else
|
113
|
-
target[k] = v
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def delete
|
120
|
-
client.delete(url)
|
121
|
-
@deleted = true
|
122
|
-
end
|
123
|
-
|
124
|
-
def url
|
125
|
-
if @attrs['self']
|
126
|
-
@attrs['self']
|
127
|
-
elsif @attrs[self.class.key_attribute.to_s]
|
128
|
-
rest_base_path + "/" + @attrs[self.class.key_attribute.to_s].to_s
|
129
|
-
else
|
130
|
-
rest_base_path
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def to_s
|
135
|
-
"#<#{self.class.name}:#{object_id} @attrs=#{@attrs.inspect}>"
|
136
|
-
end
|
137
|
-
|
138
|
-
def to_json
|
139
|
-
attrs.to_json
|
140
|
-
end
|
141
|
-
|
142
|
-
def new_record?
|
143
|
-
@attrs['id'].nil?
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
end
|
148
|
-
end
|
@@ -1,44 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Jira::Resource::BaseFactory do
|
4
|
-
|
5
|
-
class Jira::Resource::FooFactory < Jira::Resource::BaseFactory ; end
|
6
|
-
class Jira::Resource::Foo ; end
|
7
|
-
|
8
|
-
let(:client) { mock() }
|
9
|
-
subject { Jira::Resource::FooFactory.new(client) }
|
10
|
-
|
11
|
-
it "initializes correctly" do
|
12
|
-
subject.class.should == Jira::Resource::FooFactory
|
13
|
-
subject.client.should == client
|
14
|
-
subject.target_class.should == Jira::Resource::Foo
|
15
|
-
end
|
16
|
-
|
17
|
-
it "proxies all to the target class" do
|
18
|
-
Jira::Resource::Foo.should_receive(:all).with(client)
|
19
|
-
subject.all
|
20
|
-
end
|
21
|
-
|
22
|
-
it "proxies find to the target class" do
|
23
|
-
Jira::Resource::Foo.should_receive(:find).with(client, 'FOO')
|
24
|
-
subject.find('FOO')
|
25
|
-
end
|
26
|
-
|
27
|
-
it "returns the target class" do
|
28
|
-
subject.target_class.should == Jira::Resource::Foo
|
29
|
-
end
|
30
|
-
|
31
|
-
it "proxies build to the target class" do
|
32
|
-
attrs = mock()
|
33
|
-
Jira::Resource::Foo.should_receive(:build).with(client, attrs)
|
34
|
-
subject.build(attrs)
|
35
|
-
end
|
36
|
-
end
|
@@ -1,292 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Jira::Resource::Base do
|
4
|
-
|
5
|
-
class Jira::Resource::Deadbeef < Jira::Resource::Base ; end
|
6
|
-
|
7
|
-
let(:client) { mock() }
|
8
|
-
let(:attrs) { mock() }
|
9
|
-
|
10
|
-
subject { Jira::Resource::Deadbeef.new(client, :attrs => attrs) }
|
11
|
-
|
12
|
-
it "assigns the client and attrs" do
|
13
|
-
subject.client.should == client
|
14
|
-
subject.attrs.should == attrs
|
15
|
-
end
|
16
|
-
|
17
|
-
it "returns all the deadbeefs" do
|
18
|
-
response = mock()
|
19
|
-
response.should_receive(:body).and_return('[{"self":"http://deadbeef/","key":"FOO"}]')
|
20
|
-
client.should_receive(:get).with('/jira/rest/api/2/deadbeef').and_return(response)
|
21
|
-
Jira::Resource::Deadbeef.should_receive(:rest_base_path).and_return('/jira/rest/api/2/deadbeef')
|
22
|
-
deadbeefs = Jira::Resource::Deadbeef.all(client)
|
23
|
-
deadbeefs.length.should == 1
|
24
|
-
first = deadbeefs.first
|
25
|
-
first.class.should == Jira::Resource::Deadbeef
|
26
|
-
first.attrs['self'].should == 'http://deadbeef/'
|
27
|
-
first.attrs['key'].should == 'FOO'
|
28
|
-
first.expanded?.should be_false
|
29
|
-
end
|
30
|
-
|
31
|
-
it "finds a deadbeef by key" do
|
32
|
-
response = mock()
|
33
|
-
response.stub(:body).and_return('{"self":"http://deadbeef/","key":"FOO"}')
|
34
|
-
client.should_receive(:get).with('/jira/rest/api/2/deadbeef/FOO').and_return(response)
|
35
|
-
Jira::Resource::Deadbeef.should_receive(:rest_base_path).and_return('/jira/rest/api/2/deadbeef')
|
36
|
-
deadbeef = Jira::Resource::Deadbeef.find(client, 'FOO')
|
37
|
-
deadbeef.client.should == client
|
38
|
-
deadbeef.attrs['self'].should == 'http://deadbeef/'
|
39
|
-
deadbeef.attrs['key'].should == 'FOO'
|
40
|
-
deadbeef.expanded?.should be_true
|
41
|
-
end
|
42
|
-
|
43
|
-
it "builds a deadbeef" do
|
44
|
-
deadbeef = Jira::Resource::Deadbeef.build(client, 'key' => "FOO" )
|
45
|
-
deadbeef.expanded?.should be_false
|
46
|
-
|
47
|
-
deadbeef.client.should == client
|
48
|
-
deadbeef.attrs['key'].should == 'FOO'
|
49
|
-
end
|
50
|
-
|
51
|
-
it "returns the endpoint name" do
|
52
|
-
subject.class.endpoint_name.should == 'deadbeef'
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
describe "rest_base_path" do
|
57
|
-
|
58
|
-
before(:each) do
|
59
|
-
client.should_receive(:options).and_return(:rest_base_path => '/deadbeef/bar')
|
60
|
-
end
|
61
|
-
|
62
|
-
it "returns the rest_base_path" do
|
63
|
-
subject.rest_base_path.should == '/deadbeef/bar/deadbeef'
|
64
|
-
end
|
65
|
-
|
66
|
-
it "has a class method that returns the rest_base_path" do
|
67
|
-
subject.class.rest_base_path(client).should == '/deadbeef/bar/deadbeef'
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
it "parses json" do
|
72
|
-
described_class.parse_json('{"foo":"bar"}').should == {"foo" => "bar"}
|
73
|
-
end
|
74
|
-
|
75
|
-
describe "dynamic instance methods" do
|
76
|
-
|
77
|
-
let(:attrs) { {'foo' => 'bar', 'flum' => 'goo', 'object_id' => 'dummy'} }
|
78
|
-
subject { Jira::Resource::Deadbeef.new(client, :attrs => attrs) }
|
79
|
-
|
80
|
-
it "responds to each of the top level attribute names" do
|
81
|
-
subject.should respond_to(:foo)
|
82
|
-
subject.should respond_to('flum')
|
83
|
-
subject.should respond_to(:object_id)
|
84
|
-
|
85
|
-
subject.foo.should == 'bar'
|
86
|
-
subject.flum.should == 'goo'
|
87
|
-
|
88
|
-
# Should not override existing method names, but should still allow
|
89
|
-
# access to their values via the attrs[] hash
|
90
|
-
subject.object_id.should_not == 'dummy'
|
91
|
-
subject.attrs['object_id'].should == 'dummy'
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
describe "fetch" do
|
96
|
-
|
97
|
-
subject { Jira::Resource::Deadbeef.new(client, :attrs => {'key' => 'FOO'}) }
|
98
|
-
|
99
|
-
describe "not cached" do
|
100
|
-
|
101
|
-
before(:each) do
|
102
|
-
response = mock()
|
103
|
-
response.stub(:body).and_return('{"self":"http://deadbeef/","key":"FOO"}')
|
104
|
-
client.should_receive(:get).with('/jira/rest/api/2/deadbeef/FOO').and_return(response)
|
105
|
-
Jira::Resource::Deadbeef.should_receive(:rest_base_path).and_return('/jira/rest/api/2/deadbeef')
|
106
|
-
end
|
107
|
-
|
108
|
-
it "sets expanded to true after fetch" do
|
109
|
-
subject.expanded?.should be_false
|
110
|
-
subject.fetch
|
111
|
-
subject.expanded?.should be_true
|
112
|
-
end
|
113
|
-
|
114
|
-
it "performs a fetch" do
|
115
|
-
subject.expanded?.should be_false
|
116
|
-
subject.fetch
|
117
|
-
subject.self.should == "http://deadbeef/"
|
118
|
-
subject.key.should == "FOO"
|
119
|
-
end
|
120
|
-
|
121
|
-
it "performs a fetch if already fetched and force flag is true" do
|
122
|
-
subject.expanded = true
|
123
|
-
subject.fetch(true)
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
describe "cached" do
|
129
|
-
it "doesn't perform a fetch if already fetched" do
|
130
|
-
subject.expanded = true
|
131
|
-
client.should_not_receive(:get)
|
132
|
-
subject.fetch
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
describe "save" do
|
139
|
-
|
140
|
-
let(:response) { mock() }
|
141
|
-
|
142
|
-
subject { Jira::Resource::Deadbeef.new(client) }
|
143
|
-
|
144
|
-
before(:each) do
|
145
|
-
subject.should_receive(:url).and_return('/foo/bar')
|
146
|
-
end
|
147
|
-
|
148
|
-
it "POSTs a new record" do
|
149
|
-
response.stub(:body => '{"id":"123"}')
|
150
|
-
subject.stub(:new_record? => true)
|
151
|
-
client.should_receive(:post).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
152
|
-
subject.save("foo" => "bar").should be_true
|
153
|
-
subject.id.should == "123"
|
154
|
-
subject.expanded.should be_false
|
155
|
-
end
|
156
|
-
|
157
|
-
it "PUTs an existing record" do
|
158
|
-
response.stub(:body => nil)
|
159
|
-
subject.stub(:new_record? => false)
|
160
|
-
client.should_receive(:put).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
161
|
-
subject.save("foo" => "bar").should be_true
|
162
|
-
subject.expanded.should be_false
|
163
|
-
end
|
164
|
-
|
165
|
-
it "merges attrs on save" do
|
166
|
-
response.stub(:body => nil)
|
167
|
-
client.should_receive(:post).with('/foo/bar','{"foo":{"fum":"dum"}}').and_return(response)
|
168
|
-
subject.attrs = {"foo" => {"bar" => "baz"}}
|
169
|
-
subject.save({"foo" => {"fum" => "dum"}})
|
170
|
-
subject.foo.should == {"bar" => "baz", "fum" => "dum"}
|
171
|
-
end
|
172
|
-
|
173
|
-
end
|
174
|
-
|
175
|
-
describe "set_attrs" do
|
176
|
-
it "merges hashes correctly when clobber is true (default)" do
|
177
|
-
subject.attrs = {"foo" => {"bar" => "baz"}}
|
178
|
-
subject.set_attrs({"foo" => {"fum" => "dum"}})
|
179
|
-
subject.foo.should == {"fum" => "dum"}
|
180
|
-
end
|
181
|
-
|
182
|
-
it "merges hashes correctly when clobber is false" do
|
183
|
-
subject.attrs = {"foo" => {"bar" => "baz"}}
|
184
|
-
subject.set_attrs({"foo" => {"fum" => "dum"}}, false)
|
185
|
-
subject.foo.should == {"bar" => "baz", "fum" => "dum"}
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
describe "delete" do
|
190
|
-
|
191
|
-
before(:each) do
|
192
|
-
client.should_receive(:delete).with('/foo/bar')
|
193
|
-
subject.stub(:url => '/foo/bar')
|
194
|
-
end
|
195
|
-
|
196
|
-
it "flags itself as deleted" do
|
197
|
-
subject.deleted?.should be_false
|
198
|
-
subject.delete
|
199
|
-
subject.deleted?.should be_true
|
200
|
-
end
|
201
|
-
|
202
|
-
it "sends a DELETE request" do
|
203
|
-
subject.delete
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
describe "new_record?" do
|
209
|
-
|
210
|
-
it "returns true for new_record? when new object" do
|
211
|
-
subject.attrs.stub(:[]).with('id').and_return(nil)
|
212
|
-
subject.new_record?.should be_true
|
213
|
-
end
|
214
|
-
|
215
|
-
it "returns false for new_record? when id is set" do
|
216
|
-
subject.attrs.stub(:[]).with('id').and_return('123')
|
217
|
-
subject.new_record?.should be_false
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
describe 'url' do
|
223
|
-
it "returns self as the URL if set" do
|
224
|
-
attrs.stub(:[]).with('self').and_return('http://foo/bar')
|
225
|
-
subject.url.should == "http://foo/bar"
|
226
|
-
end
|
227
|
-
|
228
|
-
it "generates the URL from key if self not set" do
|
229
|
-
attrs.stub(:[]).with('self').and_return(nil)
|
230
|
-
attrs.stub(:[]).with('key').and_return('FOO')
|
231
|
-
subject.stub(:rest_base_path => 'http://foo/bar')
|
232
|
-
subject.url.should == "http://foo/bar/FOO"
|
233
|
-
end
|
234
|
-
|
235
|
-
it "generates the URL from rest_base_path if self and key not set" do
|
236
|
-
attrs.stub(:[]).with('self').and_return(nil)
|
237
|
-
attrs.stub(:[]).with('key').and_return(nil)
|
238
|
-
subject.stub(:rest_base_path => 'http://foo/bar')
|
239
|
-
subject.url.should == "http://foo/bar"
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
it "returns the formatted attrs from to_s" do
|
244
|
-
subject.attrs.stub(:[]).with('foo').and_return('bar')
|
245
|
-
subject.attrs.stub(:[]).with('dead').and_return('beef')
|
246
|
-
|
247
|
-
subject.to_s.should match(/#<Jira::Resource::Deadbeef:\d+ @attrs=#{attrs.inspect}>/)
|
248
|
-
end
|
249
|
-
|
250
|
-
it "returns the key attribute" do
|
251
|
-
subject.class.key_attribute.should == :key
|
252
|
-
end
|
253
|
-
|
254
|
-
it "converts to json" do
|
255
|
-
subject.attrs.stub(:to_json => '{"foo":"bar","dead":"beef"}')
|
256
|
-
|
257
|
-
subject.to_json.should == '{"foo":"bar","dead":"beef"}'
|
258
|
-
end
|
259
|
-
|
260
|
-
describe "extract attrs from response" do
|
261
|
-
|
262
|
-
subject { Jira::Resource::Deadbeef.new(client, :attrs => {}) }
|
263
|
-
|
264
|
-
it "sets the attrs from a response" do
|
265
|
-
response = mock()
|
266
|
-
response.stub(:body).and_return('{"foo":"bar"}')
|
267
|
-
|
268
|
-
subject.set_attrs_from_response(response).should == {'foo' => 'bar'}
|
269
|
-
subject.foo.should == "bar"
|
270
|
-
end
|
271
|
-
|
272
|
-
it "doesn't clobber existing attrs not in response" do
|
273
|
-
response = mock()
|
274
|
-
response.stub(:body).and_return('{"foo":"bar"}')
|
275
|
-
|
276
|
-
subject.attrs = {'flum' => 'flar'}
|
277
|
-
subject.set_attrs_from_response(response).should == {'foo' => 'bar'}
|
278
|
-
subject.foo.should == "bar"
|
279
|
-
subject.flum.should == "flar"
|
280
|
-
end
|
281
|
-
|
282
|
-
it "handles nil response body" do
|
283
|
-
response = mock()
|
284
|
-
response.stub(:body).and_return(nil)
|
285
|
-
|
286
|
-
subject.attrs = {'flum' => 'flar'}
|
287
|
-
subject.set_attrs_from_response(response).should be_nil
|
288
|
-
subject.flum.should == 'flar'
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
end
|