terjira 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/Gemfile.lock +3 -3
  4. data/README.md +14 -15
  5. data/bin/console +1 -1
  6. data/bin/jira +11 -3
  7. data/bin/terjira +22 -0
  8. data/lib/terjira/base_cli.rb +5 -3
  9. data/lib/terjira/board_cli.rb +17 -2
  10. data/lib/terjira/client/agile.rb +10 -13
  11. data/lib/terjira/client/auth_option_builder.rb +1 -1
  12. data/lib/terjira/client/base.rb +9 -12
  13. data/lib/terjira/client/board.rb +13 -3
  14. data/lib/terjira/client/field.rb +12 -2
  15. data/lib/terjira/client/issue.rb +15 -13
  16. data/lib/terjira/client/issuetype.rb +18 -0
  17. data/lib/terjira/client/jql_builder.rb +25 -0
  18. data/lib/terjira/client/priority.rb +1 -1
  19. data/lib/terjira/client/resolution.rb +1 -1
  20. data/lib/terjira/client/status.rb +1 -1
  21. data/lib/terjira/client/user.rb +5 -6
  22. data/lib/terjira/ext/jira_ruby.rb +12 -4
  23. data/lib/terjira/ext/tty_prompt.rb +0 -3
  24. data/lib/terjira/issue_cli.rb +11 -7
  25. data/lib/terjira/option_support/option_selector.rb +20 -9
  26. data/lib/terjira/option_support/shared_options.rb +4 -2
  27. data/lib/terjira/option_supportable.rb +24 -0
  28. data/lib/terjira/presenters/common_presenter.rb +13 -4
  29. data/lib/terjira/presenters/issue_presenter.rb +115 -97
  30. data/lib/terjira/presenters/project_presenter.rb +1 -1
  31. data/lib/terjira/presenters/sprint_presenter.rb +9 -9
  32. data/lib/terjira/sprint_cli.rb +1 -0
  33. data/lib/terjira/utils/file_cache.rb +26 -26
  34. data/lib/terjira/version.rb +1 -1
  35. data/lib/terjira.rb +11 -0
  36. data/terjira.gemspec +4 -4
  37. metadata +11 -11
  38. data/lib/terjira/client/jql_query_builer.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5592eeb887c0b8ce2a4437c36c3e2468417f54f3
4
- data.tar.gz: 49483f95737dc6ae8f57d7871c8db30ce4da59d3
3
+ metadata.gz: 1daef227221455846fa59674d6cfeb9735d1e33d
4
+ data.tar.gz: 5dd7bbe7f90499eaf61ff4300ae5518f1a3336b3
5
5
  SHA512:
6
- metadata.gz: 82a1c765c3328a83d96e4e6e5c800acaee4fac814f672d2f59d11e8292d650d7ea1c531aef9ec42c8d97f408aabd67d2a5697f5cfd24f85b0a135211fa8d5a4f
7
- data.tar.gz: 04eea6bbe20ae4474c7b518374ad3a63fefea7f51030b7036e91b2018be799fb99670f2eba6cc51df25d4260e884d17806e2f84babbfcd403c72bb2dcdc5e8c5
6
+ metadata.gz: 6124824ac122ea72b0e7077956371b6803cd848a8a454bbb03c4e25de2a8e99902e64a5fd01763925b1cecfa9ac7e04457749c7b56ddd2c530d178bbdfd025f8
7
+ data.tar.gz: 4ecc886b8ab99b2bc3ddfb9f9694f6baba32f5b63484f9468ac9dd6368e5428108e4cedfb74e291024f1ce8e9a0326e6a237d79284c912f4828441836358f8b2
data/.rubocop.yml CHANGED
@@ -1,14 +1,23 @@
1
1
  AllCops:
2
2
  Exclude:
3
+ - 'bin/*'
3
4
  - '*.gemspec'
5
+ - 'lib/terjira/ext/**/*'
6
+ - 'Rakefile'
4
7
 
5
8
  Style/Documentation:
6
9
  Enabled: false
7
10
  Style/EachWithObject:
8
11
  Enabled: false
12
+ Style/RescueModifier:
13
+ Enabled: false
9
14
 
10
15
  Lint/AssignmentInCondition:
11
16
  Enabled: false
