logstash-filter-mutate 3.5.7 → 3.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2e5b32e8796c3dc7709a89d2e345fbd720f4c144dc90150833e357c052473b6
4
- data.tar.gz: ce9ac9d9dc6b3ea1e87e3628c6f7a3725aa4defaea47feffbbfbc966824c8113
3
+ metadata.gz: 9fd4fb3b414af0266ba7a62e6ef72b00ad1b109200d8da6b54ee7910a1ba561b
4
+ data.tar.gz: 6368b980d3d5c1b32f139bfc007341cafff738a2db055df8c3534436c836b729
5
5
  SHA512:
6
- metadata.gz: 24571cc6195435dc6edfc23cd3c4abae6c6041f77acc5749c5e944ad10f2ed71457b89bbc8c152d836855460ee3b8b0ddbb82139d4b4f1c398f2e9602005161a
7
- data.tar.gz: 2be971ad1a39c3e86a6af402c3a08710db26c86fecb7e8a28878bb87ca4e1cb7f72eb7d4290f49079af78252dd3165c17928e0f5d5da7aaf069bf11beb2476f3
6
+ metadata.gz: 6ba63632430477006fc77d5d27e3ee14c72f4e1bc5bd9557ba886c31b95b34636fd1ed7869dfc77c3a95d02355b04f0afa1e4a4c6053b715d8769c7c5fc1895b
7
+ data.tar.gz: b971f7880a0a98ee89545b5775fcde5c67adb48b624c5b6885b6a70476352950c246d1577d9e133b82b1979b89f7f1500b156a27ed5f6ca3a4fa016e327767e0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 3.5.9
2
+ - Fix `convert` to covert hexadecimal float notation and scientific notation string to float and integer [#175](https://github.com/logstash-plugins/logstash-filter-mutate/pull/175)
3
+
4
+ ## 3.5.8
5
+ - Fix "Can't modify frozen string" error when converting boolean to `string` [#171](https://github.com/logstash-plugins/logstash-filter-mutate/pull/171)
6
+
1
7
  ## 3.5.7
2
8
  - Clarify that `split` and `join` also support strings [#164](https://github.com/logstash-plugins/logstash-filter-mutate/pull/164)
3
9
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/filters/base"
3
3
  require "logstash/namespace"
4
+ require "bigdecimal"
4
5
 
5
6
  # The mutate filter allows you to perform general mutations on fields. You
6
7
  # can rename, replace, and modify fields in your events.
@@ -227,6 +228,10 @@ class LogStash::Filters::Mutate < LogStash::Filters::Base
227
228
  )
228
229
  end
229
230
  end
231
+ # fallback to old type conversion behavior prior to Logstash 8.14.0
232
+ @lenient_conversion = self.class.is_lenient_version?
233
+ # signed hex parsing support in jruby 10
234
+ @support_signed_hex = self.class.can_parse_signed_hex?
230
235
 
231
236
  @gsub_parsed = []
232
237
  @gsub.nil? or @gsub.each_slice(3) do |field, needle, replacement|
@@ -330,7 +335,8 @@ class LogStash::Filters::Mutate < LogStash::Filters::Base
330
335
  # target encoding and only change if necessary, so calling
331
336
  # valid_encoding? is redundant
332
337
  # see https://twitter.com/jordansissel/status/444613207143903232
333
- value.to_s.force_encoding(Encoding::UTF_8)
338
+ # use + since .to_s on nil/boolean returns a frozen string since ruby 2.7
339
+ (+value.to_s).force_encoding(Encoding::UTF_8)
334
340
  end
335
341
 
336
342
  def convert_boolean(value)
@@ -341,17 +347,44 @@ class LogStash::Filters::Mutate < LogStash::Filters::Base
341
347
  end
342
348
 
343
349
  def convert_integer(value)
350
+ value = value.strip.delete(',').downcase if value.is_a?(String)
351
+
344
352
  return 1 if value == true
345
353
  return 0 if value == false
346
- return value.to_i if !value.is_a?(String)
347
- value.tr(",", "").to_i
354
+ return value if value.is_a?(Integer)
355
+ return value.to_i if @lenient_conversion
356
+
357
+ if value.is_a?(String)
358
+ signed_float = parse_signed_hex_str(value)
359
+ # hex number
360
+ if signed_float
361
+ return Integer(value) if value.count(".").zero?
362
+ return Integer(signed_float)
363
+ end
364
+
365
+ # scientific notation. BigDecimal() can't parse hex string
366
+ return BigDecimal(value).to_i if value.include?("e")
367
+ # maybe a float string
368
+ return value.to_i
369
+ end
370
+
371
+ Integer(value)
348
372
  end
349
373
 
350
374
  def convert_float(value)
375
+ value = value.strip.delete(',').downcase if value.is_a?(String)
376
+
351
377
  return 1.0 if value == true
352
378
  return 0.0 if value == false
353
- value = value.delete(",") if value.kind_of?(String)
354
- value.to_f
379
+ return value if value.is_a?(Float)
380
+ return value.to_f if @lenient_conversion
381
+
382
+ if value.is_a?(String)
383
+ signed_float = parse_signed_hex_str(value)
384
+ return signed_float if signed_float
385
+ end
386
+
387
+ Float(value)
355
388
  end
356
389
 
357
390
  def convert_integer_eu(value)
@@ -373,6 +406,25 @@ class LogStash::Filters::Mutate < LogStash::Filters::Base
373
406
  value.tr(",.", ".,")
374
407
  end
375
408
 
409
+ # Parses a string to determine if it represents a signed hexadecimal number.
410
+ # If the string matches a signed hex format (eg "-0x1A"), returns the signed float value.
411
+ # JRuby Float() can parse signed hex string and uppercase hex string in version 10+,
412
+ # but not in earlier versions.
413
+ #
414
+ # @param value [String] the string to parse
415
+ # @return [Float, nil] the signed float value if hex, or nil if not a hex string
416
+ def parse_signed_hex_str(value)
417
+ return Float(value) if @support_signed_hex
418
+
419
+ if value.match?(/^[+-]?0x/i)
420
+ sign = value.start_with?('-') ? -1 : 1
421
+ unsigned = value.sub(/^[+-]/, '')
422
+ return sign * Float(unsigned)
423
+ end
424
+
425
+ nil
426
+ end
427
+
376
428
  def gsub(event)
377
429
  @gsub_parsed.each do |config|
378
430
  field = config[:field]
@@ -541,4 +593,14 @@ class LogStash::Filters::Mutate < LogStash::Filters::Base
541
593
  event.set(dest_field,LogStash::Util.deep_clone(original))
542
594
  end
543
595
  end
596
+
597
+ def self.is_lenient_version?
598
+ Gem::Version.new(LOGSTASH_VERSION) < Gem::Version.new("8.14.0")
599
+ end
600
+
601
+ def self.can_parse_signed_hex?
602
+ Float("-0x1A") == -26.0
603
+ rescue ArgumentError
604
+ false
605
+ end
544
606
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-mutate'
4
- s.version = '3.5.7'
4
+ s.version = '3.5.9'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Performs mutations on fields"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -34,7 +34,7 @@ describe 'LogStash::Filters::Mutate' do
34
34
  CONFIG
35
35
  end
36
36
 
37
- sample("message" => "hello WORLD", "lower1" => "PPQQRRSS", "lower2" => "pppqqq") do
37
+ sample({"message" => "hello WORLD", "lower1" => "PPQQRRSS", "lower2" => "pppqqq"}) do
38
38
  result = results.first
39
39
  expect(result.get("bar")).to eq('world')
40
40
  expect(result.get("lower1")).to eq("ppqqrrss")
@@ -318,7 +318,7 @@ describe LogStash::Filters::Mutate do
318
318
  convert => [ "message", "int"] #should be integer
319
319
  }
320
320
  }
