jiralicious 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +2 -1
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +5 -0
  5. data/README.md +32 -4
  6. data/jiralicious.gemspec +7 -5
  7. data/lib/jiralicious.rb +10 -3
  8. data/lib/jiralicious/base.rb +114 -0
  9. data/lib/jiralicious/configuration.rb +13 -2
  10. data/lib/jiralicious/cookie_session.rb +5 -5
  11. data/lib/jiralicious/custom_field_option.rb +27 -0
  12. data/lib/jiralicious/field.rb +39 -0
  13. data/lib/jiralicious/issue.rb +111 -26
  14. data/lib/jiralicious/issue/comment.rb +62 -0
  15. data/lib/jiralicious/issue/fields.rb +93 -0
  16. data/lib/jiralicious/issue/transitions.rb +92 -0
  17. data/lib/jiralicious/issue/watchers.rb +47 -0
  18. data/lib/jiralicious/parsers/field_parser.rb +2 -2
  19. data/lib/jiralicious/project.rb +44 -0
  20. data/lib/jiralicious/search.rb +4 -1
  21. data/lib/jiralicious/search_result.rb +4 -0
  22. data/lib/jiralicious/version.rb +1 -1
  23. data/spec/basic_session_spec.rb +4 -4
  24. data/spec/comment_spec.rb +64 -0
  25. data/spec/configuration_spec.rb +9 -0
  26. data/spec/fixtures/comment.json +30 -0
  27. data/spec/fixtures/comment_single.json +29 -0
  28. data/spec/fixtures/issue.json +89 -93
  29. data/spec/fixtures/issue_2.json +30 -0
  30. data/spec/fixtures/issue_create.json +5 -0
  31. data/spec/fixtures/issue_createmeta.json +34 -0
  32. data/spec/fixtures/issue_editmeta.json +22 -0
  33. data/spec/fixtures/issue_update.json +164 -0
  34. data/spec/fixtures/jira.yml +7 -0
  35. data/spec/fixtures/project.json +87 -0
  36. data/spec/fixtures/project_issue_list.json +20 -0
  37. data/spec/fixtures/projects.json +22 -0
  38. data/spec/fixtures/search.json +9 -9
  39. data/spec/fixtures/test.json +24 -0
  40. data/spec/fixtures/transitions.json +61 -61
  41. data/spec/fixtures/watchers.json +17 -0
  42. data/spec/issue_spec.rb +255 -21
  43. data/spec/project_spec.rb +55 -0
  44. data/spec/search_result_spec.rb +20 -8
  45. data/spec/search_spec.rb +6 -6
  46. data/spec/support/http.rb +55 -2
  47. data/spec/watchers_spec.rb +43 -0
  48. metadata +154 -100
  49. data/.rvmrc +0 -1
  50. data/spec/cookie_session_spec.rb +0 -268
data/.gitignore CHANGED
@@ -8,4 +8,5 @@ TAGS
8
8
  vendor/bundle
9
9
  *~
10
10
  \#*#
11
- *.#*
11
+ *.#*
12
+ /bin
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ jiralicious
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.2-p320
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - 1.8.7
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # jiralicious
2
2
 
