lazylead 0.1.1 → 0.4.0

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.0pdd.yml +4 -1
  3. data/.circleci/config.yml +19 -7
  4. data/.circleci/release_image.sh +6 -3
  5. data/.docker/Dockerfile +10 -9
  6. data/.docker/docker-compose.yml +3 -3
  7. data/.docker/readme.md +24 -21
  8. data/.docker/vcs.dockerfile +10 -0
  9. data/.docs/accuracy.md +107 -0
  10. data/.docs/accuracy_email.jpg +0 -0
  11. data/.docs/accuracy_jira_comment.jpg +0 -0
  12. data/.docs/duedate_expired.md +92 -0
  13. data/.docs/propagate_down.md +89 -0
  14. data/.pdd +1 -1
  15. data/.rubocop.yml +7 -1
  16. data/.rultor.yml +13 -14
  17. data/.simplecov +0 -6
  18. data/Rakefile +38 -1
  19. data/bin/lazylead +13 -5
  20. data/lazylead.gemspec +6 -17
  21. data/lib/lazylead/cc.rb +180 -0
  22. data/lib/lazylead/cli/app.rb +4 -3
  23. data/lib/lazylead/exchange.rb +15 -2
  24. data/lib/lazylead/home.rb +38 -0
  25. data/lib/lazylead/log.rb +30 -8
  26. data/lib/lazylead/model.rb +60 -16
  27. data/lib/lazylead/opts.rb +68 -0
  28. data/lib/lazylead/postman.rb +15 -15
  29. data/lib/lazylead/schedule.rb +6 -4
  30. data/lib/lazylead/smtp.rb +1 -1
  31. data/lib/lazylead/system/fake.rb +1 -1
  32. data/lib/lazylead/system/jira.rb +55 -12
  33. data/lib/lazylead/system/synced.rb +2 -1
  34. data/lib/lazylead/task/accuracy/accuracy.rb +140 -0
  35. data/lib/lazylead/task/accuracy/affected_build.rb +43 -0
  36. data/lib/lazylead/task/accuracy/requirement.rb +40 -0
  37. data/lib/lazylead/task/alert.rb +8 -6
  38. data/lib/lazylead/task/confluence_ref.rb +4 -3
  39. data/lib/lazylead/task/echo.rb +4 -0
  40. data/lib/lazylead/task/fix_version.rb +11 -7
  41. data/lib/lazylead/task/missing_comment.rb +7 -5
  42. data/lib/lazylead/task/propagate_down.rb +126 -0
  43. data/lib/lazylead/task/savepoint.rb +58 -0
  44. data/lib/lazylead/task/touch.rb +102 -0
  45. data/lib/lazylead/version.rb +1 -1
  46. data/lib/messages/accuracy.erb +118 -0
  47. data/lib/messages/due_date_expired.erb +8 -7
  48. data/lib/messages/illegal_fixversion_change.erb +9 -8
  49. data/lib/messages/missing_comment.erb +10 -9
  50. data/lib/messages/savepoint.erb +43 -0
  51. data/lib/messages/svn_touch.erb +147 -0
  52. data/readme.md +90 -80
  53. data/test/lazylead/cc_test.rb +153 -0
  54. data/test/lazylead/cli/app_test.rb +3 -4
  55. data/test/lazylead/exchange_test.rb +22 -2
  56. data/test/lazylead/model_test.rb +14 -3
  57. data/test/lazylead/opts_test.rb +66 -0
  58. data/test/lazylead/postman_test.rb +57 -0
  59. data/test/lazylead/smtp_test.rb +1 -1
  60. data/test/lazylead/system/jira_test.rb +43 -1
  61. data/test/lazylead/task/accuracy/accuracy_test.rb +73 -0
  62. data/test/lazylead/task/accuracy/affected_build_test.rb +42 -0
  63. data/test/lazylead/task/assignee_alert_test.rb +47 -0
  64. data/test/lazylead/task/duedate_test.rb +52 -30
  65. data/test/lazylead/task/fix_version_test.rb +11 -10
  66. data/test/lazylead/task/missing_comment_test.rb +13 -13
  67. data/test/lazylead/task/propagate_down_test.rb +88 -0
  68. data/test/lazylead/task/savepoint_test.rb +51 -0
  69. data/test/lazylead/task/touch_test.rb +63 -0
  70. data/test/test.rb +11 -0
  71. data/upgrades/sqlite/001-install-main-lazylead-tables.sql +3 -4
  72. data/upgrades/sqlite/999.testdata.sql +8 -2
  73. metadata +57 -177
  74. data/deploy.sh +0 -16
  75. data/todo.yml +0 -6