321
- CONFIG
321
+ CONFIG
322
322
 
323
323
  sample "not_really_important" do
324
324
  expect {subject}.to raise_error(LogStash::ConfigurationError, /Invalid conversion type/)
@@ -428,7 +428,7 @@ describe LogStash::Filters::Mutate do
428
428
  }
429
429
  }'
430
430
 
431
- sample("unicorns" => 1234) do
431
+ sample({"unicorns" => 1234}) do
432
432
  expect(subject.get("unicorns")).to eq "1234"
433
433
  end
434
434
  end
@@ -561,25 +561,25 @@ describe LogStash::Filters::Mutate do
561
561
  end
562
562
 
563
563
  context 'when field is the true value' do
564
- sample('field' => true) do
564
+ sample({'field' => true}) do
565
565
  expect(subject.get('field')).to eq(1.0)
566
566
  end
567
567
  end
568
568
 
569
569
  context 'when field is the false value' do
570
- sample('field' => false) do
570
+ sample({'field' => false}) do
571
571
  expect(subject.get('field')).to eq(0.0)
572
572
  end
573
573
  end
574
574
 
575
575
  context 'when field is nil' do
576
- sample('field' => nil) do
576
+ sample({'field' => nil}) do
577
577
  expect(subject.get('field')).to be_nil
578
578
  end
579
579
  end
580
580
 
581
581
  context 'when field is not set' do
582
- sample('field' => nil) do
582
+ sample({'field' => nil}) do
583
583
  expect(subject.get('field')).to be_nil
584
584
  end
585
585
  end
@@ -634,25 +634,25 @@ describe LogStash::Filters::Mutate do
634
634
  end
635
635
 
636
636
  context 'when field is the true value' do
637
- sample('field' => true) do
637
+ sample({'field' => true}) do
638
638
  expect(subject.get('field')).to eq(1.0)
639
639
  end
640
640
  end
641
641
 
642
642
  context 'when field is the false value' do
643
- sample('field' => false) do
643
+ sample({'field' => false}) do
644
644
  expect(subject.get('field')).to eq(0.0)
645
645
  end
646
646
  end
647
647
 
648
648
  context 'when field is nil' do
649
- sample('field' => nil) do
649
+ sample({'field' => nil}) do
650
650
  expect(subject.get('field')).to be_nil
651
651
  end
652
652
  end
653
653
 
654
654
  context 'when field is not set' do
