logstash-codec-protobuf 1.3.0-java

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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +56 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/DEVELOPER.md +2 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +202 -0
  7. data/NOTICE.TXT +4 -0
  8. data/README.md +184 -0
  9. data/docs/index.asciidoc +241 -0
  10. data/google-protobuf-lib-update.md +57 -0
  11. data/lib/logstash/codecs/protobuf.rb +804 -0
  12. data/logstash-codec-protobuf.gemspec +33 -0
  13. data/spec/codecs/pb2_spec.rb +236 -0
  14. data/spec/codecs/pb3_decode_spec.rb +665 -0
  15. data/spec/codecs/pb3_encode_spec.rb +243 -0
  16. data/spec/helpers/pb2/ColourTestcase.pb.rb +35 -0
  17. data/spec/helpers/pb2/ColourTestcase.proto +24 -0
  18. data/spec/helpers/pb2/event.pb.rb +19 -0
  19. data/spec/helpers/pb2/event.proto +12 -0
  20. data/spec/helpers/pb2/header/header.pb.rb +16 -0
  21. data/spec/helpers/pb2/header/header.proto +8 -0
  22. data/spec/helpers/pb2/human.pb.rb +26 -0
  23. data/spec/helpers/pb2/unicorn.pb.rb +19 -0
  24. data/spec/helpers/pb2/unicorn_event.pb.rb +24 -0
  25. data/spec/helpers/pb3/FantasyHorse_pb.rb +48 -0
  26. data/spec/helpers/pb3/PhoneDirectory_pb.rb +37 -0
  27. data/spec/helpers/pb3/ProbeResult_pb.rb +26 -0
  28. data/spec/helpers/pb3/ResultListComposerRequest_pb.rb +25 -0
  29. data/spec/helpers/pb3/dnsmessage_pb.rb +82 -0
  30. data/spec/helpers/pb3/events_pb.rb +17 -0
  31. data/spec/helpers/pb3/header/header.proto3 +7 -0
  32. data/spec/helpers/pb3/header/header_pb.rb +12 -0
  33. data/spec/helpers/pb3/integertest_pb.rb +18 -0
  34. data/spec/helpers/pb3/messageA_pb.rb +16 -0
  35. data/spec/helpers/pb3/messageB_pb.rb +15 -0
  36. data/spec/helpers/pb3/rum2_pb.rb +87 -0
  37. data/spec/helpers/pb3/rum3_pb.rb +87 -0
  38. data/spec/helpers/pb3/rum_pb.rb +87 -0
  39. data/spec/helpers/pb3/struct_test_pb.rb +21 -0
  40. data/spec/helpers/pb3/unicorn_pb.rb +31 -0
  41. metadata +175 -0