data/.pdd CHANGED
@@ -2,4 +2,4 @@
2
2
  --verbose
3
3
  --exclude target/**/*
4
4
  --exclude coverage/**/*
5
- --rule min-words:5
5
+ --rule min-words:7
@@ -25,7 +25,7 @@ Metrics/BlockLength:
25
25
  - "*.gemspec"
26
26
  - "Rakefile"
27
27
  - "bin/lazylead"
28
- - "test/lazylead/cli/start_test.rb"
28
+ - "test/**/*"
29
29
 
30
30
  Metrics/ClassLength:
31
31
  Exclude:
@@ -77,6 +77,12 @@ Style/HashTransformKeys:
77
77
  Style/HashTransformValues:
78
78
  Enabled: true
79
79
 
80
+ # @todo #/DEV accuracy.rb is using % symbol in text message and rubocop
81
+ # complains about it. It's false-positive violation, thus, for now ignored for this file
82
+ Style/FormatStringToken:
83
+ Exclude:
84
+ - "lib/lazylead/task/accuracy/accuracy.rb"
85
+
80
86
  Lint/RaiseException:
81
87
  Enabled: true
82
88
 
@@ -1,31 +1,30 @@
1
+ architect:
2
+ - dgroup
1
3
  assets:
2
- rubygems.yml: dgroup/ossrh#rubygems.yml
4
+ rubygems.yml: dgroup/home#rubygems.yml
5
+ circleci.header: dgroup/home#circleci.header
3
6
  install: |
4
7
  export GEM_HOME=~/.ruby
5
8
  export GEM_PATH=$GEM_HOME:$GEM_PATH
6
- sudo apt-get -y update
7
- sudo apt-get -y install libcurl4-openssl-dev
8
- sudo gem install pdd -v 0.20.5
9
+ docker:
10
+ image: ruby:2.6.5
9
11
  release:
10
12
  script: |-
13
+ set -e
11
14
  export RUBYOPT="-W0"
12
15
  [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit -1
13
16
  sed -i "s/0\.0\.0/${tag}/g" lib/lazylead/version.rb
17
+ sed -i "s/0\.0\.0/${tag}/g" lazylead.gemspec
14
18
  bundle install --no-color
15
- rake --quiet
16
- git add lib/lazylead/version.rb
19
+ bundle exec rake --trace test rubocop sqlint xcop
20
+ git add lib/lazylead/version.rb lazylead.gemspec
17
21
  git commit -m "version set to ${tag}"
18
22
  gem build lazylead.gemspec
19
23
  chmod 0600 ../rubygems.yml
20
24
  gem push *.gem --config-file ../rubygems.yml
21
- architect:
22
- - dgroup
25
+ curl -s -X POST --url "https://circleci.com/api/v2/project/gh/dgroup/lazylead/envvar" -H @../circleci.header -H "accept: application/json" -H "content-type: application/json" -d "{ \"name\": \"DOCKER_RELEASE_TAGS\", \"value\": \"${tag}\" }" -o /dev/null
26
+ curl -s -X POST --url https://circleci.com/api/v2/project/gh/dgroup/lazylead/pipeline -H @../circleci.header -H 'accept: application/json' -H 'content-type: application/json' -H 'x-attribution-login: dgroup' -o /dev/null
23
27
  merge:
24
28
  script: |-
25
29
  bundle install
26
- rake --quiet
27
- pdd -f /dev/null -v
28
- deploy:
29
- script: |-
30
- echo "There is nothing to deploy"
31
- exit -1
30
+ bundle exec rake test rubocop sqlint xcop
data/.simplecov CHANGED
@@ -1,11 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- # @todo #/DEV Increase code coverage from 75% to 80%+.
4
- # Right now it was decreased to 75% due to long manual setup of
5
- # dev. instances of Atlassian Jira and Confluence.
6
- # It was configured locally and there is no automation for now how
7
- # it can be included quickly into CI process. This need to be done later.
8
-
9
3
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
10
4
  [SimpleCov::Formatter::HTMLFormatter]
11
5
  )
