ruby-jira-cli 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|