17
+ Lint/Eval:
18
+ Enabled: false
19
+ Lint/ImplicitStringConcatenation:
20
+ Enabled: false
12
21
 
13
22
  Metrics/BlockLength:
14
23
  Enabled: false
@@ -20,7 +29,7 @@ Metrics/AbcSize:
20
29
  Max: 20
21
30
 
22
31
  Metrics/MethodLength:
23
- Max: 20
32
+ Max: 30
24
33
 
25
34
  Metrics/LineLength:
26
35
  Max: 100
data/Gemfile.lock CHANGED
@@ -6,9 +6,9 @@ PATH
6
6
  jira-ruby (~> 1.1)
7
7
  pastel (~> 0.6.1)
8
8
  thor (~> 0.19)
9
- tty-prompt (~> 0.8.0)
10
- tty-spinner (~> 0.4.1)
11
- tty-table (~> 0.6.0)
9
+ tty-prompt (~> 0.8)
10
+ tty-spinner (~> 0.4)
11
+ tty-table (~> 0.6)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -5,42 +5,41 @@
5
5
 
6
6
  # Terjira
7
7
 
8
- Terjira is a very interactive and easy to use command line interface for Jira.
8
+ Terjira is interactive and easy to use command line interface (or Application) for Jira. You do not need to remember reosurce key or id. Terjira suggests with interactive prompt.
9
9
 
10
+ Your Jira must support Rest API 2.0 and Agile Rest API 1.0
10
11
 
11
- I'm working now..
12
+
13
+ **I'm working now..**
12
14
 
13
15
  ## Installation
14
16
 
15
- Add this line to your application's Gemfile:
17
+ Install it yourself as:
16
18
 
17
- ```ruby
18
- gem 'terjira'
19
- ```
19
+ $ gem install terjira
20
20
 
21
- And then execute:
21
+ If you have permission problem,
22
22
 
23
- $ bundle
23
+ $ sudo gem install terjira
24
24
 
25
- Or install it yourself as:
25
+ # or
26
26
 
27
- $ gem install terjira
27
+ $ gem install terjira --user-install
28
+ # You need to export your gem path
28
29
 
29
30
  ## Usage
30
31
 
31
- TODO: Write usage instructions here
32
32
 
33
- * Mock data is from jira-ruby
34
33
 
35
34
  ## Development
36
35
 
37
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
37
 
39
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
+ To install this gem onto your local machine, run `bundle exec rake install`.
40
39
 
41
40
  ## Contributing
42
41
 
43
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/terjira. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
42
+ Bug reports and pull requests are welcome on GitHub at https://github.com/keepcosmos/terjira. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
44
43
 
45
44
 
46
45
  ## License
data/bin/console CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # Do I need bundler setup?
3
- # require "bundler/setup"
3
+ require "bundler/setup"
4
4
  require "terjira"
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
data/bin/jira CHANGED
@@ -1,14 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
+
2
3
  # Do I need bundler setup?
3
4
  # require 'bundler/setup'
4
5
  require 'terjira'
5
6
  require 'json'
7
+ require 'pastel'
6
8
 
7
9
  begin
8
10
  Terjira::CLI.start(ARGV)
11
+ rescue SystemExit, Interrupt
9
12
  rescue JIRA::HTTPError => e
10
13
  message = JSON.parse(e.response.body)
11
- error_message = message['errorMessages']
12
- error_message = error_message.join(' ') if error_message.is_a? Array
13
- puts "#{e.message} :: #{error_message}"
14
+ pastel = Pastel.new
15
+ if message['errorMessages'].present?
16
+ puts pastel.red(message['errorMessages'].join("\n"))
17
+ else
18
+ puts pastel.red(e.message.to_s + "\n" + e.response.body)
19
+ end
20
+ rescue => e
21
+ puts Pastel.new.dim(e.message)
14
22
  end
data/bin/terjira ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Do I need bundler setup?
4
+ require 'bundler/setup'
5
+ require 'terjira'
6
+ require 'json'
7
+ require 'pastel'
8
+
9
+ begin
10
+ Terjira::CLI.start(ARGV)
11
+ rescue SystemExit, Interrupt
12
+ rescue JIRA::HTTPError => e
13
+ message = JSON.parse(e.response.body)
14
+ pastel = Pastel.new
15
+ if message['errorMessages'].present?
16
+ puts pastel.red(message['errorMessages'].join("\n"))
17
+ else
18
+ puts pastel.red(e.message.to_s + "\n" + e.response.body)
19
+ end
20
+ rescue => e
21
+ puts Pastel.new.dim(e.message)
22
+ end
@@ -1,11 +1,13 @@
1
1
  require 'thor'