data/Rakefile CHANGED
@@ -26,6 +26,7 @@ require "rubygems"
26
26
  require "rake"
27
27
  require "date"
28
28
  require "rdoc"
29
+ require "rainbow"
29
30
  require "rake/clean"
30
31
 
31
32
  # @todo #/DEV Investigate the possibility of using migrations from active_record
@@ -44,7 +45,7 @@ def version
44
45
  Gem::Specification.load(Dir["*.gemspec"].first).version
45
46
  end
46
47
 
47
- task default: %i[clean test rubocop xcop copyright]
48
+ task default: %i[clean test rubocop sqlint xcop copyright docker]
48
49
 
49
50
  require "rake/testtask"
50
51
  desc "Run all unit tests"
@@ -88,6 +89,42 @@ Xcop::RakeTask.new :xcop do |task|
88
89
  task.excludes = %w[target/**/* coverage/**/* wp/**/*]
89
90
  end
90
91
 
92
+ # @todo #/DEV Enable rule "Checks SQL against the ANSI syntax" fully.
93
+ # Right now all violations related to PRAGMA https://www.sqlite.org/pragma.html
94
+ # are suppressed as PRAGMA is sqlite specific option.
95
+ # Potential fix is to move this option into vcs4sql lib and remove from our
96
+ # sql files.
97
+ task :sqlint do
98
+ puts "Running sqlint..."
99
+ require "sqlint"
100
+ src = Dir.glob("upgrades/**/*.sql")
101
+ puts "Inspecting #{src.size} files"
102
+ total = 0
103
+ src.each do |f|
104
+ violations = SQLint::Linter.new(f, File.open(f, "r"))
105
+ .run
106
+ .first(1000)
107
+ .reject { |v| v.message.include? '"PRAGMA"' }
108
+ violations.each do |v|
109
+ msg_lines = v.message.split("\n")
110
+ p [v.filename, v.line, v.column, "#{v.type} #{msg_lines.shift}"].join ":"
111
+ end
112
+ total += violations.count { |lint| lint.type == :error }
113
+ end
114
+ if total.positive?
115
+ abort "#{Rainbow(total).red} SQL violations found."
116
+ else
117
+ puts "#{src.size} files inspected, #{Rainbow('no offenses').green} detected"
118
+ end
119
+ end
120
+
91
121
  task :clean do
92
122
  Dir.glob("test/resources/**/*.db").each { |f| File.delete(f) }
93
123
  end
124
+
125
+ task :docker do
126
+ puts "Building docker image..."
127
+ system "docker-compose -f .docker/docker-compose.yml build "\
128
+ " --build-arg release_tags='latest 1.0'"\
129
+ " --build-arg version=1.0"
130
+ end
@@ -34,15 +34,19 @@ require "logging"
34
34
  require "backtrace"
35
35
  require "memory_profiler"
36
36
  require_relative "../lib/lazylead/log"
37
+ require_relative "../lib/lazylead/home"
37
38
  require_relative "../lib/lazylead/schedule"
38
39
  require_relative "../lib/lazylead/allocated"
39
40
  require_relative "../lib/lazylead/cli/app"
40
41
 
41
- log = Lazylead::Log::ERRORS
42
+ log = Lazylead::Log.new
42
43
  Thread.current.name = "main"
