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,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