redmine_remotes 0.11.0 → 0.15.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/project_remote_issues_controller/create.rb +1 -1
  3. data/app/controllers/project_remote_issues_controller/update.rb +1 -1
  4. data/app/controllers/remote_trackers_controller.rb +4 -1
  5. data/app/models/remote_issue.rb +11 -0
  6. data/app/models/remote_issue_fetch/base.rb +45 -0
  7. data/app/models/remote_issue_fetch/base/_local_issue.rb +77 -0
  8. data/app/models/remote_issue_fetch/base/_provider_issue.rb +18 -0
  9. data/app/models/remote_issue_fetch/base/_remote_issue.rb +19 -0
  10. data/app/models/remote_issue_fetch/create.rb +44 -0
  11. data/app/models/remote_issue_fetch/create/_local_issue.rb +21 -0
  12. data/app/models/remote_issue_fetch/create/_remote_issue.rb +21 -0
  13. data/app/models/remote_issue_fetch/update.rb +18 -0
  14. data/app/models/remote_issue_fetch/update/_local_issue.rb +15 -0
  15. data/app/models/remote_issue_fetch/update/_remote_issue.rb +15 -0
  16. data/app/models/remote_tracker.rb +7 -1
  17. data/app/models/remote_tracker/find_issue_local_status.rb +1 -1
  18. data/app/models/remote_tracker/remote.rb +32 -8
  19. data/app/models/remote_tracker_fetch.rb +47 -0
  20. data/app/models/remote_tracker_fetch/provider.rb +13 -0
  21. data/app/models/remote_tracker_fetch/remote_issues.rb +27 -0
  22. data/app/models/remote_tracker_fetch/remote_tracker.rb +16 -0
  23. data/app/models/remote_user/find_local_user.rb +3 -3
  24. data/config/initializers/001_patches.rb +1 -0
  25. data/config/locales/en.yml +1 -0
  26. data/config/locales/pt-BR.yml +1 -0
  27. data/config/routes.rb +6 -5
  28. data/db/migrate/20190531171641_create_remote_trackers.rb +1 -1
  29. data/db/migrate/20190910182842_create_remote_users.rb +1 -1
  30. data/db/migrate/20190910203552_create_remote_issue_statuses.rb +1 -1
  31. data/db/migrate/20190912000000_create_remote_issues.rb +1 -1
  32. data/db/migrate/20200602192120_add_outdated_at_to_remote_issues.rb +7 -0
  33. data/db/migrate/20200602194753_add_fetched_at_to_remote_issues.rb +7 -0
  34. data/db/migrate/20200602203020_add_fetched_at_to_remote_trackers.rb +7 -0
  35. data/lib/redmine_remotes/esosti/entities/issue.rb +40 -1
  36. data/lib/redmine_remotes/esosti/instance.rb +8 -5
  37. data/lib/redmine_remotes/jira/entities/issue.rb +6 -2
  38. data/lib/redmine_remotes/jira/instance.rb +4 -17
  39. data/lib/redmine_remotes/jobs/fetch_issues.rb +31 -0
  40. data/lib/redmine_remotes/jobs/fetch_trackers.rb +30 -0
  41. data/lib/redmine_remotes/patches/avmtrf1_rest_provider_instance.rb +22 -0
  42. data/lib/redmine_remotes/version.rb +1 -1
  43. data/lib/tasks/redmine_remotes.rake +16 -11
  44. metadata +49 -27
  45. data/app/models/redmine_remotes/tableless/remote_issue_fetch/base.rb +0 -39
  46. data/app/models/redmine_remotes/tableless/remote_issue_fetch/base/_local_issue.rb +0 -67
  47. data/app/models/redmine_remotes/tableless/remote_issue_fetch/base/_provider_issue.rb +0 -22
  48. data/app/models/redmine_remotes/tableless/remote_issue_fetch/create.rb +0 -57
  49. data/app/models/redmine_remotes/tableless/remote_issue_fetch/create/_local_issue.rb +0 -25
  50. data/app/models/redmine_remotes/tableless/remote_issue_fetch/create/_remote_issue.rb +0 -32
  51. data/app/models/redmine_remotes/tableless/remote_issue_fetch/update.rb +0 -30
  52. data/app/models/redmine_remotes/tableless/remote_issue_fetch/update/_local_issue.rb +0 -19
  53. data/app/models/redmine_remotes/tableless/remote_issue_fetch/update/_remote_issue.rb +0 -19
  54. data/lib/redmine_remotes/rest_provider/http_response.rb +0 -31
  55. data/lib/redmine_remotes/rest_provider/instance.rb +0 -35
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ class RemoteTrackerFetch < ::EacRailsUtils::Models::Tableless
6
+ require_sub __FILE__, include_modules: true, require_dependency: true
7
+ enable_simple_cache
8
+ include ::EacRailsUtils::Models::FetchErrors
9
+
10
+ attribute :remote_tracker_id, ::Integer
11
+ belongs_to :remote_tracker, class_name: 'RemoteTracker'
12
+
13
+ validates :remote_tracker, presence: true
14
+ validates :start_time, presence: true
15
+ validates :end_time, presence: true
16
+ validate :remote_tracker_fetchable_validation
17
+
18
+ def save
19
+ ::Issue.transaction do
20
+ return false unless valid?
21
+ return false unless save_remote_issues
22
+ return false unless save_remote_tracker
23
+ end
24
+ true
25
+ end
26
+
27
+ def default_error_column
28
+ :remote_tracker_id
29
+ end
30
+
31
+ private
32
+
33
+ def end_time_uncached
34
+ ::Time.zone.now
35
+ end
36
+
37
+ def start_time_uncached
38
+ remote_tracker.try(:fetched_at) || end_time
39
+ end
40
+
41
+ def remote_tracker_fetchable_validation
42
+ return if remote_tracker.blank?
43
+ return if remote_tracker.fetchable?
44
+
45
+ errors.add(:remote_tracker, "Remote tracker \"#{remote_tracker}\" is not fetchable")
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ class RemoteTrackerFetch < ::EacRailsUtils::Models::Tableless
6
+ module Provider
7
+ private
8
+
9
+ def provider_issues_codes
10
+ remote_tracker.remote_instance.fetch_issues_changed(start_time, end_time)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ class RemoteTrackerFetch < ::EacRailsUtils::Models::Tableless
6
+ module RemoteIssues
7
+ private
8
+
9
+ def save_remote_issues
10
+ remote_issues.all? { |remote_issue| save_remote_issue(remote_issue) }
11
+ end
12
+
13
+ def save_remote_issue(remote_issue)
14
+ return true if remote_issue.update(outdated_at: end_time)
15
+
16
+ fetch_record_errors(remote_issue, default_column: default_error_column)
17
+ false
18
+ end
19
+
20
+ def remote_issues_uncached
21
+ provider_issues_codes.map do |code|
22
+ ::RemoteIssue.find_by(remote_tracker: remote_tracker,
23
+ remote_code: ::RemoteIssue.remote_code_sanitize(code))
24
+ end.reject(&:blank?)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ class RemoteTrackerFetch < ::EacRailsUtils::Models::Tableless
6
+ module RemoteTracker
7
+ private
8
+
9
+ def save_remote_tracker
10
+ return true if remote_tracker.update(fetched_at: end_time)
11
+
12
+ fetch_record_errors(remote_tracker, default_column: default_error_column)
13
+ false
14
+ end
15
+ end
16
+ end
@@ -25,7 +25,7 @@ class RemoteUser < ActiveRecord::Base
25
25
 