655
- sample('field' => nil) do
655
+ sample({'field' => nil}) do
656
656
  expect(subject.get('field')).to be_nil
657
657
  end
658
658
  end
@@ -666,7 +666,7 @@ describe LogStash::Filters::Mutate do
666
666
  }
667
667
  }'
668
668
 
669
- sample("unicorns" => "Magnificient, but extinct, animals") do
669
+ sample({"unicorns" => "Magnificient, but extinct, animals"}) do
670
670
  expect(subject.get("unicorns")).to eq "Magnificient, and common, animals"
671
671
  end
672
672
  end
@@ -679,8 +679,8 @@ describe LogStash::Filters::Mutate do
679
679
  }
680
680
  }'
681
681
 
682
- sample("unicorns" => [
683
- "Magnificient extinct animals", "Other extinct ideas" ]
682
+ sample({"unicorns" => [
683
+ "Magnificient extinct animals", "Other extinct ideas" ]}
684
684
  ) do
685
685
  expect(subject.get("unicorns")).to eq [
686
686
  "Magnificient common animals",
@@ -698,7 +698,7 @@ describe LogStash::Filters::Mutate do
698
698
  }
699
699
  }'
700
700
 
701
- sample("colors" => "One red car", "shapes" => "Four red squares") do
701
+ sample({"colors" => "One red car", "shapes" => "Four red squares"}) do
702
702
  expect(subject.get("colors")).to eq "One blue car"
703
703
  expect(subject.get("shapes")).to eq "Four red circles"
704
704
  end
@@ -712,7 +712,7 @@ describe LogStash::Filters::Mutate do
712
712
  }
713
713
  }'
714
714
 
715
- sample("colors" => "red3") do
715
+ sample({"colors" => "red3"}) do
716
716
  expect(subject.get("colors")).to eq "redblue"
717
717
  end
718
718
  end
@@ -758,7 +758,7 @@ describe LogStash::Filters::Mutate do
758
758
  }
759
759
  CONFIG
760
760
 
761
- sample("field_one" => "value", "x" => "one") do
761
+ sample({"field_one" => "value", "x" => "one"}) do
762
762
  expect(subject).to_not include("field_one")
763
763
  expect(subject).to include("destination")
764
764
  end
@@ -773,7 +773,7 @@ describe LogStash::Filters::Mutate do
773
773
  }
774
774
  CONFIG
775
775
 
776
- sample("field_one" => "value", "x" => "one") do
776
+ sample({"field_one" => "value", "x" => "one"}) do
777
777
  expect(subject).to_not include("origin")
778
778
  expect(subject).to include("field_one")
779
779
  end
@@ -934,6 +934,26 @@ describe LogStash::Filters::Mutate do
934
934
  end
935
935
  end
936
936
 
937
+ describe "convert auto-frozen values to string" do
938
+ config <<-CONFIG
939
+ filter {
940
+ mutate {
941
+ convert => {
942
+ "true_field" => "string"
943
+ "false_field" => "string"
944
+ }
945
+ }
946
+ }
947
+ CONFIG
948
+
949
+ sample({ "true_field" => true, "false_field" => false }) do
950
+ expect(subject.get("true_field")).to eq "true"
951
+ expect(subject.get("true_field")).to be_a(String)
952
+ expect(subject.get("false_field")).to eq "false"
953
+ expect(subject.get("false_field")).to be_a(String)
954
+ end
955
+ end
956
+
937
957
  #LOGSTASH-1529
938
958
  describe "gsub on a String with dynamic fields (%{}) in pattern" do
939
959
  config '
@@ -943,7 +963,7 @@ describe LogStash::Filters::Mutate do
943
963
  }
944
964
  }'
945
965
 
946
- sample("unicorns" => "Unicorns of type blue are common", "unicorn_type" => "blue") do
966
+ sample({"unicorns" => "Unicorns of type blue are common", "unicorn_type" => "blue"}) do
947
967
  expect(subject.get("unicorns")).to eq "Unicorns green are common"
948
968
  end
949
969
  end
@@ -957,7 +977,7 @@ describe LogStash::Filters::Mutate do
957
977
  }
958
978
  }'
959
979
 
960
- sample("unicorns2" => "Unicorns of type blue are common", "unicorn_color" => "blue") do
980
+ sample({"unicorns2" => "Unicorns of type blue are common", "unicorn_color" => "blue"}) do
961
981
  expect(subject.get("unicorns2")).to eq "Unicorns blue and green are common"
962
982
  end
963
983
  end
@@ -971,9 +991,9 @@ describe LogStash::Filters::Mutate do
971
991
  }
972
992
  }'
973
993
 
974
- sample("unicorns_array" => [
994
+ sample({"unicorns_array" => [
975
995
  "Unicorns of type blue are found in Alaska", "Unicorns of type blue are extinct" ],
976
- "color" => "blue"
996
+ "color" => "blue" }
977
997
  ) do
978
998
  expect(subject.get("unicorns_array")).to eq [
979
999
  "Unicorns blue and green are found in Alaska",
@@ -990,7 +1010,7 @@ describe LogStash::Filters::Mutate do
990
1010
  }
991
1011
  }'
992
1012
 
993
- sample("foo" => "bar") do
1013
+ sample({"foo" => "bar"}) do
994
1014
  expect(subject.get("list")).to eq ["bar"]
995
1015
  expect(subject.get("foo")).to eq "bar"
996
1016
  end
@@ -1004,7 +1024,7 @@ describe LogStash::Filters::Mutate do
1004
1024
  }