2
2
 
3
3
  require_relative 'option_supportable'
4
- Dir[File.dirname(__FILE__) + "/presenters/*.rb"].each { |f| require f }
4
+ Dir[File.dirname(__FILE__) + '/presenters/*.rb'].each { |f| require f }
5
5
 
6
6
  module Terjira
7
+ # Jira client based on jira-ruby gem
7
8
  module Client
8
- %w[Base Field Project Board Sprint Issue User Status Resolution Priority RapidView Agile].each do |klass|
9
+ %w(Base Field Issuetype Project Board Sprint Issue User
10
+ Status Resolution Priority RapidView Agile).each do |klass|
9
11
  autoload klass, "terjira/client/#{klass.gsub(/(.)([A-Z](?=[a-z]))/,'\1_\2').downcase}"
10
12
  end
11
13
  end
@@ -19,7 +21,7 @@ module Terjira
19
21
  include BoardPresenter
20
22
  include SprintPresenter
21
23
 
22
- def self.banner(command, namespace = nil, subcommand = false)
24
+ def self.banner(command, _namespace = nil, _subcommand = false)
23
25
  "#{basename} #{subcommand_prefix} #{command.usage}"
24
26
  end
25
27
 
@@ -2,11 +2,26 @@ require_relative 'base_cli'
2
2
 
3
3
  module Terjira
4
4
  class BoardCLI < BaseCLI
5
- desc "list(ls)", "list all boards"
5
+ no_commands do
6
+ def client_class
7
+ Client::Board
8
+ end
9
+ end
10
+
11
+ desc "( ls | list)", "list all boards"
6
12
  map ls: :list
7
13
  def list
8
- boards = Client::Board.all
14
+ boards = client_class.all
9
15
  render_boards_summary(boards.sort_by { |b| b.id })
10
16
  end
17
+
18
+ desc "backlog", "Backlog from the board"
19
+ jira_options :board, :assignee, :issuetype, :priority
20
+ def backlog
21
+ opts = suggest_options(required: [:board])
22
+ board = opts.delete(:board)
23
+ issues = client_class.backlog(board.key_value, opts)
24
+ render_issues(issues)
25
+ end
11
26
  end
12
27
  end
@@ -7,22 +7,22 @@ module Terjira
7
7
  delegate :all, :get_sprints, :backlog_issues, to: :resource
8
8
 
9
9
  def project_by_board(board_id)
10
- resp = agile_api_get("board/#{board_id}/project")
10
+ agile_api_get("board/#{board_id}/project")
11
11
  end
12
12
 
13
13
  def boards
14
- all["values"]
14
+ all['values']
15
15
  end
16
16
 
17
- def sprints(board_id, options = {})
18
- sprints = get_sprints(board_id)["values"]
17
+ def sprints(board_id)
18
+ sprints = get_sprints(board_id)['values']
19
19
  sprints.sort_by do |sprint|
20
- if sprint["state"] == 'active'
21
- [0, sprint["id"]]
22
- elsif sprint["state"] == 'future'
23
- [1, sprint["id"]]
24
- elsif sprint["state"] == 'closed'
25
- [2, sprint["id"] * -1]
20
+ if sprint['state'] == 'active'
21
+ [0, sprint['id']]
22
+ elsif sprint['state'] == 'future'
23
+ [1, sprint['id']]
24
+ elsif sprint['state'] == 'closed'
25
+ [2, sprint['id'] * -1]
26
26
  else
27
27
  [3, 0]
28
28
  end
@@ -32,9 +32,6 @@ module Terjira
32
32
  def backlog_issues(board_id)
33
33
  get_backlog_issues(board_id)
34
34
  end
35
-
36
- def sprint_issues(board_id)
37
- end
38
35
  end
39
36
  end
40
37
  end
@@ -17,7 +17,7 @@ module Terjira
17
17
  end
18
18
 
19
19
  def expire_auth_options(cache_key = AUTH_CACHE_KEY)