@@ -0,0 +1,665 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/codecs/protobuf"
4
+ require "logstash/event"
5
+
6
+ require 'google/protobuf' # for protobuf3
7
+
8
+ # absolute path to the protobuf helpers directory
9
+ pb_include_path = File.expand_path(".") + "/spec/helpers"
10
+ pb_include_path.freeze
11
+
12
+ # Include the protobuf definitions so that we can reference the classes
13
+ # directly instead of looking them up in the pb decriptor pool
14
+ ['./pb3/header/*.rb', './pb3/*.rb'].each do | d |
15
+ Dir.glob(pb_include_path + d).each do |file|
16
+ require file
17
+ end
18
+ end
19
+
20
+ describe LogStash::Codecs::Protobuf do
21
+
22
+ context ".reloadable?" do
23
+ subject do
24
+ next LogStash::Codecs::Protobuf.new(
25
+ "class_name" => "Unicorn",
26
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
27
+ "protobuf_version" => 3
28
+ )
29
+ end
30
+
31
+ it "returns false" do
32
+ expect(subject.reloadable?).to be_falsey
33
+ end
34
+ end
35
+
36
+ ##################################################
37
+
38
+ context "config" do
39
+ context "using class_file and include_path" do
40
+ let(:plugin) {
41
+ LogStash::Codecs::Protobuf.new(
42
+ "class_name" => "Unicorn",
43
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
44
+ "class_file" => pb_include_path + '/pb3/unicorn_pb.rb',
45
+ "protobuf_version" => 3
46
+ )
47
+ }
48
+
49
+ it "should fail to register the plugin with ConfigurationError" do
50
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` and `class_file`/)
51
+ end # it
52
+ end
53
+
54
+ context "not using class_file or include_path" do
55
+ let(:plugin) {
56
+ LogStash::Codecs::Protobuf.new("class_name" => "Unicorn")
57
+ }
58
+
59
+ it "should fail to register the plugin with ConfigurationError" do
60
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` or `class_file`/)
61
+ end # it
62
+ end
63
+
64
+ RSpec::Expectations.configuration.on_potential_false_positives = :nothing
65
+
66
+ context "re-registering the plugin with a valid configuration" do
67
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
68
+ "class_name" => "A.MessageA",
69
+ "class_file" => [ pb_include_path + '/pb3/messageA_pb.rb' ],
70
+ "protobuf_version" => 3,
71
+ "protobuf_root_directory" => File.expand_path(File.dirname(__FILE__) + pb_include_path + '/pb3/'))
72
+ }
73
+
74
+ it "should not fail" do
75
+ expect {
76
+ # this triggers the `register()` method of the plugin multiple times
77
+ plugin.register
78
+ plugin.register
79
+ }.not_to raise_error(RuntimeError)
80
+ end # it
81
+ end
82
+
83
+ end # context config
84
+
85
+ ##################################################
86
+
87
+ context "#pb3decoder_test1" do
88
+
89
+ # Test case 1: Decode simple protobuf
90
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new(
91
+ "class_name" => "Unicorn", "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3)
92
+ }
93
+
94
+ it "should return an event from protobuf data" do
95
+
96
+ data = {:name => 'Pinkie', :age => 18, :is_pegasus => false, :favourite_numbers => [4711,23],
97
+ :fur_colour => Colour::PINK, :favourite_colours => [Colour::GREEN, Colour::BLUE]
98
+ }
99
+
100
+ unicorn_object = Unicorn.new(data)
101
+ bin = Unicorn.encode(unicorn_object)
102
+ plugin_unicorn.decode(bin) do |event|
103
+ expect(event.get("name")).to eq(data[:name] )
104
+ expect(event.get("age")).to eq(data[:age])
105
+ expect(event.get("fur_colour")).to eq("PINK")
106
+ expect(event.get("favourite_numbers")).to eq(data[:favourite_numbers])
107
+ expect(event.get("favourite_colours")).to eq(["GREEN","BLUE"])
108
+ expect(event.get("is_pegasus")).to eq(data[:is_pegasus] )
109
+ end
110
+ end # it
111
+ end # context
112
+
113
+ ##################################################
114
+
115
+ context "#pb3decoder_test2" do
116
+
117
+ # Test case 2: decode nested protobuf
118
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Unicorn",
119
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3) }
120
+
121
+ it "should return an event from protobuf data with nested classes" do
122
+ father = Unicorn.new({:name=> "Sparkle", :age => 50, :fur_colour => 3 })
123
+ data = {:name => 'Glitter', :fur_colour => Colour::GLITTER, :father => father}
124
+
125
+ unicorn_object = Unicorn.new(data)
126
+ bin = Unicorn.encode(unicorn_object)
127
+ plugin_unicorn.decode(bin) do |event|
128
+ expect(event.get("name")).to eq(data[:name] )
129
+ expect(event.get("fur_colour")).to eq("GLITTER" )
130
+ expect(event.get("father")["name"]).to eq(data[:father][:name] )
131
+ expect(event.get("father")["age"]).to eq(data[:father][:age] )
132
+ expect(event.get("father")["fur_colour"]).to eq("SILVER")
133
+
134
+ end
135
+ end # it
136
+
137
+ end # context
138
+
139
+ ##################################################
140
+
141
+ context "#pb3decoder_test3" do
142
+
143
+ # Test case 3: decode ProbeResult
144
+ let(:plugin_3) { LogStash::Codecs::Protobuf.new("class_name" => "ProbeResult",
145
+ "include_path" => [pb_include_path + '/pb3/ProbeResult_pb.rb'], "protobuf_version" => 3) }
146
+
147
+ before do
148
+ plugin_3.register
149
+ end
150
+
151
+ it "should return an event from protobuf data with nested classes" do
152
+ ping_result_data = {:status=> PingIPv4Result::Status::ERROR,
153
+ :latency => 50, :ip => "8.8.8.8", :probe_ip => "127.0.0.1", :geolocation => "New York City" }
154
+ ping_result_object = PingIPv4Result.new(ping_result_data)
155
+
156
+ probe_result_data = {:UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object}
157
+ probe_result_object = ProbeResult.new(probe_result_data)
158
+ bin = ProbeResult.encode(probe_result_object)
159
+ plugin_3.decode(bin) do |event|
160
+ expect(event.get("UUID")).to eq(probe_result_data[:UUID] )
161
+ expect(event.get("TaskPingIPv4Result")["status"]).to eq("ERROR")
162
+ expect(event.get("TaskPingIPv4Result")["latency"]).to eq(ping_result_data[:latency] )
163
+ expect(event.get("TaskPingIPv4Result")["ip"]).to eq(ping_result_data[:ip] )
164
+ expect(event.get("TaskPingIPv4Result")["probe_ip"]).to eq(ping_result_data[:probe_ip] )
165
+ expect(event.get("TaskPingIPv4Result")["geolocation"]).to eq(ping_result_data[:geolocation] )
166
+ end
167
+ end # it
168
+ end # context #pb3decoder_test3
169
+
170
+ ##################################################
171
+
172
+ context "#pb3decoder_test4" do
173
+
174
+ # Test case 4: decode PBDNSMessage
175
+ let(:plugin_4) { LogStash::Codecs::Protobuf.new("class_name" => "PBDNSMessage",
176
+ "include_path" => [pb_include_path + '/pb3/dnsmessage_pb.rb'], "protobuf_version" => 3) }
177
+
178
+ before do
179
+ plugin_4.register
180
+ end
181
+
182
+ it "should return an event from protobuf data with nested classes" do
183
+ dns_question_data = {:qName => "Foo", :qType => 12345, :qClass => 67890 }
184
+
185
+ dns_response_data = {:rcode => 12345, :appliedPolicy => "baz", :tags => ["a","b","c"],
186
+ :queryTimeSec => 123, :queryTimeUsec => 456,
187
+ :appliedPolicyType => PBDNSMessage::PolicyType::NSIP}
188
+
189
+ dns_rr_data = [
190
+ {:name => "abc", :type => 9000, :class => 8000, :ttl => 20, :rdata => "300"},
191
+ {:name => "def", :type => 19000, :class => 18000, :ttl => 120, :rdata => "1300"}
192
+ ]
193
+ dns_response_data[:rrs] = dns_rr_data.map { | d | d = PBDNSMessage::DNSResponse::DNSRR.new(d) }
194
+
195
+ pbdns_message_data = {
196
+ # :UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object
197
+ :type => PBDNSMessage::Type::DNSIncomingResponseType,
198
+ :messageId => "15",
199
+ :serverIdentity => "16",
200
+ :socketFamily => PBDNSMessage::SocketFamily::INET6,
201
+ :socketProtocol => PBDNSMessage::SocketProtocol::TCP,
202
+ :from => "17",
203
+ :to => "18",
204
+ :inBytes => 70000,
205
+ :timeSec => 80000,
206
+ :timeUsec => 90000,
207
+ :id => 20000,
208
+ :question => PBDNSMessage::DNSQuestion.new(dns_question_data),
209
+ :response => PBDNSMessage::DNSResponse.new(dns_response_data),
210
+ :originalRequestorSubnet => "19",
211
+ :requestorId => "Bar",
212
+ :initialRequestId => "20",
213
+ :deviceId => "21",
214
+ }
215
+ pbdns_message_object = PBDNSMessage.new(pbdns_message_data)
216
+ bin = PBDNSMessage.encode(pbdns_message_object)
217
+ plugin_4.decode(bin) do |event|
218
+ ['messageId', 'serverIdentity','from','to','inBytes','timeUsec','timeSec','id', 'originalRequestorSubnet', 'requestorId' ,'initialRequestId','deviceIdf'].each { |n|
219
+ expect(event.get(n)).to eq(pbdns_message_data[n.to_sym] ) }
220
+
221
+ # enum test:
222
+ expect(event.get("type")).to eq("DNSIncomingResponseType" )
223
+ expect(event.get("socketFamily")).to eq("INET6" )
224
+ expect(event.get("socketProtocol")).to eq("TCP" )
225
+
226
+ expect(event.get("question")["qName"]).to eq(dns_question_data[:qName] )
227
+ expect(event.get("question")["qType"]).to eq(dns_question_data[:qType] )
228
+ expect(event.get("question")["qClass"]).to eq(dns_question_data[:qClass] )
229
+
230
+ ['rcode', 'appliedPolicy','tags','queryTimeSec','queryTimeUsec'].each { |n| expect(event.get('response')[n]).to eq(dns_response_data[n.to_sym] ) }
231
+ expect(event.get("response")['appliedPolicyType']).to eq("NSIP" )
232
+
233
+ dns_rr_data.each_with_index { | data, index |
234
+ found = event.get("response")['rrs'][index]
235
+ ['name', 'type','class','ttl','rdata'].each { |n| expect(found[n]).to eq(data[n.to_sym]) }
236
+ }
237
+
238
+ end
239
+ end # it
240
+
241
+ end # context pb3decoder_test4
242
+
243
+ ##################################################
244
+
245
+ context "#pb3decoder_test5" do
246
+
247
+ # Test case 5: decode test case for github issue 17
248
+ let(:plugin_5) {
249
+ LogStash::Codecs::Protobuf.new("class_name" => "com.foo.bar.IntegerTestMessage",
250
+ "include_path" => [pb_include_path + '/pb3/integertest_pb.rb'], "protobuf_version" => 3)
251
+ }
252
+
253
+ before do
254
+ plugin_5.register
255
+ end
256
+
257
+ it "should return an event from protobuf data with nested classes" do
258
+ integertest_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.foo.bar.IntegerTestMessage").msgclass
259
+ integertest_object = integertest_class.new({:response_time => 500})
260
+ bin = integertest_class.encode(integertest_object)
261
+ plugin_5.decode(bin) do |event|
262
+ expect(event.get("response_time")).to eq(500)
263
+ end
264
+ end # it
265
+
266
+ end # context pb3decoder_test5
267
+
268
+ ##################################################
269
+
270
+ context "#pb3decoder_test6" do
271
+
272
+ let(:execution_context) { double("execution_context")}
273
+ let(:pipeline_id) {rand(36**8).to_s(36)}
274
+
275
+ # Test case 6: decode a message automatically loading the dependencies
276
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
277
+ "class_name" => "A.MessageA",
278
+ "class_file" => [ 'messageA_pb.rb' ],
279
+ "protobuf_version" => 3,
280
+ "protobuf_root_directory" => pb_include_path + '/pb3/')
281
+ }
282
+
283
+ before do
284
+ allow(plugin).to receive(:execution_context).and_return(execution_context)
285
+ allow(execution_context).to receive(:pipeline_id).and_return(pipeline_id)
286
+
287
+ # this is normally done on the input plugins. we "mock" it here to avoid
288
+ # instantiating a dummy input plugin. See
289
+ # https://github.com/ph/logstash/blob/37551a89b8137c1dc6fa4fbd992584c363a36065/logstash-core/lib/logstash/inputs/base.rb#L108
290
+ plugin.execution_context = execution_context
291
+ end
292
+
293
+ it "should return an event from protobuf data" do
294
+ header_data = {:name => {'a' => 'b'}}
295
+ header_object = Header.new(header_data)
296
+
297
+ message_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.MessageA").msgclass
298
+ data = {:name => "Test name", :header => header_object}
299
+
300
+ message_object = message_class.new(data)
301
+ bin = message_class.encode(message_object)
302
+ plugin.decode(bin) do |event|
303
+ expect(event.get("name")).to eq(data[:name] )
304
+ expect(event.get("header")['name']).to eq(header_data[:name])
305
+ end
306
+ end # it
307
+ end # context
308
+
309
+ ##################################################
310
+
311
+ context "#pb3decoder_test7" do
312
+
313
+ # Test case 6: decode test case for github issue 17
314
+ let(:plugin_7) { LogStash::Codecs::Protobuf.new("class_name" => "RepeatedEvents",
315
+ "include_path" => [pb_include_path + '/pb3/events_pb.rb'], "protobuf_version" => 3) }
316
+ before do
317
+ plugin_7.register
318
+ end
319
+
320
+ it "should return an event from protobuf data with repeated top level objects" do
321
+ test_a = RepeatedEvent.new({:id => "1", :msg => "a"})
322
+ test_b = RepeatedEvent.new({:id => "2", :msg => "b"})
323
+ test_c = RepeatedEvent.new({:id => "3", :msg => "c"})
324
+ event_obj = RepeatedEvents.new({:repeated_events=>[test_a, test_b, test_c]})
325
+ bin = RepeatedEvents.encode(event_obj)
326
+ plugin_7.decode(bin) do |event|
327
+ expect(event.get("repeated_events").size ).to eq(3)
328
+ expect(event.get("repeated_events")[0]["id"]).to eq("1")
329
+ expect(event.get("repeated_events")[2]["id"]).to eq("3")
330
+ expect(event.get("repeated_events")[0]["msg"]).to eq("a")
331
+ expect(event.get("repeated_events")[2]["msg"]).to eq("c")
332
+ end
333
+ end # it
334
+
335
+ end # context pb3decoder_test7
336
+
337
+ ##################################################
338
+
339
+ context "#pb3decoder_test8a" do
340
+
341
+ let(:plugin_8a) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
342
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
343
+ before do
344
+ plugin_8a.register
345
+ end
346
+
347
+ it "should add meta information on oneof fields" do
348
+ pegasus_data = {:wings_length => 100}
349
+ horsey = FantasyPegasus.new(pegasus_data)
350
+
351
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
352
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
353
+ tail = FantasyHorseTail.new(tail_data)
354
+
355
+ data = {:name=>"Reinhold", :pegasus => horsey, :tail => tail}
356
+ pb_obj = FantasyHorse.new(data)
357
+ bin = FantasyHorse.encode(pb_obj)
358
+ plugin_8a.decode(bin) do |event|
359
+
360
+ expect(event.get("name")).to eq(data[:name])
361
+ expect(event.get("pegasus")["wings_length"]).to eq(pegasus_data[:wings_length])
362
+ expect(event.get("tail")['tail_length']).to eq(tail_data[:tail_length])
363
+ expect(event.get("tail")['braided']['braiding_style']).to eq(braid_data[:braiding_style])
364
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"]).to eq("pegasus")
365
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"]).to eq("braided")
366
+
367
+ end
368
+ end # it
369
+
370
+ end # context pb3decoder_test8a
371
+
372
+ ##################################################
373
+
374
+ context "#pb3decoder_test8b" do
375
+
376
+ # same test as 8a just with different one_of options selected
377
+
378
+ let(:plugin_8b) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
379
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => false) }
380
+ before do
381
+ plugin_8b.register
382
+ end
383
+
384
+ it "should not add meta information on oneof fields" do
385
+ pegasus_data = {:wings_length => 100}
386
+ horsey = FantasyPegasus.new(pegasus_data)
387
+
388
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
389
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
390
+ tail = FantasyHorseTail.new(tail_data)
391
+
392
+ data = {:name=>"Winfried", :pegasus => horsey, :tail => tail}
393
+ pb_obj = FantasyHorse.new(data)
394
+ bin = FantasyHorse.encode(pb_obj)
395
+ plugin_8b.decode(bin) do |event|
396
+ expect(event.get("name")).to eq(data[:name])
397
+ expect(event.get("pegasus")["wings_length"]).to eq(pegasus_data[:wings_length])
398
+ expect(event.get("tail")['tail_length']).to eq(tail_data[:tail_length])
399
+ expect(event.get("tail")['braided']['braiding_style']).to eq(braid_data[:braiding_style])
400
+ expect(event.get("@metadata")["pb_oneof"]).to be_nil
401
+
402
+ end
403
+ end # it
404
+
405
+ end # context pb3decoder_test8b
406
+
407
+ ##################################################
408
+
409
+ context "#pb3decoder_test8c" do
410
+
411
+ # activate the meta info for the one-ofs
412
+
413
+ let(:plugin_8c) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
414
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
415
+ before do
416
+ plugin_8c.register
417
+ end
418
+
419
+ it "should add meta information on oneof fields" do
420
+ unicorn_data = {:horn_length => 30}
421
+ horsey = FantasyUnicorn.new(unicorn_data)
422
+
423
+ natural_data = {:wavyness => "B"}
424
+ tail_data = {:natural => NaturalHorseTail.new(natural_data) }
425
+ tail = FantasyHorseTail.new(tail_data)
426
+
427
+ data = {:name=>"Hubert", :unicorn => horsey, :tail => tail}
428
+ pb_obj = FantasyHorse.new(data)
429
+ bin = FantasyHorse.encode(pb_obj)
430
+ plugin_8c.decode(bin) do |event|
431
+ expect(event.get("name")).to eq(data[:name])
432
+ expect(event.get("unicorn")["horn_length"]).to eq(unicorn_data[:horn_length])
433
+ # TODO fix: the codec should not set default values for fields which were not set by the producer
434
+ # TODO activate after fix: expect(event.get("unicorn")["horn_colour"]).to be_nil
435
+ # TODO activate after fix: expect(event.get("tail")['tail_length']).to be_nil
436
+ expect(event.get("tail")['natural']['wavyness']).to eq(natural_data[:wavyness])
437
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"]).to eq("unicorn")
438
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"]).to eq("natural")
439
+ end
440
+ end # it
441
+
442
+ end # context pb3decoder_test8c
443
+
444
+ ##################################################
445
+
446
+ context "#pb3decoder_test9a" do
447
+
448
+ let(:plugin_9) { LogStash::Codecs::Protobuf.new("class_name" => "messages.SendJsonRequest", "class_file" => 'pb3/struct_test_pb.rb',
449
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => false) }
450
+ before do
451
+ plugin_9.register
452
+ end
453
+
454
+ it "should decode a message with an embedded struct" do
455
+ # nested struct field
456
+ struct = Google::Protobuf::Struct.new(fields: {"field_a" => {:string_value => "value_a"}},)
457
+ data = {:UserID=>"123-456", :Details => struct}
458
+ pb_obj = Messages::SendJsonRequest.new(data)
459
+ bin = Messages::SendJsonRequest.encode(pb_obj)
460
+
461
+ plugin_9.decode(bin) do |event|
462
+ expect(event.get("@metadata")["pb_oneof"]).to be_nil
463
+ expect(event.get("UserID")).to eq(data[:UserID])
464
+ expect(event.get("Details")).to eq({"field_a"=>"value_a"})
465
+ end
466
+ end # it
467
+ end # context pb3decoder_test9a
468
+
469
+ context "#pb3decoder_test9b" do # same as 9a but with one-of metainfo activated
470
+
471
+ let(:plugin_9) { LogStash::Codecs::Protobuf.new("class_name" => "messages.SendJsonRequest", "class_file" => 'pb3/struct_test_pb.rb',
472
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true)}
473
+ before do
474
+ plugin_9.register
475
+ end
476
+
477
+ it "should decode a message with an embedded struct" do
478
+ # nested struct field
479
+ details = Google::Protobuf::Struct.new(
480
+ fields: {"field_a" => {:string_value => "value_a"}},
481
+ )
482
+ data = {:UserID=>"123-456", :Details => details}
483
+ pb_obj = Messages::SendJsonRequest.new(data)
484
+ bin = Messages::SendJsonRequest.encode(pb_obj)
485
+
486
+ plugin_9.decode(bin) do |event|
487
+ expect(event.get("@metadata")["pb_oneof"]).to eq({})
488
+ expect(event.get("UserID")).to eq(data[:UserID])
489
+ expect(event.get("Details")).to eq({"field_a"=>"value_a"})
490
+ end
491
+ end # it
492
+ end # context pb3decoder_test9b
493
+
494
+ ##################################################
495
+
496
+ context "#pb3decoder_test10a" do
497
+
498
+ let(:plugin_10) { LogStash::Codecs::Protobuf.new("class_name" => "ProtoResultListCompositionCriteria", "class_file" => 'pb3/ResultListComposerRequest_pb.rb',
499
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
500
+ before do
501
+ plugin_10.register
502
+ end
503
+
504
+ it "should have only one option set for a double-choice oneOf" do
505
+ input_criterion = {:sort_criterion => "descending", :top_accommodation_id => 4711}
506
+ pb_obj = ProtoResultListCompositionCriteria.new(input_criterion)
507
+
508
+ bin = ProtoResultListCompositionCriteria.encode(pb_obj)
509
+ plugin_10.decode(bin) do |event|
510
+ expect(event.get("sort_criterion")).to eq(input_criterion[:sort_criterion])
511
+ expect(event.get("top_accommodation_id")).to eq(input_criterion[:top_accommodation_id])
512
+ expect(event.get("recommend_similar_accommodation_id")).to be_nil
513
+ expect(event.get("@metadata")["pb_oneof"]['accommodation_id']).to eq("top_accommodation_id")
514
+ end
515
+ end # it
516
+ end # context pb3decoder_test10a
517
+
518
+
519
+ context "#pb3decoder_test10b" do
520
+
521
+ # same as 10a but now as a nested field and with a value that equals the default
522
+
523
+ let(:plugin_10) { LogStash::Codecs::Protobuf.new("class_name" => "ProtoResultListComposerRequest", "class_file" => 'pb3/ResultListComposerRequest_pb.rb',
524
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
525
+ before do
526
+ plugin_10.register
527
+ end
528
+
529
+ it "should have only one option set for a nested double-choice oneOf" do
530
+ input_criterion = {:sort_criterion => "descending", :top_accommodation_id => 0}
531
+ input_resultlist = {:metadata => [], :page_number => 3, :results_per_page => 100,
532
+ :result_list_composition_criteria => ProtoResultListCompositionCriteria.new(input_criterion)}
533
+ pb_obj = ProtoResultListComposerRequest.new(input_resultlist)
534
+
535
+ bin = ProtoResultListComposerRequest.encode(pb_obj)
536
+ plugin_10.decode(bin) do |event|
537
+ expect(event.get("@metadata")["pb_oneof"]['result_list_composition_criteria']['accommodation_id']).to eq('top_accommodation_id')
538
+ expect(event.get("page_number")).to eq(input_resultlist[:page_number])
539
+ expect(event.get("results_per_page")).to eq(input_resultlist[:results_per_page])
540
+ expect(event.get("metadata")).to eq(input_resultlist[:metadata])
541
+ expect(event.get("result_list_composition_criteria")["sort_criterion"]).to eq(input_criterion[:sort_criterion])
542
+ expect(event.get("result_list_composition_criteria")["top_accommodation_id"]).to eq(input_criterion[:top_accommodation_id])
543
+ expect(event.get("result_list_composition_criteria")["recommend_similar_accommodation_id"]).to be_nil
544
+ end
545
+ end # it
546
+ end # context pb3decoder_test10b
547
+
548
+ ##################################################
549
+
550
+ context "#pb3decoder_test11" do
551
+
552
+ # XOR test for one-of but this time with objects instead of scalars, also triple-option of which only 1 must be set.
553
+
554
+ let(:plugin_11) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
555
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => false) }
556
+ before do
557
+ plugin_11.register
558
+ end
559
+
560
+ it "should have only one option set for a triple-choice oneOf" do
561
+ pegasus_data = {:wings_length => 200}
562
+ horsey = FantasyPegasus.new(pegasus_data)
563
+
564
+ braid_data = {:braid_thickness => 3, :braiding_style => "french"}
565
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
566
+ tail = FantasyHorseTail.new(tail_data)
567
+
568
+ data = {:name=>"Reinhold", :pegasus => horsey, :tail => tail}
569
+ pb_obj = FantasyHorse.new(data)
570
+ bin = FantasyHorse.encode(pb_obj)
571
+ plugin_11.decode(bin) do |event|
572
+ expect(event.get("name")).to eq(data[:name])
573
+ expect(event.get("pegasus")["wings_length"]).to eq(pegasus_data[:wings_length])
574
+ expect(event.get("tail")['tail_length']).to eq(tail_data[:tail_length])
575
+ expect(event.get("tail")['braided']['braiding_style']).to eq(braid_data[:braiding_style])
576
+ expect(event.get("tail")['natural']).to be_nil
577
+ expect(event.get("tail")['short']).to be_nil
578
+ expect(event.get("tail")['hair_type']).to be_nil
579
+ expect(event.get("@metadata")["pb_oneof"]).to be_nil
580
+ end
581
+ end # it
582
+ end # context pb3decoder_test11
583
+
584
+ ##################################################
585
+
586
+ context "#pb3decoder_test12" do
587
+ # One-of metadata with nested class names. Class lookup in the pb descriptor pool has previously been an issue.
588
+ let(:plugin_12) { LogStash::Codecs::Protobuf.new("class_name" => "company.communication.directories.PhoneDirectory", "class_file" => 'pb3/PhoneDirectory_pb.rb',
589
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
590
+ before do
591
+ plugin_12.register
592
+ end
593
+
594
+ it "should do one-of meta info lookup for nested classes" do
595
+ contacts = []
596
+ hans = {:name => "Horst Test", :address => "Test street 12, 90210 Test hills", :prefered_email => "horst@test.com"}
597
+ contacts << Company::Communication::Directories::Contact.new(hans)
598
+ jane = {:name => "Jane Trial", :address => "Test street 13, 90210 Test hills", :prefered_phone => 1234567}
599
+ contacts << Company::Communication::Directories::Contact.new(jane)
600
+ kimmy = {:name => "Kimmy Experiment", :address => "Test street 14, 90210 Test hills", :prefered_fax => 666777888}
601
+ contacts << Company::Communication::Directories::Contact.new(kimmy)
602
+
603
+ data = {:last_updated_timestamp=>1900000000, :internal => true, :contacts => contacts}
604
+ pb_obj = Company::Communication::Directories::PhoneDirectory.new(data)
605
+ bin = Company::Communication::Directories::PhoneDirectory.encode(pb_obj)
606
+
607
+ plugin_12.decode(bin) do |event|
608
+
609
+ expect(event.get("internal")).to eq(data[:internal])
610
+ expect(event.get("external")).to be_nil
611
+ expect(event.get("@metadata")['pb_oneof']["scope"]).to eq('internal')
612
+
613
+ expect(event.get("contacts")[0]["name"]).to eq(hans[:name])
614
+ expect(event.get("contacts")[0]["address"]).to eq(hans[:address])
615
+ expect(event.get("contacts")[0]["prefered_email"]).to eq(hans[:prefered_email])
616
+ expect(event.get("contacts")[0]["prefered_fax"]).to be_nil
617
+ expect(event.get("contacts")[0]["prefered_phone"]).to be_nil
618
+ expect(event.get("@metadata")['pb_oneof']["contacts"]).not_to be_nil
619
+ expect(event.get("@metadata")['pb_oneof']["contacts"]).to be_a(Array)
620
+ expect(event.get("@metadata")['pb_oneof']["contacts"].length()).to eq(3)
621
+ expect(event.get("@metadata")['pb_oneof']["contacts"][0]['prefered_contact']).to eq('prefered_email')
622
+
623
+ expect(event.get("contacts")[1]["name"]).to eq(jane[:name])
624
+ expect(event.get("contacts")[1]["address"]).to eq(jane[:address])
625
+ expect(event.get("contacts")[1]["prefered_phone"]).to eq(jane[:prefered_phone])
626
+ expect(event.get("contacts")[1]["prefered_fax"]).to be_nil
627
+ expect(event.get("contacts")[1]["prefered_email"]).to be_nil
628
+ expect(event.get("@metadata")['pb_oneof']["contacts"][1]['prefered_contact']).to eq('prefered_phone')
629
+
630
+ expect(event.get("contacts")[2]["name"]).to eq(kimmy[:name])
631
+ expect(event.get("contacts")[2]["address"]).to eq(kimmy[:address])
632
+ expect(event.get("contacts")[2]["prefered_fax"]).to eq(kimmy[:prefered_fax])
633
+ expect(event.get("contacts")[2]["prefered_email"]).to be_nil
634
+ expect(event.get("contacts")[2]["prefered_phone"]).to be_nil
635
+ expect(event.get("@metadata")['pb_oneof']["contacts"][2]['prefered_contact']).to eq('prefered_fax')
636
+
637
+ end
638
+ end # it
639
+ end # context pb3decoder_test12
640
+
641
+ ##################################################
642
+
643
+ context "#pb3decoder_test13" do
644
+ let(:plugin_13) { LogStash::Codecs::Protobuf.new(
645
+ "class_name" => "B.MessageB",
646
+ "class_file" => [ pb_include_path + '/pb3/messageB_pb.rb' ],
647
+ "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true,
648
+ "protobuf_root_directory" => pb_include_path)
649
+ }
650
+ before do
651
+ plugin_13.register
652
+ end
653
+
654
+ it "should find the class name in the protobuf descriptor pool" do
655
+ data = {:name => "test"}
656
+ pb_obj = B::MessageB.new(data)
657
+ result = plugin_13.pb3_class_for_name(pb_obj)
658
+ expect {result.not_to be_nil}
659
+ end # it
660
+
661
+ end # context pb3decoder_test13
662
+
663
+ ##################################################
664
+
665
+ end # describe