44
+ Logging.mdc["tid"] = Thread.current.name
43
45
  Encoding.default_external = Encoding::UTF_8
44
46
  Encoding.default_internal = Encoding::UTF_8
45
47
 
48
+ # @todo #/DEV Decorate ARGV with custom methods in order to avoid code
49
+ # duplication, like { ARGV.include? "--trace" }
46
50
  opts = Slop.parse(ARGV, strict: false, suppress_errors: true) do |o|
47
51
  o.banner = "Usage: lazylead [options]
48
52
  Available options:"
@@ -51,25 +55,29 @@ Available options:"
51
55
  o.bool "--memory-dump", "Dump memory snapshot afterwards, to the console",
52
56
  default: false
53
57
  o.string "--home",
54
- "Home directory (default: #{Dir.pwd})",
55
- default: ENV["LL_HOME"].nil? ? Dir.pwd : ENV["LL_HOME"]
58
+ "Home directory (default #{Lazylead::Home.new.dir})",
59
+ default: Lazylead::Home.new.dir
56
60
  o.string "--sqlite",
57
61
  "SQLite database file name",
58
62
  default: "lazylead.db"
59
63
  o.string "--vcs4sql",
60
64
  "Home directory with database version control(vcs) scripts",
61
65
  default: "upgrades/sqlite"
66
+ o.integer "--max-connections",
67
+ "SQL connections pool size",
68
+ default: 5
62
69
  o.bool "--testdata",
63
70
  "Apply the database VCS migration with test data",
64
71
  default: false
65
72
  o.on "--verbose", "Enable extra logging information" do
66
- log = Lazylead::Log::VERBOSE
73
+ log.verbose
67
74
  end
68
75
  o.on "-v", "--version", "Show current version" do
69
76
  log.debug Lazylead::VERSION
70
77
  exit
71
78
  end
72
79
  end
80
+ log.debug("Version: #{Lazylead::VERSION}")
73
81
  log.debug("Memory footprint at start is #{Lazylead::Allocated.new}")
74
82
  cmd = lambda do
