lazylead 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/ .dockerignore +12 -0
  3. data/.0pdd.yml +5 -0
  4. data/.circleci/config.yml +50 -0
  5. data/.circleci/release_image.sh +13 -0
  6. data/.circleci/validate.sh +10 -0
  7. data/.docker/Dockerfile +31 -0
  8. data/.docker/build.sh +6 -0
  9. data/.docker/docker-compose.yml +23 -0
  10. data/.docker/readme.md +21 -0
  11. data/.gitattributes +9 -0
  12. data/.github/CODE_OF_CONDUCT.md +76 -0
  13. data/.github/CONTRIBUTING.md +9 -0
  14. data/.github/ISSUE_TEMPLATE.md +14 -0
  15. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  16. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  17. data/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  18. data/.github/tasks.yml +24 -0
  19. data/.github/trello.md +18 -0
  20. data/.gitignore +12 -0
  21. data/.pdd +5 -0
  22. data/.rubocop.yml +87 -0
  23. data/.ruby-version +1 -0
  24. data/.rultor.yml +31 -0
  25. data/.simplecov +16 -0
  26. data/.travis.yml +16 -0
  27. data/Gemfile +27 -0
  28. data/Guardfile +33 -0
  29. data/Rakefile +93 -0
  30. data/appveyor.yml +50 -0
  31. data/bin/.ruby-version +1 -0
  32. data/bin/lazylead +99 -0
  33. data/build.sh +6 -0
  34. data/deploy.sh +16 -0
  35. data/lazylead.gemspec +106 -0
  36. data/lib/lazylead.rb +52 -0
  37. data/lib/lazylead/allocated.rb +56 -0
  38. data/lib/lazylead/cli/app.rb +87 -0
  39. data/lib/lazylead/confluence.rb +157 -0
  40. data/lib/lazylead/email.rb +74 -0
  41. data/lib/lazylead/exchange.rb +83 -0
  42. data/lib/lazylead/log.rb +71 -0
  43. data/lib/lazylead/model.rb +140 -0
  44. data/lib/lazylead/postman.rb +78 -0
  45. data/lib/lazylead/salt.rb +91 -0
  46. data/lib/lazylead/schedule.rb +88 -0
  47. data/lib/lazylead/smtp.rb +82 -0
  48. data/lib/lazylead/system/empty.rb +36 -0
  49. data/lib/lazylead/system/fake.rb +41 -0
  50. data/lib/lazylead/system/jira.rb +249 -0
  51. data/lib/lazylead/system/synced.rb +41 -0
  52. data/lib/lazylead/task/alert.rb +105 -0
  53. data/lib/lazylead/task/confluence_ref.rb +59 -0
  54. data/lib/lazylead/task/echo.rb +38 -0
  55. data/lib/lazylead/task/fix_version.rb +79 -0
  56. data/lib/lazylead/task/missing_comment.rb +53 -0
  57. data/lib/lazylead/version.rb +27 -0
  58. data/lib/messages/due_date_expired.erb +96 -0
  59. data/lib/messages/illegal_fixversion_change.erb +120 -0
  60. data/lib/messages/missing_comment.erb +128 -0
  61. data/license.txt +21 -0
  62. data/readme.md +160 -0
  63. data/test/lazylead/allocated_test.rb +51 -0
  64. data/test/lazylead/cli/app_test.rb +74 -0
  65. data/test/lazylead/confluence_test.rb +55 -0
  66. data/test/lazylead/exchange_test.rb +68 -0
  67. data/test/lazylead/model_test.rb +65 -0
  68. data/test/lazylead/salt_test.rb +42 -0
  69. data/test/lazylead/smoke_test.rb +38 -0
  70. data/test/lazylead/smtp_test.rb +65 -0
  71. data/test/lazylead/system/jira_test.rb +110 -0
  72. data/test/lazylead/task/confluence_ref_test.rb +66 -0
  73. data/test/lazylead/task/duedate_test.rb +126 -0
  74. data/test/lazylead/task/echo_test.rb +34 -0
  75. data/test/lazylead/task/fix_version_test.rb +52 -0
  76. data/test/lazylead/task/missing_comment_test.rb +56 -0
  77. data/test/lazylead/version_test.rb +36 -0
  78. data/test/sqlite_test.rb +80 -0
  79. data/test/test.rb +103 -0
  80. data/todo.yml +16 -0
  81. data/upgrades/sqlite/001-install-main-lazylead-tables.sql +63 -0
  82. data/upgrades/sqlite/999.testdata.sql +39 -0
  83. metadata +815 -0
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ require "active_support"
26
+
27
+ module Lazylead
28
+ #
29
+ # A cryptography salt defined in environment variables.
30
+ #
31
+ # Salt is random data that is used as an additional input to a one-way
32
+ # function that hashes data, a password or passphrase. Salts are used to
33
+ # safeguard passwords in storage. Historically a password was stored in
34
+ # plaintext on a system, but over time additional safeguards developed to
35
+ # protect a user's password against being read from the system. A salt is one
36
+ # of those methods.
37
+ #
38
+ # Read more: https://en.wikipedia.org/wiki/Salt_(cryptography).
39
+ #
40
+ class Salt
41
+ attr_reader :id
42
+ #
43
+ # Each salt should be defined as a environment variable with id, like
44
+ # salt1=E1F53135E559C253
45
+ # salt2=84B03D034B409D4E
46
+ # ...
47
+ # saltN=xxxxxxxxx
48
+ #
49
+ def initialize(id, env = ENV.to_h)
50
+ @id = id
51
+ @env = env
52
+ end
53
+
54
+ def encrypt(password)
55
+ ActiveSupport::MessageEncryptor.new(@env[@id]).encrypt_and_sign password
56
+ end
57
+
58
+ def decrypt(password)
59
+ ActiveSupport::MessageEncryptor.new(@env[@id]).decrypt_and_verify password
60
+ end
61
+
62
+ def specified?
63
+ @env.key?(@id) && !@env[@id].blank?
64
+ end
65
+ end
66
+
67
+ #
68
+ # No cryptography salt defined within environment variables.
69
+ #
70
+ class NoSalt
71
+ def id
72
+ "No salt"
73
+ end
74
+
75
+ def encrypt(_)
76
+ raise "ll-003: Unsupported operation: 'encrypt'"
77
+ end
78
+
79
+ def decrypt(_)
80
+ raise "ll-004: Unsupported operation: 'decrypt'"
81
+ end
82
+
83
+ def specified?
84
+ false
85
+ end
86
+
87
+ def key
88
+ raise "ll-005: Unsupported operation: 'key'"
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ require "json"
26
+ require "rufus-scheduler"
27
+ require_relative "log"
28
+ require_relative "model"
29
+
30
+ module Lazylead
31
+ # The tasks schedule
32
+ #
33
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
34
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
35
+ # License:: MIT
36
+ class Schedule
37
+ # @todo #/DEV New scheduling types like 'at', 'once' is required.
38
+ # The minimum time period for cron is 1 minute and it's not suitable for
39
+ # unit testing, thus its better to introduce new types which allows to
40
+ # schedule some task once or at particular time period like in next 200ms).
41
+ # For cron expressions we should define separate test suite which will test
42
+ # in parallel without blocking main CI process.
43
+ def initialize(log = Log::NOTHING, cling = true)
44
+ @log = log
45
+ @cling = cling
46
+ @trigger = Rufus::Scheduler.new
47
+ end
48
+
49
+ # @todo #/DEV error code is required for reach 'raise' statement within the
50
+ # application.
51
+ def register(task)
52
+ raise "ll-002: task can't be a null" if task.nil?
53
+ @trigger.cron task.cron do
54
+ task.exec @log
55
+ end
56
+ @log.debug "Task scheduled: #{task}"
57
+ end
58
+
59
+ # @todo #/DEV inspect the current execution status. This method should
60
+ # support several format for output, by default is `json`.
61
+ def ps; end
62
+
63
+ def join
64
+ @trigger.join if @cling
65
+ end
66
+
67
+ # @todo #/DEV stop the execution of current jobs (shutdown?).
68
+ # The test is required.
69
+ def stop
70
+ @trigger.shutdown(:kill)
71
+ end
72
+ end
73
+
74
+ # Fake application schedule for unit testing purposes
75
+ class NoSchedule
76
+ def initialize(log = Log::NOTHING)
77
+ @log = log
78
+ end
79
+
80
+ def register(task)
81
+ @log.debug("Task registered: #{task}")
82
+ end
83
+
84
+ def ps; end
85
+
86
+ def join; end
87
+ end
88
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ require "mail"
26
+ require_relative "log"
27
+ require_relative "salt"
28
+
29
+ module Lazylead
30
+ #
31
+ # The emails configuration over SMTP protocol.
32
+ #
33
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
34
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
35
+ # License:: MIT
36
+ class Smtp
37
+ def initialize(log = Log::NOTHING, salt = NoSalt.new, opts = {})
38
+ @log = log
39
+ @salt = salt
40
+ @opts = opts
41
+ end
42
+
43
+ def enable
44
+ if @opts.empty? || @opts[:test_mode] || @opts[:smtp_host].blank?
45
+ Mail.defaults do
46
+ delivery_method :test
47
+ end
48
+ @log.warn "SMTP connection enabled in test mode."
49
+ else
50
+ setup_smtp
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def setup_smtp
57
+ host = @opts[:smtp_host]
58
+ port = @opts[:smtp_port]
59
+ user = decrypted(:smtp_user)
60
+ pass = decrypted(:smtp_pass)
61
+ Mail.defaults do
62
+ delivery_method :smtp,
63
+ address: host,
64
+ port: port,
65
+ user_name: user,
66
+ password: pass,
67
+ authentication: "plain",
68
+ enable_starttls_auto: true
69
+ end
70
+ @log.debug "SMTP connection established with #{host} as #{user}."
71
+ end
72
+
73
+ # Decrypt the value of configuration property using cryptography salt.
74
+ def decrypted(key)
75
+ if @salt.specified?
76
+ @salt.decrypt(@opts[key])
77
+ else
78
+ @opts[key]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ module Lazylead
25
+ #
26
+ # A empty ticketing system
27
+ #
28
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
29
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
30
+ # License:: MIT
31
+ class Empty
32
+ def issues(_)
33
+ []
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ # A fake ticketing system
28
+ #
29
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
30
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
31
+ # License:: MIT
32
+ class Fake
33
+ def initialize(issues = [])
34
+ @issues = issues
35
+ end
36
+
37
+ def issues(_)
38
+ @issues
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 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
+ require "jira-ruby"
26
+ require_relative "../salt"
27
+
28
+ module Lazylead
29
+ # Jira system for manipulation with issues.
30
+ #
31
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
32
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
33
+ # License:: MIT
34
+ class Jira
35
+ # @todo #57/DEV The debug method should be moved outside of ctor.
36
+ # This was moved here from 'client' method because Rubocop failed the build
37
+ # due to 'Metrics/AbcSize' violation.
38
+ def initialize(opts, salt = NoSalt.new, log = Log::NOTHING)
39
+ @opts = opts
40
+ @salt = salt
41
+ @log = log
42
+ @log.debug "Initiate a Jira client using following opts: " \
43
+ "#{@opts.except 'password', :password} " \
44
+ " and salt #{@salt.id} (found=#{@salt.specified?})"
45
+ end
46
+
47
+ def issues(jql, opts = {})
48
+ raw do |jira|
49
+ jira.Issue.jql(jql, opts).map { |i| Lazylead::Issue.new(i) }
50
+ end
51
+ end
52
+
53
+ # Execute request to the ticketing system using raw client.
54
+ # For Jira the raw client is 'jira-ruby' gem.
55
+ def raw
56
+ raise "ll-06: No block given to method" unless block_given?
57
+ yield client
58
+ end
59
+
60
+ private
61
+
62
+ def client
63
+ return @client if defined? @client
64
+ @opts[:auth_type] = :basic if @opts[:auth_type].nil?
65
+ @opts["username"] = @salt.decrypt(@opts["username"]) if @salt.specified?
66
+ @opts["password"] = @salt.decrypt(@opts["password"]) if @salt.specified?
67
+ cp("site", :site)
68
+ cp("username", :username)
69
+ cp("password", :password)
70
+ cp("context_path", :context_path)
71
+ @client = JIRA::Client.new(@opts)
72
+ end
73
+
74
+ # Copy the required/mandatory parameter(s) for Jira client which can't
75
+ # be specified/defined at database level.
76
+ #
77
+ # @todo #/DEV Jira.cp - find a way how to avoid this method.
78
+ # Potentially, hash with indifferent access might be used.
79
+ # http://jocellyn.cz/2014/05/03/hash-with-indifferent-access.html
80
+ # key.kind_of?(Symbol) ? key.to_s : key
81
+ def cp(act, exp)
82
+ @opts[exp] = @opts[act] if @opts.key? act
83
+ end
84
+ end
85
+
86
+ #
87
+ # An user from Jira which might be reporter, assignee, etc.
88
+ #
89
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
90
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
91
+ # License:: MIT
92
+ class User
93
+ def initialize(usr)
94
+ @usr = usr
95
+ end
96
+
97
+ def id
98
+ @usr["name"]
99
+ end
100
+
101
+ def name
102
+ @usr["displayName"]
103
+ end
104
+
105
+ def email
106
+ @usr["emailAddress"]
107
+ end
108
+
109
+ def ==(other)
110
+ if other.respond_to?(:id)
111
+ other.id == id
112
+ else
113
+ false
114
+ end
115
+ end
116
+
117
+ alias eql? ==
118
+
119
+ def hash
120
+ id.hash
121
+ end
122
+
123
+ def to_s
124
+ inspect
125
+ end
126
+
127
+ def inspect
128
+ "#{@opts['site']} (#{@opts['username']})"
129
+ end
130
+ end
131
+
132
+ #
133
+ # An issue from Jira
134
+ #
135
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
136
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
137
+ # License:: MIT
138
+ class Issue
139
+ def initialize(issue)
140
+ @issue = issue
141
+ end
142
+
143
+ def id
144
+ @issue.id
145
+ end
146
+
147
+ def key
148
+ @issue.key
149
+ end
150
+
151
+ def summary
152
+ fields["summary"]
153
+ end
154
+
155
+ def url
156
+ @issue.attrs["self"].split("/rest/api/").first + "/browse/" + key
157
+ end
158
+
159
+ def duedate
160
+ @issue.fields["duedate"]
161
+ end
162
+
163
+ def priority
164
+ fields["priority"]["name"]
165
+ end
166
+
167
+ def reporter
168
+ Lazylead::User.new(fields["reporter"])
169
+ end
170
+
171
+ def assignee
172
+ Lazylead::User.new(@issue.assignee.attrs)
173
+ end
174
+
175
+ def fields
176
+ @issue.fields
177
+ end
178
+
179
+ def history
180
+ return [] unless @issue.respond_to? :changelog
181
+ return [] if @issue.changelog == nil? || @issue.changelog.empty?
182
+ @issue.changelog["histories"]
183
+ end
184
+
185
+ def comments
186
+ @issue.comments
187
+ end
188
+
189
+ def to_s
190
+ "#{key} #{summary}"
191
+ end
192
+
193
+ def inspect
194
+ to_s
195
+ end
196
+ end
197
+
198
+ # The jira issue comments
199
+ #
200
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
201
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
202
+ # License:: MIT
203
+ class Comments
204
+ attr_reader :issue
205
+
206
+ def initialize(issue, sys)
207
+ @issue = issue
208
+ @sys = sys
209
+ end
210
+
211
+ def body?(text)
212
+ comments.any? { |c| c.attrs["body"].include? text }
213
+ end
214
+
215
+ def comments
216
+ @comments ||= @sys.raw do |conn|
217
+ conn.Issue.find(@issue.id, expand: "comments,changelog", fields: "")
218
+ .comments
219
+ end
220
+ end
221
+
222
+ def last(quantity)
223
+ comments.last(quantity).map { |c| c.attrs["body"] }
224
+ end
225
+ end
226
+
227
+ # Jira instance without authentication in order to access public filters
228
+ # or dashboards.
229
+ class NoAuthJira
230
+ def initialize(url, log = Log::NOTHING)
231
+ @jira = Jira.new(
232
+ { username: nil, password: nil, site: url, context_path: "" },
233
+ NoSalt.new,
234
+ log
235
+ )
236
+ end
237
+
238
+ def issues(jql, opts = {})
239
+ @jira.issues(jql, opts)
240
+ end
241
+
242
+ # Execute request to the ticketing system using raw client.
243
+ # For Jira the raw client is 'jira-ruby' gem.
244
+ def raw(&block)
245
+ raise "ll-07: No block given to method" unless block_given?
246
+ @jira.raw(&block)
247
+ end
248
+ end
249
+ end