26
26
  def result_by_local_user_mail_address
27
27
  condition = ::EmailAddress.arel_table[:address].matches("#{remote_login}@%")
28
- ::EmailAddress.where(condition).first.try(:user)
28
+ ::EmailAddress.find_by(condition).try(:user)
29
29
  end
30
30
 
31
31
  def result_by_register_not_found
@@ -47,10 +47,10 @@ class RemoteUser < ActiveRecord::Base
47
47
  end
48
48
 
49
49
  def remote_tracker_result(remote_tracker)
50
- ::RemoteUser.where(
50
+ ::RemoteUser.find_by(
51
51
  remote_tracker: remote_tracker,
52
52
  remote_login: remote_login
53
- ).first.try(:local_user)
53
+ ).try(:local_user)
54
54
  end
55
55
 
56
56
  def remote_trackers_to_search
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'redmine_remotes/patches/avmtrf1_rest_provider_instance'
3
4
  require 'redmine_remotes/patches/issue'
4
5
  require 'redmine_remotes/patches/issue_status'
5
6
  require 'redmine_remotes/patches/project'
@@ -22,3 +22,4 @@ en:
22
22
  label_remote_issue_statuses: Remote issue statuses
23
23
  label_remote_users: Remote users
24
24
  label_remotes: Remotes