75
83
  Lazylead::CLI::App.new(
@@ -86,7 +94,7 @@ cmd = lambda do
86
94
  return 0
87
95
  rescue StandardError => e
88
96
  log.error("#{e.message} (#{e.class.name})")
89
- log.error(Backtrace.new(e)) if opts["trace"]
97
+ log.error(Backtrace.new(e)) if ARGV.include? "--trace"
90
98
  return -1
91
99
  end
92
100
  code = 0
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
  s.rubygems_version = "2.2"
33
33
  s.required_ruby_version = ">=2.6.5"
34
34
  s.name = "lazylead"
35
- s.version = "0.1.1"
35
+ s.version = "0.4.0"
36
36
  s.license = "MIT"
37
37
  s.summary = "Eliminate the annoying work within bug-trackers."
38
38
  s.description = "Ticketing systems (Github, Jira, etc.) are strongly
@@ -45,7 +45,7 @@ tasks instead of solving technical problems."
45
45
  s.authors = ["Yurii Dubinka"]
46
46
  s.email = "yurii.dubinka@gmail.com"
47
47
  s.homepage = "http://github.com/dgroup/lazylead"
48
- s.post_install_message = "Thanks for installing Lazylead v0.0.0!
48
+ s.post_install_message = "Thanks for installing Lazylead v0.4.0!
49
49
  Read our blog posts: https://lazylead.org
50
50
  Stay in touch with the community in Telegram: https://t.me/lazylead
51
51
  Follow us on Twitter: https://twitter.com/lazylead
@@ -54,17 +54,13 @@ tasks instead of solving technical problems."
54
54
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
55
55
  s.test_files = s.files.grep(%r{^(test|features)/})
56
56
  s.rdoc_options = ["--charset=UTF-8"]
57
- s.extra_rdoc_files = ["readme.md", "license.txt"]
57
+ s.extra_rdoc_files = %w[readme.md license.txt]
58
58
  s.add_runtime_dependency "activerecord", "6.0.3"
59
59
  s.add_runtime_dependency "backtrace", "0.3"
60
- s.add_runtime_dependency "concurrent-ruby", "1.1.5"
61
- s.add_runtime_dependency "diffy", "3.3.0"
62
60
  s.add_runtime_dependency "faraday", "1.0.1"
63
- s.add_runtime_dependency "futex", "0.8.5"
64
61
  s.add_runtime_dependency "get_process_mem", "0.2.5"
65
- s.add_runtime_dependency "haml", "5.0.4"
66
62
  s.add_runtime_dependency "jira-ruby", "1.7.1"
67
- s.add_runtime_dependency "json", "2.2.0"
63
+ s.add_runtime_dependency "json", "2.3.0"
68
64
  s.add_runtime_dependency "logging", "2.2.2"
69
65
  s.add_runtime_dependency "mail", "2.7.1"
70
66
  s.add_runtime_dependency "memory_profiler", "0.9.13"
@@ -72,22 +68,14 @@ tasks instead of solving technical problems."
72
68
  s.add_runtime_dependency "rainbow", "3.0.0"
73
69
  s.add_runtime_dependency "require_all", "3.0.0"
74
70
  s.add_runtime_dependency "rufus-scheduler", "3.6.0"
75
- s.add_runtime_dependency "semantic", "1.6.1"
76
- s.add_runtime_dependency "sinatra", "2.0.5"
77
71
  s.add_runtime_dependency "slop", "4.4"
78
72
  s.add_runtime_dependency "sqlite3", "1.4.2"
79
- s.add_runtime_dependency "sys-proctable", "1.2.1"
80
- s.add_runtime_dependency "threads", "0.3"
81
73
  s.add_runtime_dependency "tilt", "2.0.10"
82
- s.add_runtime_dependency "total", "0.3"
83
- s.add_runtime_dependency "typhoeus", "1.3.1"
84
74
  s.add_runtime_dependency "tzinfo", "1.1"
85
75
  s.add_runtime_dependency "tzinfo-data", "1.2019.3"
86
- s.add_runtime_dependency "usagewatch_ext", "0.2.1"
87
76
  s.add_runtime_dependency "vcs4sql", "0.1.0"
88
77
  s.add_runtime_dependency "viewpoint", "1.1.0"
89
- s.add_runtime_dependency "zache", "0.12"
90
- s.add_development_dependency "codecov", "0.1.14"
78
+ s.add_development_dependency "codecov", "0.2.3"
91
79
  s.add_development_dependency "guard", "2.15.0"
92
80
  s.add_development_dependency "guard-minitest", "2.4.6"
93
81
  s.add_development_dependency "minitest", "5.11.3"
@@ -101,5 +89,6 @@ tasks instead of solving technical problems."
101
89
  s.add_development_dependency "rubocop-minitest", "0.5.1"
102
90
  s.add_development_dependency "rubocop-performance", "1.5.2"
103
91
  s.add_development_dependency "rubocop-rspec", "1.33.0"
92
+ s.add_development_dependency "sqlint", "0.1.10"
104
93
  s.add_development_dependency "xcop", "0.6"
105
94
  end
@@ -0,0 +1,180 @@
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
+ require "forwardable"
25
+
26
+ module Lazylead
27
+ # Entry point for email CC detection.
28
+ # The email may need CC email addresses, thus, there are various strategies
29
+ # how it can be done.
30
+ class CC
31
+ # Build an CC in order to detect email addresses by different conditions.
32
+ #
33
+ # Supported conditions(types):
34
+ # - PlainCC
35
+ # - PredefinedCC
36
+ # - ComponentCC
37
+ # - Empty
38
+ #
39
+ def detect(emails, sys)
40
+ return emails if recognized?(emails)
41
+ return PlainCC.new(emails) if plain?(emails)
42
+ return EmptyCC.new if undefined?(emails)
43
+ type = emails["type"].constantize
44
+ return ComponentCC.new(emails["project"], sys) if type.is_a? ComponentCC
45
+ type.new(emails["opts"])
46
+ end
47
+
48
+ # Detect that raw CC is a string which may has plain email addresses
49
+ def plain?(text)
50
+ (text.is_a? String) && text.to_s.include?("@")
51
+ end
52
+
53
+ def recognized?(emails)
54
+ [EmptyCC, PlainCC, ComponentCC, PredefinedCC].member? emails.class
55
+ end
56
+
57
+ def undefined?(emails)
58
+ return true unless emails.key? "type"
59
+ emails["type"].nil? || emails["type"].blank?
60
+ end
61
+ end
62
+
63
+ # Array of CC addresses from text for email notification.
64
+ #
65
+ # PlainCC.new("a@f.com, , -,b@f.com").cc # ==> ["a@f.com", "b@f.com"]
66
+ #
67
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
68
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
69
+ # License:: MIT
70
+ class PlainCC
71
+ include Enumerable
72
+ extend Forwardable
73
+ def_delegators :@cc, :each
74
+
75
+ # The regexp expression for email notification is very simple, here is the
76
+ # reason why https://bit.ly/38iLKeo
77
+ def initialize(text, regxp = /[^\s]@[^\s]/)
78
+ @text = text
79
+ @regxp = regxp
80
+ end
81
+
82
+ def cc
83
+ @cc ||= begin
84
+ if @text.include? ","
85
+ @text.split(",").map(&:strip).select { |e| e[@regxp] }
86
+ elsif @text[@regxp]
87
+ [@text.strip]
88
+ end
89
+ end
90
+ end
91
+
92
+ def each(&block)
93
+ cc.each(&block)
94
+ end
95
+ end
96
+
97
+ # Empty CC email addresses.
98
+ class EmptyCC
99
+ def cc
100
+ []
101
+ end
102
+ end
103
+
104
+ # Predefined CC addresses for email notification.
105
+ # You may define a hash where
106
+ # - key is Jira ticket component
107
+ # - value is CC email address(es)
108
+ #
109
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
110
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
111
+ # License:: MIT
112
+ class PredefinedCC
113
+ def initialize(orig)
114
+ @orig = orig
115
+ end
116
+
117
+ def [](key)
118
+ to_h[key]
119
+ end
120
+
121
+ def cc(*names)
122
+ return to_h.values.flatten.uniq.compact if names.count.zero?
123
+ return self[names.first] if names.count == 1
124
+ to_h.values_at(names.first, *names.drop(1)).flatten.uniq.compact
125
+ end
126
+
127
+ def to_h
128
+ @to_h ||= begin
129
+ if @orig.is_a? Hash
130
+ @orig.each_with_object({}) do |i, o|
131
+ o[i.first] = Lazylead::PlainCC.new(i.last).cc
132
+ end
133
+ else
134
+ {}
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ # CC addresses based on Jira component owners for email notification.
141
+ # Allows to detect the CC for particular ticket based on its component.
142
+ #
143
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
144
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
145
+ # License:: MIT
146
+ class ComponentCC < Lazylead::PredefinedCC
147
+ def initialize(prj, jira)
148
+ @prj = prj
149
+ @jira = jira
150
+ end
151
+
152
+ def to_h
153
+ @to_h ||= begin
154
+ components.each_with_object({}) do |c, h|
155
+ email = lead(c.attrs["id"])
156
+ next if email.nil? || email.blank?
157
+ h[c.attrs["name"]] = email
158
+ end
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def lead(component_id)
165
+ @jira.raw do |j|
166
+ lead = j.Component
167
+ .find(component_id, expand: "", fields: "")
168
+ .attrs["lead"]
169
+ next if lead.nil? || lead.empty?
170
+ j.User.find(lead["key"]).attrs["emailAddress"]
171
+ end
172
+ end
173
+
174
+ def components
175
+ @jira.raw do |j|
176
+ j.Project.find(@prj, expand: "components", fields: "").components
177
+ end
178
+ end
179
+ end
180
+ end