1005
1025
  }'
1006
1026
 
1007
- sample("foo" => "bar", "list" => []) do
1027
+ sample({"foo" => "bar", "list" => []}) do
1008
1028
  expect(subject.get("list")).to eq ["bar"]
1009
1029
  expect(subject.get("foo")).to eq "bar"
1010
1030
  end
@@ -1018,7 +1038,7 @@ describe LogStash::Filters::Mutate do
1018
1038
  }
1019
1039
  }'
1020
1040
 
1021
- sample("foo" => "bar", "list" => ["baz"]) do
1041
+ sample({"foo" => "bar", "list" => ["baz"]}) do
1022
1042
  expect(subject.get("list")).to eq ["baz", "bar"]
1023
1043
  expect(subject.get("foo")).to eq "bar"
1024
1044
  end
@@ -1032,7 +1052,7 @@ describe LogStash::Filters::Mutate do
1032
1052
  }
1033
1053
  }'
1034
1054
 
1035
- sample("foo" => ["bar"], "list" => ["baz"]) do
1055
+ sample({"foo" => ["bar"], "list" => ["baz"]}) do
1036
1056
  expect(subject.get("list")).to eq ["baz", "bar"]
1037
1057
  expect(subject.get("foo")).to eq ["bar"]
1038
1058
  end
@@ -1046,7 +1066,7 @@ describe LogStash::Filters::Mutate do
1046
1066
  }
1047
1067
  }'
1048
1068
 
1049
- sample("foo" => [], "list" => ["baz"]) do
1069
+ sample({"foo" => [], "list" => ["baz"]}) do
1050
1070
  expect(subject.get("list")).to eq ["baz"]
1051
1071
  expect(subject.get("foo")).to eq []
1052
1072
  end
@@ -1060,7 +1080,7 @@ describe LogStash::Filters::Mutate do
1060
1080
  }
1061
1081
  }'
1062
1082
 
1063
- sample("foo" => ["bar"], "list" => "baz") do
1083
+ sample({"foo" => ["bar"], "list" => "baz"}) do
1064
1084
  expect(subject.get("list")).to eq ["baz", "bar"]
1065
1085
  expect(subject.get("foo")).to eq ["bar"]
1066
1086
  end
@@ -1074,7 +1094,7 @@ describe LogStash::Filters::Mutate do
1074
1094
  }
1075
1095
  }'
1076
1096
 
1077
- sample("foo" => "bar", "list" => "baz") do
1097
+ sample({"foo" => "bar", "list" => "baz"}) do
1078
1098
  expect(subject.get("list")).to eq ["baz", "bar"]
1079
1099
  expect(subject.get("foo")).to eq "bar"
1080
1100
  end
@@ -1094,7 +1114,7 @@ describe LogStash::Filters::Mutate do
1094
1114
  }'
1095
1115
 
1096
1116
 
1097
- sample("field1" => nil, "field2" => nil, "field3" => nil, "field4" => true) do
1117
+ sample({"field1" => nil, "field2" => nil, "field3" => nil, "field4" => true}) do
1098
1118
  expect(subject.get("field1")).to eq("Hello")
1099
1119
  expect(subject.get("field2")).to eq("Bye")
1100
1120
  expect(subject.get("field3")).to eq("5")
@@ -1102,4 +1122,451 @@ describe LogStash::Filters::Mutate do
1102
1122
  end
1103
1123
  end
1104
1124
 