25
+ menu_redmine_remotes: Remotes
@@ -22,3 +22,4 @@ pt-BR:
22
22
  label_remote_issue_statuses: Situações de demandas remotas
23
23
  label_remote_users: Usuários remotos
24
24
  label_remotes: Remotos
25
+ menu_redmine_remotes: Remotos
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RedmineApp::Application.routes.draw do
4
+ concern :active_scaffold, ActiveScaffold::Routing::Basic.new(association: true)
4
5
  get '/projects/:id/remotes', to: 'project_remote_issues#index'
5
6
  get '/projects/:id/tracker/:tracker_id/new_issue', to: 'project_remote_issues#new',
6
7
  as: 'new_project_remote_issue'
@@ -9,9 +10,9 @@ RedmineApp::Application.routes.draw do
9
10
  put '/projects/:id/remote_issue/:remote_issue_id/update', to: 'project_remote_issues#update',
10
11
  as: 'update_project_remote_issue'
11
12
  resources(:project_remote_issues, only: [:index])
12
- resources(:remote_trackers) { as_routes }
13
- resources(:remote_tracker_settings) { as_routes }
14
- resources(:remote_issues) { as_routes }
15
- resources(:remote_issue_statuses) { as_routes }
16
- resources(:remote_users) { as_routes }
13
+ resources(:remote_trackers, concerns: :active_scaffold)
14
+ resources(:remote_tracker_settings, concerns: :active_scaffold)
15
+ resources(:remote_issues, concerns: :active_scaffold)
16
+ resources(:remote_issue_statuses, concerns: :active_scaffold)
17
+ resources(:remote_users, concerns: :active_scaffold)
17
18
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class CreateRemoteTrackers < ActiveRecord::Migration
4
4
  def change
5
- create_table :remote_trackers do |t|
5
+ create_table :remote_trackers do |t| # rubocop:disable Rails/CreateTableWithTimestamps
6
6
  t.string :profile
7
7
  t.string :root_url
8
8
  t.string :username
@@ -2,7 +2,7 @@
2
2
 
3
3
  class CreateRemoteUsers < ActiveRecord::Migration
4
4
  def change
5
- create_table :remote_users do |t|
5
+ create_table :remote_users do |t| # rubocop:disable Rails/CreateTableWithTimestamps
6
6
  t.references :remote_tracker, index: true
7
7
  t.string :remote_login
8
8
  t.references :local_user, index: true
@@ -2,7 +2,7 @@
2
2
 
3
3
  class CreateRemoteIssueStatuses < ActiveRecord::Migration
4
4
  def change
5
- create_table :remote_issue_statuses do |t|
5
+ create_table :remote_issue_statuses do |t| # rubocop:disable Rails/CreateTableWithTimestamps
6
6
  t.references :remote_tracker, index: true
7
7
  t.references :local_tracker, index: true
8
8
  t.string :remote_name
@@ -2,7 +2,7 @@
2
2
 
3
3
  class CreateRemoteIssues < ActiveRecord::Migration
4
4
  def change
5
- create_table :remote_issues do |t|
5
+ create_table :remote_issues do |t| # rubocop:disable Rails/CreateTableWithTimestamps
6
6
  t.references :remote_tracker, index: true
7
7
  t.references :local_issue, index: true, nullable: true
