lazylead 0.1.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 (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,6 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Build project
4
+ #
5
+ export RUBYOPT='-W:no-deprecated'
6
+ clear && rake -A
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ set -e
3
+ set -x
4
+
5
+ cd $(dirname $0)
6
+ bundle update
7
+ # rake
8
+ trap 'git reset HEAD~1 && rm bonus.key && git checkout -- .gitignore' EXIT
9
+ cp /code/home/assets/lazylead/bonus.key .
10
+ sed -i -s 's|Gemfile.lock||g' .gitignore
11
+ git add bonus.key
12
+ git add Gemfile.lock
13
+ git add .gitignore
14
+ git commit -m 'configs for heroku'
15
+ git push heroku master -f
16
+
@@ -0,0 +1,106 @@
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 "English"
26
+
27
+ lib = File.expand_path("lib", __dir__)
28
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
29
+ require "lazylead/version"
30
+
31
+ Gem::Specification.new do |s|
32
+ s.specification_version = 2 if s.respond_to? :specification_version=
33
+ s.rubygems_version = "2.2"
34
+ s.required_ruby_version = ">=2.6.5"
35
+ s.name = "lazylead"
36
+ s.version = Lazylead::VERSION
37
+ s.license = "MIT"
38
+ s.summary = "Eliminate the annoying work within bug-trackers."
39
+ s.description = "Ticketing systems (Github, Jira, etc.) are strongly
40
+ integrated into our processes and everyone understands their necessity. As soon
41
+ as a developer becomes a lead/technical manager, he or she faces a set of
42
+ routine tasks that are related to ticketing work. On large projects this becomes
43
+ a problem, more and more you spend time running around on dashboards and
44
+ tickets, looking for incorrect deviations in tickets and performing routine
45
+ tasks instead of solving technical problems."
46
+ s.authors = ["Yurii Dubinka"]
47
+ s.email = "yurii.dubinka@gmail.com"
48
+ s.homepage = "http://github.com/dgroup/lazylead"
49
+ s.post_install_message = "Thanks for installing Lazylead v0.0.0!
50
+ Read our blog posts: https://lazylead.org
51
+ Stay in touch with the community in Telegram: https://t.me/lazylead
52
+ Follow us on Twitter: https://twitter.com/lazylead
53
+ If you have any issues, report to our GitHub repo: https://github.com/dgroup/lazylead"
54
+ s.files = `git ls-files`.split($RS)
55
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
56
+ s.test_files = s.files.grep(%r{^(test|features)/})
57
+ s.rdoc_options = ["--charset=UTF-8"]
58
+ s.extra_rdoc_files = ["readme.md", "license.txt"]
59
+ s.add_runtime_dependency "activerecord", "6.0.3"
60
+ s.add_runtime_dependency "backtrace", "0.3"
61
+ s.add_runtime_dependency "concurrent-ruby", "1.1.5"
62
+ s.add_runtime_dependency "diffy", "3.3.0"
63
+ s.add_runtime_dependency "faraday", "1.0.1"
64
+ s.add_runtime_dependency "futex", "0.8.5"
65
+ s.add_runtime_dependency "get_process_mem", "0.2.5"
66
+ s.add_runtime_dependency "haml", "5.0.4"
67
+ s.add_runtime_dependency "jira-ruby", "1.7.1"
68
+ s.add_runtime_dependency "json", "2.2.0"
69
+ s.add_runtime_dependency "logging", "2.2.2"
70
+ s.add_runtime_dependency "mail", "2.7.1"
71
+ s.add_runtime_dependency "memory_profiler", "0.9.13"
72
+ s.add_runtime_dependency "openssl", "2.1.2"
73
+ s.add_runtime_dependency "rainbow", "3.0.0"
74
+ s.add_runtime_dependency "require_all", "3.0.0"
75
+ s.add_runtime_dependency "rufus-scheduler", "3.6.0"
76
+ s.add_runtime_dependency "semantic", "1.6.1"
77
+ s.add_runtime_dependency "sinatra", "2.0.5"
78
+ s.add_runtime_dependency "slop", "4.4"
79
+ s.add_runtime_dependency "sqlite3", "1.4.2"
80
+ s.add_runtime_dependency "sys-proctable", "1.2.1"
81
+ s.add_runtime_dependency "threads", "0.3"
82
+ s.add_runtime_dependency "tilt", "2.0.10"
83
+ s.add_runtime_dependency "total", "0.3"
84
+ s.add_runtime_dependency "typhoeus", "1.3.1"
85
+ s.add_runtime_dependency "tzinfo", "1.1"
86
+ s.add_runtime_dependency "tzinfo-data", "1.2019.3"
87
+ s.add_runtime_dependency "usagewatch_ext", "0.2.1"
88
+ s.add_runtime_dependency "vcs4sql", "0.1.0"
89
+ s.add_runtime_dependency "viewpoint", "1.1.0"
90
+ s.add_runtime_dependency "zache", "0.12"
91
+ s.add_development_dependency "codecov", "0.1.14"
92
+ s.add_development_dependency "guard", "2.15.0"
93
+ s.add_development_dependency "guard-minitest", "2.4.6"
94
+ s.add_development_dependency "minitest", "5.11.3"
95
+ s.add_development_dependency "minitest-fail-fast", "0.1.0"
96
+ s.add_development_dependency "minitest-hooks", "1.5.0"
97
+ s.add_development_dependency "minitest-reporters", "1.3.6"
98
+ s.add_development_dependency "rake", "12.3.2"
99
+ s.add_development_dependency "random-port", "0.3.1"
100
+ s.add_development_dependency "rdoc", "6.1.1"
101
+ s.add_development_dependency "rubocop", "0.82"
102
+ s.add_development_dependency "rubocop-minitest", "0.5.1"
103
+ s.add_development_dependency "rubocop-performance", "1.5.2"
104
+ s.add_development_dependency "rubocop-rspec", "1.33.0"
105
+ s.add_development_dependency "xcop", "0.6"
106
+ end
@@ -0,0 +1,52 @@
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_relative "lazylead/allocated"
26
+ require_relative "lazylead/confluence"
27
+ require_relative "lazylead/email"
28
+ require_relative "lazylead/exchange"
29
+ require_relative "lazylead/log"
30
+ require_relative "lazylead/model"
31
+ require_relative "lazylead/postman"
32
+ require_relative "lazylead/salt"
33
+ require_relative "lazylead/schedule"
34
+ require_relative "lazylead/smtp"
35
+ require_relative "lazylead/cli/app"
36
+ require_relative "lazylead/system/jira"
37
+ require_relative "lazylead/system/empty"
38
+ require_relative "lazylead/system/fake"
39
+ require_relative "lazylead/system/synced"
40
+ require_relative "lazylead/task/alert"
41
+ require_relative "lazylead/task/confluence_ref"
42
+ require_relative "lazylead/task/echo"
43
+ require_relative "lazylead/task/fix_version"
44
+ require_relative "lazylead/task/missing_comment"
45
+ require_relative "lazylead/version"
46
+
47
+ # Lazylead main module.
48
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
49
+ # Copyright:: Copyright (c) 2020 Yurii Dubinka
50
+ # License:: MIT
51
+ module Lazylead
52
+ end
@@ -0,0 +1,56 @@
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 "get_process_mem"
26
+
27
+ module Lazylead
28
+ # Human readable format of allocated bytes within current process.
29
+ #
30
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
31
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
32
+ # License:: MIT
33
+ class Allocated
34
+ def initialize(bytes = GetProcessMem.new.bytes.to_i)
35
+ @bytes = bytes
36
+ end
37
+
38
+ def to_s
39
+ if @bytes.nil?
40
+ "?"
41
+ elsif @bytes < 1024
42
+ "#{@bytes}B"
43
+ elsif @bytes < 1024 * 1024
44
+ "#{(@bytes / 1024).round}KB"
45
+ elsif @bytes < 1024 * 1024 * 1024
46
+ "#{(@bytes / (1024 * 1024)).round}MB"
47
+ else
48
+ "#{(@bytes / (1024 * 1024 * 1024)).round}GB"
49
+ end
50
+ end
51
+
52
+ def to_i
53
+ @bytes
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,87 @@
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 "vcs4sql/sqlite/migration"
26
+ require_relative "../smtp"
27
+ require_relative "../schedule"
28
+
29
+ module Lazylead
30
+ # @todo #/DEV Lazylead::CLI::Args add a new class which extends hash
31
+ # in order to access command-line arguments in easy manner.
32
+ module CLI
33
+ #
34
+ # APP start command.
35
+ #
36
+ # By default, the app will check the version of the database structure and
37
+ # apply the missing change sets (more https://github.com/dgroup/vcs4sql).
38
+ #
39
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
40
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
41
+ # License:: MIT
42
+ class App
43
+ def initialize(log, schedule, smtp = Smtp.new)
44
+ @log = log
45
+ @schedule = schedule
46
+ @smtp = smtp
47
+ end
48
+
49
+ def run(opts)
50
+ apply_vcs_migration opts
51
+ enable_active_record
52
+ @smtp.enable
53
+ schedule_tasks
54
+ end
55
+
56
+ private
57
+
58
+ def apply_vcs_migration(opts)
59
+ @db = File.expand_path(opts[:home]) + "/" + opts[:sqlite]
60
+ vcs = File.expand_path(opts[:home]) + "/" + opts[:vcs4sql]
61
+ @log.debug "Database: '#{@db}', sql migration dir: '#{vcs}'"
62
+ Vcs4sql::Sqlite::Migration.new(@db).upgrade vcs, opts[:testdata]
63
+ @log.debug "Migration applied to #{@db} from #{vcs}"
64
+ end
65
+
66
+ def enable_active_record
67
+ ActiveRecord::Base.establish_connection(
68
+ adapter: "sqlite3",
69
+ database: @db
70
+ )
71
+ @log.debug "Database connection established"
72
+ end
73
+
74
+ def schedule_tasks
75
+ todo = ORM::Task.where(enabled: "true")
76
+ if todo.empty?
77
+ @log.warn "ll-001: No tasks found."
78
+ else
79
+ todo.find_each do |task|
80
+ @schedule.register task
81
+ end
82
+ @schedule.join
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,157 @@
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 "faraday"
26
+ require "jira-ruby"
27
+
28
+ module Lazylead
29
+ # Represents single Confluence instance.
30
+ #
31
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
32
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
33
+ # License:: MIT
34
+ class Confluence
35
+ def initialize(conf)
36
+ @conf = conf
37
+ @http = Faraday.new(url: @conf.url)
38
+ @http.basic_auth(@conf.user, @conf.pass)
39
+ end
40
+
41
+ # Confluence instance url like
42
+ # http://confluence.com
43
+ # https://confluence.com
44
+ def url
45
+ @conf.url
46
+ end
47
+
48
+ # Construct the remote link from Jira ticket to Confluence page.
49
+ # Read more:
50
+ # - https://bit.ly/2zkfTwB
51
+ # - https://bit.ly/2zk8iOB
52
+ # - https://bit.ly/3bkGkiU
53
+ def make_link(url, type = "Wiki Page")
54
+ {
55
+ globalId: "appId=#{@conf.app}&pageId=#{url[/pageId=(?<id>\d+)/, 1]}",
56
+ application: { type: @conf.type, name: @conf.name },
57
+ relationship: type,
58
+ object: { url: url, title: type }
59
+ }
60
+ end
61
+
62
+ # Fetch the page url with pageId (Atlassian global id)
63
+ # using confluence space name and page title.
64
+ #
65
+ # @return url with following format
66
+ # http://host/pages/viewpage.action?pageId=xxxxx or blank string in case of
67
+ # errors
68
+ #
69
+ # @todo #/DEV Unable to find confluence pages where title has ":" symbol.
70
+ # The symbol ":" might be replaced by "%3A". The search needs to consider
71
+ # this case as well.
72
+ def fetch_page_id(space, title)
73
+ resp = @http.get(
74
+ "/rest/api/content", limit: 1, spaceKey: space, title: title
75
+ ).body
76
+ return "" if resp.blank?
77
+ pages = JSON.parse(resp)
78
+ return "" if pages["results"].empty?
79
+ "#{@conf.url}/pages/viewpage.action?"\
80
+ "pageId=#{pages['results'].first['id']}"
81
+ end
82
+ end
83
+
84
+ # The reference from Jira issue to Confluence system.
85
+ class Link
86
+ # :issue :: The issue from external system
87
+ # :sys :: The jira ticketing system
88
+ # :cnfl :: The Confluence instances details (host, appId)
89
+ # which expected within the comments
90
+ def initialize(issue, sys, cnfl)
91
+ @issue = issue
92
+ @sys = sys
93
+ @confl = cnfl
94
+ end
95
+
96
+ # Fetch ticket links from comments
97
+ def fetch_links
98
+ ticket = @sys.raw do |conn|
99
+ conn.Issue.find(@issue.id, expand: "comments,changelog", fields: "")
100
+ end
101
+ @mentioned = mentioned_links(ticket)
102
+ @existing = existing_links(ticket)
103
+ @diff = @mentioned.reject { |url| @existing.include? url }
104
+ end
105
+
106
+ # Detect links mentioned in ticket comments
107
+ def mentioned_links(ticket)
108
+ ticket.comments
109
+ .map { |cmnt| cmnt.attrs["body"] }
110
+ .select { |cmnt| @confl.any? { |c| cmnt.include? c.url } }
111
+ .flat_map { |cmnt| cmnt.split " " }
112
+ .select { |cmnt| @confl.any? { |c| cmnt.start_with? c.url } }
113
+ .map(&method(:to_page_id))
114
+ .reject(&:blank?)
115
+ .uniq
116
+ end
117
+
118
+ # Convert confluence page url to the following format:
119
+ # http://confluence.com/pages/viewpage.action?pageId=xxxxx
120
+ #
121
+ # Sometimes links to confluence pages have the following formats:
122
+ # http://confluence.com/pages/spaceKey=THESPACE&title=THETITLE
123
+ # http://confluence.com/display/THESPACE/THETITLE
124
+ # and can't be linked though the "remote link" Jira functionality.
125
+ def to_page_id(url)
126
+ return url if url.include? "pageId="
127
+ if url.include? "&title="
128
+ space = url[/spaceKey=(?<space>[A-Z0-9+.]+)/, 1]
129
+ title = url[/&title=(?<title>[A-Za-z0-9+.]+)/, 1]
130
+ else
131
+ space, title = url.split("/").last(2)
132
+ end
133
+ @confl.find { |c| url.start_with? c.url }
134
+ .fetch_page_id(space, title.tr("+", " "))
135
+ end
136
+
137
+ # Detect existing links from the ticket
138
+ def existing_links(ticket, type = "Wiki Page")
139
+ ticket.remotelink
140
+ .all
141
+ .select { |l| l.attrs["relationship"] == type }
142
+ .map { |e| e.attrs["object"]["url"] }
143
+ end
144
+
145
+ def need_link?
146
+ !@diff.empty?
147
+ end
148
+
149
+ # Add reference to Confluence page from current issue
150
+ def add_link
151
+ @diff.each do |url|
152
+ cnf = @confl.find { |c| url.start_with? c.url }
153
+ @issue.remotelink.build.save cnf.make_link(url)
154
+ end
155
+ end
156
+ end
157
+ end