1125
+ describe "convert makes type conversions" do
1126
+ context "to string" do
1127
+ config <<-CONFIG
1128
+ filter {
1129
+ mutate { convert => { "a" => "string" } }
1130
+ }
1131
+ CONFIG
1132
+
1133
+ context "123" do
1134
+ # Integer
1135
+ sample({ "a" => 123 }) do
1136
+ expect(subject.get("a")).to be_a(String).and eq("123")
1137
+ end
1138
+ # Float
1139
+ sample({ "a" => Float(123.0) }) do
1140
+ expect(subject.get("a")).to be_a(String).and eq("123.0")
1141
+ end
1142
+ # String
1143
+ sample({ "a" => "123" }) do
1144
+ expect(subject.get("a")).to be_a(String).and eq("123")
1145
+ end
1146
+ sample({ "a" => "0x7b" }) do
1147
+ expect(subject.get("a")).to be_a(String).and eq("0x7b")
1148
+ end
1149
+ sample({ "a" => "123.0" }) do
1150
+ expect(subject.get("a")).to be_a(String).and eq("123.0")
1151
+ end
1152
+ sample({ "a" => "1.230000e+02" }) do
1153
+ expect(subject.get("a")).to be_a(String).and eq("1.230000e+02")
1154
+ end
1155
+ end
1156
+
1157
+ context "123.45" do
1158
+ # Float
1159
+ sample({ "a" => Float(123.45) }) do
1160
+ expect(subject.get("a")).to be_a(String).and eq("123.45")
1161
+ end
1162
+ # String
1163
+ sample({ "a" => "123.45" }) do
1164
+ expect(subject.get("a")).to be_a(String).and eq("123.45")
1165
+ end
1166
+ sample({ "a" => "1.234500e+02" }) do
1167
+ expect(subject.get("a")).to be_a(String).and eq("1.234500e+02")
1168
+ end
1169
+ sample({ "a" => "0x1.edcdp6" }) do
1170
+ expect(subject.get("a")).to be_a(String).and eq( "0x1.edcdp6" )
1171
+ end
1172
+ end
1173
+
1174
+ context "16777217" do
1175
+ # Integer
1176
+ sample({ "a" => 16777217 }) do
1177
+ expect(subject.get("a")).to be_a(String).and eq("16777217")
1178
+ end
1179
+ # Float
1180
+ sample({ "a" => 1.6777217E7 }) do
1181
+ expect(subject.get("a")).to be_a(String).and eq("16777217.0")
1182
+ end
1183
+ # String
1184
+ sample({ "a" => "16777217" }) do
1185
+ expect(subject.get("a")).to be_a(String).and eq("16777217")
1186
+ end
1187
+ sample({ "a" => "16777217.0" }) do
1188
+ expect(subject.get("a")).to be_a(String).and eq("16777217.0")
1189
+ end
1190
+ end
1191
+
1192
+ context "2147483648" do
1193
+ # Long
1194
+ sample({ "a" => 2147483648 }) do
1195
+ expect(subject.get("a")).to be_a(String).and eq("2147483648")
1196
+ end
1197
+ # Double
1198
+ sample({ "a" => 2.147483648E9 }) do
1199
+ expect(subject.get("a")).to be_a(String).and eq("2147483648.0")
1200
+ end
1201
+ # String
1202
+ sample({ "a" => "2147483648" }) do
1203
+ expect(subject.get("a")).to be_a(String).and eq("2147483648")
1204
+ end
1205
+ sample({ "a" => "2147483648.0" }) do
1206
+ expect(subject.get("a")).to be_a(String).and eq("2147483648.0")
1207
+ end
1208
+ end
1209
+
1210
+ context "9007199254740993" do
1211
+ # Long
1212
+ sample({ "a" => 9007199254740993 }) do
1213
+ expect(subject.get("a")).to be_a(String).and eq("9007199254740993")
1214
+ end
1215
+ # String
1216
+ sample({ "a" => "9007199254740993" }) do
1217
+ expect(subject.get("a")).to be_a(String).and eq("9007199254740993")
1218
+ end
1219
+ sample({ "a" => "9007199254740993.0" }) do
1220
+ expect(subject.get("a")).to be_a(String).and eq("9007199254740993.0")
1221
+ end
1222
+ end
1223
+
1224
+
1225
+ context "9223372036854775808" do
1226
+ # String
1227
+ sample({ "a" => "9223372036854775808" }) do
1228
+ expect(subject.get("a")).to be_a(String).and eq("9223372036854775808")
1229
+ end
1230
+ sample({ "a" => "9223372036854775808.0" }) do
1231
+ expect(subject.get("a")).to be_a(String).and eq("9223372036854775808.0")
1232
+ end
1233
+ end
1234
+
1235
+ context "680564693277057720000000000000000000000" do
1236
+ # String
1237
+ sample({ "a" => "680564693277057720000000000000000000000" }) do
1238
+ expect(subject.get("a")).to be_a(String).and eq("680564693277057720000000000000000000000")
1239
+ end
1240
+ sample({ "a" => "680564693277057720000000000000000000000.0" }) do
1241
+ expect(subject.get("a")).to be_a(String).and eq("680564693277057720000000000000000000000.0")
1242
+ end
1243
+ end
1244
+ end
1245
+
1246
+ context "to integer" do
1247
+ config <<-CONFIG
1248
+ filter {
1249
+ mutate { convert => { "a" => "integer" } }
1250
+ }
1251
+ CONFIG
1252
+
1253
+ context "123" do
1254
+ # Integer
1255
+ sample({ "a" => 123 }) do
1256
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1257
+ end
1258
+ # Float
1259
+ sample({ "a" => Float(123.0) }) do
1260
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1261
+ end
1262
+ # String
1263
+ sample({ "a" => "123" }) do
1264
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1265
+ end
1266
+ sample({ "a" => "0x7b" }) do
1267
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1268
+ end
1269
+ sample({ "a" => "123.0" }) do
1270
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1271
+ end
1272
+ sample({ "a" => "1.230000e+02" }) do
1273
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1274
+ end
1275
+ end
1276
+
1277
+ context "123.45" do
1278
+ # Float
1279
+ sample({ "a" => Float(123.45) }) do
1280
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1281
+ end
1282
+ # String
1283
+ sample({ "a" => "123.45" }) do
1284
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1285
+ end
1286
+ sample({ "a" => "1.234500e+02" }) do
1287
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1288
+ end
1289
+ sample({ "a" => "0x1.edcdp6" }) do
1290
+ expect(subject.get("a")).to be_a(Integer).and eq(123)
1291
+ end
1292
+ end
1293
+
1294
+ context "-123.45" do
1295
+ # Float
1296
+ sample({ "a" => Float(-123.45) }) do
1297
+ expect(subject.get("a")).to be_a(Integer).and eq(-123)
1298
+ end
1299
+ # String
1300
+ sample({ "a" => "-123.45" }) do
1301
+ expect(subject.get("a")).to be_a(Integer).and eq(-123)
1302
+ end
1303
+ sample({ "a" => "-1.234500e+02" }) do
1304
+ expect(subject.get("a")).to be_a(Integer).and eq(-123)
1305
+ end
1306
+ sample({ "a" => "-0x1.edcdp6" }) do
1307
+ expect(subject.get("a")).to be_a(Integer).and eq(-123)
1308
+ end
1309
+ end
1310
+
1311
+ context "16777217" do
1312
+ # Integer
1313
+ sample({ "a" => 16777217 }) do
1314
+ expect(subject.get("a")).to be_a(Integer).and eq(16777217)
1315
+ end
1316
+ # Double
1317
+ sample({ "a" => 1.6777217E7 }) do
1318
+ expect(subject.get("a")).to be_a(Integer).and eq(16777217)
1319
+ end
1320
+ # String
1321
+ sample({ "a" => "16777217" }) do
1322
+ expect(subject.get("a")).to be_a(Integer).and eq(16777217)
1323
+ end
1324
+ sample({ "a" => "16777217.0" }) do
1325
+ expect(subject.get("a")).to be_a(Integer).and eq(16777217)
1326
+ end
1327
+ end
1328
+
1329
+ context "2147483648" do
1330
+ # Long
1331
+ sample({ "a" => 2147483648 }) do
1332
+ expect(subject.get("a")).to be_a(Integer).and eq(2147483648)
1333
+ end
1334
+ # Double
1335
+ sample({ "a" => 2.147483648E9 }) do
1336
+ expect(subject.get("a")).to be_a(Integer).and eq(2147483648)
1337
+ end
1338
+ # String
1339
+ sample({ "a" => "2147483648" }) do
1340
+ expect(subject.get("a")).to be_a(Integer).and eq(2147483648)
1341
+ end
1342
+ sample({ "a" => "2147483648.0" }) do
1343
+ expect(subject.get("a")).to be_a(Integer).and eq(2147483648)
1344
+ end
1345
+ end
1346
+
1347
+ context "9007199254740993" do
1348
+ # Long
1349
+ sample({ "a" => 9007199254740993 }) do
1350
+ expect(subject.get("a")).to be_a(Integer).and eq(9007199254740993)
1351
+ end
1352
+ # String
1353
+ sample({ "a" => "9007199254740993" }) do
1354
+ expect(subject.get("a")).to be_a(Integer).and eq(9007199254740993)
1355
+ end
1356
+ sample({ "a" => "9007199254740993.0" }) do
1357
+ expect(subject.get("a")).to be_a(Integer).and eq(9007199254740993)
1358
+ end
1359
+ sample({ "a" => "9.007199254740993e+15" }) do
1360
+ expect(subject.get("a")).to be_a(Integer).and eq(9007199254740993)
1361
+ end
1362
+ end
1363
+
1364
+ context "9223372036854775808" do
1365
+ # String
1366
+ sample({ "a" => "9223372036854775808" }) do
1367
+ expect(subject.get("a")).to be_a(Integer).and eq(9223372036854775808)
1368
+ end
1369
+ sample({ "a" => "9223372036854775808.0" }) do
1370
+ expect(subject.get("a")).to be_a(Integer).and eq(9223372036854775808)
1371
+ end
1372
+ end
1373
+
1374
+ context "680564693277057720000000000000000000000" do
1375
+ # String
1376
+ sample({ "a" => "680564693277057720000000000000000000000" }) do
1377
+ expect(subject.get("a")).to be_a(Integer).and eq(680564693277057720000000000000000000000)
1378
+ end
1379
+ sample({ "a" => "680564693277057720000000000000000000000.0" }) do
1380
+ expect(subject.get("a")).to be_a(Integer).and eq(680564693277057720000000000000000000000)
1381
+ end
1382
+ end
1383
+
1384
+ end
1385
+
1386
+ context "to float" do
1387
+ config <<-CONFIG
1388
+ filter {
1389
+ mutate { convert => { "a" => "float" } }
1390
+ }
1391
+ CONFIG
1392
+
1393
+ context "123" do
1394
+ # Integer
1395
+ sample({ "a" => 123 }) do
1396
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1397
+ end
1398
+ # Float
1399
+ sample({ "a" => Float(123) }) do
1400
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1401
+ end
1402
+ # String
1403
+ sample({ "a" => "123" }) do
1404
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1405
+ end
1406
+ sample({ "a" => "0x7b" }) do
1407
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1408
+ end
1409
+ sample({ "a" => "123.0" }) do
1410
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1411
+ end
1412
+ sample({ "a" => "1.230000e+02" }) do
1413
+ expect(subject.get("a")).to be_a(Float).and eq(123.0)
1414
+ end
1415
+ end
1416
+
1417
+ context "123.45" do
1418
+ # Float
1419
+ sample({ "a" => Float(123.45) }) do
1420
+ expect(subject.get("a")).to be_a(Float).and eq(123.45)
1421
+ end
1422
+ # String
1423
+ sample({ "a" => "123.45" }) do
1424
+ expect(subject.get("a")).to be_a(Float).and eq(123.45)
1425
+ end
1426
+ sample({ "a" => "1.234500e+02" }) do
1427
+ expect(subject.get("a")).to be_a(Float).and eq(123.45)
1428
+ end
1429
+ sample({ "a" => "0x1.edcdp6" }) do
1430
+ expect(subject.get("a")).to be_a(Float).and eq(123.4501953125)
1431
+ end
1432
+ end
1433
+
1434
+ context "-123.45" do
1435
+ # Float
1436
+ sample({ "a" => Float(-123.45) }) do
1437
+ expect(subject.get("a")).to be_a(Float).and eq(-123.45)
1438
+ end
1439
+ # String
1440
+ sample({ "a" => "-123.45" }) do
1441
+ expect(subject.get("a")).to be_a(Float).and eq(-123.45)
1442
+ end
1443
+ sample({ "a" => "-1.234500e+02" }) do
1444
+ expect(subject.get("a")).to be_a(Float).and eq(-123.45)
1445
+ end
1446
+ sample({ "a" => "-0x1.edcdp6" }) do
1447
+ expect(subject.get("a")).to be_a(Float).and eq(-123.4501953125)
1448
+ end
1449
+ end
1450
+
1451
+ context "16777217" do
1452
+ # Integer
1453
+ sample({ "a" => 16777217 }) do
1454
+ expect(subject.get("a")).to be_a(Float).and eq(1.6777217E7)
1455
+ end
1456
+ # Float
1457
+ sample({ "a" => 1.6777217E7 }) do
1458
+ expect(subject.get("a")).to be_a(Float).and eq(1.6777217E7)
1459
+ end
1460
+ # String
1461
+ sample({ "a" => "16777217" }) do
1462
+ expect(subject.get("a")).to be_a(Float).and eq(1.6777217E7)
1463
+ end
1464
+ sample({ "a" => "16777217.0" }) do
1465
+ expect(subject.get("a")).to be_a(Float).and eq(1.6777217E7)
1466
+ end
1467
+ end
1468
+
1469
+
1470
+ context "2147483648" do
1471
+ # Long
1472
+ sample({ "a" => 2147483648 }) do
1473
+ expect(subject.get("a")).to be_a(Float).and eq(2.147483648E9)
1474
+ end
1475
+ # Double
1476
+ sample({ "a" => 2.147483648E9 }) do
1477
+ expect(subject.get("a")).to be_a(Float).and eq(2.147483648E9)
1478
+ end
1479
+ # String
1480
+ sample({ "a" => "2147483648" }) do
1481
+ expect(subject.get("a")).to be_a(Float).and eq(2.147483648E9)
1482
+ end
1483
+ sample({ "a" => "2147483648.0" }) do
1484
+ expect(subject.get("a")).to be_a(Float).and eq(2.147483648E9)
1485
+ end
1486
+ end
1487
+
1488
+ context "9007199254740993" do
1489
+ # Long
1490
+ sample({ "a" => 9007199254740993 }) do
1491
+ expect(subject.get("a")).to be_a(Float).and eq(9.007199254740992E15)
1492
+ end
1493
+ # String
1494
+ sample({ "a" => "9007199254740993" }) do
1495
+ expect(subject.get("a")).to be_a(Float).and eq(9.007199254740992E15)
1496
+ end
1497
+ sample({ "a" => "9007199254740993.0" }) do
1498
+ expect(subject.get("a")).to be_a(Float).and eq(9.007199254740992E15)
1499
+ end
1500
+ sample({ "a" => "9.007199254740993e+15" }) do
1501
+ expect(subject.get("a")).to be_a(Float).and eq(9007199254740992)
1502
+ end
1503
+ end
1504
+
1505
+ context "9223372036854775808" do
1506
+ # String
1507
+ sample({ "a" => "9223372036854775808" }) do
1508
+ expect(subject.get("a")).to be_a(Float).and eq(9.223372036854775808E18)
1509
+ end
1510
+ sample({ "a" => "9223372036854775808.0" }) do
1511
+ expect(subject.get("a")).to be_a(Float).and eq(9.223372036854775808E18)
1512
+ end
1513
+ end
1514
+
1515
+ context "680564693277057720000000000000000000000" do
1516
+ # String
1517
+ sample({ "a" => "680564693277057720000000000000000000000" }) do
1518
+ expect(subject.get("a")).to be_a(Float).and eq(6.805646932770577e+38)
1519
+ end
1520
+ sample({ "a" => "680564693277057720000000000000000000000.0" }) do
1521
+ expect(subject.get("a")).to be_a(Float).and eq(6.805646932770577e+38)
1522
+ end
1523
+ end
1524
+ end
1525
+
1526
+ end unless LogStash::Filters::Mutate.is_lenient_version? # only test type conversions in v8.14+
1527
+
1528
+ describe "parse_signed_hex_str" do
1529
+ subject { LogStash::Filters::Mutate.new({ }) }
1530
+
1531
+ context 'hexadecimal integers' do
1532
+ it 'parses positive hex integer' do
1533
+ expect(subject.send(:parse_signed_hex_str, '0x1A')).to eq(26.0)
1534
+ end
1535
+
1536
+ it 'parses negative hex integer' do
1537
+ expect(subject.send(:parse_signed_hex_str, '-0x1A')).to eq(-26.0)
1538
+ expect(subject.send(:parse_signed_hex_str,'-0xFF')).to eq(-255.0)
1539
+ end
1540
+
1541
+ it 'ignores case in hex prefix' do
1542
+ expect(subject.send(:parse_signed_hex_str,'0x1A')).to eq(26.0)
1543
+ expect(subject.send(:parse_signed_hex_str,'-0x1A')).to eq(-26.0)
1544
+ end
1545
+ end
1546
+
1547
+ context 'hexadecimal floats' do
1548
+ it 'parses positive hex float' do
1549
+ expect(subject.send(:parse_signed_hex_str,'0x1.8p+1')).to eq(3.0)
1550
+ end
1551
+
1552
+ it 'parses negative hex float' do
1553
+ expect(subject.send(:parse_signed_hex_str,'-0x1.8p+1')).to eq(-3.0)
1554
+ expect(subject.send(:parse_signed_hex_str,'-0x1.2p+2')).to eq(-4.5)
1555
+ end
1556
+ end
1557
+
1558
+ context 'non-hex strings' do
1559
+ it 'returns nil for decimal numbers' do
1560
+ expect(subject.send(:parse_signed_hex_str,'123')).to be_nil
1561
+ expect(subject.send(:parse_signed_hex_str,'-456')).to be_nil
1562
+ expect(subject.send(:parse_signed_hex_str,'1.23')).to be_nil
1563
+ end
1564
+
1565
+ it 'returns nil for random strings' do
1566
+ expect(subject.send(:parse_signed_hex_str,'abc')).to be_nil
1567
+ expect(subject.send(:parse_signed_hex_str,'0b1010')).to be_nil
1568
+ end
1569
+ end
1570
+ end unless LogStash::Filters::Mutate.is_lenient_version?
1571
+
1105
1572
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-mutate
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.7
4
+ version: 3.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-05-11 00:00:00.000000000 Z
10
+ date: 2025-10-31 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
13
+ name: logstash-core-plugin-api
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -19,9 +19,8 @@ dependencies:
19
19
  - - "<="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
