topological_inventory-providers-common 1.0.2 → 1.0.7

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +49 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +3 -0
  5. data/.rubocop_cc.yml +4 -0
  6. data/.rubocop_local.yml +2 -0
  7. data/.travis.yml +8 -0
  8. data/CHANGELOG.md +29 -1
  9. data/Gemfile +0 -3
  10. data/lib/topological_inventory/providers/common/logging.rb +8 -0
  11. data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +3 -0
  12. data/lib/topological_inventory/providers/common/operations/source.rb +191 -0
  13. data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +15 -6
  14. data/lib/topological_inventory/providers/common/save_inventory/saver.rb +13 -4
  15. data/lib/topological_inventory/providers/common/version.rb +1 -1
  16. data/spec/spec_helper.rb +22 -0
  17. data/spec/support/inventory_helper.rb +14 -0
  18. data/spec/support/shared/availability_check.rb +236 -0
  19. data/spec/topological_inventory/providers/common/collector_spec.rb +171 -0
  20. data/spec/topological_inventory/providers/common/collectors/inventory_collection_storage_spec.rb +44 -0
  21. data/spec/topological_inventory/providers/common/collectors/inventory_collection_wrapper_spec.rb +9 -0
  22. data/spec/topological_inventory/providers/common/collectors_pool_spec.rb +150 -0
  23. data/spec/topological_inventory/providers/common/logger_spec.rb +38 -0
  24. data/spec/topological_inventory/providers/common/operations/processor_spec.rb +102 -0
  25. data/spec/topological_inventory/providers/common/operations/source_spec.rb +5 -0
  26. data/spec/topological_inventory/providers/common/save_inventory/saver_spec.rb +65 -0
  27. data/spec/topological_inventory/providers/common_spec.rb +3 -0
  28. data/topological_inventory-providers-common.gemspec +7 -1
  29. metadata +104 -3
@@ -1,7 +1,7 @@
1
1
  module TopologicalInventory
2
2
  module Providers
