foreman-tasks 3.0.5 → 4.1.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_tests.yml +5 -3
- data/app/controllers/concerns/foreman_tasks/find_tasks_common.rb +14 -0
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -6
- data/app/controllers/foreman_tasks/tasks_controller.rb +3 -11
- data/app/lib/actions/entry_action.rb +8 -4
- data/app/lib/actions/helpers/lock.rb +11 -5
- data/app/lib/actions/middleware/keep_current_request_id.rb +4 -1
- data/app/lib/actions/middleware/keep_current_user.rb +11 -1
- data/app/lib/actions/middleware/load_setting_values.rb +35 -0
- data/app/lib/actions/observable_action.rb +80 -0
- data/app/lib/actions/proxy_action.rb +2 -4
- data/app/models/foreman_tasks/concerns/action_subject.rb +0 -6
- data/app/models/foreman_tasks/link.rb +60 -0
- data/app/models/foreman_tasks/lock.rb +30 -128
- data/app/models/foreman_tasks/task.rb +20 -7
- data/app/models/foreman_tasks/task/search.rb +6 -6
- data/app/views/foreman_tasks/api/locks/show.json.rabl +4 -0
- data/app/views/foreman_tasks/api/tasks/details.json.rabl +5 -3
- data/app/views/foreman_tasks/tasks/_lock_card.html.erb +10 -0
- data/db/migrate/20180927120509_add_user_id.foreman_tasks.rb +4 -2
- data/db/migrate/20181206123910_create_foreman_tasks_links.foreman_tasks.rb +26 -0
- data/db/migrate/20181206124952_migrate_non_exclusive_locks_to_links.foreman_tasks.rb +14 -0
- data/db/migrate/20181206131436_drop_old_locks.foreman_tasks.rb +20 -0
- data/db/migrate/20181206131627_make_locks_exclusive.foreman_tasks.rb +25 -0
- data/lib/foreman_tasks/cleaner.rb +10 -0
- data/lib/foreman_tasks/engine.rb +6 -2
- data/lib/foreman_tasks/tasks/export_tasks.rake +2 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/action_names.rb +3 -2
- data/locale/en/foreman_tasks.po +60 -27
- data/locale/foreman_tasks.pot +180 -132
- data/locale/fr/foreman_tasks.po +61 -28
- data/locale/ja/foreman_tasks.po +61 -28
- data/locale/zh_CN/foreman_tasks.po +61 -28
- data/package.json +6 -6
- data/test/controllers/api/tasks_controller_test.rb +16 -1
- data/test/controllers/tasks_controller_test.rb +3 -3
- data/test/core/unit/runner_test.rb +4 -17
- data/test/factories/task_factory.rb +31 -4
- data/test/unit/actions/action_with_sub_plans_test.rb +5 -2
- data/test/unit/actions/proxy_action_test.rb +4 -1
- data/test/unit/cleaner_test.rb +4 -4
- data/test/unit/locking_test.rb +85 -0
- data/test/unit/task_test.rb +15 -15
- data/test/unit/triggering_test.rb +2 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +2 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +3 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +4 -4
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +4 -1
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +5 -1
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +3 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +2 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.scss +4 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +2 -2
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionNameCellFormatter.test.js.snap +2 -3
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionNameCellFormatter.js +2 -3
- metadata +14 -4
- data/test/unit/lock_test.rb +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 676dae1bc60b1633da496445b980e4b5fb491931bb227a70f2dcec4c44a9ae9e
|
|
4
|
+
data.tar.gz: 98d4f9798338d20bf7885a9e11b40d492eeeca4d30e2a7f84c4734e4f83e30d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8482b8a8e5ce147f2bd81d1eea004ba9507c95ecf5d83d0ebd50b7c74cc467a561f035d0049f0d1b44c3d63a29c35f3e55792705d37dfc0b4431e02d2370732a
|
|
7
|
+
data.tar.gz: c317248a7e396b2d556f397ba1fd91d162b5d38b75a593a7f8a28f555ef77ca15ec097f548162edd6c7a7198bfa7e84c6a902bbde6d7515c17b585f7632e9add
|
|
@@ -12,7 +12,7 @@ jobs:
|
|
|
12
12
|
- name: Setup Ruby
|
|
13
13
|
uses: ruby/setup-ruby@v1
|
|
14
14
|
with:
|
|
15
|
-
ruby-version: 2.
|
|
15
|
+
ruby-version: 2.7
|
|
16
16
|
- name: Setup
|
|
17
17
|
run: |
|
|
18
18
|
gem install bundler
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
fail-fast: false
|
|
32
32
|
matrix:
|
|
33
33
|
foreman-core-branch: [develop]
|
|
34
|
-
ruby-version: [2.5, 2.6]
|
|
34
|
+
ruby-version: [2.5, 2.6, 2.7]
|
|
35
35
|
node-version: [12]
|
|
36
36
|
steps:
|
|
37
37
|
- run: sudo apt-get update
|
|
@@ -60,6 +60,7 @@ jobs:
|
|
|
60
60
|
- name: Setup Bundler
|
|
61
61
|
run: |
|
|
62
62
|
echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
|
|
63
|
+
echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
|
|
63
64
|
gem install bundler
|
|
64
65
|
bundle config set without journald development console libvirt
|
|
65
66
|
bundle config set path vendor/bundle
|
|
@@ -70,5 +71,6 @@ jobs:
|
|
|
70
71
|
bundle exec rake db:migrate
|
|
71
72
|
- name: Run plugin tests
|
|
72
73
|
run: |
|
|
73
|
-
bundle exec rake test:
|
|
74
|
+
bundle exec rake test:foreman_tasks
|
|
75
|
+
bundle exec rake test:foreman_tasks_core
|
|
74
76
|
bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module ForemanTasks
|
|
2
|
+
module FindTasksCommon
|
|
3
|
+
def search_query
|
|
4
|
+
[current_taxonomy_search, params[:search]].select(&:present?).join(' AND ')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def current_taxonomy_search
|
|
8
|
+
conditions = []
|
|
9
|
+
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
|
10
|
+
conditions << "location_id = #{Location.current.id}" if Location.current
|
|
11
|
+
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module ForemanTasks
|
|
2
2
|
module Api
|
|
3
3
|
class TasksController < ::Api::V2::BaseController
|
|
4
|
+
include ForemanTasks::FindTasksCommon
|
|
4
5
|
include ::Foreman::Controller::SmartProxyAuth
|
|
5
6
|
add_smart_proxy_filters :callback, :features => 'Dynflow'
|
|
6
7
|
|
|
@@ -51,7 +52,7 @@ module ForemanTasks
|
|
|
51
52
|
In case :type = 'resource', what resource id we're searching the tasks for
|
|
52
53
|
DESC
|
|
53
54
|
param :action_types, [String], :desc => <<-DESC
|
|
54
|
-
Return just tasks of given action type, e.g. ["Actions::Katello::Repository::Synchronize"]
|
|
55
|
+
Return just tasks of given action type, e.g. `["Actions::Katello::Repository::Synchronize"]`
|
|
55
56
|
DESC
|
|
56
57
|
param :active_only, :bool
|
|
57
58
|
param :page, String
|
|
@@ -195,7 +196,7 @@ module ForemanTasks
|
|
|
195
196
|
end
|
|
196
197
|
|
|
197
198
|
def search_options
|
|
198
|
-
[
|
|
199
|
+
[search_query, {}]
|
|
199
200
|
end
|
|
200
201
|
|
|
201
202
|
def_param_group :callback_target do
|
|
@@ -253,10 +254,7 @@ module ForemanTasks
|
|
|
253
254
|
if search_params[:user_id].blank?
|
|
254
255
|
raise BadRequest, _('User search_params requires user_id to be specified')
|
|
255
256
|
end
|
|
256
|
-
scope.
|
|
257
|
-
{ name: ::ForemanTasks::Lock::OWNER_LOCK_NAME,
|
|
258
|
-
resource_type: 'User',
|
|
259
|
-
resource_id: search_params[:user_id] })
|
|
257
|
+
scope.search_for("user_id = #{search_params[:user_id]}")
|
|
260
258
|
when 'resource'
|
|
261
259
|
if search_params[:resource_type].blank? || search_params[:resource_id].blank?
|
|
262
260
|
raise BadRequest,
|
|
@@ -2,6 +2,7 @@ module ForemanTasks
|
|
|
2
2
|
class TasksController < ::ApplicationController
|
|
3
3
|
include Foreman::Controller::AutoCompleteSearch
|
|
4
4
|
include Foreman::Controller::CsvResponder
|
|
5
|
+
include ForemanTasks::FindTasksCommon
|
|
5
6
|
|
|
6
7
|
def show
|
|
7
8
|
@task = resource_base.find(params[:id])
|
|
@@ -126,19 +127,10 @@ module ForemanTasks
|
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
def filter(scope, paginate: true)
|
|
129
|
-
search = current_taxonomy_search
|
|
130
|
-
search = [search, params[:search]].select(&:present?).join(' AND ')
|
|
131
130
|
scope = DashboardTableFilter.new(scope, params).scope
|
|
132
|
-
scope = scope.search_for(
|
|
133
|
-
scope = scope.paginate(:
|
|
131
|
+
scope = scope.search_for(search_query, order: params[:order])
|
|
132
|
+
scope = scope.paginate(page: params[:page], per_page: params[:per_page]) if paginate
|
|
134
133
|
scope.distinct
|
|
135
134
|
end
|
|
136
|
-
|
|
137
|
-
def current_taxonomy_search
|
|
138
|
-
conditions = []
|
|
139
|
-
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
|
140
|
-
conditions << "location_id = #{Location.current.id}" if Location.current
|
|
141
|
-
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
|
142
|
-
end
|
|
143
135
|
end
|
|
144
136
|
end
|
|
@@ -3,6 +3,8 @@ module Actions
|
|
|
3
3
|
include Helpers::ArgsSerialization
|
|
4
4
|
include Helpers::Lock
|
|
5
5
|
|
|
6
|
+
execution_plan_hooks.use :drop_all_locks!, :on => :stopped
|
|
7
|
+
|
|
6
8
|
# what locks to use on the resource? All by default, can be overriden.
|
|
7
9
|
# It might one or more locks available for the resource. This following
|
|
8
10
|
# special values are supported as well:
|
|
@@ -34,12 +36,10 @@ module Actions
|
|
|
34
36
|
input.update serialize_args(resource, *resource.all_related_resources, *additional_args)
|
|
35
37
|
|
|
36
38
|
if resource.is_a? ActiveRecord::Base
|
|
37
|
-
if resource_locks == :
|
|
38
|
-
exclusive_lock!(resource)
|
|
39
|
-
elsif resource_locks == :link
|
|
39
|
+
if resource_locks == :link
|
|
40
40
|
link!(resource)
|
|
41
41
|
else
|
|
42
|
-
|
|
42
|
+
exclusive_lock!(resource)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -63,5 +63,9 @@ module Actions
|
|
|
63
63
|
def self.serializer_class
|
|
64
64
|
Serializers::ActiveRecordSerializer
|
|
65
65
|
end
|
|
66
|
+
|
|
67
|
+
def drop_all_locks!(_execution_plan)
|
|
68
|
+
ForemanTasks::Lock.where(:task_id => task.id).destroy_all
|
|
69
|
+
end
|
|
66
70
|
end
|
|
67
71
|
end
|
|
@@ -4,19 +4,25 @@ module Actions
|
|
|
4
4
|
# @see Lock.exclusive!
|
|
5
5
|
def exclusive_lock!(resource)
|
|
6
6
|
phase! Dynflow::Action::Plan
|
|
7
|
-
::ForemanTasks::Lock.
|
|
7
|
+
parent_lock = ::ForemanTasks::Lock.for_resource(resource).where(:task_id => task.self_and_parents.map(&:id)).first
|
|
8
|
+
if parent_lock
|
|
9
|
+
ForemanTasks::Link.link_resource_and_related!(resource, task)
|
|
10
|
+
parent_lock
|
|
11
|
+
else
|
|
12
|
+
::ForemanTasks::Lock.exclusive!(resource, task)
|
|
13
|
+
end
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
# @see Lock.lock!
|
|
11
|
-
def lock!(resource, *
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
def lock!(resource, *_lock_names)
|
|
18
|
+
Foreman::Deprecation.deprecation_warning('2.4', 'locking in foreman-tasks was reworked, please use a combination of exclusive_lock! and link! instead.')
|
|
19
|
+
exclusive_lock!(resource)
|
|
14
20
|
end
|
|
15
21
|
|
|
16
22
|
# @see Lock.link!
|
|
17
23
|
def link!(resource)
|
|
18
24
|
phase! Dynflow::Action::Plan
|
|
19
|
-
::ForemanTasks::
|
|
25
|
+
::ForemanTasks::Link.link!(resource, task)
|
|
20
26
|
end
|
|
21
27
|
end
|
|
22
28
|
end
|
|
@@ -21,7 +21,10 @@ module Actions
|
|
|
21
21
|
|
|
22
22
|
# Run all execution plan lifecycle hooks as the original request_id
|
|
23
23
|
def hook(*args)
|
|
24
|
-
|
|
24
|
+
store_current_request_id if !action.input.key?(:current_request_id) && ::Logging.mdc['request']
|
|
25
|
+
restore_current_request_id do
|
|
26
|
+
pass(*args)
|
|
27
|
+
end
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
private
|
|
@@ -16,9 +16,19 @@ module Actions
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def finalize
|
|
19
|
+
current_id = User.current.try(:id)
|
|
20
|
+
saved_id = action.input[:current_user_id]
|
|
21
|
+
if User.current && saved_id && current_id != saved_id
|
|
22
|
+
Foreman::Deprecation.deprecation_warning('2.5', 'relying on per-step setting of current user in finalize phase')
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
restore_curent_user { pass }
|
|
20
26
|
end
|
|
21
27
|
|
|
28
|
+
def finalize_phase(execution_plan, *args)
|
|
29
|
+
restore_curent_user(execution_plan.entry_action) { pass(execution_plan, *args) }
|
|
30
|
+
end
|
|
31
|
+
|
|
22
32
|
# Run all execution plan lifecycle hooks as the original user
|
|
23
33
|
def hook(*args)
|
|
24
34
|
restore_curent_user { pass(*args) }
|
|
@@ -38,7 +48,7 @@ module Actions
|
|
|
38
48
|
action.input[:current_user_id] = User.current.try(:id)
|
|
39
49
|
end
|
|
40
50
|
|
|
41
|
-
def restore_curent_user
|
|
51
|
+
def restore_curent_user(action = self.action)
|
|
42
52
|
old_user = User.current
|
|
43
53
|
User.current = User.unscoped.find(action.input[:current_user_id]) if action.input[:current_user_id].present?
|
|
44
54
|
yield
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Actions
|
|
2
|
+
module Middleware
|
|
3
|
+
class LoadSettingValues < ::Dynflow::Middleware
|
|
4
|
+
# ::Actions::Middleware::LoadSettingValues
|
|
5
|
+
#
|
|
6
|
+
# A middleware to ensure we load current setting values
|
|
7
|
+
|
|
8
|
+
def delay(*args)
|
|
9
|
+
reload_setting_values
|
|
10
|
+
pass(*args)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def plan(*args)
|
|
14
|
+
reload_setting_values
|
|
15
|
+
pass(*args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run(*args)
|
|
19
|
+
reload_setting_values
|
|
20
|
+
pass(*args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def finalize(*args)
|
|
24
|
+
reload_setting_values
|
|
25
|
+
pass(*args)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def reload_setting_values
|
|
31
|
+
::Foreman.settings.load_values
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Actions
|
|
2
|
+
# Examples:
|
|
3
|
+
|
|
4
|
+
# # Action A which emits an event when it successfully finishes.
|
|
5
|
+
# class A
|
|
6
|
+
# include ::Actions::ObservableAction
|
|
7
|
+
# # ... rest ...
|
|
8
|
+
# end
|
|
9
|
+
|
|
10
|
+
# # Action B which emits an event when it successfully finishes or fails.
|
|
11
|
+
# class B
|
|
12
|
+
# include ::Actions::ObservableAction
|
|
13
|
+
#
|
|
14
|
+
# execution_plan_hooks.use :emit_event_failure, :on => [:failure]
|
|
15
|
+
#
|
|
16
|
+
# def self.event_names
|
|
17
|
+
# super + [event_name_base + '_' + event_name_suffix(:failure)]
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# def emit_event_failure(plan)
|
|
21
|
+
# emit_event(plan, :failure)
|
|
22
|
+
# end
|
|
23
|
+
# # ... rest ...
|
|
24
|
+
# end
|
|
25
|
+
module ObservableAction
|
|
26
|
+
module ClassMethods
|
|
27
|
+
def event_name_suffix(hook)
|
|
28
|
+
case hook
|
|
29
|
+
when :success
|
|
30
|
+
'succeeded'
|
|
31
|
+
when :failure
|
|
32
|
+
'failed'
|
|
33
|
+
else
|
|
34
|
+
hook
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def event_names
|
|
39
|
+
[event_name_base + '_' + event_name_suffix(:success)]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def namespaced_event_names
|
|
43
|
+
event_names.map { |e| ::Foreman::Observable.event_name_for(e) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def event_name_base
|
|
47
|
+
to_s.underscore.tr('/', '.')
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.included(base)
|
|
52
|
+
base.extend ClassMethods
|
|
53
|
+
base.include ::Foreman::Observable
|
|
54
|
+
base.execution_plan_hooks.use :emit_event, :on => :success
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def emit_event(execution_plan, hook = :success)
|
|
58
|
+
return unless root_action?
|
|
59
|
+
|
|
60
|
+
trigger_hook "#{self.class.event_name_base}_#{self.class.event_name_suffix(hook)}",
|
|
61
|
+
payload: event_payload(execution_plan)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def event_payload(_execution_plan)
|
|
65
|
+
{ object: self }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
extend ApipieDSL::Module
|
|
69
|
+
|
|
70
|
+
apipie :class, "An common ancestor action for observable actions" do
|
|
71
|
+
name 'Actions::ObservableAction'
|
|
72
|
+
refs 'Actions::ObservableAction'
|
|
73
|
+
sections only: %w[all webhooks]
|
|
74
|
+
property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
|
|
75
|
+
end
|
|
76
|
+
class Jail < Safemode::Jail
|
|
77
|
+
allow :task
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -218,10 +218,8 @@ module Actions
|
|
|
218
218
|
end
|
|
219
219
|
|
|
220
220
|
def get_proxy_data(response)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
end
|
|
224
|
-
proxy_data.fetch('output', {})
|
|
221
|
+
response['actions'].detect { |action| action.fetch('input', {})['task_id'] == task.id }
|
|
222
|
+
.try(:fetch, 'output', {}) || {}
|
|
225
223
|
end
|
|
226
224
|
|
|
227
225
|
def proxy_version(proxy)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module ForemanTasks
|
|
2
|
+
class Link < ApplicationRecord
|
|
3
|
+
belongs_to :task
|
|
4
|
+
|
|
5
|
+
belongs_to :resource, polymorphic: true
|
|
6
|
+
|
|
7
|
+
scope :for_resource, ->(resource) { where(:resource => resource) }
|
|
8
|
+
|
|
9
|
+
validates :task_id, :resource_id, :resource_type, presence: true
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Assigns the resource to the task to easily track the task in context of
|
|
13
|
+
# the resource. This doesn't prevent other actions to lock the resource
|
|
14
|
+
# and should be used only for actions that tolerate other actions to be
|
|
15
|
+
# performed on the resource. Usually, this shouldn't needed to be done
|
|
16
|
+
# through the action directly, because the lock should assign it's parent
|
|
17
|
+
# objects to the action recursively (using +related_resources+ method in model
|
|
18
|
+
# objects)
|
|
19
|
+
def link!(resource, task)
|
|
20
|
+
link = build(resource, task)
|
|
21
|
+
link.save!
|
|
22
|
+
link
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Records the information about the user that triggered the task
|
|
26
|
+
def owner!(user, task)
|
|
27
|
+
link!(user, task)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def link_resource_and_related!(resource, task)
|
|
31
|
+
link!(resource, task)
|
|
32
|
+
build_related_links(resource, task).each(&:save!)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_related_links(resource, task)
|
|
36
|
+
related_resources(resource).map do |related_resource|
|
|
37
|
+
build(related_resource, task)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def build(resource, task)
|
|
44
|
+
find_or_initialize_by(task_id: task.id,
|
|
45
|
+
resource_type: resource.class.name,
|
|
46
|
+
resource_id: resource.id)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# recursively search for related resources of the resource (using
|
|
50
|
+
# the +related_resources+ method, avoiding the cycles
|
|
51
|
+
def related_resources(resource)
|
|
52
|
+
if resource.respond_to?(:all_related_resources)
|
|
53
|
+
resource.all_related_resources
|
|
54
|
+
else
|
|
55
|
+
[]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|