20
- auth_file_cache.delete(cache_key)
20
+ Terjira::FileCache.clear_all
21
21
  end
22
22
 
23
23
  def build_auth_options_by_tty
@@ -1,23 +1,22 @@
1
- require_relative 'jql_query_builer'
1
+ require_relative 'jql_builder'
2
2
  require_relative 'auth_option_builder'
3
3
 
4
4
  module Terjira
5
5
  module Client
6
6
  # Abstract class to delegate jira-ruby resource class
7
7
  class Base
8
- extend JQLQueryBuilder
8
+ extend JQLBuilder
9
9
  extend AuthOptionBuilder
10
10
 
11
11
  DEFAULT_CACHE_SEC = 60
12
- DEFAULT_API_PATH = "/rest/api/2/"
13
- AGILE_API_PATH = "/rest/agile/1.0/"
12
+ DEFAULT_API_PATH = '/rest/api/2/'.freeze
13
+ AGILE_API_PATH = '/rest/agile/1.0/'.freeze
14
14
 
15
15
  class << self
16
-
17
16
  delegate :build, to: :resource
18
17
 
19
18
  def client
20
- @@client ||= JIRA::Client.new(build_auth_options)
19
+ @client ||= JIRA::Client.new(build_auth_options)
21
20
  end
22
21
 
23
22
  def resource
@@ -29,7 +28,7 @@ module Terjira
29
28
  end
30
29
 
31
30
  def class_name
32
- self.to_s.split("::").last
31
+ to_s.split('::').last
33
32
  end
34
33
 
35
34
  def cache(options = {})
@@ -38,16 +37,14 @@ module Terjira
38
37
  end
39
38
 
40
39
  # define `#api_get(post, put, delete)` and `#agile_api_get(post, put, delete)`
41
- { DEFAULT_API_PATH => "api_",
42
- AGILE_API_PATH => "agile_api_"
43
- }.each do |url_prefix, method_prefix|
44
-
40
+ { DEFAULT_API_PATH => 'api_',
41
+ AGILE_API_PATH => 'agile_api_' }.each do |url_prefix, method_prefix|
45
42
  [:get, :delete].each do |http_method|
46
43
  method_name = "#{method_prefix}#{http_method}"
47
44
  define_method(method_name) do |path, params = {}, headers = {}|
48
45
  url = url_prefix + path
49
46
  if params.present?
50
- params.reject! { |k, v| v.blank? }
47
+ params.reject! { |_k, v| v.blank? }
51
48
  url += "?#{URI.encode_www_form(params)}"
52
49
  end
53
50
  parse_body client.send(http_method, url, headers)
@@ -6,13 +6,23 @@ module Terjira
6
6
 
7
7
  def all(options = {})
8
8
  params = options.slice(:type)
9
- resp = agile_api_get("board", params)
10
- resp["values"].map { |value| build(value) }
9
+ resp = agile_api_get('board', params)
10
+ resp['values'].map { |value| build(value) }
11
11
  end
12
12
 
13
13
  def find(board_id)
14
14
  resp = agile_api_get("board/#{board_id}")
15
- self.build(resp)
15
+ build(resp)
16
+ end
17
+
18
+ def backlog(board_id, options = {})
19
+ jql = build_jql(options)
20
+ resp = if jql.present?
21
+ agile_api_get("board/#{board_id}/backlog", jql: jql)
22
+ else
23
+ agile_api_get("board/#{board_id}/backlog")
24
+ end
25
+ resp["issues"].map { |issue| Issue.build(issue) }
16
26
  end
17
27
  end
18
28
  end
@@ -5,11 +5,21 @@ module Terjira
5
5
  class Field < Base
6
6
  class << self
7
7
  def all
8
- @all_fields ||= resource.all
8
+ @all_fields ||= file_cache.fetch("all") do
9
+ resource.all
10
+ end
9
11
  end
10
12
 
11
13
  def epic_name
12
- all.find { |field| field.name == "Epic Name" }
14
+ all.find { |field| field.name == 'Epic Name' }
15
+ end
16
+
17
+ def epiclink
18
+ all.find { |field| field.name == 'Epic Link' }
19
+ end
20
+
21
+ def file_cache
22
+ Terjira::FileCache.new("resource/fields", 60 * 60 * 24)
13
23
  end
14
24
  end
15
25
  end
