lazylead 0.6.2 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +7 -2
  3. data/.docker/Dockerfile +17 -6
  4. data/.docs/duedate_expired.md +1 -1
  5. data/.rubocop.yml +119 -3
  6. data/.simplecov +1 -1
  7. data/Gemfile +1 -1
  8. data/Guardfile +2 -2
  9. data/Rakefile +7 -4
  10. data/bin/lazylead +4 -2
  11. data/lazylead.gemspec +33 -29
  12. data/lib/lazylead.rb +2 -2
  13. data/lib/lazylead/allocated.rb +1 -1
  14. data/lib/lazylead/cc.rb +22 -21
  15. data/lib/lazylead/cli/app.rb +3 -3
  16. data/lib/lazylead/confluence.rb +10 -3
  17. data/lib/lazylead/email.rb +1 -1
  18. data/lib/lazylead/exchange.rb +1 -1
  19. data/lib/lazylead/home.rb +1 -1
  20. data/lib/lazylead/log.rb +1 -1
  21. data/lib/lazylead/model.rb +25 -17
  22. data/lib/lazylead/opts.rb +22 -4
  23. data/lib/lazylead/postman.rb +1 -1
  24. data/lib/lazylead/requires.rb +38 -0
  25. data/lib/lazylead/salt.rb +2 -1
  26. data/lib/lazylead/schedule.rb +1 -1
  27. data/lib/lazylead/smtp.rb +1 -1
  28. data/lib/lazylead/system/empty.rb +1 -1
  29. data/lib/lazylead/system/fake.rb +1 -1
  30. data/lib/lazylead/system/jira.rb +28 -20
  31. data/lib/lazylead/system/synced.rb +1 -1
  32. data/lib/lazylead/task/accuracy/accuracy.rb +26 -32
  33. data/lib/lazylead/task/accuracy/affected_build.rb +1 -1
  34. data/lib/lazylead/task/accuracy/attachment.rb +3 -7
  35. data/lib/lazylead/task/accuracy/environment.rb +1 -1
  36. data/lib/lazylead/task/accuracy/logs.rb +9 -6
  37. data/lib/lazylead/task/accuracy/onlyll.rb +147 -0
  38. data/lib/lazylead/task/accuracy/records.rb +2 -2
  39. data/lib/lazylead/task/accuracy/requirement.rb +1 -1
  40. data/lib/lazylead/task/accuracy/servers.rb +17 -8
  41. data/lib/lazylead/task/accuracy/stacktrace.rb +2 -2
  42. data/lib/lazylead/task/accuracy/testcase.rb +3 -3
  43. data/lib/lazylead/task/accuracy/wiki.rb +1 -1
  44. data/lib/lazylead/task/{alert.rb → alert/alert.rb} +6 -6
  45. data/lib/lazylead/task/alert/alertif.rb +75 -0
  46. data/lib/lazylead/task/alert/changed_to.rb +58 -0
  47. data/lib/lazylead/task/assignment.rb +1 -1
  48. data/lib/lazylead/task/confluence_ref.rb +1 -1
  49. data/lib/lazylead/task/echo.rb +1 -1
  50. data/lib/lazylead/task/fix_version.rb +1 -1
  51. data/lib/lazylead/task/loading.rb +94 -0
  52. data/lib/lazylead/task/missing_comment.rb +1 -1
  53. data/lib/lazylead/task/propagate_down.rb +4 -4
  54. data/lib/lazylead/task/savepoint.rb +1 -1
  55. data/lib/lazylead/task/svn/diff.rb +2 -2
  56. data/lib/lazylead/task/svn/grep.rb +27 -20
  57. data/lib/lazylead/task/svn/touch.rb +3 -5
  58. data/lib/lazylead/version.rb +2 -2
  59. data/lib/messages/accuracy.erb +1 -1
  60. data/lib/messages/alertif.erb +134 -0
  61. data/lib/messages/created_recently.erb +110 -0
  62. data/lib/messages/loading.erb +117 -0
  63. data/lib/messages/only_ll.erb +107 -0
  64. data/readme.md +26 -18
  65. data/test/lazylead/allocated_test.rb +1 -1
  66. data/test/lazylead/cc_test.rb +2 -1
  67. data/test/lazylead/cli/app_test.rb +2 -2
  68. data/test/lazylead/confluence_test.rb +1 -1
  69. data/test/lazylead/exchange_test.rb +1 -1
  70. data/test/lazylead/model_test.rb +11 -1
  71. data/test/lazylead/opts_test.rb +13 -1
  72. data/test/lazylead/postman_test.rb +1 -1
  73. data/test/lazylead/salt_test.rb +1 -1
  74. data/test/lazylead/smoke_test.rb +14 -1
  75. data/test/lazylead/smtp_test.rb +2 -5
  76. data/test/lazylead/system/jira_test.rb +10 -1
  77. data/test/lazylead/task/accuracy/accuracy_test.rb +1 -1
  78. data/test/lazylead/task/accuracy/affected_build_test.rb +1 -1
  79. data/test/lazylead/task/accuracy/attachment_test.rb +2 -2
  80. data/test/lazylead/task/accuracy/environment_test.rb +1 -1
  81. data/test/lazylead/task/accuracy/logs_test.rb +13 -1
  82. data/test/lazylead/task/accuracy/onlyll_test.rb +138 -0
  83. data/test/lazylead/task/accuracy/records_test.rb +1 -1
  84. data/test/lazylead/task/accuracy/score_test.rb +29 -2
  85. data/test/lazylead/task/accuracy/servers_test.rb +3 -3
  86. data/test/lazylead/task/accuracy/stacktrace_test.rb +1 -1
  87. data/test/lazylead/task/accuracy/testcase_test.rb +28 -1
  88. data/test/lazylead/task/accuracy/wiki_test.rb +1 -1
  89. data/test/lazylead/task/alert/alertif_test.rb +54 -0
  90. data/test/lazylead/task/{assignee_alert_test.rb → alert/assignee_alert_test.rb} +10 -10
  91. data/test/lazylead/task/alert/changed_to_test.rb +42 -0
  92. data/test/lazylead/task/assignment_test.rb +1 -1
  93. data/test/lazylead/task/confluence_ref_test.rb +1 -1
  94. data/test/lazylead/task/created_recently_test.rb +53 -0
  95. data/test/lazylead/task/duedate_test.rb +5 -12
  96. data/test/lazylead/task/echo_test.rb +1 -1
  97. data/test/lazylead/task/fix_version_test.rb +1 -1
  98. data/test/lazylead/task/loading_test.rb +54 -0
  99. data/test/lazylead/task/missing_comment_test.rb +1 -1
  100. data/test/lazylead/task/propagate_down_test.rb +5 -4
  101. data/test/lazylead/task/savepoint_test.rb +3 -3
  102. data/test/lazylead/task/svn/diff_test.rb +1 -1
  103. data/test/lazylead/task/svn/grep_test.rb +1 -1
  104. data/test/lazylead/task/svn/touch_test.rb +1 -1
  105. data/test/lazylead/version_test.rb +1 -1
  106. data/test/sqlite_test.rb +1 -1
  107. data/test/test.rb +8 -9
  108. data/upgrades/sqlite/001-install-main-lazylead-tables.sql +1 -1
  109. data/upgrades/sqlite/999.testdata.sql +3 -2
  110. metadata +133 -58
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -57,8 +57,8 @@ module Lazylead
57
57
  private
