logstash-codec-protobuf 1.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
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