@@ -5,22 +5,21 @@ module Terjira
5
5
  class Issue < Base
6
6
  class << self
7
7
  delegate :build, :find, to: :resource
8
- ISSUE_JQL_KEYS = [:sprint, :assignee, :reporter, :project, :issuetype, :priority, :status, :statusCategory]
9
8
 
10
9
  def all(options = {})
11
- opts = options.slice(*ISSUE_JQL_KEYS)
12
10
  return resource.all if options.blank?
13
11
  max_results = options.delete(:max_results) || 500
14
- resource.jql(build_jql_query(opts), max_results: max_results)
12
+ resource.jql(build_jql(options), max_results: max_results)
15
13
  end
16
14
 
17
- def find(issue, options = {})
18
- resp = api_get("issue/#{issue.key_value}", options)
19
- build(resp)
15
+ def all_epic_issues(epic)
16
+ resp = agile_api_get("epic/#{epic.key}/issue")
17
+ resp["issues"].map { |issue| build(issue) }
20
18
  end
21
19
 
22
- def current_my_issues
23
- jql("assignee = #{self.key_value} AND statusCategory != 'Done'")
20
+ def find(issue, options = {})
21
+ resp = agile_api_get("issue/#{issue.key_value}", options)
22
+ build(resp)
24
23
  end
25
24
 
26
25
  def assign(issue, assignee)
@@ -29,7 +28,7 @@ module Terjira
29
28
  end
30
29
 
31
30
  def write_comment(issue, message)
32
- resp = api_post("issue/#{issue.key_value}/comment", { body: message }.to_json)
31
+ api_post("issue/#{issue.key_value}/comment", { body: message }.to_json)
33
32
  find(issue)
34
33
  end
35
34
 
@@ -39,9 +38,8 @@ module Terjira
39
38
  params.merge!(transition_param)
40
39
  end
41
40
 
42
- resp = api_post "issue", params.to_json
43
- result_id = resp["id"]
44
- find(result_id)
41
+ resp = api_post 'issue', params.to_json
42
+ find(resp['id'])
45
43
  end
46
44
 
47
45
  def update(issue, options = {})
@@ -80,7 +78,7 @@ module Terjira
80
78
  params = {}
81
79
 
82
80
  custom_fields = options.keys.select { |k| k.to_s =~ /^customfield/ }
83
- (custom_fields + [:summary, :description]).each do |k, v|
81
+ (custom_fields + [:summary, :description]).each do |k, _v|
84
82
  params[k] = opts.delete(k) if opts.key?(k)
85
83
  end
86
84
 
@@ -88,6 +86,10 @@ module Terjira
88
86
  params[:project] = { key: opts.delete(:project).key_value }
89
87
  end
90
88
 
89
+ if opts.key?(:parent)
90
+ params[:parent] = { key: opts.delete(:parent).key_value }
91
+ end
92
+
91
93
  opts.each do |k, v|
92
94
  params[k] = convert_param_key_value_hash(v)
93
95
  end
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+
3
+ module Terjira
4
+ module Client
5
+ class Issuetype < Base
6
+ class << self
7
+ def all
8
+ resp = api_get("issuetype")
9
+ resp.map { |issuetype| build(issuetype) }
10
+ end
11
+
12
+ def subtask_issuetypes
13
+ all.select(&:subtask)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module Terjira
2
+ module Client
3
+ module JQLBuilder
4
+ JQL_KEYS = %w(sprint assignee issuetype priority project status statusCategory).freeze
5
+
6
+ def build_jql(options = {})
7
+ q_options = options.inject({}) do |memo, (k, v)|
8
+ memo[k.to_s] = v
9
+ memo
10
+ end.slice(*JQL_KEYS)
11
+
12
+ query = q_options.map do |key, value|
13
+ if value.is_a? Array
14
+ values = value.map { |v| "\"#{v.key_value}\"" }.join(',')
15
+ "#{key} IN (#{values})"
16
+ else
17
+ "#{key}=#{value.key_value}"
18
+ end
19
+ end.reject(&:blank?).join(' AND ')
20
+
21
+ query
22
+ end
23
+ end
24
+ end
25
+ end
@@ -5,7 +5,7 @@ module Terjira
5
5
  class Priority < Base
6
6
  class << self
7
7
  def all