3
+ [![Build Status](https://travis-ci.org/jstewart/jiralicious.png)](https://travis-ci.org/jstewart/jiralicious)
4
+
3
5
  ## Examples:
4
6
 
5
7
  Before doing anything, you must configure your session:
@@ -13,10 +15,15 @@ Before doing anything, you must configure your session:
13
15
  config.auth_type = :basic
14
16
  end
15
17
 
16
- Default auth type is now Basic auth. Cookie auth is still available with the
17
- following option:
18
+ Session configuration is also available via YAML:
19
+
20
+ jira:
21
+ username: youruser
22
+ password: yourpass
23
+ uri: https://example.com/
24
+
18
25
 
19
- config.auth_type = :cookie
26
+ Jiralicious.load_yml(File.expand_path("/path/to/jira.yml"))
20
27
 
21
28
  Search for issues:
22
29
 
@@ -28,6 +35,27 @@ Finding a single issue:
28
35
  issue = Jiralicious::Issue.find("HSP-1")
29
36
  issue.key => "HSP-1"
30
37
 
38
+
39
+ ## Deprecation Warning
40
+
41
+ Default auth type is now Basic auth. Cookie auth will be deprecated in the next version.
42
+
43
+
44
+ ## Changes from 0.1.0
45
+
46
+ * Issues can be created, updated, or deleted as needed. This includes most components such as comments, transitions, and assignees.
47
+ * Projects can now be accessed as well as related issues
48
+ * A Field class has been added to allow proper access to the meta data for create, edit, and update requests. This data is searchable via Hash or dot notation
49
+ * Some sections can now be lazy loaded
50
+ * Configuration can be loaded via yaml
51
+
52
+
53
+ ## Contributors
54
+
55
+ * Stanley Handschuh (dorack)
56
+ * Mike Fiedler (miketheman)
57
+ * Girish Sonawane (girishso)
58
+
31
59
  ## Contributing to jiralicious
32
60
 
33
61
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
@@ -40,5 +68,5 @@ Finding a single issue:
40
68
 
41
69
  ## Copyright
42
70
 
43
- Copyright (c) 2012 Jason Stewart. See LICENSE for
71
+ Copyright (c) 2013 Jason Stewart. See LICENSE for
44
72
  further details.
data/jiralicious.gemspec CHANGED
@@ -12,11 +12,13 @@ Gem::Specification.new do |s|
12
12
  s.description = %Q{A Ruby library for interacting with JIRA's REST API}
13
13
  s.email = "jstewart@fusionary.com"
14
14
  s.authors = ["Jason Stewart"]
15
- s.add_runtime_dependency 'httparty', '~>0.7.8'
16
- s.add_runtime_dependency 'hashie', '~>1.1'
17
- s.add_runtime_dependency 'json', '~>1.6.3'
18
- s.add_development_dependency 'rspec', '~>2.6.0'
19
- s.add_development_dependency 'fakeweb', '~>1.3.0'
15
+ s.add_runtime_dependency 'crack', '~> 0.1.8'
16
+ s.add_runtime_dependency 'httparty', '~> 0.11.0'
17
+ s.add_runtime_dependency 'hashie', '>= 1.1'
18
+ s.add_runtime_dependency 'json', '~> 1.7.7'
19
+ s.add_development_dependency 'rspec', '~> 2.6'
20
+ s.add_development_dependency 'rake'
21
+ s.add_development_dependency 'fakeweb', '~> 1.3.0'
20
22
 
21
23
  s.files = `git ls-files`.split("\n")
22
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
data/lib/jiralicious.rb CHANGED
@@ -1,19 +1,26 @@
1
1
  # encoding: utf-8
2
-
3
2
  require 'hashie'
3
+ require 'crack'
4
4
  require 'httparty'
5
5
  require 'json'
6
6
 
7
-
8
7
  require 'jiralicious/parsers/field_parser'
9
8
  require 'jiralicious/errors'
9
+ require 'jiralicious/base'
10
+ require 'jiralicious/field'
11
+ require 'jiralicious/custom_field_option'
10
12
  require 'jiralicious/issue'
13
+ require 'jiralicious/issue/fields'
14
+ require 'jiralicious/issue/comment'
15
+ require 'jiralicious/issue/watchers'
16
+ require 'jiralicious/issue/transitions'
17
+ require 'jiralicious/project'
11
18
  require 'jiralicious/search'
12
19
  require 'jiralicious/search_result'
13
20
  require 'jiralicious/session'
14
21
  require 'jiralicious/basic_session'
15
22
  require 'jiralicious/cookie_session'
16
-
23
+ require 'jiralicious/configuration'
17
24
 
18
25
  module Jiralicious
19
26
  extend Configuration
@@ -0,0 +1,114 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+ require "uri"
4
+ module Jiralicious
5
+ class Base < Hashie::Trash
6
+ include Jiralicious::Parsers::FieldParser
7
+
8
+ attr_accessor :loaded
9
+
10
+ ### Trash Extention Methods ###
11
+ def properties_from_hash(hash)
12
+ hash.inject({}) do |newhash, (k, v)|
13
+ k = k.gsub("-", "_")
14
+ k = "_#{k.to_s}" if k =~ /^\d/
15
+ self.class.property :"#{k}"
16
+ newhash[k] = v
17
+ newhash
18
+ end
19
+ end
20
+
21
+ ### Class Methods ###
22
+ class << self
23
+ def find(key, options = {})
24
+ response = fetch({:key => key})
25
+ if options[:reload] == true
26
+ response
27
+ else
28
+ new(response.parsed_response)
29
+ end
30
+ end
31
+
32
+ def find_all
33
+ response = fetch()
34
+ new(response)
35
+ end
36
+
37
+ def endpoint_name
38
+ self.name.split('::').last.downcase
39
+ end
40
+
41
+ def parent_name
42
+ arr = self.name.split('::')
43
+ arr[arr.length-2].downcase
44
+ end
45
+
46
+ def fetch(options = {})
47
+ options[:method] = :get unless [:get, :post, :put, :delete].include?(options[:method])
48
+ options[:parent_uri] = "#{parent_name}/#{options[:parent_key]}/" unless options[:parent].nil?
49
+ if !options[:body_override]
50
+ options[:body_uri] = (options[:body].is_a? Hash) ? options[:body] : {:body => options[:body]}
51
+ else
52
+ options[:body_uri] = options[:body]
53
+ end
54
+ if options[:body_to_params]
55
+ options[:params_uri] = "?#{options[:body].to_params}" unless options[:body].nil? || options[:body].empty?
56
+ options[:body_uri] = nil
57
+ end
58
+ options[:url_uri] = options[:url].nil? ? "#{Jiralicious.rest_path}/#{options[:parent_uri]}#{endpoint_name}/#{options[:key]}#{options[:params_uri]}" : options[:url]
59
+ Jiralicious.session.request(options[:method], options[:url_uri], :handler => handler, :body => options[:body_uri].to_json)
60
+ end
61
+
62
+ def handler
63
+ Proc.new do |response|
64
+ case response.code
65
+ when 200..204
66
+ response
67
+ when 400
68
+ raise Jiralicious::TransitionError.new(response['errorMessages'].join('\n'))
69
+ when 404
70
+ raise Jiralicious::IssueNotFound.new(response['errorMessages'].join('\n'))
71
+ else
72
+ raise Jiralicious::JiraError.new(response['errorMessages'].join('\n'))
73
+ end
74
+ end
75
+ end
76
+
77
+ alias :all :find_all
78
+ end
79
+
80
+ ### Instance Methods ###
81
+ def endpoint_name
82
+ self.class.endpoint_name
83
+ end
84
+
85
+ def parent_name
86
+ self.class.parent_name
87
+ end
88
+
89
+ def all
90
+ self.class.all
91
+ end
92
+
93
+ def loaded?
94
+ self.loaded
95
+ end
96
+
97
+ def reload
98
+ end
99
+
100
+ def method_missing(meth, *args, &block)
101
+ if !loaded?
102
+ self.loaded = true
103
+ reload
104
+ self.send(meth, *args, &block)
105
+ else
106
+ super
107
+ end
108
+ end
109
+
110
+ def numeric?(object)
111
+ true if Float(object) rescue false
112
+ end
113
+ end
114
+ end
@@ -1,11 +1,11 @@
1
1
  # encoding: utf-8
2
-
2
+ require 'ostruct'
3
3
  module Jiralicious
4
4
  module Configuration
5
5
  VALID_OPTIONS = [:username, :password, :uri, :api_version, :auth_type]
6
6
  DEFAULT_USERNAME = nil
7
- DEFAULT_AUTH_TYPE = :basic
8
7
  DEFAULT_PASSWORD = nil
8
+ DEFAULT_AUTH_TYPE = :basic
9
9
  DEFAULT_URI = nil
10
10
  DEFAULT_API_VERSION = "latest"
11
11
 
@@ -33,5 +33,16 @@ module Jiralicious
33
33
  self.api_version = DEFAULT_API_VERSION
34
34
  self.auth_type = DEFAULT_AUTH_TYPE
35
35
  end
36
+
37
+ def load_yml(yml_file)
38
+ if File.exist?(yml_file)
39
+ yml_cfg = OpenStruct.new(YAML.load_file(yml_file))
40
+ yml_cfg.jira.each do |k, v|
41
+ instance_variable_set("@#{k}", v)
42
+ end
43
+ else
44
+ reset
45
+ end
46
+ end
36
47
  end
37
48
  end
@@ -48,9 +48,9 @@ module Jiralicious
48
48
  end
49
49
 
50
50
  self.request(:post, '/rest/auth/latest/session',
51
- :body => { :username => Jiralicious.username,
52
- :password => Jiralicious.password}.to_json,
53
- :handler => handler)
51
+ :body => { :username => Jiralicious.username,
52
+ :password => Jiralicious.password}.to_json,
53
+ :handler => handler)
54
54
 
55
55
  end
56
56
 
@@ -77,8 +77,8 @@ module Jiralicious
77
77
  def captcha_required(response)
78
78
  response.code == 401 &&
79
79
  # Fakeweb lowercases headers automatically. :(
80
- (response.headers["X-Seraph-LoginReason"] == "AUTHENTICATION_DENIED" ||
81
- response.headers["x-seraph-loginreason"] == "AUTHENTICATION_DENIED")
80
+ (response.headers["X-Seraph-LoginReason"] == "AUTHENTICATION_DENIED" ||
81
+ response.headers["x-seraph-loginreason"] == "AUTHENTICATION_DENIED")
82
82
  end
83
83
 
84
84
  def cookie_invalid(response)
@@ -0,0 +1,27 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+ module Jiralicious
4
+ class CustomFieldOption < Jiralicious::Base
5
+ def initialize(decoded_json, default = nil, &blk)
6
+ @loaded = false
7
+ if decoded_json.is_a? Hash
8
+ properties_from_hash(decoded_json)
9
+ super(decoded_json)
10
+ parse!(decoded_json)
11
+ @loaded = true
12
+ end
13
+ end
14
+
15
+ class << self
16
+ def endpoint_name
17
+ "customFieldOption"
18
+ end
19
+
20
+ def find(id, options = {})
21
+ response = fetch({:key => id})
22
+ response.parsed_response['id'] = id
23
+ new(response.parsed_response)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+ module Jiralicious
4
+ class Field < Jiralicious::Base
5
+
6
+ def initialize(decoded_json, default = nil, &blk)
7
+ @loaded = false
8
+ if decoded_json.is_a? Hash
9
+ decoded_json = properties_from_hash(decoded_json)
10
+ super(decoded_json)
11
+ parse!(decoded_json)
12
+ self.each do |k, v|
13
+ if v.is_a? Hash
14
+ self[k] = self.class.new(v)
15
+ elsif v.is_a? Array
16
+ v.each_index do |i|
17
+ if v[i].is_a? Hash
18
+ v[i] = self.class.new(v[i])
19
+ end
20
+ end
21
+ self[k] = v
22
+ end
23
+ end
24
+ @loaded = true
25
+ else
26
+ decoded_json.each do |list|
27
+ if numeric? list['id']
28
+ id = :"id_#{list['id']}"
29
+ else
30
+ id = :"#{list['id']}"
31
+ end
32
+ self.class.property id
33
+ out self.class.new(list)
34
+ self.merge!({id => out})
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  module Jiralicious
3
- class Issue < Hashie::Trash
4
- include Jiralicious::Parsers::FieldParser
3
+ class Issue < Jiralicious::Base
5
4
 
6
5
  property :jira_key, :from => :key
7
6
  property :expand
@@ -9,40 +8,126 @@ module Jiralicious
9
8
  property :fields
10
9
  property :transitions
11
10
  property :id
11
+ attr_accessor :fields
12
+ attr_accessor :comments
13
+ attr_accessor :watchers
14
+ attr_accessor :createmeta
15
+ attr_accessor :editmeta
12
16
 
13
- def initialize(decoded_json, default = nil, &blk)
14
- super(decoded_json)
15
- parse!(decoded_json["fields"])
17
+ ### Initialization ###
18
+ def initialize(decoded_json = nil, default = nil, &blk)
19
+ @loaded = false
20
+ if (!decoded_json.nil?)
21
+ super(decoded_json)
22
+ parse!(decoded_json["fields"])
23
+ if default.nil?
24
+ @fields = Fields.new(self['fields']) if self['fields']
25
+ @comments = Comment.find_by_key(self.jira_key)
26
+ @watchers = Watchers.find_by_key(self.jira_key)
27
+ @loaded = true
28
+ end
29
+ end
30
+ @fields = Fields.new if @fields.nil?
31
+ @comments = Comment.new if @comments.nil?
32
+ @watchers = Watchers.new if @watchers.nil?
33
+ @createmeta = nil
34
+ @editmeta = nil
35
+ end
36
+
37
+ def load(decoded_hash, default = nil)
38
+ decoded_hash.each do |k,v|
39
+ self[:"#{k}"] = v
40
+ end
41
+ if default.nil?
42
+ parse!(self['fields'])
43
+ @fields = Fields.new(self['fields']) if self['fields']
44
+ @comments = Comment.find_by_key(self.jira_key)
45
+ @watchers = Watchers.find_by_key(self.jira_key)
46
+ @loaded = true
47
+ else
48
+ parse!(decoded_hash)
49
+ end
50
+ end
51
+
52
+ def reload
53
+ load(self.class.find(self.jira_key, {:reload => true}).parsed_response)
54
+ end
55
+
56
+
57
+ ### Class Methods ###
58
+ class << self
59
+ def assignee(name, key)
60
+ name = {"name" => name} if name.is_a? String
61
+ fetch({:method => :put, :key => "#{key}/assignee", :body => name})
62
+ end
63
+
64
+ def create(issue)
65
+ fetch({:method => :post, :body => issue})
66
+ end
67
+
68
+ def remove(key, options = {})
69
+ fetch({:method => :delete, :body_to_params => true, :key => key, :body => options})
70
+ end
71
+
72
+ def update(issue, key)
73
+ fetch({:method => :put, :key => key, :body => issue})
74
+ end
75
+
76
+ def createmeta(projectkeys, issuetypeids = nil)
77
+ response = fetch({:body_to_params => true, :key => "createmeta", :body => {:expand => "projects.issuetypes.fields.", :projectKeys => projectkeys, :issuetypeIds => issuetypeids}})
78
+ return Field.new(response.parsed_response)
79
+ end
80
+
81
+ def editmeta(key)
82
+ response = fetch({:key => "#{key}/editmeta"})
83
+ response.parsed_response["key"] = key
84
+ Field.new(response.parsed_response)
85
+ end
86
+
87
+ def get_transitions(transitions_url)
88
+ Jiralicious.session.request(:get, transitions_url, :handler => handler)
89
+ end
90
+
91
+ def transition(transitions_url, data)
92
+ Jiralicious.session.request(:post, transitions_url,
93
+ :handler => handler,
94
+ :body => data.to_json)
95
+ end
96
+ end
97
+
98
+ ### Public Classes ###
99
+
100
+ def set_assignee(name)
101
+ self.class.assignee(name, self.jira_key)
16
102
  end
17
103
 
18
- def self.find(key, options = {})
19
- response = Jiralicious.session.request(:get, "#{Jiralicious.rest_path}/issue/#{key}", :handler => handler)
20
- new(response)
104
+ def remove(options = {})
105
+ self.class.remove(self.jira_key, options)
21
106
  end
22
107
 
23
- def self.get_transitions(transitions_url)
24
- Jiralicious.session.request(:get, transitions_url, :handler => handler)
108
+ def createmeta
109
+ if @createmeta.nil?
110
+ @createmeta = self.class.createmeta(self.jira_key.split("-")[0])
111
+ end
112
+ @createmeta
25
113
  end
26
114
 
27
- def self.transition(transitions_url, data)
28
- Jiralicious.session.request(:post, transitions_url,
29
- :handler => handler,
30
- :body => data.to_json)
115
+ def editmeta
116
+ if @editmeta.nil?
117
+ @editmeta = self.class.editmeta(self.jira_key)
118
+ end
119
+ @editmeta
31
120
  end
32
121
 
33
- def self.handler
34
- Proc.new do |response|
35
- case response.code
36
- when 200..204
37
- response
38
- when 400
39
- raise Jiralicious::TransitionError.new(response['errorMessages'].join('\n'))
40
- when 404
41
- raise Jiralicious::IssueNotFound.new(response['errorMessages'].join('\n'))
42
- else
43
- raise Jiralicious::JiraError.new(response['errorMessages'].join('\n'))
44
- end
122
+ def save
123
+ if loaded?
124
+ self.class.update(@fields.format_for_update, self.jira_key)
125
+ key = self.jira_key
126
+ else
127
+ response = self.class.create(@fields.format_for_create)
128
+ key = response.parsed_response['key']
45
129
  end
130
+ load(self.class.find(key, {:reload => true}).parsed_response)
46
131
  end
47
132
  end
48
133
  end