8
8
  t.string :remote_code
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddOutdatedAtToRemoteIssues < ActiveRecord::Migration
4
+ def change
5
+ add_column :remote_issues, :outdated_at, :datetime
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddFetchedAtToRemoteIssues < ActiveRecord::Migration
4
+ def change
5
+ add_column :remote_issues, :fetched_at, :timestamp
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddFetchedAtToRemoteTrackers < ActiveRecord::Migration
4
+ def change
5
+ add_column :remote_trackers, :fetched_at, :datetime
6
+ end
7
+ end
@@ -6,8 +6,19 @@ module RedmineRemotes
6
6
  module Esosti
7
7
  module Entities
8
8
  class Issue < ::Avmtrf1::Esosti::Entities::Issue
9
+ EXTRA_DESCRIPTION_BEFORE = { reported_by: 'Relatado por', affected_person: 'Pessoa afetada',
10
+ type_description: 'Tipo de solicitação' }.freeze
11
+
12
+ def human_view_url
13
+ instance.issue_human_view_url(uid)
14
+ end
15
+
9
16
  def author_login
10
- attributes.fetch('CREATEDBY').if_present { |v| v.gsub(/@[^@]+\z/, '') }
17
+ mail_local_part(attributes.fetch('CREATEDBY'))
18
+ end
19
+
20
+ def affected_person
21
+ mail_local_part(attributes.fetch('AFFECTEDPERSON'))
11
22
  end
12
23
 
13
24
  def attributes
@@ -28,17 +39,45 @@ module RedmineRemotes
28
39
  )
29
40
  end
30
41
 
42
+ def extra_description_before
43
+ {
44
+ 'Relatado por' => "user:#{reported_by}",
45
+ 'Pessoa afetada' => "user:#{affected_person}",
46
+ 'Tipo de solicitação' => type_description
47
+ }
48
+ end
49
+
31
50
  def related_mbos
32
51
  data.first.fetch('RelatedMbos')
33
52
  end
34
53
 
54
+ def reported_by
55
+ mail_local_part(attributes.fetch('REPORTEDBY'))
56
+ end
57
+
35
58
  def status_name
36
59
  attributes.fetch('STATUS')
37
60
  end
38
61
 
39
62
  def subject
63
+ description.gsub(/\s+/, ' ')
64
+ end
65
+
66
+ def type_description
40
67
  attributes.fetch('DESCRIPTION')
41
68
  end
69
+
70
+ def uid
71
+ attributes.fetch('TICKETUID').fetch('content')
72
+ end
73
+
74
+ private
75
+
76
+ def mail_local_part(mail_address)
77
+ mail_address.if_present do |v|
78
+ /\A([^@]+)@/.if_match(v, false) { |m| m[1].downcase }.if_present(mail_address)
79
+ end
80
+ end
42
81
  end
43
82
  end
44
83
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'avmtrf1/esosti/instance'
4
+ require 'avmtrf1/esosti/raw_data_sanitizer'
5
+
3
6
  module RedmineRemotes
4
7
  module Esosti
5
- class Instance < ::RedmineRemotes::RestProvider::Instance
8
+ class Instance < ::Avmtrf1::Esosti::Instance
6
9
  ISSUE_ID_PATTERN = /\A(?:ss|in)[0-9]+\z/i.freeze
7
10
 
8
11
  class << self
@@ -14,12 +17,12 @@ module RedmineRemotes
14
17
  end
15
18
  end
16
19
 
17
- def build_service_url(service_url_suffix)
18
- "#{root_url}/maxrest/rest#{service_url_suffix}"
20
+ def issue_human_view_url(ticket_uid)
21
+ "#{root_url}/itsm/ui/?event=loadapp&value=ms_sr&uniqueid=#{ticket_uid}"
19
22
  end
20
23
 
21
- def issue_get_url_suffix(provider_issue_id)
22
- "/os/MS_RMTICKET/?ticketid=#{provider_issue_id}"
24
+ def fetch_issues_changed(start_time, _end_time)
25
+ changed(start_time)
23
26
  end
24
27
  end