8
- resp = api_get "priority"
8
+ resp = api_get 'priority'
9
9
  resp.map { |priority| build(priority) }
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module Terjira
5
5
  class Resolution < Base
6
6
  class << self
7
7
  def all
8
- resp = api_get("resolution")
8
+ resp = api_get('resolution')
9
9
  resp.map { |resolution| build(resolution) }
10
10
  end
11
11
  end
@@ -6,7 +6,7 @@ module Terjira
6
6
  class << self
7
7
  def all(project)
8
8
  resp = api_get "project/#{project.key_value}/statuses"
9
- statuses_json = resp.map { |issuetype| issuetype["statuses"] }.flatten.uniq
9
+ statuses_json = resp.map { |issuetype| issuetype['statuses'] }.flatten.uniq
10
10
  statuses_json.map { |status| build(status) }
11
11
  end
12
12
  end
@@ -6,10 +6,10 @@ module Terjira
6
6
  class << self
7
7
  def assignables_by_project(project)
8
8
  if project.is_a? Array
9
- keys = project.map(&:key_value).join(",")
10
- fetch_assignables "user/assignable/multiProjectSearch", {projectKeys: keys }
9
+ keys = project.map(&:key_value).join(',')
10
+ fetch_assignables 'user/assignable/multiProjectSearch', projectKeys: keys
11
11
  else
12
- fetch_assignables "user/assignable/search", { project: project.key_value }
12
+ fetch_assignables 'user/assignable/search', project: project.key_value
13
13
  end
14
14
  end
15
15
 
@@ -28,15 +28,14 @@ module Terjira
28
28
  end
29
29
 
30
30
  def assignables_by_issue(issue)
31
- fetch_assignables "user/assignable/search", {issueKey: issue.key_value }
31
+ fetch_assignables 'user/assignable/search', issueKey: issue.key_value
32
32
  end
33
33
 
34
34
  private
35
35
 
36
36
  def fetch_assignables(path, params)
37
37
  resp = api_get(path, params)
38
- resp.map { |user| build(user) }.
39
- reject { |user| user.key_value =~ /^addon/ }
38
+ resp.map { |user| build(user) }.reject { |user| user.key_value =~ /^addon/ }
40
39
  end
41
40
  end
42
41
  end
@@ -9,15 +9,12 @@ module JIRA
9
9
  alias origin_make_request make_request
10
10
 
11
11
  def make_request(http_method, path, body = '', headers = {})
12
- title = http_method.to_s.upcase
13
- title = Pastel.new.dim(title)
12
+ title = Pastel.new.dim(http_method.to_s.upcase)
14
13
  spinner = TTY::Spinner.new ":spinner #{title}", format: :dots, clear: true
15
14
  result = nil
16
-
17
15
  spinner.run do
18
16
  result = origin_make_request(http_method, path, body, headers)
19
17
  end
20
-
21
18
  result
22
19
  end
23
20
  end
@@ -33,16 +30,27 @@ module JIRA
33
30
  class BoardFactory < JIRA::BaseFactory # :nodoc:
34
31
  end
35
32
 
33
+ class EpicFactory < JIRA::BaseFactory # :nodoc:
34
+ end
35
+
36
36
  class Board < JIRA::Base
37
37
  def self.key_attribute; :id; end
38
38
  end
39
39
 
40
+ class Epic < JIRA::Base
41
+ def self.key_attribute; :key; end
42
+ end
43
+
40
44
  class User
41
45
  def self.key_attribute; :name; end
42
46
  end
43
47
 
44
48
  class Issue
45
49
  def self.key_attribute; :key; end
50
+ has_one :epic, class: JIRA::Resource::Epic, nested_under: 'fields'
51
+ has_one :sprint, class: JIRA::Resource::Sprint, nested_under: 'fields'
52
+ has_one :parent, class: JIRA::Resource::Issue, nested_under: 'fields'
53
+ has_many :subtasks, class: JIRA::Resource::Issue, nested_under: 'fields'
46
54
  end
47
55
 
48
56
  class Issuetype
@@ -3,9 +3,6 @@ require 'tty-prompt'
3
3
  module TTY
4
4
  class Prompt
5
5
  class Question
6
- # Decide how to handle input from user
7
- #
8
- # @api private
9
6
  def process_input
10
7
  @input = read_input
11
8
  if Utils.blank?(@input)