inventory_refresh 0.1.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -0
- data/.gitignore +6 -0
- data/.travis.yml +3 -3
- data/Gemfile +4 -0
- data/inventory_refresh.gemspec +7 -5
- data/lib/inventory_refresh.rb +1 -0
- data/lib/inventory_refresh/inventory_collection.rb +115 -646
- data/lib/inventory_refresh/inventory_collection/builder.rb +249 -0
- data/lib/inventory_refresh/inventory_collection/graph.rb +0 -15
- data/lib/inventory_refresh/inventory_collection/helpers.rb +6 -0
- data/lib/inventory_refresh/inventory_collection/helpers/associations_helper.rb +80 -0
- data/lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb +456 -0
- data/lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb +132 -0
- data/lib/inventory_refresh/inventory_collection/index/proxy.rb +1 -1
- data/lib/inventory_refresh/inventory_collection/index/type/skeletal.rb +5 -5
- data/lib/inventory_refresh/inventory_collection/reference.rb +4 -0
- data/lib/inventory_refresh/inventory_collection/scanner.rb +111 -18
- data/lib/inventory_refresh/inventory_collection/serialization.rb +7 -7
- data/lib/inventory_refresh/inventory_collection/unconnected_edge.rb +19 -0
- data/lib/inventory_refresh/inventory_object.rb +17 -11
- data/lib/inventory_refresh/inventory_object_lazy.rb +20 -10
- data/lib/inventory_refresh/persister.rb +212 -0
- data/lib/inventory_refresh/save_collection/base.rb +18 -3
- data/lib/inventory_refresh/save_collection/saver/base.rb +27 -64
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +73 -225
- data/lib/inventory_refresh/save_collection/saver/partial_upsert_helper.rb +226 -0
- data/lib/inventory_refresh/save_collection/saver/retention_helper.rb +115 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +122 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +24 -5
- data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +6 -6
- data/lib/inventory_refresh/save_collection/sweeper.rb +69 -0
- data/lib/inventory_refresh/save_inventory.rb +18 -8
- data/lib/inventory_refresh/target_collection.rb +12 -0
- data/lib/inventory_refresh/version.rb +1 -1
- metadata +61 -19
- data/lib/inventory_refresh/save_collection/recursive.rb +0 -52
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f587f79d9daabcefff5a1b479301971cdd31f940d517cfc058480a0c17bc3abc
|
4
|
+
data.tar.gz: ac2962c044aba21b8bb5963ae06c788fdeb3863d29a34b33933360ffef432438
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9487530c2f5a42191a61c632fb85d30cbf6113ba6b5208bc2a0f3f2bee6c7634e8018d5896e9ba4bbe5db4bf12c1c5b41084f1eb1e36c66a89d6a5e7b4ef082e
|
7
|
+
data.tar.gz: 66381139ddee4c8f29bc0b0b1e08864f71b4805c6fc354c4dfb372641c5bb56ebb43cc4670b2958d6431a7a0195ef4a60c468a39ace586178b7cc760259f2369
|
data/.codeclimate.yml
CHANGED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,15 +3,15 @@ sudo: false
|
|
3
3
|
language: ruby
|
4
4
|
cache: bundler
|
5
5
|
rvm:
|
6
|
-
- 2.
|
7
|
-
- 2.
|
6
|
+
- 2.5.7
|
7
|
+
- 2.6.5
|
8
8
|
addons:
|
9
9
|
postgresql: '9.5'
|
10
10
|
env:
|
11
11
|
global:
|
12
12
|
- CC_TEST_REPORTER_ID=1ef1a3a3d007395b11083d634a6fdac1e3d979b6428c39d2cd8d58556cdd68f7
|
13
13
|
before_install:
|
14
|
-
- gem install bundler -v
|
14
|
+
- gem install bundler -v 2.0.1
|
15
15
|
- psql -c "CREATE USER root SUPERUSER PASSWORD 'smartvm';" -U postgres
|
16
16
|
before_script:
|
17
17
|
# Code Climate
|
data/Gemfile
CHANGED
@@ -4,3 +4,7 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in inventory_refresh.gemspec
|
6
6
|
gemspec
|
7
|
+
|
8
|
+
# Load other additional Gemfiles
|
9
|
+
# Developers can create a file ending in .rb under bundler.d/ to specify additional development dependencies
|
10
|
+
Dir.glob(File.join(__dir__, 'bundler.d/*.rb')).each { |f| eval_gemfile(File.expand_path(f, __dir__)) }
|
data/inventory_refresh.gemspec
CHANGED
@@ -22,14 +22,16 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.add_dependency "activerecord", "~> 5.0
|
26
|
-
spec.add_dependency "more_core_extensions", "
|
27
|
-
spec.add_dependency "pg", "
|
25
|
+
spec.add_dependency "activerecord", "~> 5.0"
|
26
|
+
spec.add_dependency "more_core_extensions", ">=3.5", "< 5"
|
27
|
+
spec.add_dependency "pg", "> 0"
|
28
28
|
|
29
29
|
spec.add_development_dependency "ancestry"
|
30
|
-
spec.add_development_dependency "bundler", "~>
|
31
|
-
spec.add_development_dependency "
|
30
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
31
|
+
spec.add_development_dependency "factory_bot", "~> 4.11.1"
|
32
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
33
33
|
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
spec.add_development_dependency "rubocop"
|
35
|
+
spec.add_development_dependency "rubocop-performance"
|
34
36
|
spec.add_development_dependency "simplecov"
|
35
37
|
end
|
data/lib/inventory_refresh.rb
CHANGED
@@ -4,6 +4,7 @@ require "inventory_refresh/inventory_object"
|
|
4
4
|
require "inventory_refresh/inventory_object_lazy"
|
5
5
|
require "inventory_refresh/logging"
|
6
6
|
require "inventory_refresh/null_logger"
|
7
|
+
require "inventory_refresh/persister"
|
7
8
|
require "inventory_refresh/save_inventory"
|
8
9
|
require "inventory_refresh/target"
|
9
10
|
require "inventory_refresh/target_collection"
|
@@ -1,9 +1,14 @@
|
|
1
|
+
require "inventory_refresh/inventory_collection/builder"
|
1
2
|
require "inventory_refresh/inventory_collection/data_storage"
|
2
3
|
require "inventory_refresh/inventory_collection/index/proxy"
|
3
4
|
require "inventory_refresh/inventory_collection/reference"
|
4
5
|
require "inventory_refresh/inventory_collection/references_storage"
|
5
6
|
require "inventory_refresh/inventory_collection/scanner"
|
6
7
|
require "inventory_refresh/inventory_collection/serialization"
|
8
|
+
require "inventory_refresh/inventory_collection/unconnected_edge"
|
9
|
+
require "inventory_refresh/inventory_collection/helpers/initialize_helper"
|
10
|
+
require "inventory_refresh/inventory_collection/helpers/associations_helper"
|
11
|
+
require "inventory_refresh/inventory_collection/helpers/questions_helper"
|
7
12
|
|
8
13
|
require "active_support/core_ext/module/delegation"
|
9
14
|
|
@@ -74,10 +79,16 @@ module InventoryRefresh
|
|
74
79
|
# @return [Array, nil] nil or a list of all :manager_uuids that are present in the Provider's InventoryCollection.
|
75
80
|
attr_accessor :all_manager_uuids
|
76
81
|
|
82
|
+
# @return [Array, nil] Scope for applying :all_manager_uuids
|
83
|
+
attr_accessor :all_manager_uuids_scope
|
84
|
+
|
85
|
+
# @return [String] Timestamp in UTC before fetching :all_manager_uuids
|
86
|
+
attr_accessor :all_manager_uuids_timestamp
|
87
|
+
|
77
88
|
# @return [Set] A set of InventoryCollection objects that depends on this InventoryCollection object.
|
78
89
|
attr_accessor :dependees
|
79
90
|
|
80
|
-
# @return [Array<Symbol>] @see #parent_inventory_collections documentation of InventoryCollection.new
|
91
|
+
# @return [Array<Symbol>] @see #parent_inventory_collections documentation of InventoryCollection.new's initialize_ic_relations()
|
81
92
|
# parameters
|
82
93
|
attr_accessor :parent_inventory_collections
|
83
94
|
|
@@ -86,8 +97,9 @@ module InventoryRefresh
|
|
86
97
|
:association, :complete, :update_only, :transitive_dependency_attributes, :check_changed, :arel,
|
87
98
|
:inventory_object_attributes, :name, :saver_strategy, :targeted_scope, :default_values,
|
88
99
|
:targeted_arel, :targeted, :manager_ref_allowed_nil, :use_ar_object,
|
89
|
-
:created_records, :updated_records, :deleted_records,
|
90
|
-
:custom_reconnect_block, :batch_extra_attributes, :references_storage
|
100
|
+
:created_records, :updated_records, :deleted_records, :retention_strategy,
|
101
|
+
:custom_reconnect_block, :batch_extra_attributes, :references_storage, :unconnected_edges,
|
102
|
+
:assert_graph_integrity
|
91
103
|
|
92
104
|
delegate :<<,
|
93
105
|
:build,
|
@@ -125,346 +137,65 @@ module InventoryRefresh
|
|
125
137
|
delegate :table_name,
|
126
138
|
:to => :model_class
|
127
139
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
# stacks_inventory_collection = inventory_collection.dependency_attributes[:orchestration_stacks].try(:first)
|
188
|
-
#
|
189
|
-
# return if stacks_inventory_collection.blank?
|
190
|
-
#
|
191
|
-
# stacks_parents = stacks_inventory_collection.data.each_with_object({}) do |x, obj|
|
192
|
-
# parent_id = x.data[:parent].load.try(:id)
|
193
|
-
# obj[x.id] = parent_id if parent_id
|
194
|
-
# end
|
195
|
-
#
|
196
|
-
# model_class = stacks_inventory_collection.model_class
|
197
|
-
#
|
198
|
-
# stacks_parents_indexed = model_class
|
199
|
-
# .select([:id, :ancestry])
|
200
|
-
# .where(:id => stacks_parents.values).find_each.index_by(&:id)
|
201
|
-
#
|
202
|
-
# model_class
|
203
|
-
# .select([:id, :ancestry])
|
204
|
-
# .where(:id => stacks_parents.keys).find_each do |stack|
|
205
|
-
# parent = stacks_parents_indexed[stacks_parents[stack.id]]
|
206
|
-
# stack.update_attribute(:parent, parent)
|
207
|
-
# end
|
208
|
-
# end
|
209
|
-
# @param custom_reconnect_block [Proc] A custom lambda for reconnect logic of previously disconnected records
|
210
|
-
#
|
211
|
-
# Example - Reconnect disconnected Vms
|
212
|
-
# InventoryRefresh::InventoryCollection.new({
|
213
|
-
# :association => :orchestration_stack_ancestry,
|
214
|
-
# :custom_reconnect_block => vms_custom_reconnect_block,
|
215
|
-
# })
|
216
|
-
#
|
217
|
-
# And the lambda is defined as:
|
218
|
-
#
|
219
|
-
# vms_custom_reconnect_block = lambda do |inventory_collection, inventory_objects_index, attributes_index|
|
220
|
-
# inventory_objects_index.each_slice(1000) do |batch|
|
221
|
-
# Vm.where(:ems_ref => batch.map(&:second).map(&:manager_uuid)).each do |record|
|
222
|
-
# index = inventory_collection.object_index_with_keys(inventory_collection.manager_ref_to_cols, record)
|
223
|
-
#
|
224
|
-
# # We need to delete the record from the inventory_objects_index and attributes_index, otherwise it
|
225
|
-
# # would be sent for create.
|
226
|
-
# inventory_object = inventory_objects_index.delete(index)
|
227
|
-
# hash = attributes_index.delete(index)
|
228
|
-
#
|
229
|
-
# record.assign_attributes(hash.except(:id, :type))
|
230
|
-
# if !inventory_collection.check_changed? || record.changed?
|
231
|
-
# record.save!
|
232
|
-
# inventory_collection.store_updated_records(record)
|
233
|
-
# end
|
234
|
-
#
|
235
|
-
# inventory_object.id = record.id
|
236
|
-
# end
|
237
|
-
# end
|
238
|
-
# @param delete_method [Symbol] A delete method that will be used for deleting of the InventoryObject, if the
|
239
|
-
# object is marked for deletion. A default is :destroy, the instance method must be defined on the
|
240
|
-
# :model_class.
|
241
|
-
# @param dependency_attributes [Hash] Manually defined dependencies of this InventoryCollection. We can use this
|
242
|
-
# by manually place the InventoryCollection into the graph, to make sure the saving is invoked after the
|
243
|
-
# dependencies were saved. The dependencies itself are InventoryCollection objects. For a common use-cases
|
244
|
-
# we do not need to define dependencies manually, since those are inferred automatically by scanning of the
|
245
|
-
# data.
|
246
|
-
#
|
247
|
-
# Example:
|
248
|
-
# :dependency_attributes => {
|
249
|
-
# :orchestration_stacks => [collections[:orchestration_stacks]],
|
250
|
-
# :orchestration_stacks_resources => [collections[:orchestration_stacks_resources]]
|
251
|
-
# }
|
252
|
-
# This example is used in Example2 of the <param custom_save_block> and it means that our :custom_save_block
|
253
|
-
# will be invoked after the InventoryCollection :orchestration_stacks and :orchestration_stacks_resources
|
254
|
-
# are saved.
|
255
|
-
# @param attributes_blacklist [Array] Attributes we do not want to include into saving. We cannot blacklist an
|
256
|
-
# attribute that is needed for saving of the object.
|
257
|
-
# Note: attributes_blacklist is also used for internal resolving of the cycles in the graph.
|
258
|
-
#
|
259
|
-
# In the Example2 of the <param custom_save_block>, we have a custom saving code, that saves a :parent
|
260
|
-
# attribute of the OrchestrationStack. That means we don't want that attribute saved as a part of
|
261
|
-
# InventoryCollection for OrchestrationStack, so we would set :attributes_blacklist => [:parent]. Then the
|
262
|
-
# :parent will be ignored while saving.
|
263
|
-
# @param attributes_whitelist [Array] Same usage as the :attributes_blacklist, but defining full set of attributes
|
264
|
-
# that should be saved. Attributes that are part of :manager_ref and needed validations are automatically
|
265
|
-
# added.
|
266
|
-
# @param complete [Boolean] By default true, :complete is marking we are sending a complete dataset and therefore
|
267
|
-
# we can create/update/delete the InventoryObject objects. If :complete is false we will only do
|
268
|
-
# create/update without delete.
|
269
|
-
# @param update_only [Boolean] By default false. If true we only update the InventoryObject objects, if false we do
|
270
|
-
# create/update/delete.
|
271
|
-
# @param check_changed [Boolean] By default true. If true, before updating the InventoryObject, we call Rails
|
272
|
-
# 'changed?' method. This can optimize speed of updates heavily, but it can fail to recognize the change for
|
273
|
-
# e.g. Ancestry and Relationship based columns. If false, we always update the InventoryObject.
|
274
|
-
# @param arel [ActiveRecord::Associations::CollectionProxy|Arel::SelectManager] Instead of :parent and :association
|
275
|
-
# we can provide Arel directly to say what records should be compared to check if InventoryObject will be
|
276
|
-
# doing create/update/delete.
|
277
|
-
#
|
278
|
-
# Example:
|
279
|
-
# for a targeted refresh, we want to delete/update/create only a list of vms specified with a list of
|
280
|
-
# ems_refs:
|
281
|
-
# :arel => manager.vms.where(:ems_ref => manager_refs)
|
282
|
-
# Then we want to do the same for the hardwares of only those vms:
|
283
|
-
# :arel => manager.hardwares.joins(:vm_or_template).where(
|
284
|
-
# 'vms' => {:ems_ref => manager_refs}
|
285
|
-
# )
|
286
|
-
# And etc. for the other Vm related records.
|
287
|
-
# @param default_values [Hash] A hash of an attributes that will be added to every inventory object created by
|
288
|
-
# inventory_collection.build(hash)
|
289
|
-
#
|
290
|
-
# Example: Given
|
291
|
-
# inventory_collection = InventoryCollection.new({
|
292
|
-
# :model_class => ::Vm,
|
293
|
-
# :arel => @ems.vms,
|
294
|
-
# :default_values => {:ems_id => 10}
|
295
|
-
# })
|
296
|
-
# And building the inventory_object like:
|
297
|
-
# inventory_object = inventory_collection.build(:ems_ref => "vm_1", :name => "vm1")
|
298
|
-
# The inventory_object.data will look like:
|
299
|
-
# {:ems_ref => "vm_1", :name => "vm1", :ems_id => 10}
|
300
|
-
# @param inventory_object_attributes [Array] Array of attribute names that will be exposed as readers/writers on the
|
301
|
-
# InventoryObject objects inside.
|
302
|
-
#
|
303
|
-
# Example: Given
|
304
|
-
# inventory_collection = InventoryCollection.new({
|
305
|
-
# :model_class => ::Vm,
|
306
|
-
# :arel => @ems.vms,
|
307
|
-
# :inventory_object_attributes => [:name, :label]
|
308
|
-
# })
|
309
|
-
# And building the inventory_object like:
|
310
|
-
# inventory_object = inventory_collection.build(:ems_ref => "vm1", :name => "vm1")
|
311
|
-
# We can use inventory_object_attributes as setters and getters:
|
312
|
-
# inventory_object.name = "Name"
|
313
|
-
# inventory_object.label = inventory_object.name
|
314
|
-
# Which would be equivalent to less nicer way:
|
315
|
-
# inventory_object[:name] = "Name"
|
316
|
-
# inventory_object[:label] = inventory_object[:name]
|
317
|
-
# So by using inventory_object_attributes, we will be guarding the allowed attributes and will have an
|
318
|
-
# explicit list of allowed attributes, that can be used also for documentation purposes.
|
319
|
-
# @param name [Symbol] A unique name of the InventoryCollection under a Persister. If not provided, the :association
|
320
|
-
# attribute is used. If :association is nil as well, the :name will be inferred from the :model_class.
|
321
|
-
# @param saver_strategy [Symbol] A strategy that will be used for InventoryCollection persisting into the DB.
|
322
|
-
# Allowed saver strategies are:
|
323
|
-
# - :default => Using Rails saving methods, this way is not safe to run in multiple workers concurrently,
|
324
|
-
# since it will lead to non consistent data.
|
325
|
-
# - :batch => Using batch SQL queries, this way is not safe to run in multiple workers
|
326
|
-
# concurrently, since it will lead to non consistent data.
|
327
|
-
# - :concurrent_safe => This method is designed for concurrent saving. It uses atomic upsert to avoid
|
328
|
-
# data duplication and it uses timestamp based atomic checks to avoid new data being overwritten by the
|
329
|
-
# the old data.
|
330
|
-
# - :concurrent_safe_batch => Same as :concurrent_safe, but the upsert/update queries are executed as
|
331
|
-
# batched SQL queries, instead of sending 1 query per record.
|
332
|
-
# @param parent_inventory_collections [Array] Array of symbols having a name pointing to the
|
333
|
-
# InventoryRefresh::InventoryCollection objects, that serve as parents to this InventoryCollection. There are
|
334
|
-
# several scenarios to consider, when deciding if InventoryCollection has parent collections, see the example.
|
335
|
-
#
|
336
|
-
# Example:
|
337
|
-
# taking inventory collections :vms and :disks (local disks), if we write that:
|
338
|
-
# inventory_collection = InventoryCollection.new({
|
339
|
-
# :model_class => ::Disk,
|
340
|
-
# :association => :disks,
|
341
|
-
# :manager_ref => [:vm, :location]
|
342
|
-
# :parent_inventory_collection => [:vms],
|
343
|
-
# })
|
344
|
-
#
|
345
|
-
# Then the decision for having :parent_inventory_collection => [:vms] was probably driven by these
|
346
|
-
# points:
|
347
|
-
# 1. We can get list of all disks only by doing SQL query through the parent object (so there will be join
|
348
|
-
# from vms to disks table).
|
349
|
-
# 2. There is no API query for getting all disks from the provider API, we get them inside VM data, or as
|
350
|
-
# a Vm subquery
|
351
|
-
# 3. Part of the manager_ref of the IC is the VM object (foreign key), so the disk's location is unique
|
352
|
-
# only under 1 Vm. (In current models, this modeled going through Hardware model)
|
353
|
-
# 4. In targeted refresh, we always expect that each Vm will be saved with all its disks.
|
354
|
-
#
|
355
|
-
# Then having the above points, adding :parent_inventory_collection => [:vms], will bring these
|
356
|
-
# implications:
|
357
|
-
# 1. By archiving/deleting Vm, we can no longer see the disk, because those were owned by the Vm. Any
|
358
|
-
# archival/deletion of the Disk model, must be then done by cascade delete/hooks logic.
|
359
|
-
# 2. Having Vm as a parent ensures we always process it first. So e.g. when providing no Vms for saving
|
360
|
-
# we would have no graph dependency (no data --> no edges --> no dependencies) and Disk could be
|
361
|
-
# archived/removed before the Vm, while we always want to archive the VM first.
|
362
|
-
# 3. For targeted refresh, we always expect that all disks are saved with a VM. So for targeting :disks,
|
363
|
-
# we are not using #manager_uuids attribute, since the scope is "all disks of all targeted VMs", so we
|
364
|
-
# always use #manager_uuids of the parent. (that is why :parent_inventory_collections and
|
365
|
-
# :manager_uuids are mutually exclusive attributes)
|
366
|
-
# 4. For automatically building the #targeted_arel query, we need the parent to know what is the root node.
|
367
|
-
# While this information can be introspected from the data, it creates a scope for create&update&delete,
|
368
|
-
# which means it has to work with no data provided (causing delete all). So with no data we cannot
|
369
|
-
# introspect anything.
|
370
|
-
# @param manager_uuids [Array|Proc] Array of manager_uuids of the InventoryObjects we want to create/update/delete. Using
|
371
|
-
# this attribute, the db_collection_for_comparison will be automatically limited by the manager_uuids, in a
|
372
|
-
# case of a simple relation. In a case of a complex relation, we can leverage :manager_uuids in a
|
373
|
-
# custom :targeted_arel. We can pass also lambda, for lazy_evaluation.
|
374
|
-
# @param all_manager_uuids [Array] Array of all manager_uuids of the InventoryObjects. With the :targeted true,
|
375
|
-
# having this parameter defined will invoke only :delete_method on a complement of this set, making sure
|
376
|
-
# the DB has only this set of data after. This :attribute serves for deleting of top level
|
377
|
-
# InventoryCollections, i.e. InventoryCollections having parent_inventory_collections nil. The deleting of
|
378
|
-
# child collections is already handled by the scope of the parent_inventory_collections and using Rails
|
379
|
-
# :dependent => :destroy,
|
380
|
-
# @param targeted_arel [Proc] A callable block that receives this InventoryCollection as a first argument. In there
|
381
|
-
# we can leverage a :parent_inventory_collections or :manager_uuids to limit the query based on the
|
382
|
-
# manager_uuids available.
|
383
|
-
# Example:
|
384
|
-
# targeted_arel = lambda do |inventory_collection|
|
385
|
-
# # Getting ems_refs of parent :vms and :miq_templates
|
386
|
-
# manager_uuids = inventory_collection.parent_inventory_collections.collect(&:manager_uuids).flatten
|
387
|
-
# inventory_collection.db_collection_for_comparison.hardwares.joins(:vm_or_template).where(
|
388
|
-
# 'vms' => {:ems_ref => manager_uuids}
|
389
|
-
# )
|
390
|
-
# end
|
391
|
-
#
|
392
|
-
# inventory_collection = InventoryCollection.new({
|
393
|
-
# :model_class => ::Hardware,
|
394
|
-
# :association => :hardwares,
|
395
|
-
# :parent_inventory_collection => [:vms, :miq_templates],
|
396
|
-
# :targeted_arel => targeted_arel,
|
397
|
-
# })
|
398
|
-
# @param targeted [Boolean] True if the collection is targeted, in that case it will be leveraging :manager_uuids
|
399
|
-
# :parent_inventory_collections and :targeted_arel to save a subgraph of a data.
|
400
|
-
# @param manager_ref_allowed_nil [Array] Array of symbols having manager_ref columns, that are a foreign key an can
|
401
|
-
# be nil. Given the table are shared by many providers, it can happen, that the table is used only partially.
|
402
|
-
# Then it can happen we want to allow certain foreign keys to be nil, while being sure the referential
|
403
|
-
# integrity is not broken. Of course the DB Foreign Key can't be created in this case, so we should try to
|
404
|
-
# avoid this usecase by a proper modeling.
|
405
|
-
# @param use_ar_object [Boolean] True or False. Whether we need to initialize AR object as part of the saving
|
406
|
-
# it's needed if the model have special setters, serialize of columns, etc. This setting is relevant only
|
407
|
-
# for the batch saver strategy.
|
408
|
-
# @param batch_extra_attributes [Array] Array of symbols marking which extra attributes we want to store into the
|
409
|
-
# db. These extra attributes might be a product of :use_ar_object assignment and we need to specify them
|
410
|
-
# manually, if we want to use a batch saving strategy and we have models that populate attributes as a side
|
411
|
-
# effect.
|
412
|
-
def initialize(model_class: nil, manager_ref: nil, association: nil, parent: nil, strategy: nil,
|
413
|
-
custom_save_block: nil, delete_method: nil, dependency_attributes: nil,
|
414
|
-
attributes_blacklist: nil, attributes_whitelist: nil, complete: nil, update_only: nil,
|
415
|
-
check_changed: nil, arel: nil, default_values: {}, create_only: nil,
|
416
|
-
inventory_object_attributes: nil, name: nil, saver_strategy: nil,
|
417
|
-
parent_inventory_collections: nil, manager_uuids: [], all_manager_uuids: nil, targeted_arel: nil,
|
418
|
-
targeted: nil, manager_ref_allowed_nil: nil, secondary_refs: {}, use_ar_object: nil,
|
419
|
-
custom_reconnect_block: nil, batch_extra_attributes: [])
|
420
|
-
@model_class = model_class
|
421
|
-
@manager_ref = manager_ref || [:ems_ref]
|
422
|
-
@secondary_refs = secondary_refs
|
423
|
-
@association = association
|
424
|
-
@parent = parent || nil
|
425
|
-
@arel = arel
|
426
|
-
@dependency_attributes = dependency_attributes || {}
|
427
|
-
@strategy = process_strategy(strategy)
|
428
|
-
@delete_method = delete_method || :destroy
|
429
|
-
@custom_save_block = custom_save_block
|
430
|
-
@custom_reconnect_block = custom_reconnect_block
|
431
|
-
@check_changed = check_changed.nil? ? true : check_changed
|
432
|
-
@internal_attributes = %i(__feedback_edge_set_parent __parent_inventory_collections)
|
433
|
-
@complete = complete.nil? ? true : complete
|
434
|
-
@update_only = update_only.nil? ? false : update_only
|
435
|
-
@create_only = create_only.nil? ? false : create_only
|
436
|
-
@default_values = default_values
|
437
|
-
@name = name || association || model_class.to_s.demodulize.tableize
|
438
|
-
@saver_strategy = process_saver_strategy(saver_strategy)
|
439
|
-
@use_ar_object = use_ar_object || false
|
440
|
-
@batch_extra_attributes = batch_extra_attributes
|
441
|
-
|
442
|
-
@manager_ref_allowed_nil = manager_ref_allowed_nil || []
|
443
|
-
|
444
|
-
# Targeted mode related attributes
|
445
|
-
# TODO(lsmola) Should we refactor this to use references too?
|
446
|
-
@all_manager_uuids = all_manager_uuids
|
447
|
-
@parent_inventory_collections = parent_inventory_collections
|
448
|
-
@targeted_arel = targeted_arel
|
449
|
-
@targeted = !!targeted
|
450
|
-
|
451
|
-
@inventory_object_attributes = inventory_object_attributes
|
452
|
-
|
453
|
-
@saved ||= false
|
454
|
-
@attributes_blacklist = Set.new
|
455
|
-
@attributes_whitelist = Set.new
|
456
|
-
@transitive_dependency_attributes = Set.new
|
457
|
-
@dependees = Set.new
|
458
|
-
@data_storage = ::InventoryRefresh::InventoryCollection::DataStorage.new(self, secondary_refs)
|
459
|
-
@references_storage = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy)
|
460
|
-
@targeted_scope = ::InventoryRefresh::InventoryCollection::ReferencesStorage.new(index_proxy).merge!(manager_uuids)
|
461
|
-
|
462
|
-
@created_records = []
|
463
|
-
@updated_records = []
|
464
|
-
@deleted_records = []
|
465
|
-
|
466
|
-
blacklist_attributes!(attributes_blacklist) if attributes_blacklist.present?
|
467
|
-
whitelist_attributes!(attributes_whitelist) if attributes_whitelist.present?
|
140
|
+
include ::InventoryRefresh::InventoryCollection::Helpers::AssociationsHelper
|
141
|
+
include ::InventoryRefresh::InventoryCollection::Helpers::InitializeHelper
|
142
|
+
include ::InventoryRefresh::InventoryCollection::Helpers::QuestionsHelper
|
143
|
+
|
144
|
+
# @param [Hash] properties - see init methods for params description
|
145
|
+
def initialize(properties = {})
|
146
|
+
init_basic_properties(properties[:association],
|
147
|
+
properties[:model_class],
|
148
|
+
properties[:name],
|
149
|
+
properties[:parent])
|
150
|
+
|
151
|
+
init_flags(properties[:complete],
|
152
|
+
properties[:create_only],
|
153
|
+
properties[:check_changed],
|
154
|
+
properties[:update_only],
|
155
|
+
properties[:use_ar_object],
|
156
|
+
properties[:targeted],
|
157
|
+
properties[:assert_graph_integrity])
|
158
|
+
|
159
|
+
init_strategies(properties[:strategy],
|
160
|
+
properties[:saver_strategy],
|
161
|
+
properties[:retention_strategy],
|
162
|
+
properties[:delete_method])
|
163
|
+
|
164
|
+
init_references(properties[:manager_ref],
|
165
|
+
properties[:manager_ref_allowed_nil],
|
166
|
+
properties[:secondary_refs],
|
167
|
+
properties[:manager_uuids])
|
168
|
+
|
169
|
+
init_all_manager_uuids(properties[:all_manager_uuids],
|
170
|
+
properties[:all_manager_uuids_scope],
|
171
|
+
properties[:all_manager_uuids_timestamp])
|
172
|
+
|
173
|
+
init_ic_relations(properties[:dependency_attributes],
|
174
|
+
properties[:parent_inventory_collections])
|
175
|
+
|
176
|
+
init_arels(properties[:arel],
|
177
|
+
properties[:targeted_arel])
|
178
|
+
|
179
|
+
init_custom_procs(properties[:custom_save_block],
|
180
|
+
properties[:custom_reconnect_block])
|
181
|
+
|
182
|
+
init_model_attributes(properties[:attributes_blacklist],
|
183
|
+
properties[:attributes_whitelist],
|
184
|
+
properties[:inventory_object_attributes],
|
185
|
+
properties[:batch_extra_attributes])
|
186
|
+
|
187
|
+
init_data(properties[:default_values])
|
188
|
+
|
189
|
+
init_storages
|
190
|
+
|
191
|
+
init_changed_records_stats
|
192
|
+
end
|
193
|
+
|
194
|
+
def store_unconnected_edges(inventory_object, inventory_object_key, inventory_object_lazy)
|
195
|
+
(@unconnected_edges ||= []) <<
|
196
|
+
InventoryRefresh::InventoryCollection::UnconnectedEdge.new(
|
197
|
+
inventory_object, inventory_object_key, inventory_object_lazy
|
198
|
+
)
|
468
199
|
end
|
469
200
|
|
470
201
|
# Caches what records were created, for later use, e.g. post provision behavior
|
@@ -488,166 +219,6 @@ module InventoryRefresh
|
|
488
219
|
@deleted_records.concat(records_identities(records))
|
489
220
|
end
|
490
221
|
|
491
|
-
# Processes passed saver strategy
|
492
|
-
#
|
493
|
-
# @param saver_strategy [Symbol] Passed saver strategy
|
494
|
-
# @return [Symbol] Returns back the passed strategy if supported, or raises exception
|
495
|
-
def process_saver_strategy(saver_strategy)
|
496
|
-
return :default unless saver_strategy
|
497
|
-
|
498
|
-
saver_strategy = saver_strategy.to_sym
|
499
|
-
case saver_strategy
|
500
|
-
when :default, :batch, :concurrent_safe, :concurrent_safe_batch
|
501
|
-
saver_strategy
|
502
|
-
else
|
503
|
-
raise "Unknown InventoryCollection saver strategy: :#{saver_strategy}, allowed strategies are "\
|
504
|
-
":default, :batch, :concurrent_safe and :concurrent_safe_batch"
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
# Processes passed strategy, modifies :data_collection_finalized and :saved attributes for db only strategies
|
509
|
-
#
|
510
|
-
# @param strategy_name [Symbol] Passed saver strategy
|
511
|
-
# @return [Symbol] Returns back the passed strategy if supported, or raises exception
|
512
|
-
def process_strategy(strategy_name)
|
513
|
-
self.data_collection_finalized = false
|
514
|
-
|
515
|
-
return unless strategy_name
|
516
|
-
|
517
|
-
strategy_name = strategy_name.to_sym
|
518
|
-
case strategy_name
|
519
|
-
when :local_db_cache_all
|
520
|
-
self.data_collection_finalized = true
|
521
|
-
self.saved = true
|
522
|
-
when :local_db_find_references
|
523
|
-
self.saved = true
|
524
|
-
when :local_db_find_missing_references
|
525
|
-
else
|
526
|
-
raise "Unknown InventoryCollection strategy: :#{strategy_name}, allowed strategies are :local_db_cache_all, "\
|
527
|
-
":local_db_find_references and :local_db_find_missing_references."
|
528
|
-
end
|
529
|
-
strategy_name
|
530
|
-
end
|
531
|
-
|
532
|
-
# @return [Boolean] true means we want to call .changed? on every ActiveRecord object before saving it
|
533
|
-
def check_changed?
|
534
|
-
check_changed
|
535
|
-
end
|
536
|
-
|
537
|
-
# @return [Boolean] true means we want to use ActiveRecord object for writing attributes and we want to perform
|
538
|
-
# casting on all columns
|
539
|
-
def use_ar_object?
|
540
|
-
use_ar_object
|
541
|
-
end
|
542
|
-
|
543
|
-
# @return [Boolean] true means the data is not complete, leading to only creating and updating data
|
544
|
-
def complete?
|
545
|
-
complete
|
546
|
-
end
|
547
|
-
|
548
|
-
# @return [Boolean] true means we want to only update data
|
549
|
-
def update_only?
|
550
|
-
update_only
|
551
|
-
end
|
552
|
-
|
553
|
-
# @return [Boolean] true means we will delete/soft-delete data
|
554
|
-
def delete_allowed?
|
555
|
-
complete? && !update_only?
|
556
|
-
end
|
557
|
-
|
558
|
-
# @return [Boolean] true means we will delete/soft-delete data
|
559
|
-
def create_allowed?
|
560
|
-
!update_only?
|
561
|
-
end
|
562
|
-
|
563
|
-
# @return [Boolean] true means that only create of new data is allowed
|
564
|
-
def create_only?
|
565
|
-
create_only
|
566
|
-
end
|
567
|
-
|
568
|
-
# @return [Boolean] true if the whole InventoryCollection object has all data persisted
|
569
|
-
def saved?
|
570
|
-
saved
|
571
|
-
end
|
572
|
-
|
573
|
-
# @return [Boolean] true if all dependencies have all data persisted
|
574
|
-
def saveable?
|
575
|
-
dependencies.all?(&:saved?)
|
576
|
-
end
|
577
|
-
|
578
|
-
# @return [Boolean] true if we are using a saver strategy that allows saving in parallel processes
|
579
|
-
def parallel_safe?
|
580
|
-
@parallel_safe_cache ||= %i(concurrent_safe concurrent_safe_batch).include?(saver_strategy)
|
581
|
-
end
|
582
|
-
|
583
|
-
# @return [Boolean] true if the model_class supports STI
|
584
|
-
def supports_sti?
|
585
|
-
@supports_sti_cache = model_class.column_names.include?("type") if @supports_sti_cache.nil?
|
586
|
-
@supports_sti_cache
|
587
|
-
end
|
588
|
-
|
589
|
-
# @return [Boolean] true if the model_class has created_on column
|
590
|
-
def supports_created_on?
|
591
|
-
if @supports_created_on_cache.nil?
|
592
|
-
@supports_created_on_cache = (model_class.column_names.include?("created_on") && ActiveRecord::Base.record_timestamps)
|
593
|
-
end
|
594
|
-
@supports_created_on_cache
|
595
|
-
end
|
596
|
-
|
597
|
-
# @return [Boolean] true if the model_class has updated_on column
|
598
|
-
def supports_updated_on?
|
599
|
-
if @supports_updated_on_cache.nil?
|
600
|
-
@supports_updated_on_cache = (model_class.column_names.include?("updated_on") && ActiveRecord::Base.record_timestamps)
|
601
|
-
end
|
602
|
-
@supports_updated_on_cache
|
603
|
-
end
|
604
|
-
|
605
|
-
# @return [Boolean] true if the model_class has created_at column
|
606
|
-
def supports_created_at?
|
607
|
-
if @supports_created_at_cache.nil?
|
608
|
-
@supports_created_at_cache = (model_class.column_names.include?("created_at") && ActiveRecord::Base.record_timestamps)
|
609
|
-
end
|
610
|
-
@supports_created_at_cache
|
611
|
-
end
|
612
|
-
|
613
|
-
# @return [Boolean] true if the model_class has updated_at column
|
614
|
-
def supports_updated_at?
|
615
|
-
if @supports_updated_at_cache.nil?
|
616
|
-
@supports_updated_at_cache = (model_class.column_names.include?("updated_at") && ActiveRecord::Base.record_timestamps)
|
617
|
-
end
|
618
|
-
@supports_updated_at_cache
|
619
|
-
end
|
620
|
-
|
621
|
-
# @return [Boolean] true if the model_class has resource_timestamps_max column
|
622
|
-
def supports_resource_timestamps_max?
|
623
|
-
@supports_resource_timestamps_max_cache ||= model_class.column_names.include?("resource_timestamps_max")
|
624
|
-
end
|
625
|
-
|
626
|
-
# @return [Boolean] true if the model_class has resource_timestamps column
|
627
|
-
def supports_resource_timestamps?
|
628
|
-
@supports_resource_timestamps_cache ||= model_class.column_names.include?("resource_timestamps")
|
629
|
-
end
|
630
|
-
|
631
|
-
# @return [Boolean] true if the model_class has resource_timestamp column
|
632
|
-
def supports_resource_timestamp?
|
633
|
-
@supports_resource_timestamp_cache ||= model_class.column_names.include?("resource_timestamp")
|
634
|
-
end
|
635
|
-
|
636
|
-
# @return [Boolean] true if the model_class has resource_versions_max column
|
637
|
-
def supports_resource_versions_max?
|
638
|
-
@supports_resource_versions_max_cache ||= model_class.column_names.include?("resource_versions_max")
|
639
|
-
end
|
640
|
-
|
641
|
-
# @return [Boolean] true if the model_class has resource_versions column
|
642
|
-
def supports_resource_versions?
|
643
|
-
@supports_resource_versions_cache ||= model_class.column_names.include?("resource_versions")
|
644
|
-
end
|
645
|
-
|
646
|
-
# @return [Boolean] true if the model_class has resource_version column
|
647
|
-
def supports_resource_version?
|
648
|
-
@supports_resource_version_cache ||= model_class.column_names.include?("resource_version")
|
649
|
-
end
|
650
|
-
|
651
222
|
# @return [Array<Symbol>] all columns that are part of the best fit unique index
|
652
223
|
def unique_index_columns
|
653
224
|
return @unique_index_columns if @unique_index_columns
|
@@ -667,7 +238,7 @@ module InventoryRefresh
|
|
667
238
|
|
668
239
|
if @unique_indexes_cache.blank?
|
669
240
|
raise "#{self} and its table #{model_class.table_name} must have a unique index defined, to"\
|
670
|
-
" be able to use saver_strategy :
|
241
|
+
" be able to use saver_strategy :concurrent_safe_batch."
|
671
242
|
end
|
672
243
|
|
673
244
|
@unique_indexes_cache
|
@@ -682,16 +253,29 @@ module InventoryRefresh
|
|
682
253
|
@unique_index_for_keys_cache ||= {}
|
683
254
|
@unique_index_for_keys_cache[keys] if @unique_index_for_keys_cache[keys]
|
684
255
|
|
256
|
+
# Take the uniq key having the least number of columns
|
257
|
+
@unique_index_for_keys_cache[keys] = uniq_keys_candidates(keys).min_by { |x| x.columns.count }
|
258
|
+
end
|
259
|
+
|
260
|
+
# Find candidates for unique key. Candidate must cover all columns we are passing as keys.
|
261
|
+
#
|
262
|
+
# @param keys [Array<Symbol>]
|
263
|
+
# @raise [Exception] if the unique index for the columns was not found
|
264
|
+
# @return [Array<ActiveRecord::ConnectionAdapters::IndexDefinition>] Array of unique indexes fitting the keys
|
265
|
+
def uniq_keys_candidates(keys)
|
685
266
|
# Find all uniq indexes that that are covering our keys
|
686
267
|
uniq_key_candidates = unique_indexes.each_with_object([]) { |i, obj| obj << i if (keys - i.columns.map(&:to_sym)).empty? }
|
687
268
|
|
688
|
-
if
|
269
|
+
if unique_indexes.blank? || uniq_key_candidates.blank?
|
689
270
|
raise "#{self} and its table #{model_class.table_name} must have a unique index defined "\
|
690
|
-
"covering columns #{keys} to be able to use saver_strategy :
|
271
|
+
"covering columns #{keys} to be able to use saver_strategy :concurrent_safe_batch."
|
691
272
|
end
|
692
273
|
|
693
|
-
|
694
|
-
|
274
|
+
uniq_key_candidates
|
275
|
+
end
|
276
|
+
|
277
|
+
def resource_version_column
|
278
|
+
:resource_version
|
695
279
|
end
|
696
280
|
|
697
281
|
def internal_columns
|
@@ -699,35 +283,32 @@ module InventoryRefresh
|
|
699
283
|
|
700
284
|
@internal_columns = [] + internal_timestamp_columns
|
701
285
|
@internal_columns << :type if supports_sti?
|
702
|
-
@internal_columns
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
286
|
+
@internal_columns += [resource_version_column,
|
287
|
+
:resource_timestamps_max,
|
288
|
+
:resource_timestamps,
|
289
|
+
:resource_timestamp,
|
290
|
+
:resource_counters_max,
|
291
|
+
:resource_counters,
|
292
|
+
:resource_counter].collect do |col|
|
293
|
+
col if supports_column?(col)
|
294
|
+
end.compact
|
709
295
|
end
|
710
296
|
|
711
297
|
def internal_timestamp_columns
|
712
298
|
return @internal_timestamp_columns if @internal_timestamp_columns
|
713
299
|
|
714
|
-
@internal_timestamp_columns =
|
715
|
-
|
716
|
-
|
717
|
-
@internal_timestamp_columns << :updated_on if supports_updated_on?
|
718
|
-
@internal_timestamp_columns << :updated_at if supports_updated_at?
|
719
|
-
|
720
|
-
@internal_timestamp_columns
|
300
|
+
@internal_timestamp_columns = %i(created_at created_on updated_at updated_on).collect do |timestamp_col|
|
301
|
+
timestamp_col if supports_column?(timestamp_col)
|
302
|
+
end.compact
|
721
303
|
end
|
722
304
|
|
723
|
-
|
724
|
-
|
305
|
+
# @return [Array] Array of column names that have not null constraint
|
306
|
+
def not_null_columns
|
307
|
+
@not_null_constraint_columns ||= model_class.columns.reject(&:null).map { |x| x.name.to_sym } - [model_class.primary_key.to_sym]
|
725
308
|
end
|
726
309
|
|
727
|
-
|
728
|
-
|
729
|
-
def data_collection_finalized?
|
730
|
-
data_collection_finalized
|
310
|
+
def base_columns
|
311
|
+
@base_columns ||= (unique_index_columns + internal_columns + not_null_columns).uniq
|
731
312
|
end
|
732
313
|
|
733
314
|
# @param value [Object] Object we want to test
|
@@ -752,38 +333,6 @@ module InventoryRefresh
|
|
752
333
|
build_stringified_reference_for_record(record, keys)
|
753
334
|
end
|
754
335
|
|
755
|
-
# True if processing of this InventoryCollection object would lead to no operations. Then we use this marker to
|
756
|
-
# stop processing of the InventoryCollector object very soon, to avoid a lot of unnecessary Db queries, etc.
|
757
|
-
#
|
758
|
-
# @return [Boolean] true if processing of this InventoryCollection object would lead to no operations.
|
759
|
-
def noop?
|
760
|
-
# If this InventoryCollection doesn't do anything. it can easily happen for targeted/batched strategies.
|
761
|
-
if targeted?
|
762
|
-
if parent_inventory_collections.nil? && targeted_scope.primary_references.blank? &&
|
763
|
-
all_manager_uuids.nil? && parent_inventory_collections.blank? && custom_save_block.nil? &&
|
764
|
-
skeletal_primary_index.blank?
|
765
|
-
# It's a noop Parent targeted InventoryCollection
|
766
|
-
true
|
767
|
-
elsif !parent_inventory_collections.nil? && parent_inventory_collections.all? { |x| x.targeted_scope.primary_references.blank? } &&
|
768
|
-
skeletal_primary_index.blank?
|
769
|
-
# It's a noop Child targeted InventoryCollection
|
770
|
-
true
|
771
|
-
else
|
772
|
-
false
|
773
|
-
end
|
774
|
-
elsif data.blank? && !delete_allowed? && skeletal_primary_index.blank?
|
775
|
-
# If we have no data to save and delete is not allowed, we can just skip
|
776
|
-
true
|
777
|
-
else
|
778
|
-
false
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
# @return [Boolean] true is processing of this InventoryCollection will be in targeted mode
|
783
|
-
def targeted?
|
784
|
-
targeted
|
785
|
-
end
|
786
|
-
|
787
336
|
# Convert manager_ref list of attributes to list of DB columns
|
788
337
|
#
|
789
338
|
# @return [Array<String>] true is processing of this InventoryCollection will be in targeted mode
|
@@ -897,77 +446,6 @@ module InventoryRefresh
|
|
897
446
|
cloned
|
898
447
|
end
|
899
448
|
|
900
|
-
# @return [Array<ActiveRecord::Reflection::BelongsToReflection">] All belongs_to associations
|
901
|
-
def belongs_to_associations
|
902
|
-
model_class.reflect_on_all_associations.select { |x| x.kind_of?(ActiveRecord::Reflection::BelongsToReflection) }
|
903
|
-
end
|
904
|
-
|
905
|
-
# @return [Hash{Symbol => String}] Hash with association name mapped to foreign key column name
|
906
|
-
def association_to_foreign_key_mapping
|
907
|
-
return {} unless model_class
|
908
|
-
|
909
|
-
@association_to_foreign_key_mapping ||= belongs_to_associations.each_with_object({}) do |x, obj|
|
910
|
-
obj[x.name] = x.foreign_key
|
911
|
-
end
|
912
|
-
end
|
913
|
-
|
914
|
-
# @return [Hash{String => Hash}] Hash with foreign_key column name mapped to association name
|
915
|
-
def foreign_key_to_association_mapping
|
916
|
-
return {} unless model_class
|
917
|
-
|
918
|
-
@foreign_key_to_association_mapping ||= belongs_to_associations.each_with_object({}) do |x, obj|
|
919
|
-
obj[x.foreign_key] = x.name
|
920
|
-
end
|
921
|
-
end
|
922
|
-
|
923
|
-
# @return [Hash{Symbol => String}] Hash with association name mapped to polymorphic foreign key type column name
|
924
|
-
def association_to_foreign_type_mapping
|
925
|
-
return {} unless model_class
|
926
|
-
|
927
|
-
@association_to_foreign_type_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
|
928
|
-
obj[x.name] = x.foreign_type if x.polymorphic?
|
929
|
-
end
|
930
|
-
end
|
931
|
-
|
932
|
-
# @return [Hash{Symbol => String}] Hash with polymorphic foreign key type column name mapped to association name
|
933
|
-
def foreign_type_to_association_mapping
|
934
|
-
return {} unless model_class
|
935
|
-
|
936
|
-
@foreign_type_to_association_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
|
937
|
-
obj[x.foreign_type] = x.name if x.polymorphic?
|
938
|
-
end
|
939
|
-
end
|
940
|
-
|
941
|
-
# @return [Hash{Symbol => String}] Hash with association name mapped to base class of the association
|
942
|
-
def association_to_base_class_mapping
|
943
|
-
return {} unless model_class
|
944
|
-
|
945
|
-
@association_to_base_class_mapping ||= model_class.reflect_on_all_associations.each_with_object({}) do |x, obj|
|
946
|
-
obj[x.name] = x.klass.base_class.name unless x.polymorphic?
|
947
|
-
end
|
948
|
-
end
|
949
|
-
|
950
|
-
# @return [Array<Symbol>] List of all column names that are foreign keys
|
951
|
-
def foreign_keys
|
952
|
-
return [] unless model_class
|
953
|
-
|
954
|
-
@foreign_keys_cache ||= belongs_to_associations.map(&:foreign_key).map!(&:to_sym)
|
955
|
-
end
|
956
|
-
|
957
|
-
# @return [Array<Symbol>] List of all column names that are foreign keys and cannot removed, otherwise we couldn't
|
958
|
-
# save the record
|
959
|
-
def fixed_foreign_keys
|
960
|
-
# Foreign keys that are part of a manager_ref must be present, otherwise the record would get lost. This is a
|
961
|
-
# minimum check we can do to not break a referential integrity.
|
962
|
-
return @fixed_foreign_keys_cache unless @fixed_foreign_keys_cache.nil?
|
963
|
-
|
964
|
-
manager_ref_set = (manager_ref - manager_ref_allowed_nil)
|
965
|
-
@fixed_foreign_keys_cache = manager_ref_set.map { |x| association_to_foreign_key_mapping[x] }.compact
|
966
|
-
@fixed_foreign_keys_cache += foreign_keys & manager_ref
|
967
|
-
@fixed_foreign_keys_cache.map!(&:to_sym)
|
968
|
-
@fixed_foreign_keys_cache
|
969
|
-
end
|
970
|
-
|
971
449
|
# @return [String] Base class name of the model_class of this InventoryCollection
|
972
450
|
def base_class_name
|
973
451
|
return "" unless model_class
|
@@ -1057,11 +535,11 @@ module InventoryRefresh
|
|
1057
535
|
|
1058
536
|
# Gets targeted references and transforms them into list of hashes
|
1059
537
|
#
|
1060
|
-
# @param references [Array, InventoryRefresh::
|
538
|
+
# @param references [Array, InventoryRefresh::InventoryCollection::TargetedScope] passed references
|
1061
539
|
# @return [Array<Hash>] References transformed into the array of hashes
|
1062
540
|
def transform_references_to_hashes(references)
|
1063
541
|
if references.kind_of?(Array)
|
1064
|
-
# Sliced InventoryRefresh::
|
542
|
+
# Sliced InventoryRefresh::InventoryCollection::TargetedScope
|
1065
543
|
references.map { |x| x.second.full_reference }
|
1066
544
|
else
|
1067
545
|
references.values.map(&:full_reference)
|
@@ -1098,22 +576,12 @@ module InventoryRefresh
|
|
1098
576
|
full_collection_for_comparison.where(targeted_selection_for(references))
|
1099
577
|
end
|
1100
578
|
|
1101
|
-
# Builds an ActiveRecord::Relation that can fetch complement of all the references from the DB
|
1102
|
-
#
|
1103
|
-
# @param manager_uuids_set [Array<String>] passed references
|
1104
|
-
# @return [ActiveRecord::Relation] relation that can fetch complement of all the references from the DB
|
1105
|
-
def db_collection_for_comparison_for_complement_of(manager_uuids_set)
|
1106
|
-
# TODO(lsmola) this should have the build_multi_selection_condition, like in the method above
|
1107
|
-
# TODO(lsmola) this query will be highly ineffective, we will try approach with updating a timestamp of all
|
1108
|
-
# records, then we can get list of all records that were not update. That would be equivalent to result of this
|
1109
|
-
# more effective query and without need of all manager_uuids
|
1110
|
-
full_collection_for_comparison.where.not(manager_ref.first => manager_uuids_set)
|
1111
|
-
end
|
1112
|
-
|
1113
579
|
# @return [ActiveRecord::Relation] relation that can fetch all the references from the DB
|
1114
580
|
def full_collection_for_comparison
|
1115
581
|
return arel unless arel.nil?
|
1116
|
-
parent.send(association)
|
582
|
+
rel = parent.send(association)
|
583
|
+
rel = rel.active if rel && supports_column?(:archived_at) && retention_strategy == :archive
|
584
|
+
rel
|
1117
585
|
end
|
1118
586
|
|
1119
587
|
# Creates InventoryRefresh::InventoryObject object from passed hash data
|
@@ -1167,6 +635,7 @@ module InventoryRefresh
|
|
1167
635
|
}
|
1168
636
|
end
|
1169
637
|
|
638
|
+
# TODO: Not used!
|
1170
639
|
# @return [Array<Symbol>] all association attributes and foreign keys of the model class
|
1171
640
|
def association_attributes
|
1172
641
|
model_class.reflect_on_all_associations.map { |x| [x.name, x.foreign_key] }.flatten.compact.map(&:to_sym)
|