25
28
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'avmtrf1/rest_provider/entity'
3
+ require 'avmtrf1/jira/entities/issue'
4
4
 
5
5
  module RedmineRemotes
6
6
  module Jira
7
7
  module Entities
8
- class Issue < ::Avmtrf1::RestProvider::Entity
8
+ class Issue < ::Avmtrf1::Jira::Entities::Issue
9
+ def human_view_url
10
+ instance.issue_human_view_url(code)
11
+ end
12
+
9
13
  def code
10
14
  data.fetch('key')
11
15
  end
@@ -1,33 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'eac_ruby_utils/core_ext'
4
+ require 'avmtrf1/jira/instance'
4
5
 
5
6
  module RedmineRemotes
6
7
  module Jira
7
- class Instance < ::RedmineRemotes::RestProvider::Instance
8
- ISSUE_ID_PATTERN = /\A([a-z][a-z0-9]+)\-(\d+)\z/i.freeze
8
+ class Instance < ::Avmtrf1::Jira::Instance
9
9
  DEFAULT_DESCRIPTION_FIELD = 'description'
10
10
 
11
- class << self
12
- def parse_issue_id(global_issue_id)
13
- m = ISSUE_ID_PATTERN.match(global_issue_id)
14
- return nil unless m
15
-
16
- ::OpenStruct.new(provider_issue_id: m[0], project_id: m[1], project_issue_id: m[2])
17
- end
18
- end
19
-
20
- def build_service_url(service_url_suffix)
21
- "#{root_url}/rest/api/latest#{service_url_suffix}"
22
- end
23
-
24
11
  def description_field
25
12
  remote_tracker.setting_value(::RemoteTrackerSetting::NAME_FIELD_DESCRIPTION)
26
13
  .if_present(DEFAULT_DESCRIPTION_FIELD)
27
14
  end
28
15
 
29
- def issue_get_url_suffix(provider_issue_id)
30
- "/issue/#{provider_issue_id}"
16
+ def issue_human_view_url(issue_code)
17
+ "#{root_url}/browse/#{issue_code.to_s.upcase}"
31
18
  end
32
19
  end
33
20
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RedmineRemotes
4
+ module Jobs
5
+ class FetchIssues
6
+ enable_simple_cache
7
+
8
+ def run
9
+ ::Rails.logger.info("Issues to fetch: #{issues_to_fetch.count}")
10
+ issues_to_fetch.each { |issue| fetch_issue(issue) }
11
+ end
12
+
13
+ private
14
+
15
+ def issues_to_fetch_uncached
16
+ ::RemoteIssue.outdated
17
+ end
18
+
19
+ def fetch_issue(remote_issue)
20
+ ::Rails.logger.info 'Fetching remote issue ' \
21
+ "\"#{remote_issue}|\##{remote_issue.local_issue.id}\""
22
+ record = ::RemoteIssueFetch::Update.new(
23
+ remote_issue: remote_issue
24
+ )
25
+ return if record.save
26
+
27
+ ::Rails.logger.warn "Fetch failed with errors: #{record.errors.messages}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RedmineRemotes
4
+ module Jobs
5
+ class FetchTrackers
6
+ enable_simple_cache
7
+
8
+ def run
9
+ ::Rails.logger.info("Trackers to fetch: #{trackers_to_fetch.count}")
10
+ trackers_to_fetch.each { |tracker| fetch_tracker(tracker) }
11
+ end
12
+
13
+ private
14
+
15
+ def fetch_tracker(remote_tracker)
16
+ ::Rails.logger.info "Fetch remote tracker \"#{remote_tracker}\"..."
17
+ record = ::RemoteTrackerFetch.new(
18
+ remote_tracker: remote_tracker
19
+ )
20
+ return if record.save
21
+
22
+ ::Rails.logger.warn "Fetched failed with errors: #{record.errors.messages}"
23
+ end
24
+
25
+ def trackers_to_fetch_uncached
26
+ ::RemoteTracker.where(profile: ::RemoteTracker::PROFILE_ESOSTI)
27
+ end
28
+ end
29
+ end
30
+ end