- name: logstash-core-plugin-api
23
- prerelease: false
24
22
  type: :runtime
23
+ prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - ">="
@@ -31,56 +30,56 @@ dependencies:
31
30
  - !ruby/object:Gem::Version
32
31
  version: '2.99'
33
32
  - !ruby/object:Gem::Dependency
33
+ name: logstash-patterns-core
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
38
  version: '0'
39
- name: logstash-patterns-core
40
- prerelease: false
41
39
  type: :development
40
+ prerelease: false
42
41
  version_requirements: !ruby/object:Gem::Requirement
43
42
  requirements:
44
43
  - - ">="
45
44
  - !ruby/object:Gem::Version
46
45
  version: '0'
47
46
  - !ruby/object:Gem::Dependency
47
+ name: logstash-filter-grok
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: '0'
53
- name: logstash-filter-grok
54
- prerelease: false
55
53
  type: :development
54
+ prerelease: false
56
55
  version_requirements: !ruby/object:Gem::Requirement
57
56
  requirements:
58
57
  - - ">="
59
58
  - !ruby/object:Gem::Version
60
59
  version: '0'
61
60
  - !ruby/object:Gem::Dependency
61
+ name: logstash-codec-plain
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
- name: logstash-codec-plain
68
- prerelease: false
69
67
  type: :development
68
+ prerelease: false
70
69
  version_requirements: !ruby/object:Gem::Requirement
71
70
  requirements:
72
71
  - - ">="
73
72
  - !ruby/object:Gem::Version
74
73
  version: '0'
75
74
  - !ruby/object:Gem::Dependency
75
+ name: logstash-devutils
76
76
  requirement: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - ">="
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
- name: logstash-devutils
82
- prerelease: false
83
81
  type: :development
82
+ prerelease: false
84
83
  version_requirements: !ruby/object:Gem::Requirement
85
84
  requirements:
86
85
  - - ">="
@@ -111,7 +110,6 @@ licenses:
111
110
  metadata:
112
111
  logstash_plugin: 'true'
113
112
  logstash_group: filter
114
- post_install_message:
115
113
  rdoc_options: []
116
114
  require_paths:
117
115
  - lib
@@ -126,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
124
  - !ruby/object:Gem::Version
127
125
  version: '0'
128
126
  requirements: []
129
- rubygems_version: 3.2.33
130
- signing_key:
127
+ rubygems_version: 3.6.3
131
128
  specification_version: 4
132
129
  summary: Performs mutations on fields
133
130
  test_files: