ruby-jira-cli 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +47 -0
- data/bin/jira +26 -0
- data/lib/jira/api.rb +76 -0
- data/lib/jira/auth_api.rb +11 -0
- data/lib/jira/command.rb +60 -0
- data/lib/jira/commands/all.rb +72 -0
- data/lib/jira/commands/assign.rb +65 -0
- data/lib/jira/commands/attachments.rb +45 -0
- data/lib/jira/commands/checkout.rb +87 -0
- data/lib/jira/commands/comment/add.rb +52 -0
- data/lib/jira/commands/comment/delete.rb +80 -0
- data/lib/jira/commands/comment/list.rb +72 -0
- data/lib/jira/commands/comment/update.rb +88 -0
- data/lib/jira/commands/comment.rb +14 -0
- data/lib/jira/commands/delete.rb +92 -0
- data/lib/jira/commands/describe.rb +64 -0
- data/lib/jira/commands/install.rb +121 -0
- data/lib/jira/commands/link.rb +94 -0
- data/lib/jira/commands/log/add.rb +52 -0
- data/lib/jira/commands/log/delete.rb +80 -0
- data/lib/jira/commands/log/list.rb +69 -0
- data/lib/jira/commands/log/update.rb +89 -0
- data/lib/jira/commands/log.rb +13 -0
- data/lib/jira/commands/new.rb +174 -0
- data/lib/jira/commands/rename.rb +53 -0
- data/lib/jira/commands/sprint.rb +109 -0
- data/lib/jira/commands/tickets.rb +55 -0
- data/lib/jira/commands/transition.rb +97 -0
- data/lib/jira/commands/version.rb +10 -0
- data/lib/jira/commands/vote/add.rb +43 -0
- data/lib/jira/commands/vote/delete.rb +46 -0
- data/lib/jira/commands/vote/list.rb +59 -0
- data/lib/jira/commands/vote.rb +12 -0
- data/lib/jira/commands/watch/add.rb +43 -0
- data/lib/jira/commands/watch/delete.rb +46 -0
- data/lib/jira/commands/watch/list.rb +59 -0
- data/lib/jira/commands/watch.rb +12 -0
- data/lib/jira/constants.rb +7 -0
- data/lib/jira/core.rb +101 -0
- data/lib/jira/exceptions.rb +3 -0
- data/lib/jira/format.rb +78 -0
- data/lib/jira/sprint_api.rb +33 -0
- data/lib/jira.rb +19 -0
- metadata +202 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'update', 'Updates work logged on the input ticket'
|
5
|
+
def update(ticket=Jira::Core.ticket)
|
6
|
+
Command::Log::Update.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Log
|
13
|
+
class Update < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket=Jira::Core.ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return unless logs?
|
23
|
+
api.patch endpoint,
|
24
|
+
params: params,
|
25
|
+
success: on_success,
|
26
|
+
failure: on_failure
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def params
|
32
|
+
{ timeSpent: updated_time_spent }
|
33
|
+
end
|
34
|
+
|
35
|
+
def updated_time_spent
|
36
|
+
io.ask('Updated time spent?')
|
37
|
+
end
|
38
|
+
|
39
|
+
def logs?
|
40
|
+
if json.empty?
|
41
|
+
puts "Ticket #{ticket} has no work logged."
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def endpoint
|
48
|
+
"issue/#{ticket}/worklog/#{to_update['id']}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_success
|
52
|
+
->{ puts "Successfully updated #{to_update['timeSpent']}." }
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_failure
|
56
|
+
->{ puts "No logged work updated." }
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_update
|
60
|
+
@to_delete ||= logs[
|
61
|
+
io.select("Select a worklog to update:", logs.keys)
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def logs
|
66
|
+
@logs ||= (
|
67
|
+
logs = {}
|
68
|
+
json.each do |log|
|
69
|
+
logs[description_for(log)] = log
|
70
|
+
end
|
71
|
+
logs
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def description_for(log)
|
76
|
+
author = log['updateAuthor']['displayName']
|
77
|
+
updated_at = Jira::Format.time(Time.parse(log['updated']))
|
78
|
+
time_spent = log['timeSpent']
|
79
|
+
"#{author} @ #{updated_at}: #{time_spent}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def json
|
83
|
+
@json ||= api.get("issue/#{ticket}/worklog")['worklogs']
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'jira/commands/log/add'
|
2
|
+
require 'jira/commands/log/delete'
|
3
|
+
require 'jira/commands/log/list'
|
4
|
+
require 'jira/commands/log/update'
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
class CLI < Thor
|
8
|
+
|
9
|
+
desc 'log <command>', 'Commands for logging operations in JIRA'
|
10
|
+
subcommand 'log', Log
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "new", "Creates a new ticket in JIRA and checks out the git branch"
|
5
|
+
method_option :project, aliases: "-p", type: :string, default: nil, banner: "PROJECT"
|
6
|
+
method_option :components, aliases: "-c", type: :array, default: nil, lazy_default: [], banner: "COMPONENTS"
|
7
|
+
method_option :issuetype, aliases: "-i", type: :string, default: nil, banner: "ISSUETYPE"
|
8
|
+
method_option :parent, type: :string, default: nil, lazy_default: "", banner: "PARENT"
|
9
|
+
method_option :summary, aliases: "-s", type: :string, default: nil, banner: "SUMMARY"
|
10
|
+
method_option :description, aliases: "-d", type: :string, default: nil, lazy_default: "", banner: "DESCRIPTION"
|
11
|
+
method_option :assignee, aliases: "-a", type: :string, default: nil, lazy_default: "auto", banner: "ASSIGNEE"
|
12
|
+
def new
|
13
|
+
Command::New.new(options).run
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
module Command
|
19
|
+
class New < Base
|
20
|
+
|
21
|
+
attr_accessor :options
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
self.options = options
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
return if metadata.empty?
|
29
|
+
return if project.nil? || project.empty?
|
30
|
+
return if project_metadata.empty?
|
31
|
+
components # Select components if any after a project
|
32
|
+
return if issue_type.nil? || issue_type.empty?
|
33
|
+
return if assign_parent? && parent.empty?
|
34
|
+
return if summary.empty?
|
35
|
+
|
36
|
+
api.post 'issue',
|
37
|
+
params: params,
|
38
|
+
success: on_success,
|
39
|
+
failure: on_failure
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_accessor :ticket
|
45
|
+
|
46
|
+
def params
|
47
|
+
{
|
48
|
+
fields: {
|
49
|
+
project: { id: project['id'] },
|
50
|
+
issuetype: { id: issue_type['id'] },
|
51
|
+
summary: summary,
|
52
|
+
description: description,
|
53
|
+
parent: @parent.nil? ? {} : { key: @parent },
|
54
|
+
components: @components.nil? ? [] : @components
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_success
|
60
|
+
->(json) do
|
61
|
+
self.ticket = json['key']
|
62
|
+
io.say("Ticket #{ticket} created.")
|
63
|
+
assign? if options.empty? || !options['assignee'].nil?
|
64
|
+
create_branch? && checkout_branch? if options.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def assign?
|
69
|
+
Command::Assign.new(ticket, options).run if !options['assignee'].nil? || io.yes?('Assign?')
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_branch?
|
73
|
+
return false if io.no?("Create branch?")
|
74
|
+
`git branch #{ticket} 2> /dev/null`
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def checkout_branch?
|
79
|
+
return false if io.no?("Check-out branch?")
|
80
|
+
`git checkout #{ticket} 2> /dev/null`
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
def on_failure
|
85
|
+
->{ puts "No ticket created." }
|
86
|
+
end
|
87
|
+
|
88
|
+
def metadata
|
89
|
+
# TODO: {} on 200 but jira error
|
90
|
+
@metadata ||= api.get('issue/createmeta')
|
91
|
+
end
|
92
|
+
|
93
|
+
def project
|
94
|
+
@project ||= projects[
|
95
|
+
options['project'] || io.select("Select a project:", projects.keys)
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
def projects
|
100
|
+
@projects ||= (
|
101
|
+
projects = {}
|
102
|
+
metadata['projects'].each do |project|
|
103
|
+
projects[project['name']] = {
|
104
|
+
'id' => project['id'],
|
105
|
+
'issues' => project['issuetypes']
|
106
|
+
}
|
107
|
+
end
|
108
|
+
projects
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def project_metadata
|
113
|
+
id = project['id']
|
114
|
+
@project_metadata ||= api.get("project/#{id}")
|
115
|
+
end
|
116
|
+
|
117
|
+
def components
|
118
|
+
@components ||= (
|
119
|
+
components = {}
|
120
|
+
project_metadata['components'].each do |component|
|
121
|
+
components[component['name']] = { 'id' => component['id'] }
|
122
|
+
end
|
123
|
+
unless components.empty?
|
124
|
+
if options['components'].nil?
|
125
|
+
components = io.multi_select("Select component(s):", components)
|
126
|
+
else
|
127
|
+
components.select! { |k| options['components'].include?(k) }
|
128
|
+
components = components.values
|
129
|
+
end
|
130
|
+
end
|
131
|
+
components.to_a
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
def assign_parent?
|
136
|
+
return false unless issue_type['subtask']
|
137
|
+
return false if options['parent'].nil? && io.no?('Set parent of subtask?')
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def parent
|
142
|
+
@parent ||= options['parent'] || io.ask('Subtask parent:', default: Jira::Core.ticket)
|
143
|
+
end
|
144
|
+
|
145
|
+
def issue_type
|
146
|
+
@issue_type ||= issue_types[
|
147
|
+
options['issuetype'] || io.select("Select an issue type:", issue_types.keys)
|
148
|
+
]
|
149
|
+
end
|
150
|
+
|
151
|
+
def issue_types
|
152
|
+
@issue_types ||= (
|
153
|
+
issue_types = {}
|
154
|
+
project['issues'].each do |issue_type|
|
155
|
+
issue_types[issue_type['name']] = issue_type
|
156
|
+
end
|
157
|
+
issue_types
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
def summary
|
162
|
+
@summary ||= options['summary'] || io.ask("Summary:", default: '')
|
163
|
+
end
|
164
|
+
|
165
|
+
def description
|
166
|
+
@description ||= (
|
167
|
+
description = options['description'] || (io.ask("Description:", default: '') if options['summary'].nil?)
|
168
|
+
description ||= ""
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "rename", "Updates the summary of the input ticket"
|
5
|
+
method_option :summary, aliases: "-s", type: :string, default: nil, lazy_default: "", banner: "SUMMARY"
|
6
|
+
def rename(ticket=Jira::Core.ticket)
|
7
|
+
Command::Rename.new(ticket, options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
class Rename < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket, :options
|
16
|
+
|
17
|
+
def initialize(ticket, options)
|
18
|
+
self.ticket = ticket
|
19
|
+
self.options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
return if ticket.empty?
|
24
|
+
return if summary.empty?
|
25
|
+
api.patch "issue/#{ticket}",
|
26
|
+
params: params,
|
27
|
+
success: on_success,
|
28
|
+
failure: on_failure
|
29
|
+
end
|
30
|
+
|
31
|
+
def params
|
32
|
+
{
|
33
|
+
fields: {
|
34
|
+
summary: summary
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_success
|
40
|
+
->{ puts "Successfully updated ticket #{ticket}'s summary." }
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_failure
|
44
|
+
->{ puts "No change made to ticket #{ticket}." }
|
45
|
+
end
|
46
|
+
|
47
|
+
def summary
|
48
|
+
@summary ||= options['summary'] || io.ask("New summary for ticket #{ticket}:", default: '')
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "sprint", "Lists sprint info"
|
5
|
+
def sprint(active = false)
|
6
|
+
Command::Sprint.new(active).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class Sprint < Base
|
13
|
+
|
14
|
+
attr_accessor :active
|
15
|
+
|
16
|
+
def initialize(active)
|
17
|
+
self.active = active
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def run
|
22
|
+
return if rapid_view.empty?
|
23
|
+
return if no_sprints?
|
24
|
+
return if sprint.empty?
|
25
|
+
if active == active
|
26
|
+
sprint_id = info['sprint']['id']
|
27
|
+
jql = "sprint = #{sprint_id}"
|
28
|
+
Command::Tickets.new(jql).run
|
29
|
+
else
|
30
|
+
render_table(
|
31
|
+
[ 'Sprint', 'State' ],
|
32
|
+
[ [ info['sprint']['name'], info['sprint']['state'] ] ]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def no_sprints?
|
40
|
+
if sprints.empty?
|
41
|
+
puts "The #{rapid_view['name']} board has no sprints."
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def info
|
48
|
+
@info ||= sprint_api.sprint(rapid_view['id'], sprint['id'])
|
49
|
+
end
|
50
|
+
|
51
|
+
def sprint
|
52
|
+
@sprint ||= active_sprint || sprints[
|
53
|
+
io.select("Select a sprint:", sprints.keys[-10..-1])
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
def sprints
|
58
|
+
@sprints ||= (
|
59
|
+
sprints = {}
|
60
|
+
sprint_api.sprints(rapid_view['id'])['sprints'].each do |sprint|
|
61
|
+
sprints["#{sprint['name']}"] = {
|
62
|
+
'id' => sprint['id'],
|
63
|
+
'name' => sprint['name'],
|
64
|
+
'state' => sprint['state']
|
65
|
+
}
|
66
|
+
end
|
67
|
+
sprints
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def active_sprint
|
72
|
+
sprints = {}
|
73
|
+
sprint_api.sprints(rapid_view['id'])['sprints'].each do |sprint|
|
74
|
+
if sprint['state'] == "ACTIVE"
|
75
|
+
sprints = {
|
76
|
+
'id' => sprint['id'],
|
77
|
+
'name' => sprint['name'],
|
78
|
+
'state' => sprint['state']
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
sprints
|
83
|
+
end
|
84
|
+
|
85
|
+
def rapid_view
|
86
|
+
keys = rapid_views.keys
|
87
|
+
return '' if keys.empty?
|
88
|
+
# @rapid_view ||= rapid_views[
|
89
|
+
# io.select("Select a rapid view:", keys)
|
90
|
+
# ]
|
91
|
+
@rapid_view = {"id"=>15, "name"=>"Ruby Team"}
|
92
|
+
end
|
93
|
+
|
94
|
+
def rapid_views
|
95
|
+
@rapid_views ||= (
|
96
|
+
rapid_views = {}
|
97
|
+
sprint_api.rapid_views.each do |rapid_view|
|
98
|
+
rapid_views[rapid_view['name']] = {
|
99
|
+
'id' => rapid_view['id'],
|
100
|
+
'name' => rapid_view['name']
|
101
|
+
}
|
102
|
+
end
|
103
|
+
rapid_views
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "tickets [jql]", "List the tickets of the input jql"
|
5
|
+
def tickets(jql="assignee=#{Jira::Core.username} AND status = \"In Progress\"")
|
6
|
+
Command::Tickets.new(jql).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class Tickets < Base
|
13
|
+
|
14
|
+
attr_accessor :jql
|
15
|
+
|
16
|
+
def initialize(jql)
|
17
|
+
self.jql = jql
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
return if jql.empty?
|
22
|
+
return if metadata.empty?
|
23
|
+
return unless metadata['errorMessages'].nil?
|
24
|
+
|
25
|
+
if rows.empty?
|
26
|
+
puts "There are no tickets for jql=#{jql}."
|
27
|
+
return
|
28
|
+
end
|
29
|
+
render_table(header, rows)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def header
|
35
|
+
[ 'Ticket', 'Assignee', 'Status', 'Summary']
|
36
|
+
end
|
37
|
+
|
38
|
+
def rows
|
39
|
+
metadata['issues'].map do |issue|
|
40
|
+
[
|
41
|
+
issue['key'],
|
42
|
+
(issue['fields']['assignee']['name'] unless issue['fields']['assignee'].nil?) || 'Unassigned',
|
43
|
+
(issue['fields']['status']['name'] unless issue['fields']['status'].nil?) || 'Unknown',
|
44
|
+
truncate(issue['fields']['summary'] || '', 45)
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def metadata
|
50
|
+
@metadata ||= api.get("search?jql=#{jql}")
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "transition", "Transitions the input ticket to the next state"
|
5
|
+
method_option :transition, aliases: "-t", type: :string, default: nil, lazy_default: "", banner: "TRANSITION"
|
6
|
+
method_option :resolution, aliases: "-r", type: :string, default: nil, lazy_default: "", banner: "RESOLUTION"
|
7
|
+
def transition(ticket=Jira::Core.ticket)
|
8
|
+
Command::Transition.new(ticket, options).run
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
module Command
|
14
|
+
class Transition < Base
|
15
|
+
|
16
|
+
attr_accessor :ticket, :options
|
17
|
+
|
18
|
+
def initialize(ticket, options)
|
19
|
+
self.ticket = ticket
|
20
|
+
self.options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
return if ticket.empty?
|
25
|
+
return if metadata.empty?
|
26
|
+
return unless metadata['errorMessages'].nil?
|
27
|
+
return if transition.nil? || transition.empty?
|
28
|
+
|
29
|
+
api.post "issue/#{ticket}/transitions",
|
30
|
+
params: params,
|
31
|
+
success: on_success,
|
32
|
+
failure: on_failure
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def params
|
38
|
+
{
|
39
|
+
transition: { id: transition[:id] },
|
40
|
+
fields: transition[:resolution?] ? { resolution: { name: resolution } } : {}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_success
|
45
|
+
->{ puts "Transitioned ticket #{ticket} to #{transition_name}." }
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_failure
|
49
|
+
->{ puts "Failed to transition ticket #{ticket}." }
|
50
|
+
end
|
51
|
+
|
52
|
+
def transition_name
|
53
|
+
transitions.invert[transition]
|
54
|
+
end
|
55
|
+
|
56
|
+
def transition
|
57
|
+
@transition ||= transitions[
|
58
|
+
options['transition'] || io.select("Transition #{ticket} to:", transitions.keys)
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
def transitions
|
63
|
+
@transitions ||= (
|
64
|
+
transitions = {}
|
65
|
+
metadata['transitions'].each do |transition|
|
66
|
+
transitions[transition['to']['name']] = {
|
67
|
+
id: transition['id'],
|
68
|
+
resolution?: !!transition['fields'].fetch('resolution', {})['required']
|
69
|
+
}
|
70
|
+
end
|
71
|
+
transitions
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def metadata
|
76
|
+
@metadata ||= api.get("issue/#{ticket}/transitions?expand=transitions.fields")
|
77
|
+
end
|
78
|
+
|
79
|
+
def resolution
|
80
|
+
@resolution ||= resolutions[
|
81
|
+
options['resolution'] || io.select("Resolve #{ticket} as:", resolutions.keys)
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
def resolutions
|
86
|
+
@resolutions ||= (
|
87
|
+
resolutions = {}
|
88
|
+
api.get("resolution").each do |resolution|
|
89
|
+
resolutions[resolution['name']] = resolution['name']
|
90
|
+
end
|
91
|
+
resolutions
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Jira
|
2
|
+
class Vote < Thor
|
3
|
+
|
4
|
+
desc 'add', 'Vote for the input ticket'
|
5
|
+
def add(ticket=Jira::Core.ticket)
|
6
|
+
Command::Vote::Add.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Vote
|
13
|
+
class Add < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return if ticket.empty?
|
23
|
+
|
24
|
+
api.post "issue/#{ticket}/votes",
|
25
|
+
params: "\"#{Jira::Core.username}\"",
|
26
|
+
success: on_success,
|
27
|
+
failure: on_failure
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def on_success
|
33
|
+
->{ puts "Successfully voted for ticket #{ticket}" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_failure
|
37
|
+
->{ puts "No vote cast." }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Jira
|
2
|
+
class Vote < Thor
|
3
|
+
|
4
|
+
desc 'delete', 'Delete vote for the input ticket'
|
5
|
+
def delete(ticket=Jira::Core.ticket)
|
6
|
+
Command::Vote::Delete.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Vote
|
13
|
+
class Delete < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return if ticket.empty?
|
23
|
+
|
24
|
+
api.delete endpoint,
|
25
|
+
success: on_success,
|
26
|
+
failure: on_failure
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def endpoint
|
32
|
+
"issue/#{ticket}/votes?username=#{Jira::Core.username}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_success
|
36
|
+
->{ puts "Successfully removed vote from ticket #{ticket}" }
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_failure
|
40
|
+
->{ puts "No unvote." }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|