jira-ruby-2.3.0/0000755000004100000410000000000014370022124013443 5ustar www-datawww-datajira-ruby-2.3.0/http-basic-example.rb0000644000004100000410000000533214370022124017462 0ustar www-datawww-datarequire 'rubygems' require 'pp' require 'jira-ruby' if ARGV.empty? # If not passed any command line arguments, prompt the # user for the username and password. puts 'Enter the username: ' username = gets.strip puts 'Enter the password: ' password = gets.strip elsif ARGV.length == 2 username = ARGV[0] password = ARGV[1] else # Script must be passed 0 or 2 arguments raise "Usage: #{$PROGRAM_NAME} [ username password ]" end options = { username: username, password: password, site: 'http://localhost:8080/', context_path: '', auth_type: :basic, use_ssl: false } client = JIRA::Client.new(options) # Show all projects projects = client.Project.all projects.each do |project| puts "Project -> key: #{project.key}, name: #{project.name}" end # # Find a specific project by key # # ------------------------------ # project = client.Project.find('SAMPLEPROJECT') # pp project # project.issues.each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # List all Issues # # --------------- # client.Issue.all.each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # List issues by JQL query # # ------------------------ # client.Issue.jql('PROJECT = "SAMPLEPROJECT"', {fields: %w(summary status)}).each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # Delete an issue # # --------------- # issue = client.Issue.find('SAMPLEPROJECT-2') # if issue.delete # puts "Delete of issue SAMPLEPROJECT-2 sucessful" # else # puts "Delete of issue SAMPLEPROJECT-2 failed" # end # # # Create an issue # # --------------- # issue = client.Issue.build # issue.save({"fields"=>{"summary"=>"blarg from in example.rb","project"=>{"id"=>"10001"},"issuetype"=>{"id"=>"3"}}}) # issue.fetch # pp issue # # # Update an issue # # --------------- # issue = client.Issue.find("10002") # issue.save({"fields"=>{"summary"=>"EVEN MOOOOOOARRR NINJAAAA!"}}) # pp issue # # # Find a user # # ----------- # user = client.User.find('admin') # pp user # # # Get all issue types # # ------------------- # issuetypes = client.Issuetype.all # pp issuetypes # # # Get a single issue type # # ----------------------- # issuetype = client.Issuetype.find('5') # pp issuetype # # # Get all comments for an issue # # ----------------------------- # issue.comments.each do |comment| # pp comment # end # # # Build and Save a comment # # ------------------------ # comment = issue.comments.build # comment.save!(:body => "New comment from example script") # # # Delete a comment from the collection # # ------------------------------------ # issue.comments.last.delete # # # Update an existing comment # # -------------------------- # issue.comments.first.save({"body" => "an updated comment frome example.rb"}) jira-ruby-2.3.0/.travis.yml0000644000004100000410000000021614370022124015553 0ustar www-datawww-datalanguage: ruby rvm: - 2.4 - 2.5 - 2.6 - 2.7 before_script: - rake jira:generate_public_cert script: bundle exec rake spec jira-ruby-2.3.0/README.md0000644000004100000410000003324414370022124014730 0ustar www-datawww-data# JIRA API Gem [![Code Climate](https://codeclimate.com/github/sumoheavy/jira-ruby.svg)](https://codeclimate.com/github/sumoheavy/jira-ruby) [![Build Status](https://travis-ci.org/sumoheavy/jira-ruby.svg?branch=master)](https://travis-ci.org/sumoheavy/jira-ruby) This gem provides access to the Atlassian JIRA REST API. ## Slack Join our Slack channel! You can find us [here](https://jira-ruby-slackin.herokuapp.com/) ## Example usage ```ruby require 'rubygems' require 'jira-ruby' options = { :username => 'username', :password => 'pass1234', :site => 'http://mydomain.atlassian.net:443/', :context_path => '', :auth_type => :basic } client = JIRA::Client.new(options) project = client.Project.find('SAMPLEPROJECT') project.issues.each do |issue| puts "#{issue.id} - #{issue.summary}" end ``` ## Links to JIRA REST API documentation * [Overview](https://developer.atlassian.com/display/JIRADEV/JIRA+REST+APIs) * [Reference](http://docs.atlassian.com/jira/REST/latest/) ## Running tests Before running tests, you will need a public certificate generated. ```shell rake jira:generate_public_cert ``` ## Setting up the JIRA SDK On Mac OS, * Follow the instructions under "Mac OSX Installer" here: https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system * From within the archive directory, run: ```shell ./bin/atlas-run-standalone --product jira ``` Once this is running, you should be able to connect to http://localhost:2990/ and login to the JIRA admin system using `admin:admin` You'll need to create a dummy project and probably some issues to test using this library. ## Configuring JIRA to use OAuth From the JIRA API tutorial > The first step is to register a new consumer in JIRA. This is done through > the Application Links administration screens in JIRA. Create a new > Application Link. > [Administration/Plugins/Application Links](http://localhost:2990/jira/plugins/servlet/applinks/listApplicationLinks) > > When creating the Application Link use a placeholder URL or the correct URL > to your client (e.g. `http://localhost:3000`), if your client can be reached > via HTTP and choose the Generic Application type. After this Application Link > has been created, edit the configuration and go to the incoming > authentication configuration screen and select OAuth. Enter in this the > public key and the consumer key which your client will use when making > requests to JIRA. This public key and consumer key will need to be generated by the Gem user, using OpenSSL or similar to generate the public key and the provided rake task to generate the consumer key. > After you have entered all the information click OK and ensure OAuth authentication is > enabled. For 2 legged oauth in server mode only, not in cloud based JIRA, make sure to `Allow 2-Legged OAuth` ## Configuring JIRA to use HTTP Basic Auth Follow the same steps described above to set up a new Application Link in JIRA, however there is no need to set up any "incoming authentication" as this defaults to HTTP Basic Auth. ## Configuring JIRA to use Cookie-Based Auth Jira supports cookie based authentication whereby user credentials are passed to JIRA via a JIRA REST API call. This call returns a session cookie which must then be sent to all following JIRA REST API calls. To enable cookie based authentication, set `:auth_type` to `:cookie`, set `:use_cookies` to `true` and set `:username` and `:password` accordingly. ```ruby require 'jira-ruby' options = { :username => 'username', :password => 'pass1234', :site => 'http://mydomain.atlassian.net:443/', :context_path => '', :auth_type => :cookie, # Set cookie based authentication :use_cookies => true, # Send cookies with each request :additional_cookies => ['AUTH=vV7uzixt0SScJKg7'] # Optional cookies to send # with each request } client = JIRA::Client.new(options) project = client.Project.find('SAMPLEPROJECT') project.issues.each do |issue| puts "#{issue.id} - #{issue.summary}" end ``` Some authentication schemes might require additional cookies to be sent with each request. Cookies added to the `:additional_cookies` option will be added to each request. This option should be an array of strings representing each cookie to add to the request. Some authentication schemes that require additional cookies ignore the username and password sent in the JIRA REST API call. For those use cases, `:username` and `:password` may be omitted from `options`. ## Configuring JIRA to use Personal Access Tokens Auth If your JIRA system is configured to support Personal Access Token authorization, minor modifications are needed in how credentials are communicated to the server. Specifically, the paremeters `:username` and `:password` are not needed. Also, the parameter `:default_headers` is needed to contain the api_token, which can be obtained following the official documentation from [Atlassian](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html). Please note that the Personal Access Token can only be used as it is. If it is encoded (with base64 or any other encoding method) then the token will not work correctly and authentication will fail. ```ruby require 'jira-ruby' # NOTE: the token should not be encoded api_token = API_TOKEN_OBTAINED_FROM_JIRA_UI options = { :site => 'http://mydomain.atlassian.net:443/', :context_path => '', :username => '', :password => api_token, :auth_type => :basic } client = JIRA::Client.new(options) project = client.Project.find('SAMPLEPROJECT') project.issues.each do |issue| puts "#{issue.id} - #{issue.summary}" end ``` ## Using the API Gem in a command line application Using HTTP Basic Authentication, configure and connect a client to your instance of JIRA. Note: If your Jira install is hosted on [atlassian.net](atlassian.net), it will have no context path by default. If you're having issues connecting, try setting context_path to an empty string in the options hash. ```ruby require 'rubygems' require 'pp' require 'jira-ruby' # Consider the use of :use_ssl and :ssl_verify_mode options if running locally # for tests. # NOTE basic auth no longer works with Jira, you must generate an API token, to do so you must have jira instance access rights. You can generate a token here: https://id.atlassian.com/manage/api-tokens # You will see JIRA::HTTPError (JIRA::HTTPError) if you attempt to use basic auth with your user's password username = "myremoteuser" api_token = "myApiToken" options = { :username => username, :password => api_token, :site => 'http://localhost:8080/', # or 'https://.atlassian.net/' :context_path => '/myjira', # often blank :auth_type => :basic, :read_timeout => 120 } client = JIRA::Client.new(options) # Show all projects projects = client.Project.all projects.each do |project| puts "Project -> key: #{project.key}, name: #{project.name}" end ``` ## Using the API Gem in your Rails application Using oauth, the 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. Note that currently the rake task which generates the public certificate requires OpenSSL to be installed on the machine. Below is an example for setting up a rails application for OAuth authorization. Ensure the JIRA gem is loaded correctly ```ruby # Gemfile ... gem 'jira-ruby', :require => 'jira-ruby' ... ``` Add common methods to your application controller and ensure access token errors are handled gracefully ```ruby # app/controllers/application_controller.rb class ApplicationController < ActionController::Base protect_from_forgery rescue_from JIRA::OauthClient::UninitializedAccessTokenError do redirect_to new_jira_session_url end private def get_jira_client # add any extra configuration options for your instance of JIRA, # e.g. :use_ssl, :ssl_verify_mode, :context_path, :site options = { :private_key_file => "rsakey.pem", :consumer_key => 'test' } @jira_client = JIRA::Client.new(options) # Add AccessToken if authorised previously. if session[:jira_auth] @jira_client.set_access_token( session[:jira_auth]['access_token'], session[:jira_auth]['access_key'] ) end end end ``` Create a controller for handling the OAuth conversation. ```ruby # app/controllers/jira_sessions_controller.rb class JiraSessionsController < ApplicationController before_filter :get_jira_client def new callback_url = 'http://callback' request_token = @jira_client.request_token(oauth_callback: callback_url) session[:request_token] = request_token.token session[:request_secret] = request_token.secret redirect_to request_token.authorize_url end def authorize request_token = @jira_client.set_request_token( session[:request_token], session[:request_secret] ) access_token = @jira_client.init_access_token( :oauth_verifier => params[:oauth_verifier] ) session[:jira_auth] = { :access_token => access_token.token, :access_key => access_token.secret } session.delete(:request_token) session.delete(:request_secret) redirect_to projects_path end def destroy session.data.delete(:jira_auth) end end ``` Create your own controllers for the JIRA resources you wish to access. ```ruby # app/controllers/issues_controller.rb class IssuesController < ApplicationController before_filter :get_jira_client def index @issues = @jira_client.Issue.all end def show @issue = @jira_client.Issue.find(params[:id]) end end ``` ## Using the API Gem in your Sinatra application Here's the same example as a Sinatra application: ```ruby require 'jira-ruby' class App < Sinatra::Base enable :sessions # This section gets called before every request. Here, we set up the # OAuth consumer details including the consumer key, private key, # site uri, and the request token, access token, and authorize paths before do options = { :site => 'http://localhost:2990/', :context_path => '/jira', :signature_method => 'RSA-SHA1', :request_token_path => "/plugins/servlet/oauth/request-token", :authorize_path => "/plugins/servlet/oauth/authorize", :access_token_path => "/plugins/servlet/oauth/access-token", :private_key_file => "rsakey.pem", :rest_base_path => "/rest/api/2", :consumer_key => "jira-ruby-example" } @jira_client = JIRA::Client.new(options) @jira_client.consumer.http.set_debug_output($stderr) # Add AccessToken if authorised previously. if session[:jira_auth] @jira_client.set_access_token( session[:jira_auth][:access_token], session[:jira_auth][:access_key] ) end end # Starting point: http:/// # This will serve up a login link if you're not logged in. If you are, it'll show some user info and a # signout link get '/' do if !session[:jira_auth] # not logged in <<-eos

jira-ruby (JIRA 5 Ruby Gem) demo

