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,88 @@
|
|
1
|
+
module Jira
|
2
|
+
class Comment < Thor
|
3
|
+
|
4
|
+
desc 'update', 'Update a comment to the input ticket'
|
5
|
+
def update(ticket=Jira::Core.ticket)
|
6
|
+
Command::Comment::Update.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Comment
|
13
|
+
class Update < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return unless comments?
|
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
|
+
{ body: body }
|
33
|
+
end
|
34
|
+
|
35
|
+
def comments?
|
36
|
+
if json.empty?
|
37
|
+
puts "Ticket #{ticket} has no comments."
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def endpoint
|
44
|
+
"issue/#{ticket}/comment/#{to_update['id']}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_success
|
48
|
+
->{
|
49
|
+
puts "Successfully updated comment originally from"\
|
50
|
+
" #{to_update['updateAuthor']['displayName']}."
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_failure
|
55
|
+
->{ puts "No comment updated." }
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_update
|
59
|
+
@to_update ||= comments[
|
60
|
+
io.select("Select a comment to update:", comments.keys)
|
61
|
+
]
|
62
|
+
end
|
63
|
+
|
64
|
+
def comments
|
65
|
+
@comments ||= (
|
66
|
+
comments = {}
|
67
|
+
json.each do |comment|
|
68
|
+
comments[description_for(comment)] = comment
|
69
|
+
end
|
70
|
+
comments
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def description_for(comment)
|
75
|
+
author = comment['updateAuthor']['displayName']
|
76
|
+
updated_at = Jira::Format.time(Time.parse(comment['updated']))
|
77
|
+
body = comment['body'].split.join(" ")
|
78
|
+
truncate("#{author} @ #{updated_at}: #{body}", 160)
|
79
|
+
end
|
80
|
+
|
81
|
+
def json
|
82
|
+
@json ||= api.get("issue/#{ticket}/comment")['comments']
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'jira/commands/comment/add'
|
2
|
+
require 'jira/commands/comment/delete'
|
3
|
+
require 'jira/commands/comment/list'
|
4
|
+
require 'jira/commands/comment/update'
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
class CLI < Thor
|
8
|
+
|
9
|
+
desc 'comment <command>', 'Commands for comment operations in JIRA'
|
10
|
+
subcommand 'comment', Comment
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "delete", "Deletes a ticket in JIRA and the git branch"
|
5
|
+
method_option :force, type: :boolean, default: false
|
6
|
+
def delete(ticket=Jira::Core.ticket)
|
7
|
+
Command::Delete.new(ticket, options[:force]).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
class Delete < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket, :force
|
16
|
+
|
17
|
+
def initialize(ticket, force)
|
18
|
+
self.ticket = ticket
|
19
|
+
self.force = force
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
return if ticket.empty?
|
24
|
+
return if metadata.empty?
|
25
|
+
return if metadata['fields'].nil?
|
26
|
+
return if subtasks_failure?
|
27
|
+
|
28
|
+
api.delete "issue/#{ticket}?deleteSubtasks=#{force}",
|
29
|
+
success: on_success,
|
30
|
+
failure: on_failure
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def on_success
|
36
|
+
-> do
|
37
|
+
on_failure and return unless create_branch?
|
38
|
+
on_failure and return unless delete_branch?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_failure
|
43
|
+
-> { puts "No change made to ticket #{ticket}." }
|
44
|
+
end
|
45
|
+
|
46
|
+
def branches
|
47
|
+
branches = `git branch --list 2> /dev/null`.split(' ')
|
48
|
+
branches.delete("*")
|
49
|
+
branches.delete(ticket.to_s)
|
50
|
+
branches
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_branch?
|
54
|
+
response = io.yes?("Create branch?")
|
55
|
+
|
56
|
+
if branches.count == 1 or response
|
57
|
+
io.say("Creating a new branch.")
|
58
|
+
new_branch = io.ask("Branch?").strip
|
59
|
+
new_branch.delete!(" ")
|
60
|
+
on_failure and return false if new_branch.empty?
|
61
|
+
`git branch #{new_branch} 2> /dev/null`
|
62
|
+
end
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_branch?
|
67
|
+
response = self.io.select("Select a branch:", branches)
|
68
|
+
`git checkout #{response} 2> /dev/null`
|
69
|
+
`git branch -D #{ticket} 2> /dev/null`
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def subtasks_failure?
|
74
|
+
return false unless subtask?
|
75
|
+
if !metadata['fields']['subtasks'].empty? && !force
|
76
|
+
self.force = io.yes?("Delete all sub-tasks for ticket #{ticket}?")
|
77
|
+
return true unless force
|
78
|
+
end
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def subtask?
|
83
|
+
metadata['fields']['issuetype']['subtask']
|
84
|
+
end
|
85
|
+
|
86
|
+
def metadata
|
87
|
+
@metadata ||= api.get("issue/#{ticket}")
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "describe", "Describes the input ticket"
|
5
|
+
def describe(ticket=Jira::Core.ticket)
|
6
|
+
Command::Describe.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class Describe < Base
|
13
|
+
|
14
|
+
attr_accessor :ticket
|
15
|
+
|
16
|
+
def initialize(ticket)
|
17
|
+
self.ticket = ticket
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
return if json.empty?
|
22
|
+
return unless errors.empty?
|
23
|
+
render_table(header, [row])
|
24
|
+
end
|
25
|
+
|
26
|
+
def header
|
27
|
+
[ 'Ticket', 'Assignee', 'Status', 'Summary', 'Description' ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def row
|
31
|
+
[ ticket, assignee, status, summary, description ]
|
32
|
+
end
|
33
|
+
|
34
|
+
def errors
|
35
|
+
@errors ||= (json['errorMessages'] || []).join('. ')
|
36
|
+
end
|
37
|
+
|
38
|
+
def assignee
|
39
|
+
(fields['assignee'] || {})['name'] || 'Unassigned'
|
40
|
+
end
|
41
|
+
|
42
|
+
def status
|
43
|
+
(fields['status'] || {})['name'] || 'Unknown'
|
44
|
+
end
|
45
|
+
|
46
|
+
def summary
|
47
|
+
truncate(json['fields']['summary'], 45)
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
json['fields']['description']
|
52
|
+
end
|
53
|
+
|
54
|
+
def fields
|
55
|
+
json['fields'] || {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def json
|
59
|
+
@json ||= api.get "issue/#{ticket}"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "install", "Guides the user through JIRA CLI installation"
|
5
|
+
def install
|
6
|
+
Command::Install.new.run
|
7
|
+
end
|
8
|
+
|
9
|
+
no_tasks do
|
10
|
+
def try_install_cookie
|
11
|
+
return false if Jira::Core.cookie.empty?
|
12
|
+
puts " ... cookie expired, renewing your cookie"
|
13
|
+
Command::Install.new.run_rescue_cookie
|
14
|
+
puts "Cookie renewed, updating .jira-rescue-cookie"
|
15
|
+
File.open(Jira::Core.rescue_cookie_path, "a") do |f|
|
16
|
+
f << "r"
|
17
|
+
end
|
18
|
+
puts " ... updated .jira-rescue-cookie"
|
19
|
+
return true
|
20
|
+
rescue Interrupt, StandardError
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module Command
|
28
|
+
class Install < Base
|
29
|
+
|
30
|
+
def run
|
31
|
+
io.say('Please enter your JIRA information.')
|
32
|
+
inifile[:global] = base_params
|
33
|
+
inifile.write # Do this now because cookie authentication uses api calls
|
34
|
+
|
35
|
+
inifile.delete_section("cookie") if inifile.has_section?("cookie")
|
36
|
+
case authentication
|
37
|
+
when "basic"
|
38
|
+
inifile[:global][:password] = password
|
39
|
+
when "token"
|
40
|
+
inifile[:global][:token] = token
|
41
|
+
when "cookie"
|
42
|
+
response = cookie(session_params)
|
43
|
+
inifile[:cookie] = {}
|
44
|
+
inifile[:cookie][:name] = response['name']
|
45
|
+
inifile[:cookie][:value] = response['value']
|
46
|
+
end
|
47
|
+
inifile.write
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_rescue_cookie
|
51
|
+
response = cookie(rescue_cookie_params)
|
52
|
+
config = Jira::Core.config
|
53
|
+
config[:cookie] = {}
|
54
|
+
config[:cookie][:name] = response['name']
|
55
|
+
config[:cookie][:value] = response['value']
|
56
|
+
config.write
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def base_params
|
62
|
+
{
|
63
|
+
url: url,
|
64
|
+
username: username
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def rescue_cookie_params
|
69
|
+
{
|
70
|
+
username: Jira::Core.username,
|
71
|
+
password: password
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def session_params
|
76
|
+
{
|
77
|
+
username: username,
|
78
|
+
password: password
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def authentication
|
83
|
+
@authentication ||= io.select(
|
84
|
+
"Select an authentication type:",
|
85
|
+
["basic", "cookie", "token"]
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def url
|
90
|
+
@url ||= io.ask("JIRA URL:")
|
91
|
+
end
|
92
|
+
|
93
|
+
def username
|
94
|
+
@username ||= io.ask("JIRA username:")
|
95
|
+
end
|
96
|
+
|
97
|
+
def password
|
98
|
+
io.mask("JIRA password:")
|
99
|
+
end
|
100
|
+
|
101
|
+
def token
|
102
|
+
io.ask("JIRA token:")
|
103
|
+
end
|
104
|
+
|
105
|
+
def cookie(params)
|
106
|
+
response = auth_api.post('session', params: params)
|
107
|
+
return {} unless response['errorMessages'].nil?
|
108
|
+
response['session']
|
109
|
+
end
|
110
|
+
|
111
|
+
def inifile
|
112
|
+
@inifile ||= IniFile.new(
|
113
|
+
comment: '#',
|
114
|
+
encoding: 'UTF-8',
|
115
|
+
filename: Jira::Core.cli_path
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "link", "Creates a link between two tickets in JIRA"
|
5
|
+
def link(ticket=Jira::Core.ticket)
|
6
|
+
Command::Link.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class Link < Base
|
13
|
+
|
14
|
+
attr_accessor :ticket
|
15
|
+
|
16
|
+
def initialize(ticket)
|
17
|
+
self.ticket = ticket
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
return if ticket.empty?
|
22
|
+
return if metadata.empty?
|
23
|
+
return if issue_link_type.empty?
|
24
|
+
return if outward_ticket.empty?
|
25
|
+
return unless invalid_ticket?
|
26
|
+
|
27
|
+
begin
|
28
|
+
api.post "issueLink",
|
29
|
+
params: params,
|
30
|
+
success: on_success,
|
31
|
+
failure: on_failure
|
32
|
+
rescue CommandException
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def params
|
39
|
+
{
|
40
|
+
type: {
|
41
|
+
name: issue_link_type[:name]
|
42
|
+
},
|
43
|
+
inwardIssue: {
|
44
|
+
key: ticket
|
45
|
+
},
|
46
|
+
outwardIssue: {
|
47
|
+
key: outward_ticket
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def issue_link_type
|
53
|
+
return @issue_link_type unless @issue_link_type.nil?
|
54
|
+
|
55
|
+
types = {}
|
56
|
+
metadata['issueLinkTypes'].each do |type|
|
57
|
+
data = {
|
58
|
+
id: type['id'],
|
59
|
+
name: type['name'],
|
60
|
+
inward: type['inward'],
|
61
|
+
outward: type['outward']
|
62
|
+
}
|
63
|
+
types[type['name']] = data
|
64
|
+
end
|
65
|
+
choice = io.select("Select a link type:", types.keys)
|
66
|
+
@issue_link_type = types[choice]
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_success
|
70
|
+
->{
|
71
|
+
puts "Successfully linked ticket #{ticket} to"\
|
72
|
+
" ticket #{outward_ticket}."
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_failure
|
77
|
+
->{ puts "No ticket linked." }
|
78
|
+
end
|
79
|
+
|
80
|
+
def outward_ticket
|
81
|
+
@outward_ticket ||= io.ask("Outward ticket:").strip
|
82
|
+
end
|
83
|
+
|
84
|
+
def invalid_ticket?
|
85
|
+
!Jira::Core.ticket?(outward_ticket)
|
86
|
+
end
|
87
|
+
|
88
|
+
def metadata
|
89
|
+
@metadata ||= api.get("issueLinkType")
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'add', 'Logs work against the input ticket'
|
5
|
+
method_option :time, aliases: "-t", type: :string, default: nil, lazy_default: "", banner: "TIME"
|
6
|
+
def add(ticket=Jira::Core.ticket)
|
7
|
+
Command::Log::Add.new(ticket, options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
module Log
|
14
|
+
class Add < 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 time_spent.empty?
|
25
|
+
api.post "issue/#{ticket}/worklog",
|
26
|
+
params: params,
|
27
|
+
success: on_success,
|
28
|
+
failure: on_failure
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def params
|
34
|
+
{ timeSpent: time_spent }
|
35
|
+
end
|
36
|
+
|
37
|
+
def time_spent
|
38
|
+
@time_spent ||= options['time'] || io.ask("Time spent on ticket #{ticket}:")
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_success
|
42
|
+
->{ puts "Successfully logged #{time_spent} on ticket #{ticket}." }
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_failure
|
46
|
+
->{ puts "No work was logged on ticket #{ticket}." }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'delete', 'Deletes logged work against the input ticket'
|
5
|
+
def delete(ticket=Jira::Core.ticket)
|
6
|
+
Command::Log::Delete.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Log
|
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 unless logs?
|
23
|
+
api.delete endpoint,
|
24
|
+
success: on_success,
|
25
|
+
failure: on_failure
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def logs?
|
31
|
+
if json.empty?
|
32
|
+
puts "Ticket #{ticket} has no work logged."
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def endpoint
|
39
|
+
"issue/#{ticket}/worklog/#{to_delete['id']}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_success
|
43
|
+
->{ puts "Successfully deleted #{to_delete['timeSpent']}." }
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_failure
|
47
|
+
->{ puts "No logged work deleted." }
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_delete
|
51
|
+
@to_delete ||= logs[
|
52
|
+
io.select("Select a worklog to delete:", logs.keys)
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
def logs
|
57
|
+
@logs ||= (
|
58
|
+
logs = {}
|
59
|
+
json.each do |log|
|
60
|
+
logs[description_for(log)] = log
|
61
|
+
end
|
62
|
+
logs
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def description_for(log)
|
67
|
+
author = log['updateAuthor']['displayName']
|
68
|
+
updated_at = Jira::Format.time(Time.parse(log['updated']))
|
69
|
+
time_spent = log['timeSpent']
|
70
|
+
"#{author} @ #{updated_at}: #{time_spent}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def json
|
74
|
+
@json ||= api.get("issue/#{ticket}/worklog")['worklogs']
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'list', 'Lists work logged on the input ticket'
|
5
|
+
def list(ticket=Jira::Core.ticket)
|
6
|
+
Command::Log::List.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Log
|
13
|
+
class List < 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
|
+
if logs.empty?
|
23
|
+
puts "Ticket #{ticket} has no work logged."
|
24
|
+
return
|
25
|
+
end
|
26
|
+
render_table(header, rows)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_accessor :log
|
32
|
+
|
33
|
+
def header
|
34
|
+
[ 'Author', 'Updated At', 'Time Spent' ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def rows
|
38
|
+
rows = []
|
39
|
+
logs.each do |log|
|
40
|
+
self.log = log
|
41
|
+
rows << row
|
42
|
+
end
|
43
|
+
rows
|
44
|
+
end
|
45
|
+
|
46
|
+
def row
|
47
|
+
[ author, updated_at, time_spent ]
|
48
|
+
end
|
49
|
+
|
50
|
+
def author
|
51
|
+
log['updateAuthor']['displayName']
|
52
|
+
end
|
53
|
+
|
54
|
+
def updated_at
|
55
|
+
Jira::Format.time(Time.parse(log['updated']))
|
56
|
+
end
|
57
|
+
|
58
|
+
def time_spent
|
59
|
+
log['timeSpent']
|
60
|
+
end
|
61
|
+
|
62
|
+
def logs
|
63
|
+
@logs ||= api.get("issue/#{ticket}/worklog")['worklogs']
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|