58
58
 
59
59
  def apply_vcs_migration(opts)
60
- @db = File.expand_path(opts[:home]) + "/" + opts[:sqlite]
61
- vcs = File.expand_path(opts[:home]) + "/" + opts[:vcs4sql]
60
+ @db = "#{File.expand_path(opts[:home])}/#{opts[:sqlite]}"
61
+ vcs = "#{File.expand_path(opts[:home])}/#{opts[:vcs4sql]}"
62
62
  @log.debug "Database: '#{@db.colorize(:light_blue)}', "\
63
63
  "sql migration dir: '#{vcs.colorize(:light_blue)}'"
64
64
  Vcs4sql::Sqlite::Migration.new(@db).upgrade vcs, opts[:testdata]
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -104,16 +104,23 @@ module Lazylead
104
104
  end
105
105
 
106
106
  # Detect links mentioned in ticket comments
107
+ # @todo #/DEV Refactor this method in order to make it more human-readable and remove the
108
+ # suppresion below
109
+ #
110
+ # rubocop:disable Metrics/CyclomaticComplexity
111
+ # rubocop:disable Metrics/PerceivedComplexity
107
112
  def mentioned_links(ticket)
108
113
  ticket.comments
109
114
  .map { |cmnt| cmnt.attrs["body"] }
110
115
  .select { |cmnt| @confl.any? { |c| cmnt.include? c.url } }
111
- .flat_map { |cmnt| cmnt.split " " }
116
+ .flat_map(&:split)
112
117
  .select { |cmnt| @confl.any? { |c| cmnt.start_with? c.url } }
113
- .map(&method(:to_page_id))
118
+ .map { |url| to_page_id(url) }
114
119
  .reject(&:blank?)
115
120
  .uniq
116
121
  end
122
+ # rubocop:enable Metrics/CyclomaticComplexity
123
+ # rubocop:enable Metrics/PerceivedComplexity
117
124
 
118
125
  # Convert confluence page url to the following format:
119
126
  # http://confluence.com/pages/viewpage.action?pageId=xxxxx
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
data/lib/lazylead/home.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
data/lib/lazylead/log.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -68,9 +68,7 @@ module Lazylead
68
68
  opts.each_with_object({}) do |e, o|