3
3
  module Common
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.7"
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,22 @@
1
+ if ENV['CI']
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
5
+
6
+ require "bundler/setup"
7
+ require "topological_inventory/providers/common"
8
+ require "webmock/rspec"
9
+
10
+ Dir["./spec/support/**/*.rb"].each {|f| require f}
11
+
12
+ spec_path = File.dirname(__FILE__)
13
+ Dir[File.join(spec_path, "support/**/*.rb")].each { |f| require f }
14
+
15
+ RSpec.configure do |config|
16
+ # Enable flags like --only-failures and --next-failure
17
+ config.example_status_persistence_file_path = ".rspec_status"
18
+
19
+ config.expect_with :rspec do |c|
20
+ c.syntax = :expect
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module InventorySpecHelper
2
+ def self.big_inventory(size, chunk_size)
3
+ {
4
+ :collections => [
5
+ :name => SecureRandom.uuid,
6
+ :data => data_chunks(size, chunk_size)
7
+ ]
8
+ }
9
+ end
10
+
11
+ def self.data_chunks(size, chunk)
12
+ Array.new(size / chunk) { "a" * chunk }
13
+ end
14
+ end
@@ -0,0 +1,236 @@
1
+ require "topological_inventory/providers/common/operations/source"
2
+
3
+ RSpec.shared_examples "availability_check" do
4
+ let(:host_url) { 'https://cloud.redhat.com' }
5
+ let(:sources_api_path) { '/api/sources/v3.0' }
6
+ let(:sources_internal_api_path) { '/internal/v1.0' }
7
+ let(:sources_api_url) { "#{host_url}#{sources_api_path}" }
8
+
9
+ let(:external_tenant) { '11001' }
10
+ let(:identity) { {'x-rh-identity' => Base64.strict_encode64({'identity' => {'account_number' => external_tenant, 'user' => {'is_org_admin' => true}}}.to_json)} }
11
+ let(:headers) { {'Content-Type' => 'application/json'}.merge(identity) }
12
+ let(:source_id) { '123' }
13
+ let(:endpoint_id) { '234' }
14
+ let(:application_id) { '345' }
15
+ let(:authentication_id) { '345' }
16
+ let(:payload) do
17
+ {
18
+ 'params' => {
19
+ 'source_id' => source_id,
20
+ 'external_tenant' => external_tenant,
21
+ 'timestamp' => Time.now.utc
22
+ }
23
+ }
24
+ end
25
+
26
+ let(:list_endpoints_response) { "{\"data\":[{\"default\":true,\"host\":\"10.0.0.1\",\"id\":\"#{endpoint_id}\",\"path\":\"/\",\"role\":\"ansible\",\"scheme\":\"https\",\"source_id\":\"#{source_id}\",\"tenant\":\"#{external_tenant}\"}]}" }
27
+ let(:list_endpoint_authentications_response) { "{\"data\":[{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\"}]}" }
28
+ let(:list_endpoint_authentications_response_empty) { "{\"data\":[]}" }
29
+ let(:internal_api_authentication_response) { "{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\",\"password\":\"xxx\"}" }
30
+ let(:list_applications_response) { {:data => [{:id => "345", :availability_status => "available"}]}.to_json }
31
+ let(:list_applications_unavailable_response) { {:data => [{:id => "345", :availability_status => "unavailable"}]}.to_json }
32
+
33
+ subject { described_class.new(payload["params"]) }
34
+
35
+ context "when not checked recently" do
36
+ before do
37
+ allow(subject).to receive(:checked_recently?).and_return(false)
38
+ end
39
+
40
+ it "updates Source and Endpoint when available" do
41
+ # GET
42
+ stub_get(:endpoint, list_endpoints_response)
43
+ stub_get(:authentication, list_endpoint_authentications_response)
44
+ stub_get(:password, internal_api_authentication_response)
45
+ stub_get(:application, "[]")
46
+
47
+ # PATCH
48
+ source_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
49
+ endpoint_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'availability_status_error' => '', 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
50
+
51
+ stub_patch(:source, source_patch_body)
52
+ stub_patch(:endpoint, endpoint_patch_body)
53
+
54
+ # Check ---
55
+ expect(subject).to receive(:connection_check).and_return([described_class::STATUS_AVAILABLE, nil])
56
+
57
+ subject.availability_check
58
+
59
+ assert_patch(:source, source_patch_body)
60
+ assert_patch(:endpoint, endpoint_patch_body)
61
+ end
62
+
63
+ it "updates Source and Endpoint when unavailable" do
64
+ # GET
65
+ stub_get(:endpoint, list_endpoints_response)
66
+ stub_get(:authentication, list_endpoint_authentications_response)
67
+ stub_get(:password, internal_api_authentication_response)
68
+ stub_get(:application, "[]")
69
+
70
+ # PATCH
71
+ connection_error_message = "Some connection error"
72
+ source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
73
+ endpoint_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'availability_status_error' => connection_error_message, 'last_checked_at' => subject.send(:check_time)}.to_json
74
+
75
+ stub_patch(:source, source_patch_body)
76
+ stub_patch(:endpoint, endpoint_patch_body)
77
+
78
+ # Check ---
79
+ expect(subject).to receive(:connection_check).and_return([described_class::STATUS_UNAVAILABLE, connection_error_message])
80
+
81
+ subject.availability_check
82
+
83
+ assert_patch(:source, source_patch_body)
84
+ assert_patch(:endpoint, endpoint_patch_body)
85
+ end
86
+
87
+ it "updates only Source to 'unavailable' status if Endpoint not found" do
88
+ # GET
89
+ stub_get(:endpoint, '')
90
+ stub_get(:application, "[]")
91
+
92
+ # PATCH
93
+ source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
94
+ stub_patch(:source, source_patch_body)
95
+
96
+ # Check
97
+ api_client = subject.send(:api_client)
98
+ expect(api_client).not_to receive(:update_endpoint)
99
+
100
+ subject.availability_check
101
+
102
+ assert_patch(:source, source_patch_body)
103
+ end
104
+
105
+ it "updates Source and Endpoint to 'unavailable' if Authentication not found" do
106
+ # GET
107
+ stub_get(:endpoint, list_endpoints_response)
108
+ stub_get(:authentication, list_endpoint_authentications_response_empty)
109
+ stub_get(:application, "[]")
110
+
111
+ # PATCH
112
+ source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
113
+ endpoint_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'availability_status_error' => described_class::ERROR_MESSAGES[:authentication_not_found], 'last_checked_at' => subject.send(:check_time)}.to_json
114
+
115
+ stub_patch(:source, source_patch_body)
116
+ stub_patch(:endpoint, endpoint_patch_body)
117
+
118
+ # Check
119
+ expect(subject).not_to receive(:connection_check)
120
+ subject.availability_check
121
+
122
+ assert_patch(:source, source_patch_body)
123
+ assert_patch(:endpoint, endpoint_patch_body)
124
+ end
125
+ end
126
+
127
+ context "when checked recently" do
128
+ before do
129
+ allow(subject).to receive(:checked_recently?).and_return(true)
130
+ end
131
+
132
+ it "doesn't do connection check" do
133
+ expect(subject).not_to receive(:connection_check)
134
+ expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
135
+ expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
136
+
137
+ subject.availability_check
138
+ end
139
+ end
140
+
141
+ context "when there is an application" do
142
+ context "when it is available" do
143
+ it "updates the availability status to available" do
144
+ # GET
145
+ stub_get(:endpoint, "[]")
146
+ stub_get(:application, list_applications_response)
147
+ # PATCH
148
+ application_patch_body = {'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
149
+ source_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
150
+
151
+ stub_patch(:source, source_patch_body)
152
+ stub_patch(:application, application_patch_body)
153
+
154
+ # Check
155
+ expect(subject).not_to receive(:connection_check)
156
+ subject.availability_check
157
+
158
+ assert_patch(:source, source_patch_body)
159
+ assert_patch(:application, application_patch_body)
160
+ end
161
+ end
162
+
163
+ context "when it is unavailable" do
164
+ it "updates the availability status to unavailable" do
165
+ # GET
166
+ stub_get(:endpoint, "[]")
167
+ stub_get(:application, list_applications_unavailable_response)
168
+ # PATCH
169
+ application_patch_body = {'last_checked_at' => subject.send(:check_time)}.to_json
170
+ source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
171
+
172
+ stub_patch(:source, source_patch_body)
173
+ stub_patch(:application, application_patch_body)
174
+
175
+ # Check
176
+ expect(subject).not_to receive(:connection_check)
177
+ subject.availability_check
178
+
179
+ assert_patch(:source, source_patch_body)
180
+ assert_patch(:application, application_patch_body)
181
+ end
182
+ end
183
+ end
184
+
185
+ def stub_get(object_type, response)
186
+ case object_type
187
+ when :endpoint
188
+ stub_request(:get, "#{sources_api_url}/sources/#{source_id}/endpoints")
189
+ .with(:headers => headers)
190
+ .to_return(:status => 200, :body => response, :headers => {})
191
+ when :authentication
192
+ stub_request(:get, "#{sources_api_url}/endpoints/#{endpoint_id}/authentications")
193
+ .with(:headers => headers)
194
+ .to_return(:status => 200, :body => response, :headers => {})
195
+ when :password
196
+ stub_request(:get, "#{host_url}#{sources_internal_api_path}/authentications/#{authentication_id}?expose_encrypted_attribute%5B%5D=password")
197
+ .with(:headers => headers)
198
+ .to_return(:status => 200, :body => response, :headers => {})
199
+ when :application
200
+ stub_request(:get, "#{sources_api_url}/sources/#{source_id}/applications")
201
+ .with(:headers => headers)
202
+ .to_return(:status => 200, :body => response, :headers => {})
203
+ end
204
+ end
205
+
206
+ def stub_patch(object_type, data)
207
+ case object_type
208
+ when :source
209
+ stub_request(:patch, "#{sources_api_url}/sources/#{source_id}")
210
+ .with(:body => data, :headers => headers)
211
+ .to_return(:status => 200, :body => "", :headers => {})
212
+ when :endpoint
213
+ stub_request(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
214
+ .with(:body => data, :headers => headers)
215
+ .to_return(:status => 200, :body => "", :headers => {})
216
+ when :application
217
+ stub_request(:patch, "#{sources_api_url}/applications/#{application_id}")
218
+ .with(:body => data, :headers => headers)
219
+ .to_return(:status => 200, :body => "", :headers => {})
220
+ end
221
+ end
222
+
223
+ def assert_patch(object_type, data)
224
+ case object_type
225
+ when :source
226
+ expect(WebMock).to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
227
+ .with(:body => data, :headers => headers).once
228
+ when :endpoint
229
+ expect(WebMock).to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
230
+ .with(:body => data, :headers => headers).once
231
+ when :application
232
+ expect(WebMock).to have_requested(:patch, "#{sources_api_url}/applications/#{application_id}")
233
+ .with(:body => data, :headers => headers).once
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,171 @@
1
+ RSpec.describe TopologicalInventory::Providers::Common::Collector do
2
+ let(:collector) do
3
+ collector = described_class.new(source)
4
+
5
+ allow(collector).to receive(:ingress_api_client).and_return(client)
6
+ allow(collector).to receive(:logger).and_return(logger)
7
+ allow(logger).to receive(:error)
8
+
9
+ collector
10
+ end
11
+
12
+ let(:parser) { TopologicalInventory::Providers::Common::Collector::Parser.new }
13
+
14
+ let(:source) { "source_uid" }
15
+ let(:client) { double }
16
+ let(:logger) { double }
17
+ let(:refresh_state_uuid) { SecureRandom.uuid }
18
+ let(:refresh_state_part_uuid) { SecureRandom.uuid }
19
+ # based on the default, we can tell how many chunks the saver will break the payload up into
20
+ let(:max_size) { TopologicalInventory::Providers::Common::SaveInventory::Saver::KAFKA_PAYLOAD_MAX_BYTES_DEFAULT }
21
+ let(:multiplier) { 0.75 }
22
+
23
+ context "#save_inventory" do
24
+ it "does nothing with empty collections" do
25
+ parts = collector.send(:save_inventory, [], collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
26
+
27
+ expect(parts).to eq 0
28
+ end
29
+
30
+ it "saves 1 part if it fits" do
31
+ (multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
32
+
33
+ expect(inventory_size(parser.collections.values) / max_size).to eq(0)
34
+
35
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
36
+ parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
37
+ expect(parts).to eq 1
38
+ end
39
+
40
+ it "saves 2 parts if over limit with 1 collection" do
41
+ (multiplier * 2000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
42
+
43
+ expect(inventory_size(parser.collections.values) / max_size).to eq(1)
44
+
45
+ expect(client).to receive(:save_inventory_with_http_info).exactly(2).times
46
+ parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
47
+ expect(parts).to eq 2
48
+ end
49
+
50
+ it "saves 2 parts if over limit with 2 collections" do
51
+ (multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
52
+ (multiplier * 1000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
53
+
54
+ expect(inventory_size(parser.collections.values) / max_size).to eq(1)
55
+
56
+ expect(client).to receive(:save_inventory_with_http_info).exactly(2).times
57
+ parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
58
+ expect(parts).to eq 2
59
+ end
60
+
61
+ it "saves many parts" do
62
+ (multiplier * 1500).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
63
+ (multiplier * 2000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
64
+
65
+ expect(client).to receive(:save_inventory_with_http_info).exactly(4).times
66
+ parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
67
+ expect(parts).to eq 4
68
+ end
69
+
70
+ it 'raises exception when entity to save is too big' do
71
+ parser.collections.container_groups.build(:source_ref => "a" * (1_000_000 * multiplier))
72
+
73
+ expect(inventory_size(parser.collections.values) / max_size).to eq(1)
74
+ # in this case, we first save empty inventory, then the size check fails saving the rest of data
75
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
76
+
77
+ expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
78
+ raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
79
+ )
80
+ end
81
+
82
+ it 'raises exception when entity of second collection is too big' do
83
+ (multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
84
+ parser.collections.container_nodes.build(:source_ref => "a" * (1_000_000 * multiplier))
85
+
86
+ expect(inventory_size(parser.collections.values) / max_size).to eq(1)
87
+ # We save the first collection then it fails on saving the second collection
88
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
89
+
90
+ expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
91
+ raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
92
+ )
93
+ end
94
+
95
+ it 'raises exception when entity of second collection is too big then continues with smaller' do
96
+ (multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
97
+ parser.collections.container_nodes.build(:source_ref => "a" * (1_000_000 * multiplier))
98
+ (multiplier * 1000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
99
+
100
+ expect(inventory_size(parser.collections.values) / max_size).to eq(2)
101
+ # We save the first collection then it fails on saving the second collection
102
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
103
+
104
+ expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
105
+ raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
106
+ )
107
+ end
108
+ end
109
+
110
+ context "#sweep_inventory" do
111
+ it "with nil total parts" do
112
+ expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
113
+
114
+ collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, nil, [])
115
+ end
116
+
117
+ it "with empty scope " do
118
+ expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
119
+
120
+ collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, [])
121
+ end
122
+
123
+ it "with normal scope " do
124
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
125
+
126
+ collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, [:container_groups])
127
+ end
128
+
129
+ it "with normal targeted scope " do
130
+ expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
131
+
132
+ collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, {:container_groups => [{:source_ref => "a"}]})
133
+ end
134
+
135
+ it "fails with scope entity too large " do
136
+ expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
137
+
138
+ sweep_scope = {:container_groups => [{:source_ref => "a" * (1_000_002 * multiplier)}]}
139
+
140
+ expect { collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, sweep_scope) }.to(
141
+ raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
142
+ )
143
+ end
144
+
145
+ it "fails when scope is too big " do
146
+ # We should have also sweep scope chunking, that is if we'll do big targeted refresh and sweeping
147
+ expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
148
+
149
+ sweep_scope = {:container_groups => (0..1001 * multiplier).map { {:source_ref => "a" * 1_000} } }
150
+
151
+ expect { collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, sweep_scope) }.to(
152
+ raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
153
+ )
154
+ end
155
+ end
156
+
157
+ def build_inventory(collections)
158
+ TopologicalInventoryIngressApiClient::Inventory.new(
159
+ :name => collector.send(:inventory_name),
160
+ :schema => TopologicalInventoryIngressApiClient::Schema.new(:name => collector.send(:schema_name)),
161
+ :source => source,
162
+ :collections => collections,
163
+ :refresh_state_uuid => refresh_state_uuid,
164
+ :refresh_state_part_uuid => refresh_state_part_uuid,
165
+ )
166
+ end
167
+
168
+ def inventory_size(collections)
169
+ JSON.generate(build_inventory(collections).to_hash).size
170
+ end
171
+ end