You're not signed in. Why don't you sign in first. eos else #logged in @issues = @jira_client.Issue.all # HTTP response inlined with bind data below... <<-eos You're now signed in. There #{@issues.count == 1 ? "is" : "are"} #{@issues.count} issue#{@issues.count == 1 ? "" : "s"} in this JIRA instance. Signout eos end end # http:///signin # Initiates the OAuth dance by first requesting a token then redirecting to # http:///auth to get the @access_token get '/signin' do callback_url = "#{request.base_url}/callback" request_token = @jira_client.request_token(oauth_callback: callback_url) session[:request_token] = request_token.token session[:request_secret] = request_token.secret redirect request_token.authorize_url end # http:///callback # Retrieves the @access_token then stores it inside a session cookie. In a real app, # you'll want to persist the token in a datastore associated with the user. get "/callback" do request_token = @jira_client.set_request_token( session[:request_token], session[:request_secret] ) access_token = @jira_client.init_access_token( :oauth_verifier => params[:oauth_verifier] ) session[:jira_auth] = { :access_token => access_token.token, :access_key => access_token.secret } session.delete(:request_token) session.delete(:request_secret) redirect "/" end # http:///signout # Expires the session get "/signout" do session.delete(:jira_auth) redirect "/" end end ``` ## Using the API Gem in a 2 legged context Here's an example on how to use 2 legged OAuth: ```ruby require 'rubygems' require 'pp' require 'jira-ruby' options = { :site => 'http://localhost:2990/', :context_path => '/jira', :signature_method => 'RSA-SHA1', :private_key_file => "rsakey.pem", :rest_base_path => "/rest/api/2", :auth_type => :oauth_2legged, :consumer_key => "jira-ruby-example" } client = JIRA::Client.new(options) client.set_access_token("","") # Show all projects projects = client.Project.all projects.each do |project| puts "Project -> key: #{project.key}, name: #{project.name}" end ``` jira-ruby-2.3.0/spec/0000755000004100000410000000000014370022124014375 5ustar www-datawww-datajira-ruby-2.3.0/spec/jira/0000755000004100000410000000000014370022124015322 5ustar www-datawww-datajira-ruby-2.3.0/spec/jira/base_spec.rb0000644000004100000410000005401714370022124017602 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Base do class JIRADelegation < SimpleDelegator # :nodoc: end class JIRA::Resource::Deadbeef < JIRA::Base # :nodoc: end class JIRA::Resource::HasOneExample < JIRA::Base # :nodoc: has_one :deadbeef has_one :muffin, class: JIRA::Resource::Deadbeef has_one :brunchmuffin, class: JIRA::Resource::Deadbeef, nested_under: 'nested' has_one :breakfastscone, class: JIRA::Resource::Deadbeef, nested_under: %w[nested breakfastscone] has_one :irregularly_named_thing, class: JIRA::Resource::Deadbeef, attribute_key: 'irregularlyNamedThing' end class JIRA::Resource::HasManyExample < JIRA::Base # :nodoc: has_many :deadbeefs has_many :brunchmuffins, class: JIRA::Resource::Deadbeef, nested_under: 'nested' has_many :breakfastscones, class: JIRA::Resource::Deadbeef, nested_under: %w[nested breakfastscone] has_many :irregularly_named_things, class: JIRA::Resource::Deadbeef, attribute_key: 'irregularlyNamedThings' end let(:client) { double('client') } let(:attrs) { {} } subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) } let(:decorated) { JIRADelegation.new(subject) } describe '#respond_to?' do describe 'when decorated using SimpleDelegator' do it 'responds to client' do expect(decorated.respond_to?(:client)).to eq(true) end it 'does not raise an error' do expect do decorated.respond_to?(:client) end.not_to raise_error end end end it 'assigns the client and attrs' do expect(subject.client).to eq(client) expect(subject.attrs).to eq(attrs) end it 'returns all the deadbeefs' do response = double expect(response).to receive(:body).and_return('[{"self":"http://deadbeef/","id":"98765"}]') expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef').and_return(response) expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef') deadbeefs = JIRA::Resource::Deadbeef.all(client) expect(deadbeefs.length).to eq(1) first = deadbeefs.first expect(first.class).to eq(JIRA::Resource::Deadbeef) expect(first.attrs['self']).to eq('http://deadbeef/') expect(first.attrs['id']).to eq('98765') expect(first.expanded?).to be_falsey end it 'finds a deadbeef by id' do response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}') expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response) expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef') deadbeef = JIRA::Resource::Deadbeef.find(client, '98765') expect(deadbeef.client).to eq(client) expect(deadbeef.attrs['self']).to eq('http://deadbeef/') expect(deadbeef.attrs['id']).to eq('98765') expect(deadbeef.expanded?).to be_truthy end it 'finds a deadbeef containing changelog by id' do response = instance_double( 'Response', body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}' ) expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response) expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef') deadbeef = JIRA::Resource::Deadbeef.find(client, '98765', expand: 'changelog') expect(deadbeef.client).to eq(client) expect(deadbeef.attrs['self']).to eq('http://deadbeef/') expect(deadbeef.attrs['id']).to eq('98765') expect(deadbeef.expanded?).to be_truthy expect(deadbeef.attrs['changelog']['histories']).to eq([]) end it 'builds a deadbeef' do deadbeef = JIRA::Resource::Deadbeef.build(client, 'id' => '98765') expect(deadbeef.expanded?).to be_falsey expect(deadbeef.client).to eq(client) expect(deadbeef.attrs['id']).to eq('98765') end it 'returns the endpoint name' do expect(subject.class.endpoint_name).to eq('deadbeef') end it 'returns the path_component' do attrs['id'] = '123' expect(subject.path_component).to eq('/deadbeef/123') end it 'returns the path component for unsaved instances' do expect(subject.path_component).to eq('/deadbeef') end it 'converts to a symbol' do expect(subject.to_sym).to eq(:deadbeef) end describe 'collection_path' do before(:each) do expect(client).to receive(:options).and_return(rest_base_path: '/deadbeef/bar') end it 'returns the collection_path' do expect(subject.collection_path).to eq('/deadbeef/bar/deadbeef') end it 'returns the collection_path with a prefix' do expect(subject.collection_path('/baz/')).to eq('/deadbeef/bar/baz/deadbeef') end it 'has a class method that returns the collection_path' do expect(subject.class.collection_path(client)).to eq('/deadbeef/bar/deadbeef') end end it 'parses json' do expect(described_class.parse_json('{"foo":"bar"}')).to eq('foo' => 'bar') end describe 'dynamic instance methods' do let(:attrs) { { 'foo' => 'bar', 'flum' => 'goo', 'object_id' => 'dummy' } } subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) } it 'responds to each of the top level attribute names' do expect(subject).to respond_to(:foo) expect(subject).to respond_to('flum') expect(subject).to respond_to(:object_id) expect(subject.foo).to eq('bar') expect(subject.flum).to eq('goo') # Should not override existing method names, but should still allow # access to their values via the attrs[] hash expect(subject.object_id).not_to eq('dummy') expect(subject.attrs['object_id']).to eq('dummy') end end describe 'fetch' do subject { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '98765' }) } describe 'not cached' do before(:each) do response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}') expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response) expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef') end it 'sets expanded to true after fetch' do expect(subject.expanded?).to be_falsey subject.fetch expect(subject.expanded?).to be_truthy end it 'performs a fetch' do expect(subject.expanded?).to be_falsey subject.fetch expect(subject.self).to eq('http://deadbeef/') expect(subject.id).to eq('98765') end it 'performs a fetch if already fetched and force flag is true' do subject.expanded = true subject.fetch(true) end end describe 'cached' do it "doesn't perform a fetch if already fetched" do subject.expanded = true expect(client).not_to receive(:get) subject.fetch end end context "with expand parameter 'changelog'" do it "fetchs changelogs '" do response = instance_double( 'Response', body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}' ) expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response) expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef') subject.fetch(false, expand: 'changelog') expect(subject.self).to eq('http://deadbeef/') expect(subject.id).to eq('98765') expect(subject.changelog['histories']).to eq([]) end end end describe 'save' do let(:response) { double } subject { JIRA::Resource::Deadbeef.new(client) } before(:each) do expect(subject).to receive(:url).and_return('/foo/bar') end it 'POSTs a new record' do response = instance_double('Response', body: '{"id":"123"}') allow(subject).to receive(:new_record?) { true } expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response) expect(subject.save('foo' => 'bar')).to be_truthy expect(subject.id).to eq('123') expect(subject.expanded).to be_falsey end it 'PUTs an existing record' do response = instance_double('Response', body: nil) allow(subject).to receive(:new_record?) { false } expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response) expect(subject.save('foo' => 'bar')).to be_truthy expect(subject.expanded).to be_falsey end it 'merges attrs on save' do response = instance_double('Response', body: nil) expect(client).to receive(:post).with('/foo/bar', '{"foo":{"fum":"dum"}}').and_return(response) subject.attrs = { 'foo' => { 'bar' => 'baz' } } subject.save('foo' => { 'fum' => 'dum' }) expect(subject.foo).to eq('bar' => 'baz', 'fum' => 'dum') end it 'returns false when an invalid field is set' do # The JIRA REST API apparently ignores fields that you aren't allowed to set manually response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400) allow(subject).to receive(:new_record?) { false } expect(client).to receive(:put).with('/foo/bar', '{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response)) expect(subject.save('invalid_field' => 'foobar')).to be_falsey end it 'returns false with exception details when non json response body (unauthorized)' do # Unauthorized requests return a non-json body. This makes sure we can handle non-json bodies on HTTPError response = double('Response', body: 'totally invalid json', code: 401, message: 'Unauthorized') expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_raise(JIRA::HTTPError.new(response)) expect(subject.save('foo' => 'bar')).to be_falsey expect(subject.attrs['exception']['code']).to eq(401) expect(subject.attrs['exception']['message']).to eq('Unauthorized') end end describe 'save!' do let(:response) { double } subject { JIRA::Resource::Deadbeef.new(client) } before(:each) do expect(subject).to receive(:url).and_return('/foo/bar') end it 'POSTs a new record' do response = instance_double('Response', body: '{"id":"123"}') allow(subject).to receive(:new_record?) { true } expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response) expect(subject.save!('foo' => 'bar')).to be_truthy expect(subject.id).to eq('123') expect(subject.expanded).to be_falsey end it 'PUTs an existing record' do response = instance_double('Response', body: nil) allow(subject).to receive(:new_record?) { false } expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response) expect(subject.save!('foo' => 'bar')).to be_truthy expect(subject.expanded).to be_falsey end it 'throws an exception when an invalid field is set' do response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400) allow(subject).to receive(:new_record?) { false } expect(client).to receive(:put).with('/foo/bar', '{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response)) expect(-> { subject.save!('invalid_field' => 'foobar') }).to raise_error(JIRA::HTTPError) end end describe 'set_attrs' do it 'merges hashes correctly when clobber is true (default)' do subject.attrs = { 'foo' => { 'bar' => 'baz' } } subject.set_attrs('foo' => { 'fum' => 'dum' }) expect(subject.foo).to eq('fum' => 'dum') end it 'merges hashes correctly when clobber is false' do subject.attrs = { 'foo' => { 'bar' => 'baz' } } subject.set_attrs({ 'foo' => { 'fum' => 'dum' } }, false) expect(subject.foo).to eq('bar' => 'baz', 'fum' => 'dum') end end describe 'delete' do before(:each) do expect(client).to receive(:delete).with('/foo/bar') allow(subject).to receive(:url) { '/foo/bar' } end it 'flags itself as deleted' do expect(subject.deleted?).to be_falsey subject.delete expect(subject.deleted?).to be_truthy end it 'sends a DELETE request' do subject.delete end end describe 'new_record?' do it 'returns true for new_record? when new object' do subject.attrs['id'] = nil expect(subject.new_record?).to be_truthy end it 'returns false for new_record? when id is set' do subject.attrs['id'] = '123' expect(subject.new_record?).to be_falsey end end describe 'has_errors?' do it 'returns true when the response contains errors' do attrs['errors'] = { 'invalid' => 'Field invalid' } expect(subject.has_errors?).to be_truthy end it 'returns false when the response does not contain any errors' do expect(subject.has_errors?).to be_falsey end end describe 'url' do before(:each) do allow(client).to receive(:options) { { rest_base_path: '/foo/bar' } } end it 'returns self as the URL if set' do attrs['self'] = 'http://foo/bar' expect(subject.url).to eq('http://foo/bar') end it 'returns path as the URL if set and site options is specified' do allow(client).to receive(:options) { { site: 'http://foo' } } attrs['self'] = 'http://foo/bar' expect(subject.url).to eq('/bar') end it 'returns path as the URL if set and site options is specified and ends with a slash' do allow(client).to receive(:options) { { site: 'http://foo/' } } attrs['self'] = 'http://foo/bar' expect(subject.url).to eq('/bar') end it 'generates the URL from id if self not set' do attrs['self'] = nil attrs['id'] = '98765' expect(subject.url).to eq('/foo/bar/deadbeef/98765') end it 'generates the URL from collection_path if self and id not set' do attrs['self'] = nil attrs['id'] = nil expect(subject.url).to eq('/foo/bar/deadbeef') end it 'has a class method for the collection path' do expect(JIRA::Resource::Deadbeef.collection_path(client)).to eq('/foo/bar/deadbeef') # Should accept an optional prefix (flum in this case) expect(JIRA::Resource::Deadbeef.collection_path(client, '/flum/')).to eq('/foo/bar/flum/deadbeef') end it 'has a class method for the singular path' do expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123')).to eq('/foo/bar/deadbeef/abc123') # Should accept an optional prefix (flum in this case) expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123', '/flum/')).to eq('/foo/bar/flum/deadbeef/abc123') end end it 'returns the formatted attrs from to_s' do subject.attrs['foo'] = 'bar' subject.attrs['dead'] = 'beef' expect(subject.to_s).to match(/#/) end it 'returns the key attribute' do expect(subject.class.key_attribute).to eq(:id) end it 'returns the key value' do subject.attrs['id'] = '123' expect(subject.key_value).to eq('123') end it 'converts to json' do subject.attrs = { 'foo' => 'bar', 'dead' => 'beef' } expect(subject.to_json).to eq(subject.attrs.to_json) h = { 'key' => subject } h_attrs = { 'key' => subject.attrs } expect(h.to_json).to eq(h_attrs.to_json) end describe 'extract attrs from response' do subject { JIRA::Resource::Deadbeef.new(client, attrs: {}) } it 'sets the attrs from a response' do response = instance_double('Response', body: '{"foo":"bar"}') expect(subject.set_attrs_from_response(response)).to eq('foo' => 'bar') expect(subject.foo).to eq('bar') end it "doesn't clobber existing attrs not in response" do response = instance_double('Response', body: '{"foo":"bar"}') subject.attrs = { 'flum' => 'flar' } expect(subject.set_attrs_from_response(response)).to eq('foo' => 'bar') expect(subject.foo).to eq('bar') expect(subject.flum).to eq('flar') end it 'handles nil response body' do response = instance_double('Response', body: nil) subject.attrs = { 'flum' => 'flar' } expect(subject.set_attrs_from_response(response)).to be_nil expect(subject.flum).to eq('flar') end end describe 'nesting' do it 'defaults collection_attributes_are_nested to false' do expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_falsey end it 'allows collection_attributes_are_nested to be set' do JIRA::Resource::Deadbeef.nested_collections true expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_truthy end end describe 'has_many' do subject { JIRA::Resource::HasManyExample.new(client, attrs: { 'deadbeefs' => [{ 'id' => '123' }] }) } it 'returns a collection of instances for has_many relationships' do expect(subject.deadbeefs.class).to eq(JIRA::HasManyProxy) expect(subject.deadbeefs.length).to eq(1) subject.deadbeefs.each do |deadbeef| expect(deadbeef.class).to eq(JIRA::Resource::Deadbeef) end end it 'returns an empty collection for empty has_many relationships' do subject = JIRA::Resource::HasManyExample.new(client) expect(subject.deadbeefs.length).to eq(0) end it 'allows the has_many attributes to be nested inside another attribute' do subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'nested' => { 'brunchmuffins' => [{ 'id' => '123' }, { 'id' => '456' }] } }) expect(subject.brunchmuffins.length).to eq(2) subject.brunchmuffins.each do |brunchmuffin| expect(brunchmuffin.class).to eq(JIRA::Resource::Deadbeef) end end it 'allows it to be deeply nested' do subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'nested' => { 'breakfastscone' => { 'breakfastscones' => [{ 'id' => '123' }, { 'id' => '456' }] } } }) expect(subject.breakfastscones.length).to eq(2) subject.breakfastscones.each do |breakfastscone| expect(breakfastscone.class).to eq(JIRA::Resource::Deadbeef) end end it 'short circuits missing deeply nested attrs' do subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'nested' => {} }) expect(subject.breakfastscones.length).to eq(0) end it 'allows the attribute key to be specified' do subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'irregularlyNamedThings' => [{ 'id' => '123' }, { 'id' => '456' }] }) expect(subject.irregularly_named_things.length).to eq(2) subject.irregularly_named_things.each do |thing| expect(thing.class).to eq(JIRA::Resource::Deadbeef) end end it 'can build child instances' do deadbeef = subject.deadbeefs.build expect(deadbeef.class).to eq(JIRA::Resource::Deadbeef) end end describe 'has_one' do subject { JIRA::Resource::HasOneExample.new(client, attrs: { 'deadbeef' => { 'id' => '123' } }) } it 'returns an instance for a has one relationship' do expect(subject.deadbeef.class).to eq(JIRA::Resource::Deadbeef) expect(subject.deadbeef.id).to eq('123') end it 'returns nil when resource attribute is nonexistent' do subject = JIRA::Resource::HasOneExample.new(client) expect(subject.deadbeef).to be_nil end it 'returns an instance with a different class name to the attribute name' do subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'muffin' => { 'id' => '123' } }) expect(subject.muffin.class).to eq(JIRA::Resource::Deadbeef) expect(subject.muffin.id).to eq('123') end it 'allows the has_one attributes to be nested inside another attribute' do subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'nested' => { 'brunchmuffin' => { 'id' => '123' } } }) expect(subject.brunchmuffin.class).to eq(JIRA::Resource::Deadbeef) expect(subject.brunchmuffin.id).to eq('123') end it 'allows it to be deeply nested' do subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'nested' => { 'breakfastscone' => { 'breakfastscone' => { 'id' => '123' } } } }) expect(subject.breakfastscone.class).to eq(JIRA::Resource::Deadbeef) expect(subject.breakfastscone.id).to eq('123') end it 'allows the attribute key to be specified' do subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'irregularlyNamedThing' => { 'id' => '123' } }) expect(subject.irregularly_named_thing.class).to eq(JIRA::Resource::Deadbeef) expect(subject.irregularly_named_thing.id).to eq('123') end end describe 'belongs_to' do class JIRA::Resource::BelongsToExample < JIRA::Base belongs_to :deadbeef end let(:deadbeef) { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '999' }) } subject { JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef: deadbeef) } it 'sets up an accessor for the belongs to relationship' do expect(subject.deadbeef).to eq(deadbeef) end it 'raises an exception when initialized without a belongs_to instance' do expect(lambda { JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }) }).to raise_exception(ArgumentError, 'Required option :deadbeef missing') end it 'returns the right url' do allow(client).to receive(:options) { { rest_base_path: '/foo' } } expect(subject.url).to eq('/foo/deadbeef/999/belongstoexample/123') end it 'can be initialized with an instance or a key value' do allow(client).to receive(:options) { { rest_base_path: '/foo' } } subject = JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef_id: '987') expect(subject.url).to eq('/foo/deadbeef/987/belongstoexample/123') end end end jira-ruby-2.3.0/spec/jira/base_factory_spec.rb0000644000004100000410000000245314370022124021326 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::BaseFactory do class JIRA::Resource::FooFactory < JIRA::BaseFactory; end class JIRA::Resource::Foo; end let(:client) { double } subject { JIRA::Resource::FooFactory.new(client) } it 'initializes correctly' do expect(subject.class).to eq(JIRA::Resource::FooFactory) expect(subject.client).to eq(client) expect(subject.target_class).to eq(JIRA::Resource::Foo) end it 'proxies all to the target class' do expect(JIRA::Resource::Foo).to receive(:all).with(client) subject.all end it 'proxies find to the target class' do expect(JIRA::Resource::Foo).to receive(:find).with(client, 'FOO') subject.find('FOO') end it 'returns the target class' do expect(subject.target_class).to eq(JIRA::Resource::Foo) end it 'proxies build to the target class' do attrs = double expect(JIRA::Resource::Foo).to receive(:build).with(client, attrs) subject.build(attrs) end it 'proxies collection path to the target class' do expect(JIRA::Resource::Foo).to receive(:collection_path).with(client) subject.collection_path end it 'proxies singular path to the target class' do expect(JIRA::Resource::Foo).to receive(:singular_path).with(client, 'FOO') subject.singular_path('FOO') end end jira-ruby-2.3.0/spec/jira/has_many_proxy_spec.rb0000644000004100000410000000247514370022124021731 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::HasManyProxy do class Foo; end subject { JIRA::HasManyProxy.new(parent, Foo, collection) } let(:parent) { double('parent') } let(:collection) { double('collection') } it 'has a target class' do expect(subject.target_class).to eq(Foo) end it 'has a parent' do expect(subject.parent).to eq(parent) end it 'has a collection' do expect(subject.collection).to eq(collection) end it 'can build a new instance' do client = double('client') foo = double('foo') allow(parent).to receive(:client).and_return(client) allow(parent).to receive(:to_sym).and_return(:parent) expect(Foo).to receive(:new).with(client, attrs: { 'foo' => 'bar' }, parent: parent).and_return(foo) expect(collection).to receive(:<<).with(foo) expect(subject.build('foo' => 'bar')).to eq(foo) end it 'can get all the instances' do foo = double('foo') client = double('client') allow(parent).to receive(:client).and_return(client) allow(parent).to receive(:to_sym).and_return(:parent) expect(Foo).to receive(:all).with(client, parent: parent).and_return(foo) expect(subject.all).to eq(foo) end it 'delegates missing methods to the collection' do expect(collection).to receive(:missing_method) subject.missing_method end end jira-ruby-2.3.0/spec/jira/http_error_spec.rb0000644000004100000410000000111114370022124021043 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::HTTPError do let(:response) do response = double('response') allow(response).to receive(:code).and_return(401) allow(response).to receive(:message).and_return('A MESSAGE WOO') response end subject { described_class.new(response) } it 'takes the response object as an argument' do expect(subject.response).to eq(response) end it 'has a code method' do expect(subject.code).to eq(response.code) end it 'returns code and class from message' do expect(subject.message).to eq(response.message) end end jira-ruby-2.3.0/spec/jira/jwt_uri_builder_spec.rb0000644000004100000410000000270014370022124022051 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::JwtClient::JwtUriBuilder do subject(:url_builder) do JIRA::JwtClient::JwtUriBuilder.new(url, http_method, shared_secret, site, issuer) end let(:url) { '/foo' } let(:http_method) { :get } let(:shared_secret) { 'shared_secret' } let(:site) { 'http://localhost:2990' } let(:issuer) { nil } describe '#build' do subject { url_builder.build } it 'includes the jwt param' do expect(subject).to include('?jwt=') end context 'when the url already contains params' do let(:url) { '/foo?expand=projects.issuetypes.fields' } it 'includes the jwt param' do expect(subject).to include('&jwt=') end end context 'with a complete url' do let(:url) { 'http://localhost:2990/rest/api/2/issue/createmeta' } it 'includes the jwt param' do expect(subject).to include('?jwt=') end it { is_expected.to start_with('/') } it 'contains only one ?' do expect(subject.count('?')).to eq(1) end end context 'with a complete url containing a param' do let(:url) do 'http://localhost:2990/rest/api/2/issue/createmeta?expand=projects.issuetypes.fields' end it 'includes the jwt param' do expect(subject).to include('&jwt=') end it { is_expected.to start_with('/') } it 'contains only one ?' do expect(subject.count('?')).to eq(1) end end end end jira-ruby-2.3.0/spec/jira/oauth_client_spec.rb0000644000004100000410000001616714370022124021352 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::OauthClient do let(:oauth_client) do options = { consumer_key: 'foo', consumer_secret: 'bar' } options = JIRA::Client::DEFAULT_OPTIONS.merge(options) JIRA::OauthClient.new(options) end let(:response) do response = double('response') allow(response).to receive(:is_a?).with(Net::HTTPSuccess).and_return(true) response end describe 'authenticating with oauth' do it 'prepends the context path to all authorization and rest paths' do options = %i[request_token_path authorize_path access_token_path] defaults = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::OauthClient::DEFAULT_OPTIONS) options.each do |key| expect(oauth_client.options[key]).to eq(defaults[:context_path] + defaults[key]) end end it 'creates a Oauth::Consumer on initialize' do expect(oauth_client.consumer.class).to eq(OAuth::Consumer) expect(oauth_client.consumer.key).to eq(oauth_client.key) expect(oauth_client.consumer.secret).to eq(oauth_client.secret) end it 'returns an OAuth request_token' do # Cannot just check for method delegation as http connection will be attempted request_token = OAuth::RequestToken.new(oauth_client.consumer) allow(oauth_client).to receive(:get_request_token).and_return(request_token) expect(oauth_client.get_request_token).to eq(request_token) end it 'could pre-process the response body in a block' do response = Net::HTTPSuccess.new(1.0, '200', 'OK') allow_any_instance_of(OAuth::Consumer).to receive(:request).and_return(response) allow(response).to receive(:body).and_return('&oauth_token=token&oauth_token_secret=secret&password=top_secret') result = oauth_client.request_token do |response_body| CGI.parse(response_body).each_with_object({}) do |(k, v), h| next if k == 'password' h[k.strip.to_sym] = v.first end end expect(result).to be_an_instance_of(OAuth::RequestToken) expect(result.consumer).to eql(oauth_client.consumer) expect(result.params[:oauth_token]).to eql('token') expect(result.params[:oauth_token_secret]).to eql('secret') expect(result.params[:password]).to be_falsey end it 'allows setting the request token' do token = double expect(OAuth::RequestToken).to receive(:new).with(oauth_client.consumer, 'foo', 'bar').and_return(token) request_token = oauth_client.set_request_token('foo', 'bar') expect(request_token).to eq(token) expect(oauth_client.request_token).to eq(token) end it 'allows setting the consumer key' do expect(oauth_client.key).to eq('foo') end it 'allows setting the consumer secret' do expect(oauth_client.secret).to eq('bar') end describe 'the access token' do it 'initializes' do request_token = OAuth::RequestToken.new(oauth_client.consumer) allow(oauth_client).to receive(:get_request_token).and_return(request_token) mock_access_token = double expect(request_token).to receive(:get_access_token).with({ oauth_verifier: 'abc123' }).and_return(mock_access_token) oauth_client.init_access_token(oauth_verifier: 'abc123') expect(oauth_client.access_token).to eq(mock_access_token) end it 'raises an exception when accessing without initialisation' do expect do oauth_client.access_token end.to raise_exception(JIRA::OauthClient::UninitializedAccessTokenError, 'init_access_token must be called before using the client') end it 'allows setting the access token' do token = double expect(OAuth::AccessToken).to receive(:new).with(oauth_client.consumer, 'foo', 'bar').and_return(token) access_token = oauth_client.set_access_token('foo', 'bar') expect(access_token).to eq(token) expect(oauth_client.access_token).to eq(token) end end describe 'http' do let(:headers) { double } let(:access_token) { double } let(:body) { nil } before do allow(oauth_client).to receive(:access_token).and_return(access_token) end it 'responds to the http methods' do %i[delete get head].each do |method| expect(access_token).to receive(method).with('/path', headers).and_return(response) oauth_client.make_request(method, '/path', '', headers) end %i[post put].each do |method| expect(access_token).to receive(method).with('/path', '', headers).and_return(response) oauth_client.make_request(method, '/path', '', headers) end end it 'performs a request' do expect(access_token).to receive(:send).with(:get, '/foo', headers).and_return(response) oauth_client.request(:get, '/foo', body, headers) end context 'for a multipart request' do subject { oauth_client.make_multipart_request('/path', data, headers) } let(:data) { {} } let(:headers) { {} } it 'signs the access_token and performs the request' do expect(access_token).to receive(:sign!).with(an_instance_of(Net::HTTP::Post::Multipart)) expect(oauth_client.consumer).to receive_message_chain(:http, :request).with(an_instance_of(Net::HTTP::Post::Multipart)) subject end end end describe 'auth type is oauth_2legged' do let(:oauth__2legged_client) do options = { consumer_key: 'foo', consumer_secret: 'bar', auth_type: :oauth_2legged } options = JIRA::Client::DEFAULT_OPTIONS.merge(options) JIRA::OauthClient.new(options) end it 'responds to the http methods adding oauth_token parameter' do headers = double mock_access_token = double allow(oauth__2legged_client).to receive(:access_token).and_return(mock_access_token) %i[delete get head].each do |method| expect(mock_access_token).to receive(method).with('/path?oauth_token=', headers).and_return(response) oauth__2legged_client.make_request(method, '/path', '', headers) end %i[post put].each do |method| expect(mock_access_token).to receive(method).with('/path?oauth_token=', '', headers).and_return(response) oauth__2legged_client.make_request(method, '/path', '', headers) end end it 'responds to the http methods adding oauth_token parameter to any existing parameters' do headers = double mock_access_token = double allow(oauth__2legged_client).to receive(:access_token).and_return(mock_access_token) %i[delete get head].each do |method| expect(mock_access_token).to receive(method).with('/path?any_param=toto&oauth_token=', headers).and_return(response) oauth__2legged_client.make_request(method, '/path?any_param=toto', '', headers) end %i[post put].each do |method| expect(mock_access_token).to receive(method).with('/path?any_param=toto&oauth_token=', '', headers).and_return(response) oauth__2legged_client.make_request(method, '/path?any_param=toto', '', headers) end end end end end jira-ruby-2.3.0/spec/jira/client_spec.rb0000644000004100000410000002534514370022124020150 0ustar www-datawww-datarequire 'spec_helper' # We have three forms of authentication with two clases to represent the client for those different authentications. # Some behaviours are shared across all three types of authentication. these are captured here. RSpec.shared_examples 'Client Common Tests' do it { is_expected.to be_a JIRA::Client } it 'freezes the options once initialised' do expect(subject.options).to be_frozen end it 'prepends context path to rest base_path' do options = [:rest_base_path] defaults = JIRA::Client::DEFAULT_OPTIONS options.each { |key| expect(subject.options[key]).to eq(defaults[:context_path] + defaults[key]) } end it 'merges headers' do expect(subject.send(:merge_default_headers, {})).to eq('Accept' => 'application/json') end describe 'http methods' do it 'merges default headers' do # stubbed response for generic client request method expect(subject).to receive(:request).exactly(5).times.and_return(successful_response) # response for merging headers for http methods with no body expect(subject).to receive(:merge_default_headers).exactly(3).times.with({}) # response for merging headers for http methods with body expect(subject).to receive(:merge_default_headers).exactly(2).times.with(content_type_header) %i[delete get head].each { |method| subject.send(method, '/path', {}) } %i[post put].each { |method| subject.send(method, '/path', '', content_type_header) } end it 'calls the generic request method' do %i[delete get head].each do |method| expect(subject).to receive(:request).with(method, '/path', nil, headers).and_return(successful_response) subject.send(method, '/path', {}) end %i[post put].each do |method| expect(subject).to receive(:request).with(method, '/path', '', merged_headers) subject.send(method, '/path', '', {}) end end end describe 'Resource Factories' do it 'gets all projects' do expect(JIRA::Resource::Project).to receive(:all).with(subject).and_return([]) expect(subject.Project.all).to eq([]) end it 'finds a single project' do find_result = double expect(JIRA::Resource::Project).to receive(:find).with(subject, '123').and_return(find_result) expect(subject.Project.find('123')).to eq(find_result) end end describe 'SSL client options' do context 'without certificate and key' do let(:options) { { use_client_cert: true } } subject { JIRA::Client.new(options) } it 'raises an ArgumentError' do expect { subject }.to raise_exception(ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true') options[:ssl_client_cert] = '' expect { subject }.to raise_exception(ArgumentError, 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true') end end end end RSpec.shared_examples 'HttpClient tests' do it 'makes a valid request' do %i[delete get head].each do |method| expect(subject.request_client).to receive(:make_request).with(method, '/path', nil, headers).and_return(successful_response) subject.send(method, '/path', headers) end %i[post put].each do |method| expect(subject.request_client).to receive(:make_request).with(method, '/path', '', merged_headers).and_return(successful_response) subject.send(method, '/path', '', headers) end end end RSpec.shared_examples 'OAuth Common Tests' do include_examples 'Client Common Tests' specify { expect(subject.request_client).to be_a JIRA::OauthClient } it 'allows setting an access token' do token = double expect(OAuth::AccessToken).to receive(:new).with(subject.consumer, '', '').and_return(token) expect(subject.authenticated?).to be_falsey access_token = subject.set_access_token('', '') expect(access_token).to eq(token) expect(subject.access_token).to eq(token) expect(subject.authenticated?).to be_truthy end describe 'that call a oauth client' do specify 'which makes a request' do %i[delete get head].each do |method| expect(subject.request_client).to receive(:make_request).with(method, '/path', nil, headers).and_return(successful_response) subject.send(method, '/path', {}) end %i[post put].each do |method| expect(subject.request_client).to receive(:make_request).with(method, '/path', '', merged_headers).and_return(successful_response) subject.send(method, '/path', '', {}) end end end end describe JIRA::Client do let(:request) { subject.request_client.class } let(:successful_response) do response = double('response') allow(response).to receive(:is_a?).with(Net::HTTPSuccess).and_return(true) response end let(:content_type_header) { { 'Content-Type' => 'application/json' } } let(:headers) { { 'Accept' => 'application/json' } } let(:merged_headers) { headers.merge(content_type_header) } context 'behaviour that applies to all client classes irrespective of authentication method' do it 'allows the overriding of some options' do client = JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar', site: 'http://foo.com/') expect(client.options[:site]).to eq('http://foo.com/') expect(JIRA::Client::DEFAULT_OPTIONS[:site]).not_to eq('http://foo.com/') end end context 'with basic http authentication' do subject { JIRA::Client.new(username: 'foo', password: 'bar', auth_type: :basic) } before(:each) do stub_request(:get, 'https://foo:bar@localhost:2990/jira/rest/api/2/project') .to_return(status: 200, body: '[]', headers: {}) stub_request(:get, 'https://foo:badpassword@localhost:2990/jira/rest/api/2/project') .to_return(status: 401, headers: {}) end include_examples 'Client Common Tests' include_examples 'HttpClient tests' specify { expect(subject.request_client).to be_a JIRA::HttpClient } it 'sets the username and password' do expect(subject.options[:username]).to eq('foo') expect(subject.options[:password]).to eq('bar') end it 'fails with wrong user name and password' do bad_login = JIRA::Client.new(username: 'foo', password: 'badpassword', auth_type: :basic) expect(bad_login.authenticated?).to be_falsey expect { bad_login.Project.all }.to raise_error JIRA::HTTPError end it 'only returns a true for #authenticated? once we have requested some data' do expect(subject.authenticated?).to be_falsey expect(subject.Project.all).to be_empty expect(subject.authenticated?).to be_truthy end end context 'with cookie authentication' do subject { JIRA::Client.new(username: 'foo', password: 'bar', auth_type: :cookie) } let(:session_cookie) { '6E3487971234567896704A9EB4AE501F' } let(:session_body) do { 'session': { 'name' => 'JSESSIONID', 'value' => session_cookie }, 'loginInfo': { 'failedLoginCount' => 1, 'loginCount' => 2, 'lastFailedLoginTime' => (DateTime.now - 2).iso8601, 'previousLoginTime' => (DateTime.now - 5).iso8601 } } end before(:each) do # General case of API call with no authentication, or wrong authentication stub_request(:post, 'https://localhost:2990/jira/rest/auth/1/session') .to_return(status: 401, headers: {}) # Now special case of API with correct authentication. This gets checked first by RSpec. stub_request(:post, 'https://localhost:2990/jira/rest/auth/1/session') .with(body: '{"username":"foo","password":"bar"}') .to_return(status: 200, body: session_body.to_json, headers: { 'Set-Cookie': "JSESSIONID=#{session_cookie}; Path=/; HttpOnly" }) stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project') .with(headers: { cookie: "JSESSIONID=#{session_cookie}" }) .to_return(status: 200, body: '[]', headers: {}) end include_examples 'Client Common Tests' include_examples 'HttpClient tests' specify { expect(subject.request_client).to be_a JIRA::HttpClient } it 'authenticates with a correct username and password' do expect(subject).to be_authenticated expect(subject.Project.all).to be_empty end it 'does not authenticate with an incorrect username and password' do bad_client = JIRA::Client.new(username: 'foo', password: 'bad_password', auth_type: :cookie) expect(bad_client).not_to be_authenticated end it 'destroys the username and password once authenticated' do expect(subject.options[:username]).to be_nil expect(subject.options[:password]).to be_nil end end context 'with jwt authentication' do subject do JIRA::Client.new( issuer: 'foo', base_url: 'https://host.tld', shared_secret: 'shared_secret_key', auth_type: :jwt ) end before(:each) do stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project') .with(query: hash_including(:jwt)) .to_return(status: 200, body: '[]', headers: {}) end include_examples 'Client Common Tests' include_examples 'HttpClient tests' specify { expect(subject.request_client).to be_a JIRA::JwtClient } it 'sets the username and password' do expect(subject.options[:shared_secret]).to eq('shared_secret_key') end context 'with a incorrect jwt key' do before do stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project') .with(query: hash_including(:jwt)) .to_return(status: 401, body: '[]', headers: {}) end it 'is not authenticated' do expect(subject.authenticated?).to be_falsey end it 'raises a JIRA::HTTPError when trying to fetch projects' do expect { subject.Project.all }.to raise_error JIRA::HTTPError end end it 'only returns a true for #authenticated? once we have requested some data' do expect(subject.authenticated?).to be_falsey expect(subject.Project.all).to be_empty expect(subject.authenticated?).to be_truthy end end context 'oauth authentication' do subject { JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar') } include_examples 'OAuth Common Tests' end context 'with oauth_2legged' do subject { JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar', auth_type: :oauth_2legged) } include_examples 'OAuth Common Tests' end context 'with unknown options' do let(:options) { { 'username' => 'foo', 'password' => 'bar', auth_type: :basic } } subject { JIRA::Client.new(options) } it 'raises an ArgumentError' do expect { subject }.to raise_exception(ArgumentError, 'Unknown option(s) given: ["username", "password"]') end end end jira-ruby-2.3.0/spec/jira/resource/0000755000004100000410000000000014370022124017151 5ustar www-datawww-datajira-ruby-2.3.0/spec/jira/resource/createmeta_spec.rb0000644000004100000410000002327714370022124022635 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Createmeta do let(:client) do double( 'client', options: { rest_base_path: '/jira/rest/api/2' } ) end let(:response) do double( 'response', body: '{"expand":"projects","projects":[{"self":"http://localhost:2029/rest/api/2/project/TST","id":"10200","key":"test_key","name":"Test Name"}]}' ) end describe 'general' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta').and_return(response) JIRA::Resource::Createmeta.all(client) end it 'should query correct url with `expand` parameter' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?expand=projects.issuetypes.fields').and_return(response) JIRA::Resource::Createmeta.all(client, expand: 'projects.issuetypes.fields') end it 'should query correct url with `foo` parameter' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?foo=bar').and_return(response) JIRA::Resource::Createmeta.all(client, foo: 'bar') end it 'should return an array of createmeta objects' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta').and_return(response) createmetas = JIRA::Resource::Createmeta.all(client) expect(createmetas).to be_an Array createmeta = createmetas.first expect(createmeta.id).to eq '10200' expect(createmeta.key).to eq 'test_key' expect(createmeta.name).to eq 'Test Name' end end describe 'projectKeys' do it 'should query correct url when only one `projectKeys` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectKeys=PROJECT_1').and_return(response) JIRA::Resource::Createmeta.all( client, projectKeys: 'PROJECT_1' ) end it 'should query correct url when multiple `projectKeys` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectKeys=PROJECT_1%2CPROJECT_2').and_return(response) JIRA::Resource::Createmeta.all( client, projectKeys: %w[PROJECT_1 PROJECT_2] ) end it 'should query correct url when only one `projectKeys` given as Project' do prj = JIRA::Resource::Project.new(client) allow(prj).to receive(:key).and_return('PRJ') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectKeys=PRJ').and_return(response) JIRA::Resource::Createmeta.all( client, projectKeys: prj ) end it 'should query correct url when multiple `projectKeys` given as Project' do prj_1 = JIRA::Resource::Project.new(client) allow(prj_1).to receive(:key).and_return('PRJ_1') prj_2 = JIRA::Resource::Project.new(client) allow(prj_2).to receive(:key).and_return('PRJ_2') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectKeys=PRJ_2%2CPRJ_1').and_return(response) JIRA::Resource::Createmeta.all( client, projectKeys: [prj_2, prj_1] ) end it 'should query correct url when multiple `projectKeys` given as different types' do prj_5 = JIRA::Resource::Project.new(client) allow(prj_5).to receive(:key).and_return('PRJ_5') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectKeys=PROJECT_1%2CPRJ_5').and_return(response) JIRA::Resource::Createmeta.all( client, projectKeys: ['PROJECT_1', prj_5] ) end end describe 'projectIds' do it 'should query correct url when only one `projectIds` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectIds=10101').and_return(response) JIRA::Resource::Createmeta.all( client, projectIds: '10101' ) end it 'should query correct url when multiple `projectIds` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectIds=10101%2C20202').and_return(response) JIRA::Resource::Createmeta.all( client, projectIds: %w[10101 20202] ) end it 'should query correct url when only one `projectIds` given as Project' do prj = JIRA::Resource::Project.new(client) allow(prj).to receive(:id).and_return('30303') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectIds=30303').and_return(response) JIRA::Resource::Createmeta.all( client, projectIds: prj ) end it 'should query correct url when multiple `projectIds` given as Project' do prj_1 = JIRA::Resource::Project.new(client) allow(prj_1).to receive(:id).and_return('30303') prj_2 = JIRA::Resource::Project.new(client) allow(prj_2).to receive(:id).and_return('50505') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectIds=50505%2C30303').and_return(response) JIRA::Resource::Createmeta.all( client, projectIds: [prj_2, prj_1] ) end it 'should query correct url when multiple `projectIds` given as different types' do prj_5 = JIRA::Resource::Project.new(client) allow(prj_5).to receive(:id).and_return('60606') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?projectIds=10101%2C60606').and_return(response) JIRA::Resource::Createmeta.all( client, projectIds: ['10101', prj_5] ) end end describe 'issuetypeNames' do it 'should query correct url when only one `issuetypeNames` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeNames=Feature').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeNames: 'Feature' ) end it 'should query correct url when multiple `issuetypeNames` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeNames=Feature%2CBug').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeNames: %w[Feature Bug] ) end it 'should query correct url when only one `issuetypeNames` given as Issuetype' do issue_type = JIRA::Resource::Issuetype.new(client) allow(issue_type).to receive(:name).and_return('Epic') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeNames=Epic').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeNames: issue_type ) end it 'should query correct url when multiple `issuetypeNames` given as Issuetype' do issue_type_1 = JIRA::Resource::Issuetype.new(client) allow(issue_type_1).to receive(:name).and_return('Epic') issue_type_2 = JIRA::Resource::Issuetype.new(client) allow(issue_type_2).to receive(:name).and_return('Sub-Task') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeNames=Sub-Task%2CEpic').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeNames: [issue_type_2, issue_type_1] ) end it 'should query correct url when multiple `issuetypeNames` given as different types' do issue_type = JIRA::Resource::Issuetype.new(client) allow(issue_type).to receive(:name).and_return('Epic') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeNames=Feature%2CEpic').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeNames: ['Feature', issue_type] ) end end describe 'issuetypeIds' do it 'should query correct url when only one `issuetypeIds` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeIds=10101').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeIds: '10101' ) end it 'should query correct url when multiple `issuetypeIds` given as string' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeIds=10101%2C20202').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeIds: %w[10101 20202] ) end it 'should query correct url when only one `issuetypeIds` given as Issuetype' do issue_type = JIRA::Resource::Issuetype.new(client) allow(issue_type).to receive(:id).and_return('30303') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeIds=30303').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeIds: issue_type ) end it 'should query correct url when multiple `issuetypeIds` given as Issuetype' do issue_type_1 = JIRA::Resource::Issuetype.new(client) allow(issue_type_1).to receive(:id).and_return('30303') issue_type_2 = JIRA::Resource::Issuetype.new(client) allow(issue_type_2).to receive(:id).and_return('50505') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeIds=50505%2C30303').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeIds: [issue_type_2, issue_type_1] ) end it 'should query correct url when multiple `issuetypeIds` given as different types' do issue_type = JIRA::Resource::Issuetype.new(client) allow(issue_type).to receive(:id).and_return('30303') expect(client).to receive(:get).with('/jira/rest/api/2/issue/createmeta?issuetypeIds=10101%2C30303').and_return(response) JIRA::Resource::Createmeta.all( client, issuetypeIds: ['10101', issue_type] ) end end end jira-ruby-2.3.0/spec/jira/resource/board_spec.rb0000644000004100000410000001444514370022124021607 0ustar www-datawww-datarequire 'spec_helper' require 'active_support/core_ext/hash' describe JIRA::Resource::Board do class JIRAResourceDelegation < SimpleDelegator # :nodoc: end let(:client) do double(options: { rest_base_path: '/jira/rest/api/2', context_path: '' }) end let(:board) do response = double api_json_board = "{ \"id\": 84, \"self\": \"http://www.example.com/jira/rest/agile/1.0/board/84\", \"name\": \"scrum board\", \"type\": \"scrum\" }" allow(response).to receive(:body).and_return(api_json_board) expect(client).to receive(:get).with('/rest/agile/1.0/board/84') .and_return(response) expect(client).to receive(:Board).and_return(JIRA::Resource::BoardFactory.new(client)) JIRA::Resource::Board.find(client, '84') end it 'should find all boards' do response = double api_json = < 0, 'maxResults' => 1, 'total' => 2, 'issues' => [] }.to_json) end let(:result_2) do OpenStruct.new(body: { 'startAt' => 1, 'maxResults' => 1, 'total' => 2, 'issues' => [] }.to_json) end it 'makes multiple requests and increments the startAt param' do expect(client).to receive(:get).and_return(result_1) expect(client).to receive(:get).and_return(result_2) subject.issues end end context 'when there is only one page of results' do let(:result_1) do OpenStruct.new(body: { 'startAt' => 0, 'maxResults' => 2, 'total' => 2, 'issues' => [] }.to_json) end it 'only requires one request' do expect(client).to receive(:get).once.and_return(result_1) subject.issues end end end end it 'should get all sprints for a board' do response = double api_json = <<-eos { "values": [ { "id": 37, "state": "closed", "name": "sprint 1" }, { "id": 72, "state": "future", "name": "sprint 2" } ] } eos allow(response).to receive(:body).and_return(api_json) allow(board).to receive(:id).and_return(84) expect(client).to receive(:get).with('/rest/agile/1.0/board/84/sprint?').and_return(response) expect(client).to receive(:Sprint).twice.and_return(JIRA::Resource::SprintFactory.new(client)) expect(board.sprints.size).to be(2) end it 'should get board configuration for a board' do response = double api_json = <<-eos { "id":1, "name":"My Board", "type":"kanban", "self":"https://mycompany.atlassian.net/rest/agile/1.0/board/1/configuration", "location":{ "type":"project", "key":"MYPROJ", "id":"10000", "self":"https://mycompany.atlassian.net/rest/api/2/project/10000", "name":"My Project" }, "filter":{ "id":"10000", "self":"https://mycompany.atlassian.net/rest/api/2/filter/10000" }, "subQuery":{ "query":"resolution = EMPTY OR resolution != EMPTY AND resolutiondate >= -5d" }, "columnConfig":{ "columns":[ { "name":"Backlog", "statuses":[ { "id":"10000", "self":"https://mycompany.atlassian.net/rest/api/2/status/10000" } ] } ], "constraintType":"issueCount" }, "ranking":{ "rankCustomFieldId":10011 } } eos allow(response).to receive(:body).and_return(api_json) allow(board).to receive(:id).and_return(84) expect(client).to receive(:get).with('/rest/agile/1.0/board/84/configuration').and_return(response) expect(client).to receive(:BoardConfiguration).and_return(JIRA::Resource::BoardConfigurationFactory.new(client)) expect(board.configuration).not_to be(nil) end end jira-ruby-2.3.0/spec/jira/resource/sprint_spec.rb0000644000004100000410000000554314370022124022036 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Sprint do let(:client) do client = double(options: { site: 'https://foo.bar.com', context_path: '/jira' }) allow(client).to receive(:Sprint).and_return(JIRA::Resource::SprintFactory.new(client)) client end let(:sprint) { described_class.new(client) } let(:agile_sprint_path) { "#{sprint.client.options[:context_path]}/rest/agile/1.0/sprint/#{sprint.id}" } describe '::find' do let(:response) { double('Response', body: '{"some_detail":"some detail"}') } it 'fetches the sprint from JIRA' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/sprint/111').and_return(response) expect(JIRA::Resource::Sprint.find(client, '111')).to be_a(JIRA::Resource::Sprint) end end describe 'peristence' do describe '#save' do let(:instance_attrs) { { start_date: '2016-06-01' } } before do sprint.attrs = instance_attrs end context 'when attributes are specified' do let(:given_attrs) { { start_date: '2016-06-10' } } it 'calls save on the super class with the given attributes & agile url' do expect_any_instance_of(JIRA::Base).to receive(:save).with(given_attrs, agile_sprint_path) sprint.save(given_attrs) end end context 'when attributes are not specified' do it 'calls save on the super class with the instance attributes & agile url' do expect_any_instance_of(JIRA::Base).to receive(:save).with(instance_attrs, agile_sprint_path) sprint.save end end context 'when providing the path argument' do it 'ignores it' do expect_any_instance_of(JIRA::Base).to receive(:save).with(instance_attrs, agile_sprint_path) sprint.save({}, 'mavenlink.com') end end end describe '#save!' do let(:instance_attrs) { { start_date: '2016-06-01' } } before do sprint.attrs = instance_attrs end context 'when attributes are specified' do let(:given_attrs) { { start_date: '2016-06-10' } } it 'calls save! on the super class with the given attributes & agile url' do expect_any_instance_of(JIRA::Base).to receive(:save!).with(given_attrs, agile_sprint_path) sprint.save!(given_attrs) end end context 'when attributes are not specified' do it 'calls save! on the super class with the instance attributes & agile url' do expect_any_instance_of(JIRA::Base).to receive(:save!).with(instance_attrs, agile_sprint_path) sprint.save! end end context 'when providing the path argument' do it 'ignores it' do expect_any_instance_of(JIRA::Base).to receive(:save!).with(instance_attrs, agile_sprint_path) sprint.save!({}, 'mavenlink.com') end end end end end jira-ruby-2.3.0/spec/jira/resource/issue_picker_suggestions_spec.rb0000644000004100000410000000606614370022124025637 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::IssuePickerSuggestions do let(:client) do double('client', options: { rest_base_path: '/jira/rest/api/2' }) end describe 'relationships' do subject do JIRA::Resource::IssuePickerSuggestions.new(client, attrs: { 'sections' => [{ 'id' => 'hs'}, { 'id' => 'cs' }] }) end it 'has the correct relationships' do expect(subject).to have_many(:sections, JIRA::Resource::IssuePickerSuggestionsIssue) expect(subject.sections.length).to eq(2) end end describe '#all' do let(:response) { double } let(:issue_picker_suggestions) { double } before do allow(response).to receive(:body).and_return('{"sections":[{"id": "cs"}]}') allow(client).to receive(:IssuePickerSuggestions).and_return(issue_picker_suggestions) allow(issue_picker_suggestions).to receive(:build) end it 'should autocomplete issues' do allow(response).to receive(:body).and_return('{"sections":[{"id": "cs"}]}') expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query') .and_return(response) expect(client).to receive(:IssuePickerSuggestions).and_return(issue_picker_suggestions) expect(issue_picker_suggestions).to receive(:build).with({ 'sections' => [{ 'id' => 'cs' }] }) JIRA::Resource::IssuePickerSuggestions.all(client, 'query') end it 'should autocomplete issues with current jql' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query¤tJQL=project+%3D+PR') .and_return(response) JIRA::Resource::IssuePickerSuggestions.all(client, 'query', current_jql: 'project = PR') end it 'should autocomplete issues with current issue jey' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query¤tIssueKey=PR-42') .and_return(response) JIRA::Resource::IssuePickerSuggestions.all(client, 'query', current_issue_key: 'PR-42') end it 'should autocomplete issues with current project id' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query¤tProjectId=PR') .and_return(response) JIRA::Resource::IssuePickerSuggestions.all(client, 'query', current_project_id: 'PR') end it 'should autocomplete issues with show sub tasks' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query&showSubTasks=true') .and_return(response) JIRA::Resource::IssuePickerSuggestions.all(client, 'query', show_sub_tasks: true) end it 'should autocomplete issues with show sub tasks parent' do expect(client).to receive(:get).with('/jira/rest/api/2/issue/picker?query=query&showSubTaskParent=true') .and_return(response) JIRA::Resource::IssuePickerSuggestions.all(client, 'query', show_sub_task_parent: true) end end end jira-ruby-2.3.0/spec/jira/resource/issuelink_spec.rb0000644000004100000410000000036014370022124022515 0ustar www-datawww-data# require 'spec_helper' # # describe JIRA::Resource::Issuelink do # let(:client) { double() } # # describe "links" do # subject { # JIRA::Resource::Issuelink.new(client, :attrs => { # # } # ) # } # end # end jira-ruby-2.3.0/spec/jira/resource/field_spec.rb0000644000004100000410000001407614370022124021603 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Field do let(:cache) { OpenStruct.new } let(:client) do client = double(options: { rest_base_path: '/jira/rest/api/2' }) field = JIRA::Resource::FieldFactory.new(client) allow(client).to receive(:Field).and_return(field) allow(client).to receive(:cache).and_return(cache) # info about all fields on the client allow(client.Field).to receive(:all).and_return([ JIRA::Resource::Field.new(client, attrs: { 'id' => 'customfield_10666', 'name' => 'Priority', 'custom' => true, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10666]', 'Priority'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_666 } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'issuekey', 'name' => 'Key', 'custom' => false, 'orderable' => false, 'navigable' => true, 'searchable' => false, 'clauseNames' => %w[id issue issuekey key] }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'priority', 'name' => 'Priority', 'custom' => false, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['priority'], 'schema' => { 'type' => 'priority', 'system' => 'priority' } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'summary', 'name' => 'Summary', 'custom' => false, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['summary'], 'schema' => { 'type' => 'string', 'system' => 'summary' } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'issuetype', 'name' => 'Issue Type', 'custom' => false, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => %w[issuetype type], 'schema' => { 'type' => 'issuetype', 'system' => 'issuetype' } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'customfield_10111', 'name' => 'SingleWord', 'custom' => true, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10111]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_111 } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'customfield_10222', 'name' => 'Multi Word', 'custom' => true, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10222]', 'Multi Word'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_222 } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'customfield_10333', 'name' => 'Why/N@t', 'custom' => true, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10333]', 'Why/N@t'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_333 } }), JIRA::Resource::Field.new(client, attrs: { 'id' => 'customfield_10444', 'name' => 'SingleWord', 'custom' => true, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10444]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_444 } }) ]) client end describe 'field_mappings' do shared_context 'mapped or not' do subject do JIRA::Resource::Field.new(client, attrs: { 'priority' => 1, 'customfield_10111' => 'data_in_custom_field', 'customfield_10222' => 'multi word custom name', 'customfield_10333' => 'complex custom name', 'customfield_10444' => 'duplicated custom name', 'customfield_10666' => 'duplicate of a system name' }) end it 'can find a standard field by id' do expect(subject.priority).to eq(1) end it 'can find a custom field by customfield_##### name' do expect(subject.customfield_10111).to eq('data_in_custom_field') end it 'is not confused by common attribute keys' do expect { subject.name }.to raise_error(NoMethodError) expect { subject.custom }.to raise_error(NoMethodError) expect(subject.id).to eq(nil) # picks up ID from the parent - end end context 'before fields are mapped' do include_context 'mapped or not' it 'can find a standard field by id' do expect(subject.priority).to eq(1) end it 'cannot find a standard field by name before mapping' do expect { subject.Priority }.to raise_error(NoMethodError) end it 'can find a custom field by customfield_##### name' do expect(subject.customfield_10111).to eq('data_in_custom_field') end it 'is not confused by common attribute keys and raises error' do expect { subject.name }.to raise_error(NoMethodError) expect { subject.custom }.to raise_error(NoMethodError) expect(subject.id).to eq(nil) # picks up ID from the parent - end end context 'after fields are mapped' do include_context 'mapped or not' it 'warns of duplicate fields' do expect { client.Field.map_fields }.to output(/renaming as Priority_10666/).to_stderr expect { client.Field.map_fields }.to output(/renaming as SingleWord_10444/).to_stderr end end end end jira-ruby-2.3.0/spec/jira/resource/user_factory_spec.rb0000644000004100000410000000141214370022124023213 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::UserFactory do let(:client) do instance_double('Client', options: { rest_base_path: '/jira/rest/api/2' }) end subject { JIRA::Resource::UserFactory.new(client) } describe '#myself' do let(:response) do instance_double( 'Response', body: get_mock_response('user_username=admin.json') ) end let(:user) { subject.myself } before(:each) do allow(client).to receive(:get).with( '/jira/rest/api/2/myself' ).and_return(response) end it 'returns a JIRA::Resource::User with correct attrs' do expect(user).to be_a(JIRA::Resource::User) expect(user.name).to eq('admin') expect(user.emailAddress).to eq('admin@example.com') end end end jira-ruby-2.3.0/spec/jira/resource/project_spec.rb0000644000004100000410000001076614370022124022170 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Project do let(:client) do double('client', options: { rest_base_path: '/jira/rest/api/2' }) end describe 'relationships' do subject do JIRA::Resource::Project.new(client, attrs: { 'lead' => { 'foo' => 'bar' }, 'issueTypes' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }], 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] }) end it 'has the correct relationships' do expect(subject).to have_one(:lead, JIRA::Resource::User) expect(subject.lead.foo).to eq('bar') expect(subject).to have_many(:issuetypes, JIRA::Resource::Issuetype) expect(subject.issuetypes.length).to eq(2) expect(subject).to have_many(:versions, JIRA::Resource::Version) expect(subject.versions.length).to eq(2) end end describe 'issues' do subject do JIRA::Resource::Project.new(client, attrs: { 'key' => 'test' }) end it 'returns issues' do response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"}}]}' response = double('response', body: response_body) issue_factory = double('issue factory') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=project%3D%22test%22') .and_return(response) expect(client).to receive(:Issue).and_return(issue_factory) expect(issue_factory).to receive(:build) .with(JSON.parse(response_body)['issues'][0]) subject.issues end context 'with changelog' do it 'returns issues' do response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"},"changelog":{}}]}' response = double('response', body: response_body) issue_factory = double('issue factory') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=project%3D%22test%22&expand=changelog&startAt=1&maxResults=100') .and_return(response) expect(client).to receive(:Issue).and_return(issue_factory) expect(issue_factory).to receive(:build) .with(JSON.parse(response_body)['issues'][0]) subject.issues(expand: 'changelog', startAt: 1, maxResults: 100) end end end describe 'users' do let(:project) { JIRA::Resource::Project.new(client, attrs: { 'key' => project_key }) } let(:project_key) { SecureRandom.hex } let(:response) { double('response', body: '[{}]') } context 'pagination' do before(:each) do user_factory = double('user factory') expect(client).to receive(:User).and_return(user_factory) expect(user_factory).to receive(:build).with(any_args) end it 'doesn\'t use pagination parameters by default' do expect(client).to receive(:get) .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}") .and_return(response) project.users end it 'accepts start_at option' do start_at = rand(1000) expect(client).to receive(:get) .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}&startAt=#{start_at}") .and_return(response) project.users(start_at: start_at) end it 'accepts max_results option' do max_results = rand(1000) expect(client).to receive(:get) .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}&maxResults=#{max_results}") .and_return(response) project.users(max_results: max_results) end it 'accepts start_at and max_results options' do start_at = rand(1000) max_results = rand(1000) expect(client).to receive(:get) .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}&startAt=#{start_at}&maxResults=#{max_results}") .and_return(response) project.users(start_at: start_at, max_results: max_results) end end end end jira-ruby-2.3.0/spec/jira/resource/worklog_spec.rb0000644000004100000410000000125514370022124022177 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Worklog do let(:client) { double } describe 'relationships' do subject do JIRA::Resource::Worklog.new(client, issue_id: '99999', attrs: { 'author' => { 'foo' => 'bar' }, 'updateAuthor' => { 'foo' => 'bar' } }) end it 'has the correct relationships' do expect(subject).to have_one(:author, JIRA::Resource::User) expect(subject.author.foo).to eq('bar') expect(subject).to have_one(:update_author, JIRA::Resource::User) expect(subject.update_author.foo).to eq('bar') end end end jira-ruby-2.3.0/spec/jira/resource/agile_spec.rb0000644000004100000410000001341114370022124021571 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Agile do let(:client) do client = double(options: { rest_base_path: '/jira/rest/api/2', context_path: '/jira' }) allow(client).to receive(:Issue).and_return(JIRA::Resource::IssueFactory.new(client)) client end let(:response) { double } describe '#all' do it 'should query url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.all(client) end end describe '#get_backlog_issues' do it 'should query the url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/backlog?maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_backlog_issues(client, 1) end end describe '#get_board_issues' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/issue?').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1_issues.json')) expect(client).to receive(:get).with('/jira/rest/api/2/search?jql=id+IN%2810546%2C+10547%2C+10556%2C+10557%2C+10558%2C+10559%2C+10600%2C+10601%2C+10604%29').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1_issues.json')) issues = JIRA::Resource::Agile.get_board_issues(client, 1) expect(issues).to be_an(Array) expect(issues.size).to eql(9) issues.each do |issue| expect(issue.class).to eq(JIRA::Resource::Issue) expect(issue.expanded?).to be_falsey end end it 'should query correct url with parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/issue?startAt=50').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1_issues.json')) expect(client).to receive(:get).with('/jira/rest/api/2/search?jql=id+IN%2810546%2C+10547%2C+10556%2C+10557%2C+10558%2C+10559%2C+10600%2C+10601%2C+10604%29').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1_issues.json')) issues = JIRA::Resource::Agile.get_board_issues(client, 1, startAt: 50) expect(issues).to be_an(Array) expect(issues.size).to eql(9) issues.each do |issue| expect(issue.class).to eq(JIRA::Resource::Issue) expect(issue.expanded?).to be_falsey end end end describe '#get_sprints' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/sprint?maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_sprints(client, 1) end it 'should query correct url with parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/sprint?startAt=50&maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_sprints(client, 1, startAt: 50) end it 'should work with pagination starting at 0' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/sprint?maxResults=1&startAt=0').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_sprints(client, 1, maxResults: 1, startAt: 0) end it 'should work with pagination not starting at 0' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/sprint?maxResults=1&startAt=1').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_sprints(client, 1, maxResults: 1, startAt: 1) end end describe '#get_sprint_issues' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/sprint/1/issue?maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('sprint/1_issues.json')) JIRA::Resource::Agile.get_sprint_issues(client, 1) end it 'should query correct url with parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/sprint/1/issue?startAt=50&maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('sprint/1_issues.json')) JIRA::Resource::Agile.get_sprint_issues(client, 1, startAt: 50) end end describe '#get_projects_full' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/project/full').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_projects_full(client, 1) end end describe '#get_projects' do it 'should query correct url without parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/project?maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_projects(client, 1) end it 'should query correct url with parameters' do expect(client).to receive(:get).with('/jira/rest/agile/1.0/board/1/project?startAt=50&maxResults=100').and_return(response) expect(response).to receive(:body).and_return(get_mock_response('board/1.json')) JIRA::Resource::Agile.get_projects(client, 1, startAt: 50) end end end jira-ruby-2.3.0/spec/jira/resource/filter_spec.rb0000644000004100000410000000543414370022124022003 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Filter do let(:client) do client = double allow(client).to receive(:Issue).and_return(JIRA::Resource::IssueFactory.new(self)) client end let(:collection_path) { '/rest/api/2/filter' } let(:jira_user) do { self: 'https://localhost/rest/api/2/user?username=ljharb', name: 'ljharb', avatarUrls: { '16x16' => 'https://localhost/secure/useravatar?size=small&ownerId=ljharb&avatarId=1', '48x48' => 'https://localhost/secure/useravatar?ownerId=ljharb&avatarId=1' }, displayName: 'Jordan Harband', active: true } end let(:filter_attrs) do { self: "https://localhost#{collection_path}/42", id: 42, name: 'Resolved Tickets', description: '', owner: jira_user, jql: '"Git Repository" ~ jira-ruby AND status = Resolved', viewUrl: 'https://localhost/secure/IssueNavigator.jspa?mode=hide&requestId=42', searchUrl: 'https://localhost/rest/api/2/search?jql=%22Git+Repository%22+~+jira-ruby+AND+status+%3D+Resolved', favourite: false, sharePermissions: [ { id: 123, type: 'global' } ], subscriptions: { size: 0, items: [] } } end let(:filter_response) do response = double allow(response).to receive(:body).and_return(filter_attrs.to_json) response end let(:filter) do expect(client).to receive(:get).with("#{collection_path}/42").and_return(filter_response) allow(JIRA::Resource::Filter).to receive(:collection_path).and_return(collection_path) JIRA::Resource::Filter.find(client, 42) end let(:jql_issue) do { id: '663147', self: 'https://localhost/rest/api/2/issue/663147', key: 'JIRARUBY-2386', fields: { reporter: jira_user, created: '2013-12-11T23:28:02.000+0000', assignee: jira_user } } end let(:jql_attrs) do { startAt: 0, maxResults: 50, total: 2, issues: [jql_issue] } end let(:issue_jql_response) do response = double allow(response).to receive(:body).and_return(jql_attrs.to_json) response end it 'can be found by ID' do expect(JSON.parse(filter.attrs.to_json)).to eql(JSON.parse(filter_attrs.to_json)) end it 'returns issues' do expect(filter).to be_present allow(client).to receive(:options).and_return(rest_base_path: 'localhost') expect(client).to receive(:get) .with("localhost/search?jql=#{CGI.escape(filter.jql)}") .and_return(issue_jql_response) issues = filter.issues expect(issues).to be_an(Array) expect(issues.size).to eql(1) expected_issue = client.Issue.build(JSON.parse(jql_issue.to_json)) expect(issues.first.attrs).to eql(expected_issue.attrs) end end jira-ruby-2.3.0/spec/jira/resource/attachment_spec.rb0000644000004100000410000000666714370022124022657 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Attachment do subject(:attachment) do JIRA::Resource::Attachment.new( client, issue: JIRA::Resource::Issue.new(client), attrs: { 'author' => { 'foo' => 'bar' } } ) end let(:client) do double( 'client', options: { rest_base_path: '/jira/rest/api/2' }, request_client: double( options: { username: 'username', password: 'password' } ) ) end describe 'relationships' do it 'has an author' do expect(subject).to have_one(:author, JIRA::Resource::User) end it 'has the correct author name' do expect(subject.author.foo).to eq('bar') end end describe '.meta' do subject { JIRA::Resource::Attachment.meta(client) } let(:response) do double( 'response', body: '{"enabled":true,"uploadLimit":10485760}' ) end it 'returns meta information about attachment upload' do expect(client).to receive(:get).with('/jira/rest/api/2/attachment/meta').and_return(response) subject end context 'the factory delegates correctly' do subject { JIRA::Resource::AttachmentFactory.new(client) } it 'delegates #meta to to target class' do expect(subject).to respond_to(:meta) end end end describe '#save' do subject { attachment.save('file' => path_to_file) } let(:path_to_file) { './spec/mock_responses/issue.json' } let(:response) do double( body: [ { "id": 10_001, "self": 'http://www.example.com/jira/rest/api/2.0/attachments/10000', "filename": 'picture.jpg', "created": '2017-07-19T12:23:06.572+0000', "size": 23_123, "mimeType": 'image/jpeg' } ].to_json ) end let(:issue) { JIRA::Resource::Issue.new(client) } before do allow(client).to receive(:post_multipart).and_return(response) end it 'successfully update the attachment' do subject expect(attachment.filename).to eq 'picture.jpg' expect(attachment.mimeType).to eq 'image/jpeg' expect(attachment.size).to eq 23_123 end end describe '#save!' do subject { attachment.save!('file' => path_to_file) } let(:path_to_file) { './spec/mock_responses/issue.json' } let(:response) do double( body: [ { "id": 10_001, "self": 'http://www.example.com/jira/rest/api/2.0/attachments/10000', "filename": 'picture.jpg', "created": '2017-07-19T12:23:06.572+0000', "size": 23_123, "mimeType": 'image/jpeg' } ].to_json ) end let(:issue) { JIRA::Resource::Issue.new(client) } before do allow(client).to receive(:post_multipart).and_return(response) end it 'successfully update the attachment' do subject expect(attachment.filename).to eq 'picture.jpg' expect(attachment.mimeType).to eq 'image/jpeg' expect(attachment.size).to eq 23_123 end context 'when passing in a symbol as file key' do subject { attachment.save!(file: path_to_file) } it 'successfully update the attachment' do subject expect(attachment.filename).to eq 'picture.jpg' expect(attachment.mimeType).to eq 'image/jpeg' expect(attachment.size).to eq 23_123 end end end end jira-ruby-2.3.0/spec/jira/resource/jira_picker_suggestions_issue_spec.rb0000644000004100000410000000074514370022124026642 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::IssuePickerSuggestionsIssue do let(:client) { double('client') } describe 'relationships' do subject do JIRA::Resource::IssuePickerSuggestionsIssue.new(client, attrs: { 'issues' => [{ 'id' => '1'}, { 'id' => '2' }] }) end it 'has the correct relationships' do expect(subject).to have_many(:issues, JIRA::Resource::SuggestedIssue) expect(subject.issues.length).to eq(2) end end end jira-ruby-2.3.0/spec/jira/resource/issue_spec.rb0000644000004100000410000002216514370022124021646 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Issue do class JIRAResourceDelegation < SimpleDelegator # :nodoc: end let(:client) do client = double(options: { rest_base_path: '/jira/rest/api/2' }) allow(client).to receive(:Field).and_return(JIRA::Resource::FieldFactory.new(client)) allow(client).to receive(:cache).and_return(OpenStruct.new) client end describe '#respond_to?' do describe 'when decorated by SimpleDelegator' do before(:each) do response = double allow(response).to receive(:body).and_return('{"key":"foo","id":"101"}') allow(JIRA::Resource::Issue).to receive(:collection_path).and_return('/jira/rest/api/2/issue') allow(client).to receive(:get).with('/jira/rest/api/2/issue/101') .and_return(response) issue = JIRA::Resource::Issue.find(client, 101) @decorated = JIRAResourceDelegation.new(issue) end it 'responds to key' do expect(@decorated.respond_to?(:key)).to eq(true) end it 'does not raise an error' do expect do @issue.respond_to?(:project) end.not_to raise_error end end end it 'should find all issues' do response = double empty_response = double issue = double allow(response).to receive(:body).and_return('{"issues":[{"id":"1","summary":"Bugs Everywhere"}]}') expect(client).to receive(:get).with('/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=0') .and_return(response) allow(empty_response).to receive(:body).and_return('{"issues":[]}') expect(client).to receive(:get).with('/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=1') .and_return(empty_response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with({ 'id' => '1', 'summary' => 'Bugs Everywhere' }) issues = JIRA::Resource::Issue.all(client) end it 'should find an issue by key or id' do response = double allow(response).to receive(:body).and_return('{"key":"foo","id":"101"}') allow(JIRA::Resource::Issue).to receive(:collection_path).and_return('/jira/rest/api/2/issue') expect(client).to receive(:get).with('/jira/rest/api/2/issue/foo') .and_return(response) expect(client).to receive(:get).with('/jira/rest/api/2/issue/101') .and_return(response) issue_from_id = JIRA::Resource::Issue.find(client, 101) issue_from_key = JIRA::Resource::Issue.find(client, 'foo') expect(issue_from_id.attrs).to eq(issue_from_key.attrs) end it 'should search an issue with a jql query string' do response = double issue = double allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}') expect(client).to receive(:get).with('/jira/rest/api/2/search?jql=foo+bar') .and_return(response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with(%w[key foo]).and_return('') expect(JIRA::Resource::Issue.jql(client, 'foo bar')).to eq(['']) end it 'should search an issue with a jql query string and fields' do response = double issue = double allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=foo+bar&fields=foo,bar') .and_return(response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with(%w[key foo]).and_return('') expect(JIRA::Resource::Issue.jql(client, 'foo bar', fields: %w[foo bar])).to eq(['']) end it 'should search an issue with a jql query string, start at, and maxResults' do response = double issue = double allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=foo+bar&startAt=1&maxResults=3') .and_return(response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with(%w[key foo]).and_return('') expect(JIRA::Resource::Issue.jql(client, 'foo bar', start_at: 1, max_results: 3)).to eq(['']) end it 'should search an issue with a jql query string and maxResults equals zero and should return the count of tickets' do response = double issue = double allow(response).to receive(:body).and_return('{"total": 1, "issues": []}') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=foo+bar&maxResults=0') .and_return(response) expect(JIRA::Resource::Issue.jql(client, 'foo bar', max_results: 0)).to eq(1) end it 'should search an issue with a jql query string and string expand' do response = double issue = double allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=foo+bar&expand=transitions') .and_return(response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with(%w[key foo]).and_return('') expect(JIRA::Resource::Issue.jql(client, 'foo bar', expand: 'transitions')).to eq(['']) end it 'should search an issue with a jql query string and array expand' do response = double issue = double allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}') expect(client).to receive(:get) .with('/jira/rest/api/2/search?jql=foo+bar&expand=transitions') .and_return(response) expect(client).to receive(:Issue).and_return(issue) expect(issue).to receive(:build).with(%w[key foo]).and_return('') expect(JIRA::Resource::Issue.jql(client, 'foo bar', expand: %w[transitions])).to eq(['']) end it 'should return meta data available for editing an issue' do subject = JIRA::Resource::Issue.new(client, attrs: { 'fields' => { 'key' => 'TST=123' } }) response = double allow(response).to receive(:body).and_return( '{"fields":{"summary":{"required":true,"name":"Summary","operations":["set"]}}}' ) expect(client).to receive(:get) .with('/jira/rest/api/2/issue/TST=123/editmeta') .and_return(response) expect(subject.editmeta).to eq('summary' => { 'required' => true, 'name' => 'Summary', 'operations' => ['set'] }) end it 'provides direct accessors to the fields' do subject = JIRA::Resource::Issue.new(client, attrs: { 'fields' => { 'foo' => 'bar' } }) expect(subject).to respond_to(:foo) expect(subject.foo).to eq('bar') end describe 'relationships' do subject do JIRA::Resource::Issue.new(client, attrs: { 'id' => '123', 'fields' => { 'reporter' => { 'foo' => 'bar' }, 'assignee' => { 'foo' => 'bar' }, 'project' => { 'foo' => 'bar' }, 'priority' => { 'foo' => 'bar' }, 'issuetype' => { 'foo' => 'bar' }, 'status' => { 'foo' => 'bar' }, 'components' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }], 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }], 'comment' => { 'comments' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] }, 'attachment' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }], 'worklog' => { 'worklogs' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] } } }) end it 'has the correct relationships' do expect(subject).to have_one(:reporter, JIRA::Resource::User) expect(subject.reporter.foo).to eq('bar') expect(subject).to have_one(:assignee, JIRA::Resource::User) expect(subject.assignee.foo).to eq('bar') expect(subject).to have_one(:project, JIRA::Resource::Project) expect(subject.project.foo).to eq('bar') expect(subject).to have_one(:issuetype, JIRA::Resource::Issuetype) expect(subject.issuetype.foo).to eq('bar') expect(subject).to have_one(:priority, JIRA::Resource::Priority) expect(subject.priority.foo).to eq('bar') expect(subject).to have_one(:status, JIRA::Resource::Status) expect(subject.status.foo).to eq('bar') expect(subject).to have_many(:components, JIRA::Resource::Component) expect(subject.components.length).to eq(2) expect(subject).to have_many(:comments, JIRA::Resource::Comment) expect(subject.comments.length).to eq(2) expect(subject).to have_many(:attachments, JIRA::Resource::Attachment) expect(subject.attachments.length).to eq(2) expect(subject).to have_many(:versions, JIRA::Resource::Version) expect(subject.attachments.length).to eq(2) expect(subject).to have_many(:worklogs, JIRA::Resource::Worklog) expect(subject.worklogs.length).to eq(2) end end end jira-ruby-2.3.0/spec/jira/resource/project_factory_spec.rb0000644000004100000410000000046014370022124023705 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::ProjectFactory do let(:client) { double } subject { JIRA::Resource::ProjectFactory.new(client) } it 'initializes correctly' do expect(subject.class).to eq(JIRA::Resource::ProjectFactory) expect(subject.client).to eq(client) end end jira-ruby-2.3.0/spec/jira/request_client_spec.rb0000644000004100000410000000224014370022124021705 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::RequestClient do let(:request_client) { JIRA::RequestClient.new } describe '#request' do subject(:request) { request_client.request(:get, '/foo', '', {}) } context 'when doing a request fails' do let(:response) { double } before do allow(response).to receive(:kind_of?).with(Net::HTTPSuccess).and_return(false) allow(request_client).to receive(:make_request).with(:get, '/foo', '', {}).and_return(response) end it 'raises an exception' do expect{ subject }.to raise_exception(JIRA::HTTPError) end end end describe '#request_multipart' do subject(:request) { request_client.request_multipart('/foo', data, {}) } let(:data) { double } context 'when doing a request fails' do let(:response) { double } before do allow(response).to receive(:kind_of?).with(Net::HTTPSuccess).and_return(false) allow(request_client).to receive(:make_multipart_request).with('/foo', data, {}).and_return(response) end it 'raises an exception' do expect{ subject }.to raise_exception(JIRA::HTTPError) end end end endjira-ruby-2.3.0/spec/jira/http_client_spec.rb0000644000004100000410000003376414370022124021213 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::HttpClient do let(:basic_client) do options = JIRA::Client::DEFAULT_OPTIONS .merge(JIRA::HttpClient::DEFAULT_OPTIONS) .merge(basic_auth_credentials) JIRA::HttpClient.new(options) end let(:basic_cookie_client) do options = JIRA::Client::DEFAULT_OPTIONS .merge(JIRA::HttpClient::DEFAULT_OPTIONS) .merge(use_cookies: true) .merge(basic_auth_credentials) JIRA::HttpClient.new(options) end let(:custom_ssl_version_client) do options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge(ssl_version: :TLSv1_2) JIRA::HttpClient.new(options) end let(:basic_cookie_client_with_context_path) do options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge( use_cookies: true, context_path: '/context' ) JIRA::HttpClient.new(options) end let(:basic_cookie_client_with_additional_cookies) do options = JIRA::Client::DEFAULT_OPTIONS .merge(JIRA::HttpClient::DEFAULT_OPTIONS) .merge( use_cookies: true, additional_cookies: ['sessionToken=abc123', 'internal=true'] ) .merge(basic_auth_credentials) JIRA::HttpClient.new(options) end let(:basic_client_cert_client) do options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge( use_client_cert: true, cert: 'public certificate contents', key: 'private key contents' ) JIRA::HttpClient.new(options) end let(:basic_client_with_no_auth_credentials) do options = JIRA::Client::DEFAULT_OPTIONS .merge(JIRA::HttpClient::DEFAULT_OPTIONS) JIRA::HttpClient.new(options) end let(:basic_auth_credentials) do { username: 'donaldduck', password: 'supersecret' } end let(:proxy_client) do options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge( proxy_address: 'proxyAddress', proxy_port: 42, proxy_username: 'proxyUsername', proxy_password: 'proxyPassword' ) JIRA::HttpClient.new(options) end let(:response) do response = double('response') allow(response).to receive(:kind_of?).with(Net::HTTPSuccess).and_return(true) response end let(:cookie_response) do response = double('response') allow(response).to receive(:kind_of?).with(Net::HTTPSuccess).and_return(true) response end it 'creates an instance of Net:HTTP for a basic auth client' do expect(basic_client.basic_auth_http_conn.class).to eq(Net::HTTP) end it 'makes a correct HTTP request for make_cookie_auth_request' do request = double basic_auth_http_conn = double headers = { 'Content-Type' => 'application/json' } expected_path = '/context/rest/auth/1/session' expected_body = '{"username":"","password":""}' allow(basic_cookie_client_with_context_path).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) expect(basic_auth_http_conn).to receive(:request).with(request).and_return(response) allow(request).to receive(:basic_auth) allow(response).to receive(:get_fields).with('set-cookie') expect(request).to receive(:body=).with(expected_body) expect(Net::HTTP.const_get(:post.to_s.capitalize)).to receive(:new).with(expected_path, headers).and_return(request) basic_cookie_client_with_context_path.make_cookie_auth_request end it 'responds to the http methods' do body = '' headers = double basic_auth_http_conn = double request = double allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) expect(request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).exactly(5).times.and_return(request) expect(basic_auth_http_conn).to receive(:request).exactly(5).times.with(request).and_return(response) %i[delete get head].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(basic_client.make_request(method, '/path', nil, headers)).to eq(response) end %i[post put].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(request).to receive(:body=).with(body).and_return(request) expect(basic_client.make_request(method, '/path', body, headers)).to eq(response) end end it 'gets and sets cookies' do body = '' headers = double basic_auth_http_conn = double request = double allow(basic_cookie_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) expect(request).to receive(:basic_auth).with(basic_cookie_client.options[:username], basic_cookie_client.options[:password]).exactly(5).times.and_return(request) expect(cookie_response).to receive(:get_fields).with('set-cookie').exactly(5).times expect(basic_auth_http_conn).to receive(:request).exactly(5).times.with(request).and_return(cookie_response) %i[delete get head].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(basic_cookie_client.make_request(method, '/path', nil, headers)).to eq(cookie_response) end %i[post put].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(request).to receive(:body=).with(body).and_return(request) expect(basic_cookie_client.make_request(method, '/path', body, headers)).to eq(cookie_response) end end it 'sets additional cookies when they are provided' do client = basic_cookie_client_with_additional_cookies body = '' headers = double basic_auth_http_conn = double request = double allow(client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) expect(request).to receive(:basic_auth).with(client.options[:username], client.options[:password]).exactly(5).times.and_return(request) expect(request).to receive(:add_field).with('Cookie', 'sessionToken=abc123; internal=true').exactly(5).times expect(cookie_response).to receive(:get_fields).with('set-cookie').exactly(5).times expect(basic_auth_http_conn).to receive(:request).exactly(5).times.with(request).and_return(cookie_response) %i[delete get head].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(client.make_request(method, '/path', nil, headers)).to eq(cookie_response) end %i[post put].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) expect(request).to receive(:body=).with(body).and_return(request) expect(client.make_request(method, '/path', body, headers)).to eq(cookie_response) end end it 'performs a basic http client request' do body = nil headers = double basic_auth_http_conn = double http_request = double expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request) expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response) expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(http_request) allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) basic_client.make_request(:get, '/foo', body, headers) end it 'performs a basic http client request with a full domain' do body = nil headers = double basic_auth_http_conn = double http_request = double expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request) expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response) expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(http_request) allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) basic_client.make_request(:get, 'http://mydomain.com/foo', body, headers) end it 'does not try to use basic auth if the credentials are not set' do body = nil headers = double basic_auth_http_conn = double http_request = double expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request) expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response) expect(http_request).not_to receive(:basic_auth) allow(basic_client_with_no_auth_credentials).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) basic_client_with_no_auth_credentials.make_request(:get, '/foo', body, headers) end it 'returns a URI' do uri = URI.parse(basic_client.options[:site]) expect(basic_client.uri).to eq(uri) end it 'sets up a http connection with options' do http_conn = double uri = double host = double port = double expect(uri).to receive(:host).and_return(host) expect(uri).to receive(:port).and_return(port) expect(Net::HTTP).to receive(:new).with(host, port).and_return(http_conn) expect(http_conn).to receive(:use_ssl=).with(basic_client.options[:use_ssl]).and_return(http_conn) expect(http_conn).to receive(:verify_mode=).with(basic_client.options[:ssl_verify_mode]).and_return(http_conn) expect(http_conn).to receive(:read_timeout=).with(basic_client.options[:read_timeout]).and_return(http_conn) expect(basic_client.http_conn(uri)).to eq(http_conn) end it 'sets the SSL version when one is provided' do http_conn = double uri = double host = double port = double expect(uri).to receive(:host).and_return(host) expect(uri).to receive(:port).and_return(port) expect(Net::HTTP).to receive(:new).with(host, port).and_return(http_conn) expect(http_conn).to receive(:use_ssl=).with(basic_client.options[:use_ssl]).and_return(http_conn) expect(http_conn).to receive(:verify_mode=).with(basic_client.options[:ssl_verify_mode]).and_return(http_conn) expect(http_conn).to receive(:ssl_version=).with(custom_ssl_version_client.options[:ssl_version]).and_return(http_conn) expect(http_conn).to receive(:read_timeout=).with(basic_client.options[:read_timeout]).and_return(http_conn) expect(custom_ssl_version_client.http_conn(uri)).to eq(http_conn) end it 'sets up a non-proxied http connection by default' do uri = double host = double port = double expect(uri).to receive(:host).and_return(host) expect(uri).to receive(:port).and_return(port) proxy_configuration = basic_client.http_conn(uri).class expect(proxy_configuration.proxy_address).to be_nil expect(proxy_configuration.proxy_port).to be_nil expect(proxy_configuration.proxy_user).to be_nil expect(proxy_configuration.proxy_pass).to be_nil end it 'sets up a proxied http connection when using proxy options' do uri = double host = double port = double expect(uri).to receive(:host).and_return(host) expect(uri).to receive(:port).and_return(port) proxy_configuration = proxy_client.http_conn(uri).class expect(proxy_configuration.proxy_address).to eq(proxy_client.options[:proxy_address]) expect(proxy_configuration.proxy_port).to eq(proxy_client.options[:proxy_port]) expect(proxy_configuration.proxy_user).to eq(proxy_client.options[:proxy_username]) expect(proxy_configuration.proxy_pass).to eq(proxy_client.options[:proxy_password]) end it 'can use client certificates' do http_conn = double uri = double host = double port = double expect(Net::HTTP).to receive(:new).with(host, port).and_return(http_conn) expect(uri).to receive(:host).and_return(host) expect(uri).to receive(:port).and_return(port) expect(http_conn).to receive(:use_ssl=).with(basic_client.options[:use_ssl]) expect(http_conn).to receive(:verify_mode=).with(basic_client.options[:ssl_verify_mode]) expect(http_conn).to receive(:read_timeout=).with(basic_client.options[:read_timeout]) expect(http_conn).to receive(:cert=).with(basic_client_cert_client.options[:ssl_client_cert]) expect(http_conn).to receive(:key=).with(basic_client_cert_client.options[:ssl_client_key]) expect(basic_client_cert_client.http_conn(uri)).to eq(http_conn) end it 'can use a certificate authority file' do client = JIRA::HttpClient.new(JIRA::Client::DEFAULT_OPTIONS.merge(ca_file: '/opt/custom.ca.pem')) expect(client.http_conn(client.uri).ca_file).to eql('/opt/custom.ca.pem') end it 'returns a http connection' do http_conn = double uri = double expect(basic_client).to receive(:uri).and_return(uri) expect(basic_client).to receive(:http_conn).and_return(http_conn) expect(basic_client.basic_auth_http_conn).to eq(http_conn) end describe '#make_multipart_request' do subject do basic_client.make_multipart_request(path, data, headers) end let(:path) { '/foo' } let(:data) { {} } let(:headers) { { 'X-Atlassian-Token' => 'no-check' } } let(:basic_auth_http_conn) { double } let(:request) { double('Http Request', path: path) } let(:response) { double('response') } before do allow(request).to receive(:basic_auth) allow(Net::HTTP::Post::Multipart).to receive(:new).with(path, data, headers).and_return(request) allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) allow(basic_auth_http_conn).to receive(:request).with(request).and_return(response) end it 'performs a basic http client request' do expect(request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(request) subject end it 'makes a correct HTTP request' do expect(basic_auth_http_conn).to receive(:request).with(request).and_return(response) expect(response).to receive(:is_a?).with(Net::HTTPOK) subject end end end jira-ruby-2.3.0/spec/mock_responses/0000755000004100000410000000000014370022124017427 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue.post.json0000644000004100000410000000015714370022124022441 0ustar www-datawww-data{ "id": "10005", "key": "SAMPLEPROJECT-4", "self": "http://localhost:2990/jira/rest/api/2/issue/10005" } jira-ruby-2.3.0/spec/mock_responses/issue/0000755000004100000410000000000014370022124020557 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue/10002/0000755000004100000410000000000014370022124021221 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue/10002/comment/0000755000004100000410000000000014370022124022663 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue/10002/comment/10000.json0000644000004100000410000000177714370022124024232 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/comment/10000", "id": "10000", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "body": "This is a comment. Creative.", "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-11T10:02:14.430+1300", "updated": "2012-01-11T10:02:14.430+1300" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/comment/10000.put.json0000644000004100000410000000175314370022124025033 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/comment/10000", "id": "10000", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "body": "new body", "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-11T10:02:14.430+1300", "updated": "2012-01-12T14:37:41.933+1300" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/transitions.post.json0000644000004100000410000000000114370022124025444 0ustar www-datawww-data jira-ruby-2.3.0/spec/mock_responses/issue/10002/attachments/0000755000004100000410000000000014370022124023534 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue/10002/attachments/10000.json0000644000004100000410000000132614370022124025071 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/attachment/10000", "filename": "ballmer.png", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-11T10:54:50.875+1300", "size": 15360, "mimeType": "image/png", "properties": {}, "content": "http://localhost:2990/jira/secure/attachment/10000/ballmer.png", "thumbnail": "http://localhost:2990/jira/secure/thumbnail/10000/_thumb_10000.png" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/watchers.json0000644000004100000410000000052614370022124023737 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/watchers", "isWatching": false, "watchCount": 1, "watchers": [ { "self": "http://www.example.com/jira/rest/api/2/user?username=admin", "name": "admin", "displayName": "admin", "active": false } ] }jira-ruby-2.3.0/spec/mock_responses/issue/10002/transitions.json0000644000004100000410000000270014370022124024470 0ustar www-datawww-data{ "expand": "transitions", "transitions": [ { "id": "41", "name": "Review", "to": { "self": "http://localhost:2990/rest/api/2/status/10006", "description": "", "iconUrl": "http://localhost:2990/images/icons/statuses/generic.png", "name": "Reviewable", "id": "10006" } }, { "id": "101", "name": "Stop Progress", "to": { "self": "http://localhost:2990/rest/api/2/status/10017", "description": "Mapping for Accepted in Pivotal Tracker", "iconUrl": "http://localhost:2990/images/icons/statuses/closed.png", "name": "Accepted", "id": "10017" } }, { "id": "21", "name": "Remove from Backlog", "to": { "self": "http://localhost:2990/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/images/icons/statuses/open.png", "name": "Open", "id": "1" } }, { "id": "71", "name": "Resolve", "to": { "self": "http://localhost:2990/rest/api/2/status/5", "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.", "iconUrl": "http://localhost:2990/images/icons/statuses/resolved.png", "name": "Resolved", "id": "5" } } ] } jira-ruby-2.3.0/spec/mock_responses/issue/10002/worklog/0000755000004100000410000000000014370022124022705 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issue/10002/worklog/10000.json0000644000004100000410000000207314370022124024242 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10000", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "comment": "Some epic work.", "created": "2012-01-11T13:33:25.604+1300", "updated": "2012-01-11T13:33:25.604+1300", "started": "2012-01-11T13:33:00.000+1300", "timeSpent": "18w 2d", "id": "10000" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/worklog/10000.put.json0000644000004100000410000000202714370022124025050 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10001", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T16:08:58.529+1300", "updated": "2012-01-12T16:11:22.266+1300", "started": "2012-01-12T16:08:58.529+1300", "timeSpent": "4d", "id": "10001" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/worklog.post.json0000644000004100000410000000202714370022124024565 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10001", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T16:08:58.529+1300", "updated": "2012-01-12T16:08:58.529+1300", "started": "2012-01-12T16:08:58.529+1300", "timeSpent": "2d", "id": "10001" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/comment.post.json0000644000004100000410000000175614370022124024553 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/comment/10001", "id": "10001", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "body": "new comment", "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T14:29:59.209+1300", "updated": "2012-01-12T14:29:59.209+1300" } jira-ruby-2.3.0/spec/mock_responses/issue/10002/comment.json0000644000004100000410000000443714370022124023566 0ustar www-datawww-data{ "startAt": 0, "maxResults": 2, "total": 2, "comments": [ { "self": "http://localhost:2990/jira/rest/api/2/issue/10002/comment/10000", "id": "10000", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "body": "This is a comment. Creative.", "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-11T10:02:14.430+1300", "updated": "2012-01-12T15:15:13.074+1300" }, { "self": "http://localhost:2990/jira/rest/api/2/issue/10002/comment/10001", "id": "10001", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "body": "new comment", "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T14:29:59.209+1300", "updated": "2012-01-12T14:29:59.209+1300" } ] } jira-ruby-2.3.0/spec/mock_responses/issue/10002/worklog.json0000644000004100000410000000704014370022124023601 0ustar www-datawww-data{ "startAt": 0, "maxResults": 3, "total": 3, "worklogs": [ { "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10000", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "comment": "Some epic work.", "created": "2012-01-11T13:33:25.604+1300", "updated": "2012-01-11T13:33:25.604+1300", "started": "2012-01-11T13:33:00.000+1300", "timeSpent": "18w 2d", "id": "10000" }, { "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10001", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T16:08:58.529+1300", "updated": "2012-01-12T16:11:22.266+1300", "started": "2012-01-12T16:08:58.529+1300", "timeSpent": "4d", "id": "10001" }, { "self": "http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10002", "author": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "updateAuthor": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "created": "2012-01-12T16:20:39.542+1300", "updated": "2012-01-12T16:20:39.542+1300", "started": "2012-01-12T16:20:39.541+1300", "timeSpent": "3d", "id": "10002" } ] } jira-ruby-2.3.0/spec/mock_responses/issue/10002.json0000644000004100000410000000743714370022124022127 0ustar www-datawww-data{ "expand": "renderedFields,names,schema,transitions,editmeta,changelog", "id": "10002", "self": "http://localhost:2990/jira/rest/api/2/issue/10002", "key": "SAMPLEPROJECT-1", "fields": { "summary": "Sample Issue", "progress": { "progress": 0, "total": 0 }, "timetracking": {}, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T10:26:06.030+1300", "created": "2011-12-14T10:26:06.030+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": "2011-12-14", "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/watchers", "watchCount": 0, "isWatching": false }, "worklog": { "startAt": 0, "maxResults": 0, "total": 0, "worklogs": [] }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "workratio": -1, "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "attachment": [], "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [ { "self": "http://localhost:2990/jira/rest/api/2/component/10001", "id": "10001", "name": "Jamflam" }, { "self": "http://localhost:2990/jira/rest/api/2/component/10000", "id": "10000", "name": "Jammy", "description": "Description!" } ], "comment": { "startAt": 0, "maxResults": 0, "total": 0, "comments": [] }, "timeoriginalestimate": null, "aggregatetimespent": null } } jira-ruby-2.3.0/spec/mock_responses/issue/10002.put.missing_field_update.json0000644000004100000410000000013014370022124027072 0ustar www-datawww-data{ "errorMessages": [ "one of 'fields' or 'update' required" ], "errors": {} } jira-ruby-2.3.0/spec/mock_responses/issue/10002.invalid.put.json0000644000004100000410000000015714370022124024353 0ustar www-datawww-data{ "errorMessages": [], "errors": { "invalid": "Field 'invalid' is not valid for this operation." } } jira-ruby-2.3.0/spec/mock_responses/issue.json0000644000004100000410000011726114370022124021462 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 0, "maxResults": 1000, "total": 11, "issues": [ { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10014", "self": "http://localhost:2990/jira/rest/api/2/issue/10014", "key": "SAMPLEPROJECT-13", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-13/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:35:06.000+1300", "created": "2011-12-15T15:35:06.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-13/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10013", "self": "http://localhost:2990/jira/rest/api/2/issue/10013", "key": "SAMPLEPROJECT-12", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-12/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:34:58.000+1300", "created": "2011-12-15T15:34:58.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-12/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10012", "self": "http://localhost:2990/jira/rest/api/2/issue/10012", "key": "SAMPLEPROJECT-11", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-11/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:34:48.000+1300", "created": "2011-12-15T15:34:48.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-11/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10011", "self": "http://localhost:2990/jira/rest/api/2/issue/10011", "key": "SAMPLEPROJECT-10", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-10/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:11:57.000+1300", "created": "2011-12-15T15:11:57.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-10/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10010", "self": "http://localhost:2990/jira/rest/api/2/issue/10010", "key": "SAMPLEPROJECT-9", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-9/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:11:45.000+1300", "created": "2011-12-15T15:11:45.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-9/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10009", "self": "http://localhost:2990/jira/rest/api/2/issue/10009", "key": "SAMPLEPROJECT-8", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-8/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T14:19:49.000+1300", "created": "2011-12-15T14:19:49.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-8/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10008", "self": "http://localhost:2990/jira/rest/api/2/issue/10008", "key": "SAMPLEPROJECT-7", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-7/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:50:38.000+1300", "created": "2011-12-14T14:50:38.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-7/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10007", "self": "http://localhost:2990/jira/rest/api/2/issue/10007", "key": "SAMPLEPROJECT-6", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-6/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:49:35.000+1300", "created": "2011-12-14T14:49:35.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-6/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10006", "self": "http://localhost:2990/jira/rest/api/2/issue/10006", "key": "SAMPLEPROJECT-5", "fields": { "summary": "blah", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-5/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:46:58.000+1300", "created": "2011-12-14T14:46:58.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-5/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10005", "self": "http://localhost:2990/jira/rest/api/2/issue/10005", "key": "SAMPLEPROJECT-4", "fields": { "summary": "blah", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-4/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T12:11:48.000+1300", "created": "2011-12-14T12:11:48.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-4/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10002", "self": "http://localhost:2990/jira/rest/api/2/issue/10002", "key": "SAMPLEPROJECT-1", "fields": { "summary": "MOOOOOOARRR NINJAAAA!", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:37:33.000+1300", "created": "2011-12-14T10:26:06.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": "2011-12-14", "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } } ] } jira-ruby-2.3.0/spec/mock_responses/jira/0000755000004100000410000000000014370022124020354 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/jira/rest/0000755000004100000410000000000014370022124021331 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/jira/rest/webhooks/0000755000004100000410000000000014370022124023152 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/jira/rest/webhooks/1.0/0000755000004100000410000000000014370022124023450 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/jira/rest/webhooks/1.0/webhook/0000755000004100000410000000000014370022124025106 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/jira/rest/webhooks/1.0/webhook/2.json0000644000004100000410000000052614370022124026145 0ustar www-datawww-data{"name":"from API", "url":"http://localhost:3000/webhooks/1", "excludeBody":false, "filters":{"issue-related-events-section":""}, "events":[], "enabled":true, "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2", "lastUpdatedUser":"admin", "lastUpdatedDisplayName":"admin", "lastUpdated":1453306520188 }jira-ruby-2.3.0/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json0000644000004100000410000000053014370022124025777 0ustar www-datawww-data[{"name":"from API", "url":"http://localhost:3000/webhooks/1", "excludeBody":false, "filters":{"issue-related-events-section":""}, "events":[], "enabled":true, "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2", "lastUpdatedUser":"admin", "lastUpdatedDisplayName":"admin", "lastUpdated":1453306520188 }]jira-ruby-2.3.0/spec/mock_responses/sprint/0000755000004100000410000000000014370022124020746 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/sprint/1_issues.json0000644000004100000410000001045714370022124023403 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 0, "maxResults": 50, "total": 1, "issues": [ { "expand": "", "id": "10001", "self": "http://www.example.com/jira/rest/agile/1.0/board/92/issue/10001", "key": "HSP-1", "fields": { "flagged": true, "sprint": { "id": 37, "self": "http://www.example.com/jira/rest/agile/1.0/sprint/13", "state": "future", "name": "sprint 2" }, "closedSprints": [ { "id": 37, "self": "http://www.example.com/jira/rest/agile/1.0/sprint/23", "state": "closed", "name": "sprint 1", "startDate": "2015-04-11T15:22:00.000+10:00", "endDate": "2015-04-20T01:22:00.000+10:00", "completeDate": "2015-04-20T11:04:00.000+10:00" } ], "description": "example bug report", "project": { "self": "http://www.example.com/jira/rest/api/2/project/EX", "id": "10000", "key": "EX", "name": "Example", "avatarUrls": { "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000", "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000", "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000", "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000" }, "projectCategory": { "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000", "id": "10000", "name": "FIRST", "description": "First Project Category" } }, "comment": [ { "self": "http://www.example.com/jira/rest/api/2/issue/10010/comment/10000", "id": "10000", "author": { "self": "http://www.example.com/jira/rest/api/2/user?username=fred", "name": "fred", "displayName": "Fred F. User", "active": false }, "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.", "updateAuthor": { "self": "http://www.example.com/jira/rest/api/2/user?username=fred", "name": "fred", "displayName": "Fred F. User", "active": false }, "created": "2016-06-22T11:49:57.797+0200", "updated": "2016-06-22T11:49:57.800+0200", "visibility": { "type": "role", "value": "Administrators" } } ], "epic": { "id": 37, "self": "http://www.example.com/jira/rest/agile/1.0/epic/23", "name": "epic 1", "summary": "epic 1 summary", "color": { "key": "color_4" }, "done": true }, "worklog": [ { "self": "http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000", "author": { "self": "http://www.example.com/jira/rest/api/2/user?username=fred", "name": "fred", "displayName": "Fred F. User", "active": false }, "updateAuthor": { "self": "http://www.example.com/jira/rest/api/2/user?username=fred", "name": "fred", "displayName": "Fred F. User", "active": false }, "comment": "I did some work here.", "updated": "2016-06-22T11:49:57.804+0200", "visibility": { "type": "group", "value": "jira-developers" }, "started": "2016-06-22T11:49:57.804+0200", "timeSpent": "3h 20m", "timeSpentSeconds": 12000, "id": "100028", "issueId": "10002" } ], "updated": 1, "timetracking": { "originalEstimate": "10m", "remainingEstimate": "3m", "timeSpent": "6m", "originalEstimateSeconds": 600, "remainingEstimateSeconds": 200, "timeSpentSeconds": 400 } } } ] }jira-ruby-2.3.0/spec/mock_responses/rapidview.json0000644000004100000410000000021614370022124022313 0ustar www-datawww-data{ "views": [ { "id": 1, "name": "SAMPLEPROJECT", "canEdit": true, "sprintSupportEnabled": true } ] }jira-ruby-2.3.0/spec/mock_responses/version.post.json0000644000004100000410000000022014370022124022765 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/version/10001", "id": "10001", "name": "2.0", "archived": false, "released": false } jira-ruby-2.3.0/spec/mock_responses/component/0000755000004100000410000000000014370022124021431 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/component/10000.invalid.put.json0000644000004100000410000000037014370022124025220 0ustar www-datawww-data{ "errorMessages": [ "Unrecognized field \"invalid\" (Class com.atlassian.jira.rest.v2.issue.component.ComponentBean), not marked as ignorable\n at [Source: org.apache.catalina.connector.CoyoteInputStream@70faf7c7; line: 1, column: 2]" ] } jira-ruby-2.3.0/spec/mock_responses/component/10000.json0000644000004100000410000000244614370022124022772 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/component/10000", "id": "10000", "name": "Cheesecake", "description": "Description!", "lead": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "assigneeType": "PROJECT_DEFAULT", "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "realAssigneeType": "PROJECT_DEFAULT", "realAssignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "isAssigneeTypeValid": true } jira-ruby-2.3.0/spec/mock_responses/component/10000.put.json0000644000004100000410000000244114370022124023574 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/component/10000", "id": "10000", "name": "Jammy", "description": "Description!", "lead": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "assigneeType": "PROJECT_DEFAULT", "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "realAssigneeType": "PROJECT_DEFAULT", "realAssignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "isAssigneeTypeValid": true } jira-ruby-2.3.0/spec/mock_responses/issueLinkType.json0000644000004100000410000000114614370022124023134 0ustar www-datawww-data{ "issueLinkTypes": [ { "id": "10000", "name": "Blocks", "inward": "is blocked by", "outward": "blocks", "self": "http://localhost:2990/jira/rest/api/2/issueLinkType/10000" }, { "id": "10400", "name": "Cloners", "inward": "is cloned by", "outward": "clones", "self": "http://localhost:2990/jira/rest/api/2/issueLinkType/10400" }, { "id": "10401", "name": "Duplicate", "inward": "is duplicated by", "outward": "duplicates", "self": "http://localhost:2990/jira/rest/api/2/issueLinkType/10401" } ] }jira-ruby-2.3.0/spec/mock_responses/webhook/0000755000004100000410000000000014370022124021065 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/webhook/webhook.json0000644000004100000410000000052614370022124023421 0ustar www-datawww-data{"name":"from API", "url":"http://localhost:3000/webhooks/1", "excludeBody":false, "filters":{"issue-related-events-section":""}, "events":[], "enabled":true, "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2", "lastUpdatedUser":"admin", "lastUpdatedDisplayName":"admin", "lastUpdated":1453306520188 }jira-ruby-2.3.0/spec/mock_responses/field.json0000644000004100000410000000120614370022124021404 0ustar www-datawww-data[ { "id": "1", "name": "Description", "custom": false, "orderable": true, "navigable": true, "searchable": true, "clauseNames": [ "description" ], "schema": { "type": "string", "system": "description" } }, { "id": "2", "name": "Summary", "custom": false, "orderable": true, "navigable": true, "searchable": true, "clauseNames": [ "summary" ], "schema": { "type": "string", "system": "summary" } } ]jira-ruby-2.3.0/spec/mock_responses/empty_issues.json0000644000004100000410000000014714370022124023055 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 11, "maxResults": 1000, "total": 0, "issues": [ ] } jira-ruby-2.3.0/spec/mock_responses/version/0000755000004100000410000000000014370022124021114 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/version/10000.invalid.put.json0000644000004100000410000000036214370022124024704 0ustar www-datawww-data{ "errorMessages": [ "Unrecognized field \"chump\" (Class com.atlassian.jira.rest.v2.issue.version.VersionBean), not marked as ignorable\n at [Source: org.apache.catalina.connector.CoyoteInputStream@4264a42e; line: 1, column: 2]" ] } jira-ruby-2.3.0/spec/mock_responses/version/10000.json0000644000004100000410000000041114370022124022443 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/version/10000", "id": "10000", "description": "Initial version", "name": "1.0", "overdue": false, "userReleaseDate": "12/Jan/12", "archived": false, "releaseDate": "2012-01-12", "released": false } jira-ruby-2.3.0/spec/mock_responses/version/10000.put.json0000644000004100000410000000022214370022124023252 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/version/10000", "id": "10000", "name": "2.0.0", "archived": false, "released": false } jira-ruby-2.3.0/spec/mock_responses/priority.json0000644000004100000410000000267514370022124022215 0ustar www-datawww-data[ { "self": "http://localhost:2990/jira/rest/api/2/priority/1", "statusColor": "#cc0000", "description": "Blocks development and/or testing work, production could not run.", "iconUrl": "http://localhost:2990/jira/images/icons/priority_blocker.gif", "name": "Blocker", "id": "1" }, { "self": "http://localhost:2990/jira/rest/api/2/priority/2", "statusColor": "#ff0000", "description": "Crashes, loss of data, severe memory leak.", "iconUrl": "http://localhost:2990/jira/images/icons/priority_critical.gif", "name": "Critical", "id": "2" }, { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "statusColor": "#009900", "description": "Major loss of function.", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, { "self": "http://localhost:2990/jira/rest/api/2/priority/4", "statusColor": "#006600", "description": "Minor loss of function, or other problem where easy workaround is present.", "iconUrl": "http://localhost:2990/jira/images/icons/priority_minor.gif", "name": "Minor", "id": "4" }, { "self": "http://localhost:2990/jira/rest/api/2/priority/5", "statusColor": "#003300", "description": "Cosmetic problem like misspelled words or misaligned text", "iconUrl": "http://localhost:2990/jira/images/icons/priority_trivial.gif", "name": "Trivial", "id": "5" } ] jira-ruby-2.3.0/spec/mock_responses/status.json0000644000004100000410000000277614370022124021661 0ustar www-datawww-data[ { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, { "self": "http://localhost:2990/jira/rest/api/2/status/3", "description": "This issue is being actively worked on at the moment by the assignee.", "iconUrl": "http://localhost:2990/jira/images/icons/status_inprogress.gif", "name": "In Progress", "id": "3" }, { "self": "http://localhost:2990/jira/rest/api/2/status/4", "description": "This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.", "iconUrl": "http://localhost:2990/jira/images/icons/status_reopened.gif", "name": "Reopened", "id": "4" }, { "self": "http://localhost:2990/jira/rest/api/2/status/5", "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.", "iconUrl": "http://localhost:2990/jira/images/icons/status_resolved.gif", "name": "Resolved", "id": "5" }, { "self": "http://localhost:2990/jira/rest/api/2/status/6", "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "iconUrl": "http://localhost:2990/jira/images/icons/status_closed.gif", "name": "Closed", "id": "6" } ] jira-ruby-2.3.0/spec/mock_responses/resolution/0000755000004100000410000000000014370022124021632 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/resolution/1.json0000644000004100000410000000041014370022124022660 0ustar www-datawww-data{ "self": "http://www.example.com/jira/rest/api/2/resolution/1", "description": "A fix for this issue is checked into the tree and tested.", "iconUrl": "http://www.example.com/jira/images/icons/status_resolved.gif", "name": "Fixed", "id": "1" }jira-ruby-2.3.0/spec/mock_responses/priority/0000755000004100000410000000000014370022124021310 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/priority/1.json0000644000004100000410000000044214370022124022343 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/priority/1", "statusColor": "#cc0000", "description": "Blocks development and/or testing work, production could not run.", "iconUrl": "http://localhost:2990/jira/images/icons/priority_blocker.gif", "name": "Blocker", "id": "1" } jira-ruby-2.3.0/spec/mock_responses/board/0000755000004100000410000000000014370022124020516 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/board/1.json0000644000004100000410000000152114370022124021550 0ustar www-datawww-data{ "maxResults": 50, "startAt": 0, "isLast": true, "values": [ { "id": 1, "self": "https://test.com/jira/rest/agile/1.0/sprint/1", "state": "closed", "name": "Test Sprint 1", "startDate": "2016-05-03T01:45:17.624-04:00", "endDate": "2016-05-06T13:30:00.000-04:00", "completeDate": "2016-05-08T04:05:31.959-04:00", "originBoardId": 1 }, { "id": 2, "self": "https://test.com/jira/rest/agile/1.0/sprint/2", "state": "active", "name": "Test Sprint 2", "startDate": "2016-12-12T04:42:46.184-05:00", "endDate": "2017-01-02T13:56:00.000-05:00", "originBoardId": 1 }, { "id": 3, "self": "https://test.com/jira/rest/agile/1.0/sprint/2", "state": "future", "name": "Test Sprint 2", "originBoardId": 1 } ] }jira-ruby-2.3.0/spec/mock_responses/board/1_issues.json0000644000004100000410000000375014370022124023151 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 0, "maxResults": 1000, "total": 9, "issues": [ { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10546", "self": "https://jira.example.com/rest/agile/1.0/issue/10546", "key": "SBT-1" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10547", "self": "https://jira.example.com/rest/agile/1.0/issue/10547", "key": "SBT-2" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10556", "self": "https://jira.example.com/rest/agile/1.0/issue/10556", "key": "SBT-11" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10557", "self": "https://jira.example.com/rest/agile/1.0/issue/10557", "key": "SBT-12" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10558", "self": "https://jira.example.com/rest/agile/1.0/issue/10558", "key": "SBT-13" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10559", "self": "https://jira.example.com/rest/agile/1.0/issue/10559", "key": "SBT-14" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10600", "self": "https://jira.example.com/rest/agile/1.0/issue/10600", "key": "SBT-16" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10601", "self": "https://jira.example.com/rest/agile/1.0/issue/10601", "key": "SBT-17" }, { "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", "id": "10604", "self": "https://jira.example.com/rest/agile/1.0/issue/10604", "key": "SBT-19" } ] }jira-ruby-2.3.0/spec/mock_responses/status/0000755000004100000410000000000014370022124020752 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/status/1.json0000644000004100000410000000037414370022124022011 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" } jira-ruby-2.3.0/spec/mock_responses/project/0000755000004100000410000000000014370022124021075 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/project/SAMPLEPROJECT.json0000644000004100000410000000560614370022124024007 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "lead": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "components": [ { "self": "http://localhost:2990/jira/rest/api/2/component/10001", "id": "10001", "name": "Jamflam", "isAssigneeTypeValid": false }, { "self": "http://localhost:2990/jira/rest/api/2/component/10000", "id": "10000", "name": "Jammy", "description": "Description!", "isAssigneeTypeValid": false } ], "issueTypes": [ { "self": "http://localhost:2990/jira/rest/api/2/issuetype/1", "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "iconUrl": "http://localhost:2990/jira/images/icons/bug.gif", "name": "Bug", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/2", "id": "2", "description": "A new feature of the product, which has yet to be developed.", "iconUrl": "http://localhost:2990/jira/images/icons/newfeature.gif", "name": "New Feature", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/4", "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "iconUrl": "http://localhost:2990/jira/images/icons/improvement.gif", "name": "Improvement", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/5", "id": "5", "description": "The sub-task of the issue", "iconUrl": "http://localhost:2990/jira/images/icons/issue_subtask.gif", "name": "Sub-task", "subtask": true } ], "assigneeType": "PROJECT_LEAD", "versions": [], "name": "Sample Project for Developing RoR RESTful API", "roles": { "Users": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT/role/10000", "Administrators": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT/role/10002", "Developers": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT/role/10001" }, "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } } jira-ruby-2.3.0/spec/mock_responses/project/SAMPLEPROJECT.issues.json0000644000004100000410000011725714370022124025327 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 0, "maxResults": 50, "total": 11, "issues": [ { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10014", "self": "http://localhost:2990/jira/rest/api/2/issue/10014", "key": "SAMPLEPROJECT-13", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-13/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:35:06.000+1300", "created": "2011-12-15T15:35:06.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-13/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10013", "self": "http://localhost:2990/jira/rest/api/2/issue/10013", "key": "SAMPLEPROJECT-12", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-12/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:34:58.000+1300", "created": "2011-12-15T15:34:58.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-12/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10012", "self": "http://localhost:2990/jira/rest/api/2/issue/10012", "key": "SAMPLEPROJECT-11", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-11/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:34:48.000+1300", "created": "2011-12-15T15:34:48.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-11/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10011", "self": "http://localhost:2990/jira/rest/api/2/issue/10011", "key": "SAMPLEPROJECT-10", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-10/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:11:57.000+1300", "created": "2011-12-15T15:11:57.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-10/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10010", "self": "http://localhost:2990/jira/rest/api/2/issue/10010", "key": "SAMPLEPROJECT-9", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-9/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:11:45.000+1300", "created": "2011-12-15T15:11:45.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-9/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10009", "self": "http://localhost:2990/jira/rest/api/2/issue/10009", "key": "SAMPLEPROJECT-8", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-8/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T14:19:49.000+1300", "created": "2011-12-15T14:19:49.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-8/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10008", "self": "http://localhost:2990/jira/rest/api/2/issue/10008", "key": "SAMPLEPROJECT-7", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-7/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:50:38.000+1300", "created": "2011-12-14T14:50:38.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-7/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10007", "self": "http://localhost:2990/jira/rest/api/2/issue/10007", "key": "SAMPLEPROJECT-6", "fields": { "summary": "blarg from in example.rb", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-6/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:49:35.000+1300", "created": "2011-12-14T14:49:35.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-6/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10006", "self": "http://localhost:2990/jira/rest/api/2/issue/10006", "key": "SAMPLEPROJECT-5", "fields": { "summary": "blah", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-5/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T14:46:58.000+1300", "created": "2011-12-14T14:46:58.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-5/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10005", "self": "http://localhost:2990/jira/rest/api/2/issue/10005", "key": "SAMPLEPROJECT-4", "fields": { "summary": "blah", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-4/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-14T12:11:48.000+1300", "created": "2011-12-14T12:11:48.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": null, "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-4/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog", "id": "10002", "self": "http://localhost:2990/jira/rest/api/2/issue/10002", "key": "SAMPLEPROJECT-1", "fields": { "summary": "MOOOOOOARRR NINJAAAA!", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "updated": "2011-12-15T15:37:33.000+1300", "created": "2011-12-14T10:26:06.000+1300", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priority_major.gif", "name": "Major", "id": "3" }, "duedate": "2011-12-14", "issuelinks": [], "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAMPLEPROJECT-1/watchers", "watchCount": 0, "isWatching": false }, "subtasks": [], "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif", "name": "Open", "id": "1" }, "labels": [], "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "workratio": -1, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } } ] } jira-ruby-2.3.0/spec/mock_responses/rapidview/0000755000004100000410000000000014370022124021421 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/rapidview/SAMPLEPROJECT.json0000644000004100000410000000013314370022124024321 0ustar www-datawww-data{ "id": 1, "name": "SAMPLEPROJECT", "canEdit": true, "sprintSupportEnabled": true }jira-ruby-2.3.0/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json0000644000004100000410000003207214370022124026603 0ustar www-datawww-data{ "expand": "schema,names", "startAt": 0, "maxResults": 50, "total": 2, "issues": [ { "expand": "editmeta,renderedFields,transitions,changelog,operations", "id": "10001", "self": "http://localhost:2990/jira/rest/api/2/issue/10001", "key": "SAM-2", "fields": { "summary": "Test issue 2", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/10001", "id": "10001", "description": "", "iconUrl": "http://localhost:2990/jira/images/icons/ico_story.png", "name": "Story", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAM-2/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "creator": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "created": "2014-07-18T23:30:43.000+0700", "updated": "2014-07-18T23:30:44.000+0700", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priorities/major.png", "name": "Major", "id": "3" }, "duedate": null, "customfield_10001": null, "customfield_10002": null, "customfield_10003": null, "issuelinks": [], "customfield_10004": null, "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAM-2/watchers", "watchCount": 1, "isWatching": true }, "customfield_10000": null, "subtasks": [], "customfield_10009": "0|100004:", "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "name": "Open", "id": "1", "statusCategory": { "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2", "id": 2, "key": "new", "colorName": "blue-gray", "name": "New" } }, "labels": [], "customfield_10005": null, "workratio": -1, "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/10000", "id": "10000", "key": "SAM", "name": "SAMPLEPROJECT", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011", "24x24": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011", "32x32": "http://localhost:2990/jira/secure/projectavatar?size=medium&pid=10000&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10000&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "lastViewed": "2014-07-18T23:30:43.561+0700", "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } }, { "expand": "editmeta,renderedFields,transitions,changelog,operations", "id": "10000", "self": "http://localhost:2990/jira/rest/api/2/issue/10000", "key": "SAM-1", "fields": { "summary": "Test issue 1", "progress": { "progress": 0, "total": 0 }, "issuetype": { "self": "http://localhost:2990/jira/rest/api/2/issuetype/10001", "id": "10001", "description": "", "iconUrl": "http://localhost:2990/jira/images/icons/ico_story.png", "name": "Story", "subtask": false }, "votes": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAM-1/votes", "votes": 0, "hasVoted": false }, "resolution": null, "fixVersions": [], "resolutiondate": null, "timespent": null, "creator": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "reporter": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeoriginalestimate": null, "created": "2014-07-18T23:30:26.000+0700", "updated": "2014-07-18T23:30:26.000+0700", "description": null, "priority": { "self": "http://localhost:2990/jira/rest/api/2/priority/3", "iconUrl": "http://localhost:2990/jira/images/icons/priorities/major.png", "name": "Major", "id": "3" }, "duedate": null, "customfield_10001": null, "customfield_10002": null, "customfield_10003": null, "issuelinks": [], "customfield_10004": null, "watches": { "self": "http://localhost:2990/jira/rest/api/2/issue/SAM-1/watchers", "watchCount": 1, "isWatching": true }, "customfield_10000": null, "subtasks": [], "customfield_10009": "0|100000:", "status": { "self": "http://localhost:2990/jira/rest/api/2/status/1", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "name": "Open", "id": "1", "statusCategory": { "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2", "id": 2, "key": "new", "colorName": "blue-gray", "name": "New" } }, "labels": [], "customfield_10005": null, "workratio": -1, "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=xsmall&avatarId=10122", "24x24": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "32x32": "http://localhost:2990/jira/secure/useravatar?size=medium&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "aggregatetimeestimate": null, "project": { "self": "http://localhost:2990/jira/rest/api/2/project/10000", "id": "10000", "key": "SAM", "name": "SAMPLEPROJECT", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011", "24x24": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011", "32x32": "http://localhost:2990/jira/secure/projectavatar?size=medium&pid=10000&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10000&avatarId=10011" } }, "versions": [], "environment": null, "timeestimate": null, "aggregateprogress": { "progress": 0, "total": 0 }, "lastViewed": "2014-07-18T23:30:28.576+0700", "components": [], "timeoriginalestimate": null, "aggregatetimespent": null } } ] }jira-ruby-2.3.0/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json0000644000004100000410000000605414370022124025643 0ustar www-datawww-data{ "sprintMarkersMigrated": true, "issues": [ { "id": 10000, "key": "SAM-1", "hidden": false, "typeName": "Story", "typeId": "10001", "summary": "Test issue 1", "typeUrl": "http://localhost:2990/jira/images/icons/ico_story.png", "priorityUrl": "http://localhost:2990/jira/images/icons/priorities/major.png", "priorityName": "Major", "done": false, "assignee": "admin", "assigneeName": "admin", "hasCustomUserAvatar": false, "autoUserAvatar": { "letter": "a", "color": "#f691b2" }, "color": "#cc0000", "estimateStatistic": { "statFieldId": "customfield_10002", "statFieldValue": {} }, "statusId": "1", "statusName": "Open", "statusUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "status": { "id": "1", "name": "Open", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "statusCategory": { "id": "2", "key": "new", "colorName": "blue-gray" } }, "fixVersions": [], "projectId": 10000, "linkedPagesCount": 0 }, { "id": 10001, "key": "SAM-2", "hidden": false, "typeName": "Story", "typeId": "10001", "summary": "Test issue 2", "typeUrl": "http://localhost:2990/jira/images/icons/ico_story.png", "priorityUrl": "http://localhost:2990/jira/images/icons/priorities/major.png", "priorityName": "Major", "done": false, "assignee": "admin", "assigneeName": "admin", "hasCustomUserAvatar": false, "autoUserAvatar": { "letter": "a", "color": "#f691b2" }, "color": "#cc0000", "estimateStatistic": { "statFieldId": "customfield_10002", "statFieldValue": {} }, "statusId": "1", "statusName": "Open", "statusUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "status": { "id": "1", "name": "Open", "description": "The issue is open and ready for the assignee to start work on it.", "iconUrl": "http://localhost:2990/jira/images/icons/statuses/open.png", "statusCategory": { "id": "2", "key": "new", "colorName": "blue-gray" } }, "fixVersions": [], "projectId": 10000, "linkedPagesCount": 0 } ], "rankCustomFieldId": 10009, "sprints": [], "supportsPages": false, "projects": [ { "id": 10000, "key": "SAM", "name": "SAMPLEPROJECT" } ], "epicData": { "epics": [], "canEditEpics": false, "supportsPages": false }, "canManageSprints": true, "maxIssuesExceeded": false, "queryResultLimit": 2147483647, "versionData": { "versionsPerProject": { "10000": [] }, "canCreateVersion": true } }jira-ruby-2.3.0/spec/mock_responses/issuetype.json0000644000004100000410000000254414370022124022361 0ustar www-datawww-data[ { "self": "http://localhost:2990/jira/rest/api/2/issuetype/5", "id": "5", "description": "The sub-task of the issue", "iconUrl": "http://localhost:2990/jira/images/icons/issue_subtask.gif", "name": "Sub-task", "subtask": true }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/4", "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "iconUrl": "http://localhost:2990/jira/images/icons/improvement.gif", "name": "Improvement", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/3", "id": "3", "description": "A task that needs to be done.", "iconUrl": "http://localhost:2990/jira/images/icons/task.gif", "name": "Task", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/1", "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "iconUrl": "http://localhost:2990/jira/images/icons/bug.gif", "name": "Bug", "subtask": false }, { "self": "http://localhost:2990/jira/rest/api/2/issuetype/2", "id": "2", "description": "A new feature of the product, which has yet to be developed.", "iconUrl": "http://localhost:2990/jira/images/icons/newfeature.gif", "name": "New Feature", "subtask": false } ] jira-ruby-2.3.0/spec/mock_responses/field/0000755000004100000410000000000014370022124020512 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/field/1.json0000644000004100000410000000036014370022124021544 0ustar www-datawww-data{ "id": "1", "name": "Description", "custom": false, "orderable": true, "navigable": true, "searchable": true, "clauseNames": [ "description" ], "schema": { "type": "string", "system": "description" } }jira-ruby-2.3.0/spec/mock_responses/resolution.json0000644000004100000410000000076014370022124022530 0ustar www-datawww-data[ { "self": "http://www.example.com/jira/rest/api/2/resolution/1", "description": "A fix for this issue is checked into the tree and tested.", "iconUrl": "http://www.example.com/jira/images/icons/status_resolved.gif", "name": "Fixed", "id": "1" }, { "self": "http://www.example.com/jira/rest/api/2/resolution/3", "description": "This is what it is supposed to do.", "name": "Works as designed", "id": "3" } ]jira-ruby-2.3.0/spec/mock_responses/issuetype/0000755000004100000410000000000014370022124021461 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issuetype/5.json0000644000004100000410000000036014370022124022517 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/issuetype/5", "id": "5", "description": "The sub-task of the issue", "iconUrl": "http://localhost:2990/jira/images/icons/issue_subtask.gif", "name": "Sub-task", "subtask": true } jira-ruby-2.3.0/spec/mock_responses/component.post.json0000644000004100000410000000165214370022124023314 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/component/10001", "id": "10001", "name": "Test component", "assigneeType": "PROJECT_DEFAULT", "assignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "realAssigneeType": "PROJECT_DEFAULT", "realAssignee": { "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true }, "isAssigneeTypeValid": true } jira-ruby-2.3.0/spec/mock_responses/project.json0000644000004100000410000000063514370022124021774 0ustar www-datawww-data[ { "self": "http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT", "id": "10001", "key": "SAMPLEPROJECT", "name": "Sample Project for Developing RoR RESTful API", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/projectavatar?size=small&pid=10001&avatarId=10011", "48x48": "http://localhost:2990/jira/secure/projectavatar?pid=10001&avatarId=10011" } } ] jira-ruby-2.3.0/spec/mock_responses/issueLinkType/0000755000004100000410000000000014370022124022237 5ustar www-datawww-datajira-ruby-2.3.0/spec/mock_responses/issueLinkType/10000.json0000644000004100000410000000024214370022124023570 0ustar www-datawww-data{ "id": "10000", "self": "http://localhost:2990/jira/rest/api/2/issueLinkType/10000", "name": "Blocks", "inward": "is blocked by", "outward": "blocks" }jira-ruby-2.3.0/spec/mock_responses/user_username=admin.json0000644000004100000410000000074214370022124024310 0ustar www-datawww-data{ "self": "http://localhost:2990/jira/rest/api/2/user?username=admin", "name": "admin", "emailAddress": "admin@example.com", "avatarUrls": { "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122", "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122" }, "displayName": "admin", "active": true, "timeZone": "Pacific/Auckland", "groups": { "size": 3, "items": [] }, "expand": "groups" }jira-ruby-2.3.0/spec/mock_responses/webhook.json0000644000004100000410000000052714370022124021764 0ustar www-datawww-data[{"name":"from API", "url":"http://localhost:3000/webhooks/1", "excludeBody":false, "filters":{"issue-related-events-section":""}, "events":[], "enabled":true, "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2", "lastUpdatedUser":"admin", "lastUpdatedDisplayName":"admin", "lastUpdated":1453306520188} ]jira-ruby-2.3.0/spec/spec_helper.rb0000644000004100000410000000112714370022124017214 0ustar www-datawww-data$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'rubygems' require 'bundler/setup' require 'webmock/rspec' require 'pry' Dir['./spec/support/**/*.rb'].each { |f| require f } require 'jira-ruby' RSpec.configure do |config| config.extend ClientsHelper end def get_mock_response(file, value_if_file_not_found = false) file.sub!('?', '_') # we have to replace this character on Windows machine File.read(File.join(File.dirname(__FILE__), 'mock_responses/', file)) rescue Errno::ENOENT => e raise e if value_if_file_not_found == false value_if_file_not_found end jira-ruby-2.3.0/spec/support/0000755000004100000410000000000014370022124016111 5ustar www-datawww-datajira-ruby-2.3.0/spec/support/matchers/0000755000004100000410000000000014370022124017717 5ustar www-datawww-datajira-ruby-2.3.0/spec/support/matchers/have_attributes.rb0000644000004100000410000000040414370022124023433 0ustar www-datawww-dataRSpec::Matchers.define :have_attributes do |expected| match do |actual| expected.each do |key, value| expect(actual.attrs[key]).to eq(value) end end failure_message do |actual| "expected #{actual.attrs} to match #{expected}" end end jira-ruby-2.3.0/spec/support/matchers/have_one.rb0000644000004100000410000000021114370022124022022 0ustar www-datawww-dataRSpec::Matchers.define :have_one do |resource, klass| match do |actual| expect(actual.send(resource).class).to eq(klass) end end jira-ruby-2.3.0/spec/support/matchers/have_many.rb0000644000004100000410000000045514370022124022217 0ustar www-datawww-dataRSpec::Matchers.define :have_many do |collection, klass| match do |actual| expect(actual.send(collection).class).to eq(JIRA::HasManyProxy) expect(actual.send(collection).length).to be > 0 actual.send(collection).each do |member| expect(member.class).to eq(klass) end end end jira-ruby-2.3.0/spec/support/clients_helper.rb0000644000004100000410000000076014370022124021441 0ustar www-datawww-datamodule ClientsHelper def with_each_client clients = {} oauth_client = JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar') oauth_client.set_access_token('abc', '123') clients['http://localhost:2990'] = oauth_client basic_client = JIRA::Client.new(username: 'foo', password: 'bar', auth_type: :basic, use_ssl: false) clients['http://foo:bar@localhost:2990'] = basic_client clients.each do |site_url, client| yield site_url, client end end end jira-ruby-2.3.0/spec/support/shared_examples/0000755000004100000410000000000014370022124021255 5ustar www-datawww-datajira-ruby-2.3.0/spec/support/shared_examples/integration.rb0000644000004100000410000001435414370022124024134 0ustar www-datawww-datarequire 'cgi' def get_mock_from_path(method, options = {}) prefix = if defined? belongs_to belongs_to.path_component + '/' else '' end url = if options[:url] options[:url] elsif options[:key] described_class.singular_path(client, options[:key], prefix) else described_class.collection_path(client, prefix) end file_path = url.sub(client.options[:rest_base_path], '') file_path = file_path + '.' + options[:suffix] if options[:suffix] file_path = file_path + '.' + method.to_s unless method == :get value_if_not_found = options.key?(:value_if_not_found) ? options[:value_if_not_found] : false get_mock_response("#{file_path}.json", value_if_not_found) end def class_basename described_class.name.split('::').last end def options options = {} options[belongs_to.to_sym] = belongs_to if defined? belongs_to options end def prefix prefix = '/' prefix = belongs_to.path_component + '/' if defined? belongs_to prefix end def build_receiver if defined?(belongs_to) belongs_to.send(described_class.endpoint_name.pluralize.to_sym) else client.send(class_basename) end end shared_examples 'a resource' do it 'gracefully handles non-json responses' do if defined? target subject = target else subject = client.send(class_basename).build(described_class.key_attribute.to_s => '99999') end stub_request(:put, site_url + subject.url) .to_return(status: 405, body: 'Some HTML') expect(subject.save('foo' => 'bar')).to be_falsey expect(lambda do expect(subject.save!('foo' => 'bar')).to be_falsey end).to raise_error(JIRA::HTTPError) end end shared_examples 'a resource with a collection GET endpoint' do it 'should get the collection' do stub_request(:get, site_url + described_class.collection_path(client)) .to_return(status: 200, body: get_mock_from_path(:get)) collection = build_receiver.all expect(collection.length).to eq(expected_collection_length) expect(collection.first).to have_attributes(expected_attributes) end end shared_examples 'a resource with JQL inputs and a collection GET endpoint' do it 'should get the collection' do stub_request( :get, site_url + client.options[:rest_base_path] + '/search?jql=' + CGI.escape(jql_query_string) ).to_return(status: 200, body: get_mock_response('issue.json')) collection = build_receiver.jql(jql_query_string) expect(collection.length).to eq(expected_collection_length) expect(collection.first).to have_attributes(expected_attributes) end end shared_examples 'a resource with a singular GET endpoint' do it 'GETs a single resource' do # E.g., for JIRA::Resource::Project, we need to call # client.Project.find() stub_request(:get, site_url + described_class.singular_path(client, key, prefix)) .to_return(status: 200, body: get_mock_from_path(:get, key: key)) subject = client.send(class_basename).find(key, options) expect(subject).to have_attributes(expected_attributes) end it 'builds and fetches a single resource' do # E.g., for JIRA::Resource::Project, we need to call # client.Project.build('key' => 'ABC123') stub_request(:get, site_url + described_class.singular_path(client, key, prefix)) .to_return(status: 200, body: get_mock_from_path(:get, key: key)) subject = build_receiver.build(described_class.key_attribute.to_s => key) subject.fetch expect(subject).to have_attributes(expected_attributes) end it 'handles a 404' do stub_request(:get, site_url + described_class.singular_path(client, '99999', prefix)) .to_return(status: 404, body: '{"errorMessages":["' + class_basename + ' Does Not Exist"],"errors": {}}') expect(lambda do client.send(class_basename).find('99999', options) end).to raise_exception(JIRA::HTTPError) end end shared_examples 'a resource with a DELETE endpoint' do it 'deletes a resource' do # E.g., for JIRA::Resource::Project, we need to call # client.Project.delete() stub_request(:delete, site_url + described_class.singular_path(client, key, prefix)) .to_return(status: 204, body: nil) subject = build_receiver.build(described_class.key_attribute.to_s => key) expect(subject.delete).to be_truthy end end shared_examples 'a resource with a POST endpoint' do it 'saves a new resource' do stub_request(:post, site_url + described_class.collection_path(client, prefix)) .to_return(status: 201, body: get_mock_from_path(:post)) subject = build_receiver.build expect(subject.save(attributes_for_post)).to be_truthy expected_attributes_from_post.each do |method_name, value| expect(subject.send(method_name)).to eq(value) end end end shared_examples 'a resource with a PUT endpoint' do it 'saves an existing component' do stub_request(:get, site_url + described_class.singular_path(client, key, prefix)) .to_return(status: 200, body: get_mock_from_path(:get, key: key)) stub_request(:put, site_url + described_class.singular_path(client, key, prefix)) .to_return(status: 200, body: get_mock_from_path(:put, key: key, value_if_not_found: nil)) subject = build_receiver.build(described_class.key_attribute.to_s => key) subject.fetch expect(subject.save(attributes_for_put)).to be_truthy expected_attributes_from_put.each do |method_name, value| expect(subject.send(method_name)).to eq(value) end end end shared_examples 'a resource with a PUT endpoint that rejects invalid fields' do it 'fails to save with an invalid field' do stub_request(:get, site_url + described_class.singular_path(client, key)) .to_return(status: 200, body: get_mock_from_path(:get, key: key)) stub_request(:put, site_url + described_class.singular_path(client, key)) .to_return(status: 400, body: get_mock_from_path(:put, key: key, suffix: 'invalid')) subject = client.send(class_basename).build(described_class.key_attribute.to_s => key) subject.fetch expect(subject.save('fields' => { 'invalid' => 'field' })).to be_falsey expect(lambda do subject.save!('fields' => { 'invalid' => 'field' }) end).to raise_error(JIRA::HTTPError) end end jira-ruby-2.3.0/spec/integration/0000755000004100000410000000000014370022124016720 5ustar www-datawww-datajira-ruby-2.3.0/spec/integration/field_spec.rb0000644000004100000410000000141414370022124021342 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Field do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '1' } let(:expected_attributes) do { 'id' => key, 'name' => 'Description', 'custom' => false, 'orderable' => true, 'navigable' => true, 'searchable' => true, 'clauseNames' => ['description'], 'schema' => { 'type' => 'string', 'system' => 'description' } } end let(:expected_collection_length) { 2 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/priority_spec.rb0000644000004100000410000000112114370022124022133 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Priority do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '1' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/priority/1', 'id' => key, 'name' => 'Blocker' } end let(:expected_collection_length) { 5 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/watcher_spec.rb0000644000004100000410000000364314370022124021722 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Watcher do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:target) { JIRA::Resource::Watcher.new(client, attrs: { 'id' => '99999' }, issue_id: '10002') } let(:belongs_to) do JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002', 'fields' => { 'comment' => { 'comments' => [] } } }) end let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/watchers', "isWatching": false, "watchCount": 1, "watchers": [ { "self": 'http://www.example.com/jira/rest/api/2/user?username=admin', "name": 'admin', "displayName": 'admin', "active": false } ] } end describe 'watchers' do before(:each) do stub_request(:get, site_url + '/jira/rest/api/2/issue/10002') .to_return(status: 200, body: get_mock_response('issue/10002.json')) stub_request(:get, site_url + '/jira/rest/api/2/issue/10002/watchers') .to_return(status: 200, body: get_mock_response('issue/10002/watchers.json')) stub_request(:post, site_url + '/jira/rest/api/2/issue/10002/watchers') .to_return(status: 204, body: nil) end it 'should returns all the watchers' do issue = client.Issue.find('10002') watchers = client.Watcher.all(options = { issue: issue }) expect(watchers.length).to eq(1) end it 'should add a watcher' do issue = client.Issue.find('10002') watcher = JIRA::Resource::Watcher.new(client, issue: issue) user_id = "tester" watcher.save!(user_id) end end end end jira-ruby-2.3.0/spec/integration/resolution_spec.rb0000644000004100000410000000137714370022124022472 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Resolution do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '1' } let(:expected_attributes) do { 'self' => 'http://www.example.com/jira/rest/api/2/resolution/1', 'id' => key, 'name' => 'Fixed', 'description' => 'A fix for this issue is checked into the tree and tested.', 'iconUrl' => 'http://www.example.com/jira/images/icons/status_resolved.gif' } end let(:expected_collection_length) { 2 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/status_spec.rb0000644000004100000410000000111214370022124021575 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Status do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '1' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/status/1', 'id' => key, 'name' => 'Open' } end let(:expected_collection_length) { 5 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/rapidview_spec.rb0000644000004100000410000000401714370022124022253 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::RapidView do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '1' } let(:expected_collection_length) { 1 } let(:expected_attributes) do { 'id' => 1, 'name' => 'SAMPLEPROJECT', 'canEdit' => true, 'sprintSupportEnabled' => true } end it_should_behave_like 'a resource' # TODO@Anton: Add json file # it_should_behave_like 'a resource with a singular GET endpoint' describe 'GET all rapidviews' do let(:client) { client } let(:site_url) { site_url } before(:each) do stub_request(:get, site_url + '/jira/rest/greenhopper/1.0/rapidview') .to_return(status: 200, body: get_mock_response('rapidview.json')) end it_should_behave_like 'a resource with a collection GET endpoint' end describe 'issues' do it 'should return all the issues' do stub_request( :get, site_url + '/jira/rest/greenhopper/1.0/xboard/plan/backlog/data?rapidViewId=1' ).to_return( status: 200, body: get_mock_response('rapidview/SAMPLEPROJECT.issues.json') ) stub_request( :get, site_url + '/jira/rest/api/2/search?jql=id IN(10001, 10000)' ).to_return( status: 200, body: get_mock_response('rapidview/SAMPLEPROJECT.issues.full.json') ) stub_request( :get, site_url + '/jira/rest/api/2/search?jql=id IN(10000, 10001) AND sprint IS NOT EMPTY' ).to_return( status: 200, body: get_mock_response('rapidview/SAMPLEPROJECT.issues.full.json') ) subject = client.RapidView.build('id' => 1) issues = subject.issues expect(issues.length).to eq(2) issues.each do |issue| expect(issue.class).to eq(JIRA::Resource::Issue) expect(issue.expanded?).to be_falsey end end end end end jira-ruby-2.3.0/spec/integration/comment_spec.rb0000644000004100000410000000306614370022124021726 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Comment do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:target) { JIRA::Resource::Comment.new(client, attrs: { 'id' => '99999' }, issue_id: '54321') } let(:expected_collection_length) { 2 } let(:belongs_to) do JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002', 'fields' => { 'comment' => { 'comments' => [] } } }) end let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/comment/10000', 'id' => key, 'body' => 'This is a comment. Creative.' } end let(:attributes_for_post) do { 'body' => 'new comment' } end let(:expected_attributes_from_post) do { 'id' => '10001', 'body' => 'new comment' } end let(:attributes_for_put) do { 'body' => 'new body' } end let(:expected_attributes_from_put) do { 'id' => '10000', 'body' => 'new body' } end it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' it_should_behave_like 'a resource with a DELETE endpoint' it_should_behave_like 'a resource with a POST endpoint' it_should_behave_like 'a resource with a PUT endpoint' end end jira-ruby-2.3.0/spec/integration/component_spec.rb0000644000004100000410000000222214370022124022257 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Component do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/component/10000', 'id' => key, 'name' => 'Cheesecake' } end let(:attributes_for_post) do { 'name' => 'Test component', 'project' => 'SAMPLEPROJECT' } end let(:expected_attributes_from_post) do { 'id' => '10001', 'name' => 'Test component' } end let(:attributes_for_put) do { 'name' => 'Jammy', 'project' => 'SAMPLEPROJECT' } end let(:expected_attributes_from_put) do { 'id' => '10000', 'name' => 'Jammy' } end it_should_behave_like 'a resource' it_should_behave_like 'a resource with a singular GET endpoint' it_should_behave_like 'a resource with a DELETE endpoint' it_should_behave_like 'a resource with a POST endpoint' it_should_behave_like 'a resource with a PUT endpoint' it_should_behave_like 'a resource with a PUT endpoint that rejects invalid fields' end end jira-ruby-2.3.0/spec/integration/project_spec.rb0000644000004100000410000000314714370022124021732 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Project do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { 'SAMPLEPROJECT' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/project/SAMPLEPROJECT', 'key' => key, 'name' => 'Sample Project for Developing RoR RESTful API' } end let(:expected_collection_length) { 1 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' describe 'issues' do it 'returns all the issues' do stub_request(:get, site_url + '/jira/rest/api/2/search?jql=project="SAMPLEPROJECT"') .to_return(status: 200, body: get_mock_response('project/SAMPLEPROJECT.issues.json')) subject = client.Project.build('key' => key) issues = subject.issues expect(issues.length).to eq(11) issues.each do |issue| expect(issue.class).to eq(JIRA::Resource::Issue) expect(issue.expanded?).to be_falsey end end end it 'returns a collection of components' do stub_request(:get, site_url + described_class.singular_path(client, key)) .to_return(status: 200, body: get_mock_response('project/SAMPLEPROJECT.json')) subject = client.Project.find(key) expect(subject.components.length).to eq(2) subject.components.each do |component| expect(component.class).to eq(JIRA::Resource::Component) end end end end jira-ruby-2.3.0/spec/integration/webhook.rb0000644000004100000410000000201314370022124020677 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Webhook do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '2' } let(:expected_attributes) do { 'name' => 'from API', 'url' => 'http://localhost:3000/webhooks/1', 'excludeBody' => false, 'filters' => { 'issue-related-events-section' => '' }, 'events' => [], 'enabled' => true, 'self' => 'http://localhost:2990/jira/rest/webhooks/1.0/webhook/2', 'lastUpdatedUser' => 'admin', 'lastUpdatedDisplayName' => 'admin', 'lastUpdated' => 1_453_306_520_188 } end let(:expected_collection_length) { 1 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' it 'returns a collection of components' do stub_request(:get, site_url + described_class.singular_path(client, key)) .to_return(status: 200, body: get_mock_response('webhook/webhook.json')) end end end jira-ruby-2.3.0/spec/integration/worklog_spec.rb0000644000004100000410000000300014370022124021734 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Worklog do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:target) { JIRA::Resource::Worklog.new(client, attrs: { 'id' => '99999' }, issue_id: '54321') } let(:expected_collection_length) { 3 } let(:belongs_to) do JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002', 'fields' => { 'comment' => { 'comments' => [] } } }) end let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10000', 'id' => key, 'comment' => 'Some epic work.' } end let(:attributes_for_post) do { 'timeSpent' => '2d' } end let(:expected_attributes_from_post) do { 'id' => '10001', 'timeSpent' => '2d' } end let(:attributes_for_put) do { 'timeSpent' => '2d' } end let(:expected_attributes_from_put) do { 'id' => '10001', 'timeSpent' => '4d' } end it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' it_should_behave_like 'a resource with a DELETE endpoint' it_should_behave_like 'a resource with a POST endpoint' it_should_behave_like 'a resource with a PUT endpoint' end end jira-ruby-2.3.0/spec/integration/issuetype_spec.rb0000644000004100000410000000112414370022124022307 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Issuetype do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '5' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/issuetype/5', 'id' => key, 'name' => 'Sub-task' } end let(:expected_collection_length) { 5 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/user_spec.rb0000644000004100000410000000250314370022124021235 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::User do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { 'admin' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/user?username=admin', 'name' => key, 'emailAddress' => 'admin@example.com' } end it_should_behave_like 'a resource' it_should_behave_like 'a resource with a singular GET endpoint' describe '#all' do let(:client) do client = double(options: { rest_base_path: '/jira/rest/api/2' }) allow(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000').and_return(JIRA::Resource::UserFactory.new(client)) client end before do allow(client).to receive(:get) .with('/rest/api/2/users/search?username=_&maxResults=1000') { OpenStruct.new(body: '["User1"]') } allow(client).to receive_message_chain(:User, :build).with('users') { [] } end it 'gets users with maxResults of 1000' do expect(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000') expect(client).to receive_message_chain(:User, :build).with('User1') JIRA::Resource::User.all(client) end end end end jira-ruby-2.3.0/spec/integration/transition_spec.rb0000644000004100000410000000261314370022124022453 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Transition do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:target) { JIRA::Resource::Transition.new(client, attrs: { 'id' => '99999' }, issue_id: '10014') } let(:belongs_to) do JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002', 'self' => "#{site_url}/jira/rest/api/2/issue/10002", 'fields' => { 'comment' => { 'comments' => [] } } }) end let(:expected_attributes) do { 'self' => "#{site_url}/jira/rest/api/2/issue/10002/transition/10000", 'id' => key } end let(:attributes_for_post) do { 'transition' => { 'id' => '42' } } end it_should_behave_like 'a resource' describe 'POST endpoint' do it 'saves a new resource' do stub_request(:post, /#{described_class.collection_path(client, prefix)}$/) .with(body: attributes_for_post.to_json) .to_return(status: 200, body: get_mock_from_path(:post)) subject = build_receiver.build expect(subject.save(attributes_for_post)).to be_truthy end end end end jira-ruby-2.3.0/spec/integration/issuelinktype_spec.rb0000644000004100000410000000124614370022124023172 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Issuelinktype do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:expected_attributes) do { 'id' => key, 'self' => 'http://localhost:2990/jira/rest/api/2/issueLinkType/10000', 'name' => 'Blocks', 'inward' => 'is blocked by', 'outward' => 'blocks' } end let(:expected_collection_length) { 3 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a collection GET endpoint' it_should_behave_like 'a resource with a singular GET endpoint' end end jira-ruby-2.3.0/spec/integration/version_spec.rb0000644000004100000410000000214614370022124021747 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Version do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/version/10000', 'id' => key, 'description' => 'Initial version' } end let(:attributes_for_post) do { 'name' => '2.0', 'project' => 'SAMPLEPROJECT' } end let(:expected_attributes_from_post) do { 'id' => '10001', 'name' => '2.0' } end let(:attributes_for_put) do { 'name' => '2.0.0' } end let(:expected_attributes_from_put) do { 'id' => '10000', 'name' => '2.0.0' } end it_should_behave_like 'a resource' it_should_behave_like 'a resource with a singular GET endpoint' it_should_behave_like 'a resource with a DELETE endpoint' it_should_behave_like 'a resource with a POST endpoint' it_should_behave_like 'a resource with a PUT endpoint' it_should_behave_like 'a resource with a PUT endpoint that rejects invalid fields' end end jira-ruby-2.3.0/spec/integration/attachment_spec.rb0000644000004100000410000000170314370022124022410 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Attachment do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10000' } let(:target) { JIRA::Resource::Attachment.new(client, attrs: { 'id' => '99999' }, issue_id: '10002') } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/attachment/10000', 'size' => 15_360, 'filename' => 'ballmer.png' } end let(:belongs_to) do JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002', 'fields' => { 'attachment' => { 'attachments' => [] } } }) end it_should_behave_like 'a resource with a singular GET endpoint' it_should_behave_like 'a resource with a DELETE endpoint' end end jira-ruby-2.3.0/spec/integration/issue_spec.rb0000644000004100000410000000632214370022124021412 0ustar www-datawww-datarequire 'spec_helper' describe JIRA::Resource::Issue do with_each_client do |site_url, client| let(:client) { client } let(:site_url) { site_url } let(:key) { '10002' } let(:expected_attributes) do { 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002', 'key' => 'SAMPLEPROJECT-1', 'expand' => 'renderedFields,names,schema,transitions,editmeta,changelog' } end let(:attributes_for_post) do { 'foo' => 'bar' } end let(:expected_attributes_from_post) do { 'id' => '10005', 'key' => 'SAMPLEPROJECT-4' } end let(:attributes_for_put) do { 'foo' => 'bar' } end let(:expected_attributes_from_put) do { 'foo' => 'bar' } end let(:expected_collection_length) { 11 } it_should_behave_like 'a resource' it_should_behave_like 'a resource with a singular GET endpoint' describe 'GET all issues' do # JIRA::Resource::Issue.all uses the search endpoint let(:client) { client } let(:site_url) { site_url } let(:expected_attributes) do { 'id' => '10014', 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10014', 'key' => 'SAMPLEPROJECT-13' } end before(:each) do stub_request(:get, site_url + '/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=0') .to_return(status: 200, body: get_mock_response('issue.json')) stub_request(:get, site_url + '/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=11') .to_return(status: 200, body: get_mock_response('empty_issues.json')) end it_should_behave_like 'a resource with a collection GET endpoint' end it_should_behave_like 'a resource with a DELETE endpoint' it_should_behave_like 'a resource with a POST endpoint' it_should_behave_like 'a resource with a PUT endpoint' it_should_behave_like 'a resource with a PUT endpoint that rejects invalid fields' describe 'errors' do before(:each) do stub_request(:get, site_url + '/jira/rest/api/2/issue/10002') .to_return(status: 200, body: get_mock_response('issue/10002.json')) stub_request(:put, site_url + '/jira/rest/api/2/issue/10002') .with(body: '{"missing":"fields and update"}') .to_return(status: 400, body: get_mock_response('issue/10002.put.missing_field_update.json')) end it 'fails to save when fields and update are missing' do subject = client.Issue.build('id' => '10002') subject.fetch expect(subject.save('missing' => 'fields and update')).to be_falsey end end describe 'GET jql issues' do # JIRA::Resource::Issue.jql uses the search endpoint jql_query_string = "PROJECT = 'SAMPLEPROJECT'" let(:client) { client } let(:site_url) { site_url } let(:jql_query_string) { jql_query_string } let(:expected_attributes) do { 'id' => '10014', 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10014', 'key' => 'SAMPLEPROJECT-13' } end it_should_behave_like 'a resource with JQL inputs and a collection GET endpoint' end end end jira-ruby-2.3.0/.gitignore0000644000004100000410000000014414370022124015432 0ustar www-datawww-data.idea *.gem .bundle Gemfile.lock pkg/* .rvmrc .rspec *.pem .DS_STORE doc .ruby-version .rakeTasks jira-ruby-2.3.0/example.rb0000644000004100000410000001571514370022124015434 0ustar www-datawww-datarequire 'pp' require './lib/jira-ruby' CONSUMER_KEY = 'test' SITE = 'https://test.jira.com' options = { :private_key_file => "rsakey.pem", :context_path => '', :consumer_key => CONSUMER_KEY, :site => SITE } client = JIRA::Client.new(options) if ARGV.length == 0 # If not passed any command line arguments, open a browser and prompt the # user for the OAuth verifier. request_token = client.request_token puts "Opening #{request_token.authorize_url}" system "open #{request_token.authorize_url}" puts "Enter the oauth_verifier: " oauth_verifier = gets.strip access_token = client.init_access_token(:oauth_verifier => oauth_verifier) puts "Access token: #{access_token.token} secret: #{access_token.secret}" elsif ARGV.length == 2 # Otherwise assume the arguments are a previous access token and secret. access_token = client.set_access_token(ARGV[0], ARGV[1]) else # Script must be passed 0 or 2 arguments raise "Usage: #{$0} [ token secret ]" end # Show all projects projects = client.Project.all projects.each do |project| puts "Project -> key: #{project.key}, name: #{project.name}" end issue = client.Issue.find('SAMPLEPROJECT-1') pp issue # # Handling fields by name, rather than by id # # ------------------------------------------ # Cache the Field list from the server client.Field.map_fields # This allows use of friendlier names for custom fields # Say that 'Special Field' is customfield_12345 # It becomes mapped to Special_Field which is usable as a method call # # Say that there is a second 'Special Field' is customfield_54321 # Names are deduplicated so the second 'Special Field' becomes Special_Field_54321 # # Names are massaged to get rid of special characters, and spaces # So 'Special & @ Field' becomes Special_____Field - not perfect, but usable old_way = issue.customfield_12345 new_way = issue.Special_Field (old_way == new_way) && puts 'much easier' # # You can also specify fields to be returned in the response # This is especially useful in regards to shortening JQL query response times if performance becomes an issue client.Issue.jql(a_normal_jql_search, fields:[:description, :summary, :Special_field, :created]) # Or you could always do it the old way - if you can remember the custom field numbers... client.Issue.jql(a_normal_jql_search, fields:[:description, :summary, :customfield_1234, :created]) # You can also specify the maximum number of results to be returned in the response, i.e. 500 client.Issue.jql(a_normal_jql_search, max_results: 500) # # Find a specific project by key # # ------------------------------ # project = client.Project.find('SAMPLEPROJECT') # pp project # project.issues.each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # List all Issues # # --------------- # client.Issue.all.each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # List issues by JQL query # # ------------------------ # client.Issue.jql('PROJECT = "SAMPLEPROJECT"', [comments, summary]).each do |issue| # puts "#{issue.id} - #{issue.fields['summary']}" # end # # # Delete an issue # # --------------- # issue = client.Issue.find('SAMPLEPROJECT-2') # if issue.delete # puts "Delete of issue SAMPLEPROJECT-2 sucessful" # else # puts "Delete of issue SAMPLEPROJECT-2 failed" # end # # # Create an issue # # --------------- # issue = client.Issue.build # labels = ['label1', 'label2'] # issue.save({ # "fields" => { # "summary" => "blarg from in example.rb", # "project" => {"key" => "SAMPLEPROJECT"}, # "issuetype" => {"id" => "3"}, # "labels" => labels, # "priority" => {"id" => "1"} # } # }) # issue.fetch # pp issue # # # Update an issue # # --------------- # issue = client.Issue.find("10002") # issue.save({"fields"=>{"summary"=>"EVEN MOOOOOOARRR NINJAAAA!"}}) # pp issue # # # Transition an issue # # ------------------- # issue_transition = issue.transitions.build # issue_transition.save!('transition' => {'id' => transition_id}) # # # Change assignee # # ------------------- # issue.save({'fields' => {'assignee' => {'name' => person_name}}}) # # # Find a user # # ----------- # user = client.User.find('admin') # pp user # # # Get all issue watchers # # ---------------------- # issue = client.Issue.find("10002") # watchers = issue.watchers.all # watchers = client.Watcher.all(:issue => issue) # # Get all issue types # # ------------------- # issuetypes = client.Issuetype.all # pp issuetypes # # # Get a single issue type # # ----------------------- # issuetype = client.Issuetype.find('5') # pp issuetype # # # Get all comments for an issue # # ----------------------------- # issue.comments.each do |comment| # pp comment # end # # # Build and Save a comment # # ------------------------ # comment = issue.comments.build # comment.save!(:body => "New comment from example script") # # # Delete a comment from the collection # # ------------------------------------ # issue.comments.last.delete # # # Update an existing comment # # -------------------------- # issue.comments.first.save({"body" => "an updated comment frome example.rb"}) # # Add attachment to Issue # # ------------------------ # issue = client.Issue.find('PROJ-1') # attachment = issue.attachments.build # attachment.save('file': '/path/to/file') # # List all available link types # ------------------------------ pp client.Issuelinktype.all # List issue's links # ------------------------- issue = client.Issue.find("10002") pp issue.issuelinks # Link two issues (on the same Jira instance) # -------------------------------------------- link = client.Issuelink.build link.save( { :type => {:name => 'Relates'}, :inwardIssue => {:key => 'AL-1'}, :outwardIssue => {:key => 'AL-2'} } ) # List issue's remote links # ------------------------- pp issue.remotelink.all # Link two remote issues (on the different Jira instance) # In order to add remote links, you have to add # Application Links between two Jira instances first. # More information: # https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links # http://stackoverflow.com/questions/29850252/jira-api-issuelink-connect-two-different-instances # ------------------------------------------------------- client_1 = JIRA::Client.new(options) client_2 = JIRA::Client.new(options) # you have to search for your app id here, instead of getting the first client_2_app_link = client_2.ApplicationLink.manifest issue_1 = client_1.Issue.find('BB-2') issue_2 = client_2.Issue.find('AA-1') remote_link = issue_2.remotelink.build remote_link.save( { :globalId => "appId=#{client_2_app_link.id}&issueId=#{issue_1.id}", :application => { :type => 'com.atlassian.jira', :name => client_2_app_link['name'] }, :relationship => 'relates to', :object => { :url => client_1.options[:site] + client_1.options[:context_path] + "/browse/#{issue_1.key}", :title => issue_1.key, } } ) jira-ruby-2.3.0/LICENSE0000644000004100000410000000206214370022124014450 0ustar www-datawww-dataCopyright (c) 2015-2016 SUMO Heavy Industries, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.jira-ruby-2.3.0/Rakefile0000644000004100000410000000131114370022124015104 0ustar www-datawww-datarequire 'bundler/gem_tasks' require 'rubygems' require 'rspec/core/rake_task' require 'rdoc/task' Dir.glob('lib/tasks/*.rake').each { |r| import r } task default: [:test] task test: %i[prepare spec] desc 'Prepare and run rspec tests' task :prepare do rsa_key = File.expand_path('rsakey.pem') unless File.exist?(rsa_key) Rake::Task['jira:generate_public_cert'].invoke end end desc 'Run RSpec tests' # RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec, [] => [:prepare]) do |task| task.rspec_opts = ['--color', '--format', 'doc'] end Rake::RDocTask.new(:doc) do |rd| rd.main = 'README.rdoc' rd.rdoc_dir = 'doc' rd.rdoc_files.include('README.rdoc', 'lib/**/*.rb') end jira-ruby-2.3.0/lib/0000755000004100000410000000000014370022124014211 5ustar www-datawww-datajira-ruby-2.3.0/lib/jira/0000755000004100000410000000000014370022124015136 5ustar www-datawww-datajira-ruby-2.3.0/lib/jira/version.rb0000644000004100000410000000005314370022124017146 0ustar www-datawww-datamodule JIRA VERSION = '2.3.0'.freeze end jira-ruby-2.3.0/lib/jira/http_client.rb0000644000004100000410000000634014370022124020003 0ustar www-datawww-datarequire 'json' require 'net/https' require 'cgi/cookie' require 'uri' module JIRA class HttpClient < RequestClient DEFAULT_OPTIONS = { username: nil, password: nil }.freeze attr_reader :options def initialize(options) @options = DEFAULT_OPTIONS.merge(options) @cookies = {} end def make_cookie_auth_request body = { username: @options[:username].to_s, password: @options[:password].to_s }.to_json @options.delete(:username) @options.delete(:password) make_request(:post, @options[:context_path] + '/rest/auth/1/session', body, 'Content-Type' => 'application/json') end def make_request(http_method, url, body = '', headers = {}) # When a proxy is enabled, Net::HTTP expects that the request path omits the domain name path = request_path(url) request = Net::HTTP.const_get(http_method.to_s.capitalize).new(path, headers) request.body = body unless body.nil? execute_request(request) end def make_multipart_request(url, body, headers = {}) path = request_path(url) request = Net::HTTP::Post::Multipart.new(path, body, headers) execute_request(request) end def basic_auth_http_conn http_conn(uri) end def http_conn(uri) if @options[:proxy_address] http_class = Net::HTTP::Proxy(@options[:proxy_address], @options[:proxy_port] || 80, @options[:proxy_username], @options[:proxy_password]) else http_class = Net::HTTP end http_conn = http_class.new(uri.host, uri.port) http_conn.use_ssl = @options[:use_ssl] if @options[:use_client_cert] http_conn.cert = @options[:ssl_client_cert] http_conn.key = @options[:ssl_client_key] end http_conn.verify_mode = @options[:ssl_verify_mode] http_conn.ssl_version = @options[:ssl_version] if @options[:ssl_version] http_conn.read_timeout = @options[:read_timeout] http_conn.ca_file = @options[:ca_file] if @options[:ca_file] http_conn end def uri URI.parse(@options[:site]) end def authenticated? @authenticated end private def execute_request(request) add_cookies(request) if options[:use_cookies] request.basic_auth(@options[:username], @options[:password]) if @options[:username] && @options[:password] response = basic_auth_http_conn.request(request) @authenticated = response.is_a? Net::HTTPOK store_cookies(response) if options[:use_cookies] response end def request_path(url) parsed_uri = URI(url) return url unless parsed_uri.is_a?(URI::HTTP) parsed_uri.request_uri end def store_cookies(response) cookies = response.get_fields('set-cookie') if cookies cookies.each do |cookie| data = CGI::Cookie.parse(cookie) data.delete('Path') @cookies.merge!(data) end end end def add_cookies(request) cookie_array = @cookies.values.map { |cookie| "#{cookie.name}=#{cookie.value[0]}" } cookie_array += Array(@options[:additional_cookies]) if @options.key?(:additional_cookies) request.add_field('Cookie', cookie_array.join('; ')) if cookie_array.any? request end end end jira-ruby-2.3.0/lib/jira/request_client.rb0000644000004100000410000000134314370022124020512 0ustar www-datawww-datarequire 'oauth' require 'json' require 'net/https' module JIRA class RequestClient # Returns the response if the request was successful (HTTP::2xx) and # raises a JIRA::HTTPError if it was not successful, with the response # attached. def request(*args) response = make_request(*args) raise HTTPError, response unless response.is_a?(Net::HTTPSuccess) response end def request_multipart(*args) response = make_multipart_request(*args) raise HTTPError, response unless response.is_a?(Net::HTTPSuccess) response end def make_request(*args) raise NotImplementedError end def make_multipart_request(*args) raise NotImplementedError end end endjira-ruby-2.3.0/lib/jira/railtie.rb0000644000004100000410000000022714370022124017115 0ustar www-datawww-datarequire 'jira-ruby' require 'rails' module JIRA class Railtie < Rails::Railtie rake_tasks do load 'tasks/generate.rake' end end end jira-ruby-2.3.0/lib/jira/client.rb0000644000004100000410000002160314370022124016743 0ustar www-datawww-datarequire 'json' require 'forwardable' require 'ostruct' module JIRA # This class is the main access point for all JIRA::Resource instances. # # The client must be initialized with an options hash containing # configuration options. The available options are: # # :site => 'http://localhost:2990', # :context_path => '/jira', # :signature_method => 'RSA-SHA1', # :request_token_path => "/plugins/servlet/oauth/request-token", # :authorize_path => "/plugins/servlet/oauth/authorize", # :access_token_path => "/plugins/servlet/oauth/access-token", # :private_key => nil, # :private_key_file => "rsakey.pem", # :rest_base_path => "/rest/api/2", # :consumer_key => nil, # :consumer_secret => nil, # :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER, # :ssl_version => nil, # :use_ssl => true, # :username => nil, # :password => nil, # :auth_type => :oauth, # :proxy_address => nil, # :proxy_port => nil, # :proxy_username => nil, # :proxy_password => nil, # :use_cookies => nil, # :additional_cookies => nil, # :default_headers => {}, # :use_client_cert => false, # :read_timeout => nil, # :http_debug => false, # :shared_secret => nil, # :cert_path => nil, # :key_path => nil, # :ssl_client_cert => nil, # :ssl_client_key => nil # :ca_file => nil # # See the JIRA::Base class methods for all of the available methods on these accessor # objects. class Client extend Forwardable # The OAuth::Consumer instance returned by the OauthClient # # The authenticated client instance returned by the respective client type # (Oauth, Basic) attr_accessor :consumer, :request_client, :http_debug, :cache # The configuration options for this client instance attr_reader :options def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token, :authenticated? DEFINED_OPTIONS = [ :site, :context_path, :signature_method, :request_token_path, :authorize_path, :access_token_path, :private_key, :private_key_file, :rest_base_path, :consumer_key, :consumer_secret, :ssl_verify_mode, :ssl_version, :use_ssl, :username, :password, :auth_type, :proxy_address, :proxy_port, :proxy_username, :proxy_password, :use_cookies, :additional_cookies, :default_headers, :use_client_cert, :read_timeout, :http_debug, :issuer, :base_url, :shared_secret, :cert_path, :key_path, :ssl_client_cert, :ssl_client_key ].freeze DEFAULT_OPTIONS = { site: 'http://localhost:2990', context_path: '/jira', rest_base_path: '/rest/api/2', ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER, use_ssl: true, use_client_cert: false, auth_type: :oauth, http_debug: false, default_headers: {} }.freeze def initialize(options = {}) options = DEFAULT_OPTIONS.merge(options) @options = options @options[:rest_base_path] = @options[:context_path] + @options[:rest_base_path] unknown_options = options.keys.reject { |o| DEFINED_OPTIONS.include?(o) } raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty? if options[:use_client_cert] @options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(@options[:cert_path])) if @options[:cert_path] @options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path] raise ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true' unless @options[:ssl_client_cert] raise ArgumentError, 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true' unless @options[:ssl_client_key] end case options[:auth_type] when :oauth, :oauth_2legged @request_client = OauthClient.new(@options) @consumer = @request_client.consumer when :jwt @request_client = JwtClient.new(@options) when :basic @request_client = HttpClient.new(@options) when :cookie raise ArgumentError, 'Options: :use_cookies must be true for :cookie authorization type' if @options.key?(:use_cookies) && !@options[:use_cookies] @options[:use_cookies] = true @request_client = HttpClient.new(@options) @request_client.make_cookie_auth_request @options.delete(:username) @options.delete(:password) else raise ArgumentError, 'Options: ":auth_type" must be ":oauth",":oauth_2legged", ":cookie" or ":basic"' end @http_debug = @options[:http_debug] @options.freeze @cache = OpenStruct.new end def Project # :nodoc: JIRA::Resource::ProjectFactory.new(self) end def Issue # :nodoc: JIRA::Resource::IssueFactory.new(self) end def Filter # :nodoc: JIRA::Resource::FilterFactory.new(self) end def Component # :nodoc: JIRA::Resource::ComponentFactory.new(self) end def User # :nodoc: JIRA::Resource::UserFactory.new(self) end def Issuetype # :nodoc: JIRA::Resource::IssuetypeFactory.new(self) end def Priority # :nodoc: JIRA::Resource::PriorityFactory.new(self) end def Status # :nodoc: JIRA::Resource::StatusFactory.new(self) end def Resolution # :nodoc: JIRA::Resource::ResolutionFactory.new(self) end def Comment # :nodoc: JIRA::Resource::CommentFactory.new(self) end def Attachment # :nodoc: JIRA::Resource::AttachmentFactory.new(self) end def Worklog # :nodoc: JIRA::Resource::WorklogFactory.new(self) end def Version # :nodoc: JIRA::Resource::VersionFactory.new(self) end def Transition # :nodoc: JIRA::Resource::TransitionFactory.new(self) end def Field # :nodoc: JIRA::Resource::FieldFactory.new(self) end def Board JIRA::Resource::BoardFactory.new(self) end def BoardConfiguration JIRA::Resource::BoardConfigurationFactory.new(self) end def RapidView JIRA::Resource::RapidViewFactory.new(self) end def Sprint JIRA::Resource::SprintFactory.new(self) end def SprintReport JIRA::Resource::SprintReportFactory.new(self) end def ServerInfo JIRA::Resource::ServerInfoFactory.new(self) end def Createmeta JIRA::Resource::CreatemetaFactory.new(self) end def ApplicationLink JIRA::Resource::ApplicationLinkFactory.new(self) end def Watcher JIRA::Resource::WatcherFactory.new(self) end def Webhook JIRA::Resource::WebhookFactory.new(self) end def Issuelink JIRA::Resource::IssuelinkFactory.new(self) end def Issuelinktype JIRA::Resource::IssuelinktypeFactory.new(self) end def IssuePickerSuggestions JIRA::Resource::IssuePickerSuggestionsFactory.new(self) end def Remotelink JIRA::Resource::RemotelinkFactory.new(self) end def Agile JIRA::Resource::AgileFactory.new(self) end # HTTP methods without a body def delete(path, headers = {}) request(:delete, path, nil, merge_default_headers(headers)) end def get(path, headers = {}) request(:get, path, nil, merge_default_headers(headers)) end def head(path, headers = {}) request(:head, path, nil, merge_default_headers(headers)) end # HTTP methods with a body def post(path, body = '', headers = {}) headers = { 'Content-Type' => 'application/json' }.merge(headers) request(:post, path, body, merge_default_headers(headers)) end def post_multipart(path, file, headers = {}) puts "post multipart: #{path} - [#{file}]" if @http_debug @request_client.request_multipart(path, file, headers) end def put(path, body = '', headers = {}) headers = { 'Content-Type' => 'application/json' }.merge(headers) request(:put, path, body, merge_default_headers(headers)) end # Sends the specified HTTP request to the REST API through the # appropriate method (oauth, basic). def request(http_method, path, body = '', headers = {}) puts "#{http_method}: #{path} - [#{body}]" if @http_debug @request_client.request(http_method, path, body, headers) end # Stops sensitive client information from being displayed in logs def inspect "#" end protected def merge_default_headers(headers) { 'Accept' => 'application/json' }.merge(@options[:default_headers]).merge(headers) end end end jira-ruby-2.3.0/lib/jira/base.rb0000644000004100000410000004505414370022124016405 0ustar www-datawww-datarequire 'active_support/core_ext/string' require 'active_support/inflector' require 'set' module JIRA # This class provides the basic object <-> REST mapping for all JIRA::Resource subclasses, # i.e. the Create, Retrieve, Update, Delete lifecycle methods. # # == Lifecycle methods # # Note that not all lifecycle # methods are available for all resources, for example some resources cannot be updated # or deleted. # # === Retrieving all resources # # client.Resource.all # # === Retrieving a single resource # # client.Resource.find(id) # # === Creating a resource # # resource = client.Resource.build({'name' => '') # resource.save # # === Updating a resource # # resource = client.Resource.find(id) # resource.save('updated_attribute' => 'new value') # # === Deleting a resource # # resource = client.Resource.find(id) # resource.delete # # == Nested resources # # Some resources are not defined in the top level of the URL namespace # within the JIRA API, but are always nested under the context of another # resource. For example, a JIRA::Resource::Comment always belongs to a # JIRA::Resource::Issue. # # These resources must be indexed and built from an instance of the class # they are nested under: # # issue = client.Issue.find(id) # comments = issue.comments # new_comment = issue.comments.build # class Base QUERY_PARAMS_FOR_SINGLE_FETCH = Set.new %i[expand fields] QUERY_PARAMS_FOR_SEARCH = Set.new %i[expand fields startAt maxResults] # A reference to the JIRA::Client used to initialize this resource. attr_reader :client # Returns true if this instance has been fetched from the server attr_accessor :expanded # Returns true if this instance has been deleted from the server attr_accessor :deleted # The hash of attributes belonging to this instance. An exact # representation of the JSON returned from the JIRA API attr_accessor :attrs alias expanded? expanded alias deleted? deleted def initialize(client, options = {}) @client = client @attrs = options[:attrs] || {} @expanded = options[:expanded] || false @deleted = false # If this class has any belongs_to relationships, a value for # each of them must be passed in to the initializer. self.class.belongs_to_relationships.each do |relation| if options[relation] instance_variable_set("@#{relation}", options[relation]) instance_variable_set("@#{relation}_id", options[relation].key_value) elsif options["#{relation}_id".to_sym] instance_variable_set("@#{relation}_id", options["#{relation}_id".to_sym]) else raise ArgumentError, "Required option #{relation.inspect} missing" unless options[relation] end end end # The class methods are never called directly, they are always # invoked from a BaseFactory subclass instance. def self.all(client, options = {}) response = client.get(collection_path(client)) json = parse_json(response.body) json = json[endpoint_name.pluralize] if collection_attributes_are_nested json.map do |attrs| new(client, { attrs: attrs }.merge(options)) end end # Finds and retrieves a resource with the given ID. def self.find(client, key, options = {}) instance = new(client, options) instance.attrs[key_attribute.to_s] = key instance.fetch(false, query_params_for_single_fetch(options)) instance end # Builds a new instance of the resource with the given attributes. # These attributes will be posted to the JIRA Api if save is called. def self.build(client, attrs) new(client, attrs: attrs) end # Returns the name of this resource for use in URL components. # E.g. # JIRA::Resource::Issue.endpoint_name # # => issue def self.endpoint_name name.split('::').last.downcase end # Returns the full path for a collection of this resource. # E.g. # JIRA::Resource::Issue.collection_path # # => /jira/rest/api/2/issue def self.collection_path(client, prefix = '/') client.options[:rest_base_path] + prefix + endpoint_name end # Returns the singular path for the resource with the given key. # E.g. # JIRA::Resource::Issue.singular_path('123') # # => /jira/rest/api/2/issue/123 # # If a prefix parameter is provided it will be injected between the base # path and the endpoint. # E.g. # JIRA::Resource::Comment.singular_path('456','/issue/123/') # # => /jira/rest/api/2/issue/123/comment/456 def self.singular_path(client, key, prefix = '/') collection_path(client, prefix) + '/' + key end # Returns the attribute name of the attribute used for find. # Defaults to :id unless overridden. def self.key_attribute :id end def self.parse_json(string) # :nodoc: JSON.parse(string) end # Declares that this class contains a singular instance of another resource # within the JSON returned from the JIRA API. # # class Example < JIRA::Base # has_one :child # end # # example = client.Example.find(1) # example.child # Returns a JIRA::Resource::Child # # The following options can be used to override the default behaviour of the # relationship: # # [:attribute_key] The relationship will by default reference a JSON key on the # object with the same name as the relationship. # # has_one :child # => {"id":"123",{"child":{"id":"456"}}} # # Use this option if the key in the JSON is named differently. # # # Respond to resource.child, but return the value of resource.attrs['kid'] # has_one :child, :attribute_key => 'kid' # => {"id":"123",{"kid":{"id":"456"}}} # # [:class] The class of the child instance will be inferred from the name of the # relationship. E.g. has_one :child will return a JIRA::Resource::Child. # Use this option to override the inferred class. # # has_one :child, :class => JIRA::Resource::Kid # [:nested_under] In some cases, the JSON return from JIRA is nested deeply for particular # relationships. This option allows the nesting to be specified. # # # Specify a single depth of nesting. # has_one :child, :nested_under => 'foo' # # => Looks for {"foo":{"child":{}}} # # Specify deeply nested JSON # has_one :child, :nested_under => ['foo', 'bar', 'baz'] # # => Looks for {"foo":{"bar":{"baz":{"child":{}}}}} def self.has_one(resource, options = {}) attribute_key = options[:attribute_key] || resource.to_s child_class = options[:class] || ('JIRA::Resource::' + resource.to_s.classify).constantize define_method(resource) do attribute = maybe_nested_attribute(attribute_key, options[:nested_under]) return nil unless attribute child_class.new(client, attrs: attribute) end end # Declares that this class contains a collection of another resource # within the JSON returned from the JIRA API. # # class Example < JIRA::Base # has_many :children # end # # example = client.Example.find(1) # example.children # Returns an instance of Jira::Resource::HasManyProxy, # # which behaves exactly like an array of # # Jira::Resource::Child # # The following options can be used to override the default behaviour of the # relationship: # # [:attribute_key] The relationship will by default reference a JSON key on the # object with the same name as the relationship. # # has_many :children # => {"id":"123",{"children":[{"id":"456"},{"id":"789"}]}} # # Use this option if the key in the JSON is named differently. # # # Respond to resource.children, but return the value of resource.attrs['kids'] # has_many :children, :attribute_key => 'kids' # => {"id":"123",{"kids":[{"id":"456"},{"id":"789"}]}} # # [:class] The class of the child instance will be inferred from the name of the # relationship. E.g. has_many :children will return an instance # of JIRA::Resource::HasManyProxy containing the collection of # JIRA::Resource::Child. # Use this option to override the inferred class. # # has_many :children, :class => JIRA::Resource::Kid # [:nested_under] In some cases, the JSON return from JIRA is nested deeply for particular # relationships. This option allows the nesting to be specified. # # # Specify a single depth of nesting. # has_many :children, :nested_under => 'foo' # # => Looks for {"foo":{"children":{}}} # # Specify deeply nested JSON # has_many :children, :nested_under => ['foo', 'bar', 'baz'] # # => Looks for {"foo":{"bar":{"baz":{"children":{}}}}} def self.has_many(collection, options = {}) attribute_key = options[:attribute_key] || collection.to_s child_class = options[:class] || ('JIRA::Resource::' + collection.to_s.classify).constantize self_class_basename = name.split('::').last.downcase.to_sym define_method(collection) do child_class_options = { self_class_basename => self } attribute = maybe_nested_attribute(attribute_key, options[:nested_under]) || [] collection = attribute.map do |child_attributes| child_class.new(client, child_class_options.merge(attrs: child_attributes)) end HasManyProxy.new(self, child_class, collection) end end def self.belongs_to_relationships @belongs_to_relationships ||= [] end def self.belongs_to(resource) belongs_to_relationships.push(resource) attr_reader resource attr_reader "#{resource}_id" end def self.collection_attributes_are_nested @collection_attributes_are_nested ||= false end def self.nested_collections(value) @collection_attributes_are_nested = value end def id attrs['id'] end # Returns a symbol for the given instance, for example # JIRA::Resource::Issue returns :issue def to_sym self.class.endpoint_name.to_sym end # Checks if method_name is set in the attributes hash # and returns true when found, otherwise proxies the # call to the superclass. def respond_to?(method_name, _include_all = false) if attrs.key?(method_name.to_s) true else super(method_name) end end # Overrides method_missing to check the attribute hash # for resources matching method_name and proxies the call # to the superclass if no match is found. def method_missing(method_name, *_args) if attrs.key?(method_name.to_s) attrs[method_name.to_s] else super(method_name) end end # Each resource has a unique key attribute, this method returns the value # of that key for this instance. def key_value @attrs[self.class.key_attribute.to_s] end def collection_path(prefix = '/') # Just proxy this to the class method self.class.collection_path(client, prefix) end # This returns the URL path component that is specific to this instance, # for example for Issue id 123 it returns '/issue/123'. For an unsaved # issue it returns '/issue' def path_component path_component = "/#{self.class.endpoint_name}" path_component += '/' + key_value if key_value path_component end # Fetches the attributes for the specified resource from JIRA unless # the resource is already expanded and the optional force reload flag # is not set def fetch(reload = false, query_params = {}) return if expanded? && !reload response = client.get(url_with_query_params(url, query_params)) set_attrs_from_response(response) @expanded = true end # Saves the specified resource attributes by sending either a POST or PUT # request to JIRA, depending on resource.new_record? # # Accepts an attributes hash of the values to be saved. Will throw a # JIRA::HTTPError if the request fails (response is not HTTP 2xx). def save!(attrs, path = nil) path ||= new_record? ? url : patched_url http_method = new_record? ? :post : :put response = client.send(http_method, path, attrs.to_json) set_attrs(attrs, false) set_attrs_from_response(response) @expanded = false true end # Saves the specified resource attributes by sending either a POST or PUT # request to JIRA, depending on resource.new_record? # # Accepts an attributes hash of the values to be saved. Will return false # if the request fails. def save(attrs, path = url) begin save_status = save!(attrs, path) rescue JIRA::HTTPError => exception begin set_attrs_from_response(exception.response) # Merge error status generated by JIRA REST API rescue JSON::ParserError => parse_exception set_attrs('exception' => { 'class' => exception.response.class.name, 'code' => exception.response.code, 'message' => exception.response.message }) end # raise exception save_status = false end save_status end # Sets the attributes hash from a HTTPResponse object from JIRA if it is # not nil or is not a json response. def set_attrs_from_response(response) unless response.body.nil? || (response.body.length < 2) json = self.class.parse_json(response.body) set_attrs(json) end end # Set the current attributes from a hash. If clobber is true, any existing # hash values will be clobbered by the new hash, otherwise the hash will # be deeply merged into attrs. The target paramater is for internal use only # and should not be used. def set_attrs(hash, clobber = true, target = nil) target ||= @attrs if clobber target.merge!(hash) hash else hash.each do |k, v| if v.is_a?(Hash) set_attrs(v, clobber, target[k]) else target[k] = v end end end end # Sends a delete request to the JIRA Api and sets the deleted instance # variable on the object to true. def delete client.delete(url) @deleted = true end def has_errors? respond_to?('errors') end def url prefix = '/' unless self.class.belongs_to_relationships.empty? prefix = self.class.belongs_to_relationships.inject(prefix) do |prefix_so_far, relationship| prefix_so_far.to_s + relationship.to_s + '/' + send("#{relationship}_id").to_s + '/' end end if @attrs['self'] the_url = @attrs['self'] the_url = the_url.sub(@client.options[:site].chomp('/'), '') if @client.options[:site] the_url elsif key_value self.class.singular_path(client, key_value.to_s, prefix) else self.class.collection_path(client, prefix) end end # This method fixes issue that there is no / prefix in url. It is happened when we call for instance # Looks like this issue is actual only in case if you use atlassian sdk your app path is not root (like /jira in example below) # issue.save() for existing resource. # As a result we got error 400 from JIRA API: # [07/Jun/2015:15:32:19 +0400] "PUT jira/rest/api/2/issue/10111 HTTP/1.1" 400 - # After applying this fix we have normal response: # [07/Jun/2015:15:17:18 +0400] "PUT /jira/rest/api/2/issue/10111 HTTP/1.1" 204 - def patched_url result = url return result if result.start_with?('/', 'http') "/#{result}" end def to_s "#<#{self.class.name}:#{object_id} @attrs=#{@attrs.inspect}>" end # Returns a JSON representation of the current attributes hash. def to_json(options = {}) attrs.to_json(options) end # Determines if the resource is newly created by checking whether its # key_value is set. If it is nil, the record is new and the method # will return true. def new_record? key_value.nil? end protected # This allows conditional lookup of possibly nested attributes. Example usage: # # maybe_nested_attribute('foo') # => @attrs['foo'] # maybe_nested_attribute('foo', 'bar') # => @attrs['bar']['foo'] # maybe_nested_attribute('foo', ['bar', 'baz']) # => @attrs['bar']['baz']['foo'] # def maybe_nested_attribute(attribute_name, nested_under = nil) self.class.maybe_nested_attribute(@attrs, attribute_name, nested_under) end def self.maybe_nested_attribute(attributes, attribute_name, nested_under = nil) return attributes[attribute_name] if nested_under.nil? if nested_under.instance_of? Array final = nested_under.inject(attributes) do |parent, key| break if parent.nil? parent[key] end return nil if final.nil? final[attribute_name] else return attributes[nested_under][attribute_name] end end def url_with_query_params(url, query_params) self.class.url_with_query_params(url, query_params) end def self.url_with_query_params(url, query_params) if !query_params.empty? "#{url}?#{hash_to_query_string query_params}" else url end end def hash_to_query_string(query_params) self.class.hash_to_query_string(query_params) end def self.hash_to_query_string(query_params) query_params.map do |k, v| CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s) end.join('&') end def self.query_params_for_single_fetch(options) Hash[options.select do |k, _v| QUERY_PARAMS_FOR_SINGLE_FETCH.include? k end] end def self.query_params_for_search(options) Hash[options.select do |k, _v| QUERY_PARAMS_FOR_SEARCH.include? k end] end end end jira-ruby-2.3.0/lib/jira/http_error.rb0000644000004100000410000000050114370022124017647 0ustar www-datawww-datarequire 'forwardable' module JIRA class HTTPError < StandardError extend Forwardable def_instance_delegators :@response, :code attr_reader :response, :message def initialize(response) @response = response @message = response.try(:message).presence || response.try(:body) end end end jira-ruby-2.3.0/lib/jira/has_many_proxy.rb0000644000004100000410000000263614370022124020532 0ustar www-datawww-data# # Whenever a collection from a has_many relationship is accessed, an instance # of this class is returned. This instance wraps the Array of instances in # the collection with an extra build method, which allows new instances to be # built on the collection with the correct properties. # # In practice, instances of this class behave exactly like an Array. # class JIRA::HasManyProxy attr_reader :target_class, :parent attr_accessor :collection def initialize(parent, target_class, collection = []) @parent = parent @target_class = target_class @collection = collection end # Builds an instance of this class with the correct parent. # For example, issue.comments.build(attrs) will initialize a # comment as follows: # # JIRA::Resource::Comment.new(issue.client, # :attrs => attrs, # :issue => issue) def build(attrs = {}) resource = target_class.new(parent.client, :attrs => attrs, parent.to_sym => parent) collection << resource resource end # Forces an HTTP request to fetch all instances of the target class that # are associated with the parent def all target_class.all(parent.client, parent.to_sym => parent) end # Delegate any missing methods to the collection that this proxy wraps def method_missing(method_name, *args, &block) collection.send(method_name, *args, &block) end end jira-ruby-2.3.0/lib/jira/resource/0000755000004100000410000000000014370022124016765 5ustar www-datawww-datajira-ruby-2.3.0/lib/jira/resource/comment.rb0000644000004100000410000000030614370022124020753 0ustar www-datawww-datamodule JIRA module Resource class CommentFactory < JIRA::BaseFactory # :nodoc: end class Comment < JIRA::Base belongs_to :issue nested_collections true end end end jira-ruby-2.3.0/lib/jira/resource/version.rb0000644000004100000410000000021414370022124020774 0ustar www-datawww-datamodule JIRA module Resource class VersionFactory < JIRA::BaseFactory # :nodoc: end class Version < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/component.rb0000644000004100000410000000022014370022124021306 0ustar www-datawww-datamodule JIRA module Resource class ComponentFactory < JIRA::BaseFactory # :nodoc: end class Component < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/user.rb0000644000004100000410000000156214370022124020274 0ustar www-datawww-datamodule JIRA module Resource class UserFactory < JIRA::BaseFactory # :nodoc: def myself instance = build response = client.get("#{client.options[:rest_base_path]}/myself") instance.set_attrs_from_response(response) instance end end class User < JIRA::Base MAX_RESULTS = 1000 def self.singular_path(client, key, prefix = '/') collection_path(client, prefix) + '?username=' + key end # Cannot retrieve more than 1,000 users through the api, please see: https://jira.atlassian.com/browse/JRASERVER-65089 def self.all(client) response = client.get("/rest/api/2/users/search?username=_&maxResults=#{MAX_RESULTS}") all_users = JSON.parse(response.body) all_users.flatten.uniq.map do |user| client.User.build(user) end end end end end jira-ruby-2.3.0/lib/jira/resource/createmeta.rb0000644000004100000410000000267314370022124021434 0ustar www-datawww-datamodule JIRA module Resource class CreatemetaFactory < JIRA::BaseFactory # :nodoc: end class Createmeta < JIRA::Base def self.endpoint_name '/issue/createmeta' end def self.all(client, params = {}) if params.key?(:projectKeys) values = Array(params[:projectKeys]).map { |i| (i.is_a?(JIRA::Resource::Project) ? i.key : i) } params[:projectKeys] = values.join(',') end if params.key?(:projectIds) values = Array(params[:projectIds]).map { |i| (i.is_a?(JIRA::Resource::Project) ? i.id : i) } params[:projectIds] = values.join(',') end if params.key?(:issuetypeNames) values = Array(params[:issuetypeNames]).map { |i| (i.is_a?(JIRA::Resource::Issuetype) ? i.name : i) } params[:issuetypeNames] = values.join(',') end if params.key?(:issuetypeIds) values = Array(params[:issuetypeIds]).map { |i| (i.is_a?(JIRA::Resource::Issuetype) ? i.id : i) } params[:issuetypeIds] = values.join(',') end create_meta_url = client.options[:rest_base_path] + endpoint_name params = hash_to_query_string(params) response = params.empty? ? client.get(create_meta_url.to_s) : client.get("#{create_meta_url}?#{params}") json = parse_json(response.body) json['projects'].map do |attrs| new(client, attrs: attrs) end end end end end jira-ruby-2.3.0/lib/jira/resource/attachment.rb0000644000004100000410000000241714370022124021446 0ustar www-datawww-datarequire 'net/http/post/multipart' module JIRA module Resource class AttachmentFactory < JIRA::BaseFactory # :nodoc: delegate_to_target_class :meta end class Attachment < JIRA::Base belongs_to :issue has_one :author, class: JIRA::Resource::User def self.endpoint_name 'attachments' end def self.meta(client) response = client.get(client.options[:rest_base_path] + '/attachment/meta') parse_json(response.body) end def save!(attrs, path = url) file = attrs['file'] || attrs[:file] # Keep supporting 'file' parameter as a string for backward compatibility mime_type = attrs[:mimeType] || 'application/binary' headers = { 'X-Atlassian-Token' => 'nocheck' } data = { 'file' => UploadIO.new(file, mime_type, file) } response = client.post_multipart(path, data , headers) set_attributes(attrs, response) @expanded = false true end private def set_attributes(attributes, response) set_attrs(attributes, false) return if response.body.nil? || response.body.length < 2 json = self.class.parse_json(response.body) attachment = json[0] set_attrs(attachment) end end end end jira-ruby-2.3.0/lib/jira/resource/issuetype.rb0000644000004100000410000000022014370022124021336 0ustar www-datawww-datamodule JIRA module Resource class IssuetypeFactory < JIRA::BaseFactory # :nodoc: end class Issuetype < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/sprint_report.rb0000644000004100000410000000022614370022124022224 0ustar www-datawww-datamodule JIRA module Resource class SprintReportFactory < JIRA::BaseFactory # :nodoc: end class SprintReport < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/priority.rb0000644000004100000410000000021614370022124021172 0ustar www-datawww-datamodule JIRA module Resource class PriorityFactory < JIRA::BaseFactory # :nodoc: end class Priority < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/issuelinktype.rb0000644000004100000410000000037114370022124022223 0ustar www-datawww-datamodule JIRA module Resource class IssuelinktypeFactory < JIRA::BaseFactory # :nodoc: end class Issuelinktype < JIRA::Base nested_collections true def self.endpoint_name 'issueLinkType' end end end end jira-ruby-2.3.0/lib/jira/resource/status.rb0000644000004100000410000000021214370022124020630 0ustar www-datawww-datamodule JIRA module Resource class StatusFactory < JIRA::BaseFactory # :nodoc: end class Status < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/issuelink.rb0000644000004100000410000000102214370022124021313 0ustar www-datawww-datamodule JIRA module Resource class IssuelinkFactory < JIRA::BaseFactory # :nodoc: end # Because of circular dependency Issue->IssueLink->Issue # we have to declare JIRA::Resource::Issue class. class Issue < JIRA::Base; end class Issuelink < JIRA::Base has_one :type, class: JIRA::Resource::Issuelinktype has_one :inwardIssue, class: JIRA::Resource::Issue has_one :outwardIssue, class: JIRA::Resource::Issue def self.endpoint_name 'issueLink' end end end end jira-ruby-2.3.0/lib/jira/resource/webhook.rb0000644000004100000410000000144414370022124020753 0ustar www-datawww-datamodule JIRA module Resource class WebhookFactory < JIRA::BaseFactory # :nodoc: end class Webhook < JIRA::Base REST_BASE_PATH = '/rest/webhooks/1.0'.freeze def self.endpoint_name 'webhook' end def self.full_url(client) client.options[:context_path] + REST_BASE_PATH end def self.collection_path(client, prefix = '/') full_url(client) + prefix + endpoint_name end def self.all(client, options = {}) response = client.get(collection_path(client)) json = parse_json(response.body) json.map do |attrs| new(client, { attrs: attrs }.merge(options)) end end # def self.save(options={}) # end # def self.delete(options={}) # end end end end jira-ruby-2.3.0/lib/jira/resource/filter.rb0000644000004100000410000000045114370022124020577 0ustar www-datawww-datamodule JIRA module Resource class FilterFactory < JIRA::BaseFactory # :nodoc: end class Filter < JIRA::Base has_one :owner, class: JIRA::Resource::User # Returns all the issues for this filter def issues Issue.jql(client, jql) end end end end jira-ruby-2.3.0/lib/jira/resource/watcher.rb0000644000004100000410000000143314370022124020750 0ustar www-datawww-datamodule JIRA module Resource class WatcherFactory < JIRA::BaseFactory # :nodoc: end class Watcher < JIRA::Base belongs_to :issue nested_collections true def self.endpoint_name 'watchers' end def self.all(client, options = {}) issue = options[:issue] raise ArgumentError, 'parent issue is required' unless issue path = "#{issue.self}/#{endpoint_name}" response = client.get(path) json = parse_json(response.body) json['watchers'].map do |watcher| issue.watchers.build(watcher) end end def save!(user_id, path = nil) path ||= new_record? ? url : patched_url response = client.post(path, user_id.to_json) true end end end end jira-ruby-2.3.0/lib/jira/resource/agile.rb0000644000004100000410000000536614370022124020405 0ustar www-datawww-datarequire 'cgi' module JIRA module Resource class AgileFactory < JIRA::BaseFactory # :nodoc: end class Agile < JIRA::Base # @param client [JIRA::Client] # @param options [Hash] # @return [Hash] def self.all(client, options = {}) opts = options.empty? ? '' : "?#{hash_to_query_string(options)}" response = client.get(path_base(client) + "/board#{opts}") parse_json(response.body) end def self.get_backlog_issues(client, board_id, options = {}) options[:maxResults] ||= 100 response = client.get(path_base(client) + "/board/#{board_id}/backlog?#{hash_to_query_string(options)}") parse_json(response.body) end def self.get_board_issues(client, board_id, options = {}) response = client.get(path_base(client) + "/board/#{board_id}/issue?#{hash_to_query_string(options)}") json = parse_json(response.body) # To get Issue objects with the same structure as for Issue.all return {} if json['issues'].size.zero? issue_ids = json['issues'].map do |issue| issue['id'] end client.Issue.jql("id IN(#{issue_ids.join(', ')})") end def self.get_sprints(client, board_id, options = {}) options[:maxResults] ||= 100 response = client.get(path_base(client) + "/board/#{board_id}/sprint?#{hash_to_query_string(options)}") parse_json(response.body) end def self.get_sprint_issues(client, sprint_id, options = {}) options[:maxResults] ||= 100 response = client.get(path_base(client) + "/sprint/#{sprint_id}/issue?#{hash_to_query_string(options)}") parse_json(response.body) end def self.get_projects_full(client, board_id, _options = {}) response = client.get(path_base(client) + "/board/#{board_id}/project/full") parse_json(response.body) end def self.get_projects(client, board_id, options = {}) options[:maxResults] ||= 100 create_meta_url = path_base(client) + "/board/#{board_id}/project" params = hash_to_query_string(options) response = client.get("#{create_meta_url}?#{params}") parse_json(response.body) end # def self.find(client, key, options = {}) # options[:maxResults] ||= 100 # fields = options[:fields].join(',') unless options[:fields].nil? # response = client.get("/rest/api/latest/search?jql=sprint=#{key}&fields=#{fields}&maxResults=#{options[:maxResults]}") # parse_json(response.body) # end private def self.path_base(client) client.options[:context_path] + '/rest/agile/1.0' end def path_base(client) self.class.path_base(client) end end end end jira-ruby-2.3.0/lib/jira/resource/issue_picker_suggestions.rb0000644000004100000410000000217614370022124024437 0ustar www-datawww-datamodule JIRA module Resource class IssuePickerSuggestionsFactory < JIRA::BaseFactory # :nodoc: end class IssuePickerSuggestions < JIRA::Base has_many :sections, class: JIRA::Resource::IssuePickerSuggestionsIssue def self.all(client, query = '', options = { current_jql: nil, current_issue_key: nil, current_project_id: nil, show_sub_tasks: nil, show_sub_tasks_parent: nil }) url = client.options[:rest_base_path] + "/issue/picker?query=#{CGI.escape(query)}" url << "¤tJQL=#{CGI.escape(options[:current_jql])}" if options[:current_jql] url << "¤tIssueKey=#{CGI.escape(options[:current_issue_key])}" if options[:current_issue_key] url << "¤tProjectId=#{CGI.escape(options[:current_project_id])}" if options[:current_project_id] url << "&showSubTasks=#{options[:show_sub_tasks]}" if options[:show_sub_tasks] url << "&showSubTaskParent=#{options[:show_sub_task_parent]}" if options[:show_sub_task_parent] response = client.get(url) json = parse_json(response.body) client.IssuePickerSuggestions.build(json) end end end end jira-ruby-2.3.0/lib/jira/resource/board.rb0000644000004100000410000000555014370022124020406 0ustar www-datawww-datarequire 'cgi' module JIRA module Resource class BoardFactory < JIRA::BaseFactory # :nodoc: end class Board < JIRA::Base def self.all(client) path = path_base(client) + '/board' response = client.get(path) json = parse_json(response.body) results = json['values'] until json['isLast'] params = { 'startAt' => (json['startAt'] + json['maxResults']).to_s } response = client.get(url_with_query_params(path, params)) json = parse_json(response.body) results += json['values'] end results.map do |board| client.Board.build(board) end end def self.find(client, key, _options = {}) response = client.get(path_base(client) + "/board/#{key}") json = parse_json(response.body) client.Board.build(json) end def issues(params = {}) path = path_base(client) + "/board/#{id}/issue" response = client.get(url_with_query_params(path, params)) json = self.class.parse_json(response.body) results = json['issues'] while (json['startAt'] + json['maxResults']) < json['total'] params['startAt'] = (json['startAt'] + json['maxResults']) response = client.get(url_with_query_params(path, params)) json = self.class.parse_json(response.body) results += json['issues'] end results.map { |issue| client.Issue.build(issue) } end def configuration(params = {}) path = path_base(client) + "/board/#{id}/configuration" response = client.get(url_with_query_params(path, params)) json = self.class.parse_json(response.body) client.BoardConfiguration.build(json) end # options # - state ~ future, active, closed, you can define multiple states separated by commas, e.g. state=active,closed # - maxResults ~ default: 50 (JIRA API), 1000 (this library) # - startAt ~ base index, starts at 0 def sprints(options = {}) # options.reverse_merge!(DEFAULT_OPTIONS) response = client.get(path_base(client) + "/board/#{id}/sprint?#{options.to_query}") json = self.class.parse_json(response.body) json['values'].map do |sprint| sprint['rapidview_id'] = id client.Sprint.build(sprint) end end def project response = client.get(path_base(client) + "/board/#{id}/project") json = self.class.parse_json(response.body) json['values'][0] end def add_issue_to_backlog(issue) client.post(path_base(client) + '/backlog/issue', { issues: [issue.id] }.to_json) end private def self.path_base(client) client.options[:context_path] + '/rest/agile/1.0' end def path_base(client) self.class.path_base(client) end end end end jira-ruby-2.3.0/lib/jira/resource/worklog.rb0000644000004100000410000000055714370022124021005 0ustar www-datawww-datamodule JIRA module Resource class WorklogFactory < JIRA::BaseFactory # :nodoc: end class Worklog < JIRA::Base has_one :author, class: JIRA::Resource::User has_one :update_author, class: JIRA::Resource::User, attribute_key: 'updateAuthor' belongs_to :issue nested_collections true end end end jira-ruby-2.3.0/lib/jira/resource/remotelink.rb0000644000004100000410000000111714370022124021463 0ustar www-datawww-datamodule JIRA module Resource class RemotelinkFactory < JIRA::BaseFactory # :nodoc: end class Remotelink < JIRA::Base belongs_to :issue def self.endpoint_name 'remotelink' end def self.all(client, options = {}) issue = options[:issue] raise ArgumentError, 'parent issue is required' unless issue path = "#{issue.self}/#{endpoint_name}" response = client.get(path) json = parse_json(response.body) json.map do |link| issue.remotelink.build(link) end end end end end jira-ruby-2.3.0/lib/jira/resource/transition.rb0000644000004100000410000000132614370022124021506 0ustar www-datawww-datamodule JIRA module Resource class TransitionFactory < JIRA::BaseFactory # :nodoc: end class Transition < JIRA::Base has_one :to, class: JIRA::Resource::Status belongs_to :issue nested_collections true def self.endpoint_name 'transitions' end def self.all(client, options = {}) issue = options[:issue] raise ArgumentError, 'parent issue is required' unless issue path = "#{issue.self}/#{endpoint_name}?expand=transitions.fields" response = client.get(path) json = parse_json(response.body) json['transitions'].map do |transition| issue.transitions.build(transition) end end end end end jira-ruby-2.3.0/lib/jira/resource/serverinfo.rb0000644000004100000410000000063114370022124021474 0ustar www-datawww-datamodule JIRA module Resource class ServerInfoFactory < JIRA::BaseFactory # :nodoc: end class ServerInfo < JIRA::Base def self.endpoint_name 'serverInfo' end def self.all(client, options = {}) response = client.get(collection_path(client)) json = parse_json(response.body) new(client, { attrs: json }.merge(options)) end end end end jira-ruby-2.3.0/lib/jira/resource/resolution.rb0000644000004100000410000000022214370022124021511 0ustar www-datawww-datamodule JIRA module Resource class ResolutionFactory < JIRA::BaseFactory # :nodoc: end class Resolution < JIRA::Base; end end end jira-ruby-2.3.0/lib/jira/resource/board_configuration.rb0000644000004100000410000000024514370022124023331 0ustar www-datawww-datamodule JIRA module Resource class BoardConfigurationFactory < JIRA::BaseFactory # :nodoc: end class BoardConfiguration < JIRA::Base end end end jira-ruby-2.3.0/lib/jira/resource/suggested_issue.rb0000644000004100000410000000023514370022124022514 0ustar www-datawww-datamodule JIRA module Resource class SuggestedIssueFactory < JIRA::BaseFactory # :nodoc: end class SuggestedIssue < JIRA::Base end end end jira-ruby-2.3.0/lib/jira/resource/issue_picker_suggestions_issue.rb0000644000004100000410000000036514370022124025645 0ustar www-datawww-datamodule JIRA module Resource class IssuePickerSuggestionsIssueFactory < JIRA::BaseFactory # :nodoc: end class IssuePickerSuggestionsIssue < JIRA::Base has_many :issues, class: JIRA::Resource::SuggestedIssue end end end jira-ruby-2.3.0/lib/jira/resource/rapidview.rb0000644000004100000410000000423314370022124021306 0ustar www-datawww-datarequire 'cgi' module JIRA module Resource class RapidViewFactory < JIRA::BaseFactory # :nodoc: end class RapidView < JIRA::Base def self.all(client) response = client.get(path_base(client) + '/rapidview') json = parse_json(response.body) json['views'].map do |view| client.RapidView.build(view) end end def self.find(client, key, _options = {}) response = client.get(path_base(client) + "/rapidview/#{key}") json = parse_json(response.body) client.RapidView.build(json) end def issues(options = {}) response = client.get(path_base(client) + "/xboard/plan/backlog/data?rapidViewId=#{id}") json = self.class.parse_json(response.body) # To get Issue objects with the same structure as for Issue.all issue_ids = json['issues'].map { |issue| issue['id'] } # First we have to get all IDs of parent and sub tasks jql = "id IN(#{issue_ids.join(', ')})" # Filtering options jql << ' AND sprint IS NOT EMPTY' unless options[:include_backlog_items] parent_issues = client.Issue.jql(jql) subtask_ids = parent_issues.map { |t| t.subtasks.map { |sub| sub['id'] } }.flatten parent_and_sub_ids = parent_issues.map(&:id) + subtask_ids jql = "id IN(#{parent_and_sub_ids.join(', ')})" jql << " and updated >= '#{options.delete(:updated)}'" if options[:updated] client.Issue.jql(jql) end def sprints(options = {}) params = { includeHistoricSprints: options.fetch(:include_historic, false), includeFutureSprints: options.fetch(:include_future, false) } response = client.get(path_base(client) + "/sprintquery/#{id}?#{params.to_query}") json = self.class.parse_json(response.body) json['sprints'].map do |sprint| sprint['rapidview_id'] = id client.Sprint.build(sprint) end end private def self.path_base(client) client.options[:context_path] + '/rest/greenhopper/1.0' end def path_base(client) self.class.path_base(client) end end end end jira-ruby-2.3.0/lib/jira/resource/project.rb0000644000004100000410000000252214370022124020761 0ustar www-datawww-datamodule JIRA module Resource class ProjectFactory < JIRA::BaseFactory # :nodoc: end class Project < JIRA::Base has_one :lead, class: JIRA::Resource::User has_many :components has_many :issuetypes, attribute_key: 'issueTypes' has_many :versions def self.key_attribute :key end # Returns all the issues for this project def issues(options = {}) search_url = client.options[:rest_base_path] + '/search' query_params = { jql: "project=\"#{key}\"" } query_params.update Base.query_params_for_search(options) response = client.get(url_with_query_params(search_url, query_params)) json = self.class.parse_json(response.body) json['issues'].map do |issue| client.Issue.build(issue) end end def users(start_at: nil, max_results: nil) users_url = client.options[:rest_base_path] + '/user/assignable/search' query_params = { project: key_value } query_params['startAt'] = start_at if start_at query_params['maxResults'] = max_results if max_results response = client.get(url_with_query_params(users_url, query_params)) json = self.class.parse_json(response.body) json.map do |jira_user| client.User.build(jira_user) end end end end end jira-ruby-2.3.0/lib/jira/resource/sprint.rb0000644000004100000410000000635314370022124020640 0ustar www-datawww-datamodule JIRA module Resource class SprintFactory < JIRA::BaseFactory # :nodoc: end class Sprint < JIRA::Base def self.find(client, key) response = client.get(agile_path(client, key)) json = parse_json(response.body) client.Sprint.build(json) end # get all issues of sprint def issues(options = {}) jql = 'sprint = ' + id.to_s jql += " and updated >= '#{options[:updated]}'" if options[:updated] Issue.jql(client, jql) end def add_issue(issue) request_body = { issues: [issue.id] }.to_json response = client.post("#{agile_path}/issue", request_body) true end def sprint_report get_sprint_details_attribute('sprint_report') end def start_date get_sprint_details_attribute('start_date') end def end_date get_sprint_details_attribute('end_date') end def complete_date get_sprint_details_attribute('complete_date') end def get_sprint_details_attribute(attribute_name) attribute = instance_variable_get("@#{attribute_name}") return attribute if attribute get_sprint_details instance_variable_get("@#{attribute_name}") end def get_sprint_details search_url = "#{client.options[:site]}#{client.options[:client_path]}/rest/greenhopper/1.0/rapid/charts/sprintreport?rapidViewId=#{rapidview_id}&sprintId=#{id}" begin response = client.get(search_url) rescue StandardError return nil end json = self.class.parse_json(response.body) @start_date = Date.parse(json['sprint']['startDate']) unless json['sprint']['startDate'] == 'None' @end_date = Date.parse(json['sprint']['endDate']) unless json['sprint']['endDate'] == 'None' @completed_date = Date.parse(json['sprint']['completeDate']) unless json['sprint']['completeDate'] == 'None' @sprint_report = client.SprintReport.build(json['contents']) end def rapidview_id return @attrs['rapidview_id'] if @attrs['rapidview_id'] search_url = client.options[:site] + '/secure/GHGoToBoard.jspa?sprintId=' + id.to_s begin response = client.get(search_url) rescue JIRA::HTTPError => error return unless error.response.instance_of? Net::HTTPFound rapid_view_match = /rapidView=(\d+)&/.match(error.response['location']) @attrs['rapidview_id'] = rapid_view_match[1] unless rapid_view_match.nil? end end def save(attrs = {}, _path = nil) attrs = @attrs if attrs.empty? super(attrs, agile_path) end def save!(attrs = {}, _path = nil) attrs = @attrs if attrs.empty? super(attrs, agile_path) end # WORK IN PROGRESS def complete complete_url = "#{client.options[:site]}/rest/greenhopper/1.0/sprint/#{id}/complete" response = client.put(complete_url) self.class.parse_json(response.body) end private def agile_path self.class.agile_path(client, id) end def self.agile_path(client, key) "#{client.options[:context_path]}/rest/agile/1.0/sprint/#{key}" end end end end jira-ruby-2.3.0/lib/jira/resource/issue.rb0000644000004100000410000001206014370022124020441 0ustar www-datawww-datarequire 'cgi' require 'json' module JIRA module Resource class IssueFactory < JIRA::BaseFactory # :nodoc: end class Issue < JIRA::Base has_one :reporter, class: JIRA::Resource::User, nested_under: 'fields' has_one :assignee, class: JIRA::Resource::User, nested_under: 'fields' has_one :project, nested_under: 'fields' has_one :issuetype, nested_under: 'fields' has_one :priority, nested_under: 'fields' has_one :status, nested_under: 'fields' has_many :transitions has_many :components, nested_under: 'fields' has_many :comments, nested_under: %w[fields comment] has_many :attachments, nested_under: 'fields', attribute_key: 'attachment' has_many :versions, nested_under: 'fields' has_many :fixVersions, class: JIRA::Resource::Version, nested_under: 'fields' has_many :worklogs, nested_under: %w[fields worklog] has_one :sprint, class: JIRA::Resource::Sprint, nested_under: 'fields' has_many :closed_sprints, class: JIRA::Resource::Sprint, nested_under: 'fields', attribute_key: 'closedSprints' has_many :issuelinks, nested_under: 'fields' has_many :remotelink, class: JIRA::Resource::Remotelink has_many :watchers, attribute_key: 'watches', nested_under: %w[fields watches] def self.all(client) start_at = 0 max_results = 1000 result = [] loop do url = client.options[:rest_base_path] + "/search?expand=transitions.fields&maxResults=#{max_results}&startAt=#{start_at}" response = client.get(url) json = parse_json(response.body) json['issues'].map do |issue| result.push(client.Issue.build(issue)) end break if json['issues'].empty? start_at += json['issues'].size end result end def self.jql(client, jql, options = { fields: nil, start_at: nil, max_results: nil, expand: nil, validate_query: true }) url = client.options[:rest_base_path] + "/search?jql=#{CGI.escape(jql)}" url << "&fields=#{options[:fields].map { |value| CGI.escape(client.Field.name_to_id(value)) }.join(',')}" if options[:fields] url << "&startAt=#{CGI.escape(options[:start_at].to_s)}" if options[:start_at] url << "&maxResults=#{CGI.escape(options[:max_results].to_s)}" if options[:max_results] url << '&validateQuery=false' if options[:validate_query] === false if options[:expand] options[:expand] = [options[:expand]] if options[:expand].is_a?(String) url << "&expand=#{options[:expand].to_a.map { |value| CGI.escape(value.to_s) }.join(',')}" end response = client.get(url) json = parse_json(response.body) if options[:max_results] && (options[:max_results] == 0) return json['total'] end json['issues'].map do |issue| client.Issue.build(issue) end end # Fetches the attributes for the specified resource from JIRA unless # the resource is already expanded and the optional force reload flag # is not set def fetch(reload = false, query_params = {}) return if expanded? && !reload response = client.get(url_with_query_params(url, query_params)) set_attrs_from_response(response) if @attrs && @attrs['fields'] && @attrs['fields']['worklog'] && (@attrs['fields']['worklog']['total'] > @attrs['fields']['worklog']['maxResults']) worklog_url = client.options[:rest_base_path] + "/#{self.class.endpoint_name}/#{id}/worklog" response = client.get(worklog_url) unless response.body.nil? || (response.body.length < 2) set_attrs({ 'fields' => { 'worklog' => self.class.parse_json(response.body) } }, false) end end @expanded = true end def editmeta editmeta_url = client.options[:rest_base_path] + "/#{self.class.endpoint_name}/#{key}/editmeta" response = client.get(editmeta_url) json = self.class.parse_json(response.body) json['fields'] end def respond_to?(method_name, _include_all = false) if attrs.key?('fields') && [method_name.to_s, client.Field.name_to_id(method_name)].any? { |k| attrs['fields'].key?(k) } true else super(method_name) end end def method_missing(method_name, *args, &block) if attrs.key?('fields') if attrs['fields'].key?(method_name.to_s) attrs['fields'][method_name.to_s] else official_name = client.Field.name_to_id(method_name) if attrs['fields'].key?(official_name) attrs['fields'][official_name] else super(method_name, *args, &block) end end else super(method_name, *args, &block) end end end end end jira-ruby-2.3.0/lib/jira/resource/field.rb0000644000004100000410000000520714370022124020401 0ustar www-datawww-datamodule JIRA module Resource class FieldFactory < JIRA::BaseFactory # :nodoc: delegate_to_target_class :map_fields, :name_to_id, :field_map end class Field < JIRA::Base # translate a custom field description to a method-safe name def self.safe_name(description) description.gsub(/[^a-zA-Z0-9]/, '_') end # safe_name plus disambiguation if it fails it uses the original jira id (customfield_#####) def self.safer_name(description, jira_id) "#{safe_name(description)}_#{jira_id.split('_')[1]}" rescue StandardError jira_id end def self.map_fields(client) field_map = {} field_map_reverse = {} fields = client.Field.all # two pass approach, so that a custom field with the same name # as a system field can't take precedence fields.each do |f| next if f.custom name = safe_name(f.name) field_map_reverse[f.id] = [f.name, name] # capture both the official name, and the mapped name field_map[name] = f.id end fields.each do |f| next unless f.custom name = if field_map.key? f.name renamed = safer_name(f.name, f.id) warn "Duplicate Field name #{f.name} #{f.id} - renaming as #{renamed}" renamed else safe_name(f.name) end field_map_reverse[f.id] = [f.name, name] # capture both the official name, and the mapped name field_map[name] = f.id end client.cache.field_map_reverse = field_map_reverse # not sure where this will be used yet, but sure to be useful client.cache.field_map = field_map end def self.field_map(client) client.cache.field_map end def self.name_to_id(client, field_name) field_name = field_name.to_s return field_name unless client.cache.field_map && client.cache.field_map[field_name] client.cache.field_map[field_name] end def respond_to?(method_name, _include_all = false) if [method_name.to_s, client.Field.name_to_id(method_name)].any? { |k| attrs.key?(k) } true else super(method_name) end end def method_missing(method_name, *args, &block) if attrs.key?(method_name.to_s) attrs[method_name.to_s] else official_name = client.Field.name_to_id(method_name) if attrs.key?(official_name) attrs[official_name] else super(method_name, *args, &block) end end end end end end jira-ruby-2.3.0/lib/jira/resource/applinks.rb0000644000004100000410000000176514370022124021144 0ustar www-datawww-datamodule JIRA module Resource class ApplicationLinkFactory < JIRA::BaseFactory # :nodoc: delegate_to_target_class :manifest end class ApplicationLink < JIRA::Base REST_BASE_PATH = '/rest/applinks/1.0'.freeze def self.endpoint_name 'listApplicationlinks' end def self.full_url(client) client.options[:context_path] + REST_BASE_PATH end def self.collection_path(client, prefix = '/') full_url(client) + prefix + endpoint_name end def self.all(client, options = {}) response = client.get(collection_path(client)) json = parse_json(response.body) json = json['list'] json.map do |attrs| new(client, { attrs: attrs }.merge(options)) end end def self.manifest(client) url = full_url(client) + '/manifest' response = client.get(url) json = parse_json(response.body) JIRA::Base.new(client, attrs: json) end end end end jira-ruby-2.3.0/lib/jira/oauth_client.rb0000644000004100000410000000727714370022124020156 0ustar www-datawww-datarequire 'oauth' require 'json' require 'forwardable' module JIRA class OauthClient < RequestClient DEFAULT_OPTIONS = { signature_method: 'RSA-SHA1', request_token_path: '/plugins/servlet/oauth/request-token', authorize_path: '/plugins/servlet/oauth/authorize', access_token_path: '/plugins/servlet/oauth/access-token', private_key_file: 'rsakey.pem', consumer_key: nil, consumer_secret: nil }.freeze # This exception is thrown when the client is used before the OAuth access token # has been initialized. class UninitializedAccessTokenError < StandardError def message 'init_access_token must be called before using the client' end end extend Forwardable attr_accessor :consumer attr_reader :options def_instance_delegators :@consumer, :key, :secret, :get_request_token def initialize(options) @options = DEFAULT_OPTIONS.merge(options) @consumer = init_oauth_consumer(@options) end def init_oauth_consumer(_options) @options[:request_token_path] = @options[:context_path] + @options[:request_token_path] @options[:authorize_path] = @options[:context_path] + @options[:authorize_path] @options[:access_token_path] = @options[:context_path] + @options[:access_token_path] # proxy_address does not exist in oauth's gem context but proxy does @options[:proxy] = @options[:proxy_address] if @options[:proxy_address] OAuth::Consumer.new(@options[:consumer_key], @options[:consumer_secret], @options) end # Returns the current request token if it is set, else it creates # and sets a new token. def request_token(options = {}, *arguments, &block) @request_token ||= get_request_token(options, *arguments, &block) end # Sets the request token from a given token and secret. def set_request_token(token, secret) @request_token = OAuth::RequestToken.new(@consumer, token, secret) end # Initialises and returns a new access token from the params hash # returned by the OAuth transaction. def init_access_token(params) @access_token = request_token.get_access_token(params) end # Sets the access token from a preexisting token and secret. def set_access_token(token, secret) @access_token = OAuth::AccessToken.new(@consumer, token, secret) @authenticated = true @access_token end # Returns the current access token. Raises an # JIRA::Client::UninitializedAccessTokenError exception if it is not set. def access_token raise UninitializedAccessTokenError unless @access_token @access_token end def make_request(http_method, url, body = '', headers = {}) # When using oauth_2legged we need to add an empty oauth_token parameter to every request. if @options[:auth_type] == :oauth_2legged oauth_params_str = 'oauth_token=' uri = URI.parse(url) uri.query = if uri.query.to_s == '' oauth_params_str else uri.query + '&' + oauth_params_str end url = uri.to_s end case http_method when :delete, :get, :head response = access_token.send http_method, url, headers when :post, :put response = access_token.send http_method, url, body, headers end @authenticated = true response end def make_multipart_request(url, data, headers = {}) request = Net::HTTP::Post::Multipart.new url, data, headers access_token.sign! request response = consumer.http.request(request) @authenticated = true response end def authenticated? @authenticated end end end jira-ruby-2.3.0/lib/jira/base_factory.rb0000644000004100000410000000320214370022124020121 0ustar www-datawww-datamodule JIRA # This is the base class for all the JIRA resource factory instances. class BaseFactory attr_reader :client def initialize(client) @client = client end # Return the name of the class which this factory generates, i.e. # JIRA::Resource::FooFactory creates JIRA::Resource::Foo instances. def target_class # Need to do a little bit of work here as Module.const_get doesn't work # with nested class names, i.e. JIRA::Resource::Foo. # # So create a method chain from the class components. This code will # unroll to: # Module.const_get('JIRA').const_get('Resource').const_get('Foo') # target_class_name = self.class.name.sub(/Factory$/, '') class_components = target_class_name.split('::') class_components.inject(Module) do |mod, const_name| mod.const_get(const_name) end end def self.delegate_to_target_class(*method_names) method_names.each do |method_name| define_method method_name do |*args| target_class.send(method_name, @client, *args) end end end # The principle purpose of this class is to delegate methods to the corresponding # non-factory class and automatically prepend the client argument to the argument # list. delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :get_backlog_issues, :get_board_issues, :get_sprints, :get_sprint_issues, :get_projects, :get_projects_full # This method needs special handling as it has a default argument value def build(attrs = {}) target_class.build(@client, attrs) end end end jira-ruby-2.3.0/lib/jira/tasks.rb0000644000004100000410000000000014370022124016576 0ustar www-datawww-datajira-ruby-2.3.0/lib/jira/jwt_client.rb0000644000004100000410000000274414370022124017634 0ustar www-datawww-datarequire 'atlassian/jwt' module JIRA class JwtClient < HttpClient def make_request(http_method, url, body = '', headers = {}) @http_method = http_method super(http_method, url, body, headers) end def make_multipart_request(url, data, headers = {}) @http_method = :post super(url, data, headers) end class JwtUriBuilder attr_reader :request_url, :http_method, :shared_secret, :site, :issuer def initialize(request_url, http_method, shared_secret, site, issuer) @request_url = request_url @http_method = http_method @shared_secret = shared_secret @site = site @issuer = issuer end def build uri = URI.parse(request_url) new_query = URI.decode_www_form(String(uri.query)) << ['jwt', jwt_header] uri.query = URI.encode_www_form(new_query) return uri.to_s unless uri.is_a?(URI::HTTP) uri.request_uri end private def jwt_header claim = Atlassian::Jwt.build_claims \ issuer, request_url, http_method.to_s, site, (Time.now - 60).to_i, (Time.now + 86_400).to_i JWT.encode claim, shared_secret end end private attr_reader :http_method def request_path(url) JwtUriBuilder.new( url, http_method.to_s, @options[:shared_secret], @options[:site], @options[:issuer] ).build end end end jira-ruby-2.3.0/lib/tasks/0000755000004100000410000000000014370022124015336 5ustar www-datawww-datajira-ruby-2.3.0/lib/tasks/generate.rake0000644000004100000410000000152214370022124017774 0ustar www-datawww-datarequire 'securerandom' namespace :jira do desc 'Generate a consumer key for your application' task :generate_consumer_key do key = SecureRandom.hex(16) puts "You can use this as your consumer key: #{key}" end desc 'Run the system call to generate a RSA public certificate' task :generate_public_cert do puts "Executing 'openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem'" system('openssl req -x509 -subj "/C=US/ST=New York/L=New York/O=SUMO Heavy Industries/CN=www.sumoheavy.com" -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem') puts "Done. The RSA-SHA1 private keyfile is in the current directory: \'rsakey.pem\'." puts 'You will need to copy the following certificate into your application link configuration in Jira:' system('cat rsacert.pem') end end jira-ruby-2.3.0/lib/jira-ruby.rb0000644000004100000410000000304514370022124016444 0ustar www-datawww-data$LOAD_PATH << __dir__ require 'active_support' require 'active_support/inflector' ActiveSupport::Inflector.inflections do |inflector| inflector.singular /status$/, 'status' end require 'jira/base' require 'jira/base_factory' require 'jira/has_many_proxy' require 'jira/http_error' require 'jira/resource/user' require 'jira/resource/watcher' require 'jira/resource/attachment' require 'jira/resource/component' require 'jira/resource/issuetype' require 'jira/resource/version' require 'jira/resource/status' require 'jira/resource/transition' require 'jira/resource/project' require 'jira/resource/priority' require 'jira/resource/comment' require 'jira/resource/worklog' require 'jira/resource/applinks' require 'jira/resource/issuelinktype' require 'jira/resource/issuelink' require 'jira/resource/suggested_issue' require 'jira/resource/issue_picker_suggestions_issue' require 'jira/resource/issue_picker_suggestions' require 'jira/resource/remotelink' require 'jira/resource/sprint' require 'jira/resource/sprint_report' require 'jira/resource/issue' require 'jira/resource/filter' require 'jira/resource/field' require 'jira/resource/rapidview' require 'jira/resource/resolution' require 'jira/resource/serverinfo' require 'jira/resource/createmeta' require 'jira/resource/webhook' require 'jira/resource/agile' require 'jira/resource/board' require 'jira/resource/board_configuration' require 'jira/request_client' require 'jira/oauth_client' require 'jira/http_client' require 'jira/jwt_client' require 'jira/client' require 'jira/railtie' if defined?(Rails) jira-ruby-2.3.0/Guardfile0000644000004100000410000000047014370022124015271 0ustar www-datawww-datagem 'wdm', '>= 0.1.0' if Gem.win_platform? gem 'rspec', '~> 3.0.0' guard 'rspec', cmd: 'bundle exec rspec --color --format doc' do # watch /lib/ files watch(%r{^lib/(.+).rb$}) do |m| "spec/#{m[1]}_spec.rb" end # watch /spec/ files watch(%r{^spec/(.+).rb$}) do |m| "spec/#{m[1]}.rb" end end jira-ruby-2.3.0/Gemfile0000644000004100000410000000047714370022124014746 0ustar www-datawww-datasource 'http://rubygems.org' group :development do gem 'guard' gem 'guard-rspec' gem 'wdm', '>= 0.1.0' if Gem.win_platform? end group :development, :test do gem 'pry' # this was in the original Gemfile - but only needed in development & test end # Specify your gem's dependencies in jira_api.gemspec gemspec jira-ruby-2.3.0/jira-ruby.gemspec0000644000004100000410000000261414370022124016717 0ustar www-datawww-data$LOAD_PATH.push File.expand_path('lib', __dir__) require 'jira/version' Gem::Specification.new do |s| s.name = 'jira-ruby' s.version = JIRA::VERSION s.authors = ['SUMO Heavy Industries', 'test IO'] s.homepage = 'http://www.sumoheavy.com' s.summary = 'Ruby Gem for use with the Atlassian JIRA REST API' s.description = 'API for JIRA' s.licenses = ['MIT'] s.metadata = { 'source_code_uri' => 'https://github.com/sumoheavy/jira-ruby' } s.required_ruby_version = '>= 1.9.3' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ['lib'] # Runtime Dependencies s.add_runtime_dependency 'activesupport' s.add_runtime_dependency 'atlassian-jwt' s.add_runtime_dependency 'multipart-post' s.add_runtime_dependency 'oauth', '~> 0.5', '>= 0.5.0' # Development Dependencies s.add_development_dependency 'guard', '~> 2.13', '>= 2.13.0' s.add_development_dependency 'guard-rspec', '~> 4.6', '>= 4.6.5' s.add_development_dependency 'pry', '~> 0.10', '>= 0.10.3' s.add_development_dependency 'railties' s.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2' s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0' s.add_development_dependency 'webmock', '~> 1.18', '>= 1.18.0' end