69
69
  k = e[0]
70
70
  v = e[1]
71
- if v.respond_to? :start_with?
72
- v = ENV[v.slice(2, v.length - 3)] if v.start_with? "${"
73
- end
71
+ v = ENV[v.slice(2, v.length - 3)] if v.respond_to?(:start_with?) && v.start_with?("${")
74
72
  o[k] = v
75
73
  end
76
74
  end
@@ -106,6 +104,7 @@ module Lazylead
106
104
  sys = system.connect
107
105
  opts = props
108
106
  opts = detect_cc(sys) if opts.key? "cc"
107
+ opts["system"] = second_sys if opts.numeric? "system"
109
108
  action.constantize.new.run(sys, postman, opts)
110
109
  end
111
110
 
@@ -137,12 +136,12 @@ module Lazylead
137
136
 
138
137
  def props
139
138
  @props ||= begin
140
- if team.nil?
141
- Opts.new(env(to_hash))
142
- else
143
- Opts.new(env(team.to_hash.merge(to_hash)))
144
- end
145
- end
139
+ if team.nil?
140
+ Opts.new(env(to_hash))
141
+ else
142
+ Opts.new(env(team.to_hash.merge(to_hash)))
143
+ end
144
+ end
146
145
  end
147
146
 
148
147
  def postman
@@ -153,17 +152,21 @@ module Lazylead
153
152
  end
154
153
  end
155
154
 
155
+ def second_sys
156
+ sys = System.find(props["system"])
157
+ raise "ll-014: No ticketing system found for #{self}" if sys.nil?
158
+ sys.connect
159
+ end
160
+
156
161
  private
157
162
 
158
163
  # Parse scheduling #type and #unit
159
164
  def trigger
160
165
  @trigger ||= begin
161
- trg = schedule.split(":")
162
- unless trg.size == 2
163
- raise "ll-007: illegal schedule format '#{schedule}'"
164
- end
165
- trg.map(&:strip).map(&:chomp)
166
- end
166
+ trg = schedule.split(":")
167
+ raise "ll-007: illegal schedule format '#{schedule}'" unless trg.size == 2
168
+ trg.map(&:strip).map(&:chomp)
169
+ end
167
170
  end
168
171
  end
169
172
 
@@ -179,6 +182,10 @@ module Lazylead
179
182
  @log = log
180
183
  end
181
184
 
185
+ # @todo #/DEV Remove the suppression during next refactoring (or enhancements)
186
+ # for the method below
187
+ #
188
+ # rubocop:disable Metrics/AbcSize
182
189
  def exec
183
190
  Logging.mdc["tid"] = "task #{id}"
184
191
  @log.debug "'#{name}' is started."
@@ -189,12 +196,13 @@ module Lazylead
189
196
  rescue StandardError => e
190
197
  msg = <<~MSG
191
198
  ll-006: Task ##{id} #{e} (#{e.class}) at #{self}
192
- #{Backtrace.new(e) if ARGV.include? '--trace'}"
199
+ #{Backtrace.new(e) if ARGV.include? '--trace'}
193
200
  MSG
194
201
  @log.error msg
195
202
  ensure
196
203
  Logging.mdc["tid"] = ""
197
204
  end
205
+ # rubocop:enable Metrics/AbcSize
198
206
  end
199
207
 
200
208
  # Ticketing systems to monitor.
data/lib/lazylead/opts.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -34,8 +34,7 @@ module Lazylead
34
34
  # License:: MIT
35
35
  class Opts
36
36
  extend Forwardable
37
- def_delegators :@origin, :[], :[]=, :to_s, :key?, :fetch, :except, :each,
38
- :each_pair, :sort_by
37
+ def_delegators :@origin, :[], :[]=, :to_s, :key?, :fetch, :except, :each, :each_pair, :sort_by
39
38
 
40
39
  def initialize(origin = {})
41
40
  @origin = origin
@@ -44,7 +43,13 @@ module Lazylead
44
43
  # Split text value by delimiter, trim all spaces and reject blank items
45
44
  def slice(key, delim)
46
45
  return [] unless to_h.key? key
47
- to_h[key].split(delim).map(&:chomp).map(&:strip).reject(&:blank?)
46
+ trim to_h[key].split(delim)
47
+ end
48
+
49
+ # Trim all spaces and reject blank items in array
50
+ def trim(arr)
51
+ return [] if arr.nil?
52
+ arr.map(&:chomp).map(&:strip).reject(&:blank?)
48
53
  end
49
54
 
50
55
  def blank?(key)
@@ -124,5 +129,18 @@ module Lazylead
124
129
  return [val] unless val.include? delim
125
130
  slice key, delim
126
131
  end
132
+
133
+ # Ensure that particular key from options is a positive numer
134
+ # Opts.new("key" => "1").numeric? "key" => true
135
+ # Opts.new("key" => "0").numeric? "key" => false
136
+ # Opts.new("key" => ".").numeric? "key" => false
137
+ # Opts.new("key" => "nil").numeric? "key" => false
138
+ def numeric?(key)
139
+ to_h[key].to_i.positive?
140
+ end
141
+
142
+ def construct(field, delim: ",")
143
+ slice(field, delim).map(&:constantize).map(&:new)
144
+ end
127
145
  end
128
146
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module Lazylead
26
+ #
27
+ # Allows to load all ruby files within particular dir.
28
+ #
29
+ class Requires
30
+ def initialize(dir)
31
+ @dir = dir
32
+ end
33
+
34
+ def load
35
+ Dir[File.join(@dir, "*.rb")].sort.each { |f| require f }
36
+ end
37
+ end
38
+ end
data/lib/lazylead/salt.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -39,6 +39,7 @@ module Lazylead
39
39
  #
40
40
  class Salt
41
41
  attr_reader :id
42
+
42
43
  #
43
44
  # Each salt should be defined as a environment variable with id, like
44
45
  # salt1=E1F53135E559C253
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
data/lib/lazylead/smtp.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The MIT License
4
4
  #
5
- # Copyright (c) 2019-2020 Yurii Dubinka
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
6
  #
7
7
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  # of this software and associated documentation files (the "Software"),
@@ -30,6 +30,10 @@ require_relative "../opts"
30
30
  module Lazylead
31
31
  # Jira system for manipulation with issues.
32
32
  #
33
+ # Jira official documentation:
34
+ # - https://docs.atlassian.com/jira-software/REST/7.3.1/#agile/1.0/issue-getIssue
35
+ # - https://developer.atlassian.com/server/jira/platform/rest-apis/
36
+ #
33
37
  # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
34
38
  # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
35
39
  # License:: MIT
@@ -82,22 +86,7 @@ module Lazylead
82
86
  @opts[:auth_type] = :basic if @opts[:auth_type].nil?
83
87
  @opts["username"] = @salt.decrypt(@opts["username"]) if @salt.specified?
84
88
  @opts["password"] = @salt.decrypt(@opts["password"]) if @salt.specified?
85
- cp("site", :site)
86
- cp("username", :username)
87
- cp("password", :password)
88
- cp("context_path", :context_path)
89
- @client = JIRA::Client.new(@opts)
90
- end
91
-
92
- # Copy the required/mandatory parameter(s) for Jira client which can't
93
- # be specified/defined at database level.
94
- #
95
- # @todo #/DEV Jira.cp - find a way how to avoid this method.
96
- # Potentially, hash with indifferent access might be used.
97
- # http://jocellyn.cz/2014/05/03/hash-with-indifferent-access.html
98
- # key.kind_of?(Symbol) ? key.to_s : key
99
- def cp(act, exp)
100
- @opts[exp] = @opts[act] if @opts.key? act
89
+ @client = JIRA::Client.new(@opts.symbolize_keys)
101
90
  end
102
91
  end
103
92
 
@@ -177,7 +166,7 @@ module Lazylead
177
166
  end
178
167
 
179
168
  def url
180
- @issue.attrs["self"].split("/rest/api/").first + "/browse/" + key
169
+ "#{@issue.attrs['self'].split('/rest/api/').first}/browse/#{key}"
181
170
  end
182
171
 
183
172
  def duedate
@@ -257,13 +246,20 @@ module Lazylead
257
246
  lbl = [] if lbl.nil?
258
247
  lbl << label
259
248
  lbl += more if more.size.positive?
260
- save!("fields" => { "labels" => lbl.uniq })
249
+ labels! lbl
261
250
  end
262
251
 
252
+ # Get the labels for a particular issue
263
253
  def labels
264
254
  fields["labels"]
265
255
  end
266
256
 
257
+ # Update the labels for a particular issue
258
+ def labels!(lbl)
259
+ return if lbl.nil? || lbl.empty?
260
+ save!("fields" => { "labels" => lbl.uniq })
261
+ end
262
+
267
263
  def save!(opts)
268
264
  @issue.save(opts)
269
265
  end
@@ -306,7 +302,19 @@ module Lazylead
306
302
 
307
303
  # Check that comment has expected text
308
304
  def include?(text)
309
- @comment.attrs["body"].include? text
305
+ to_s.include? text
306
+ end
307
+
308
+ def to_s
309
+ @comment.attrs["body"]
310
+ end
311
+
312
+ def lines
313
+ to_s.split("\n").reject(&:blank?)
314
+ end
315
+
316
+ def author
317
+ @comment.attrs["author"]["displayName"]
310
318
  end
311
319
  end
312
320